Swift is seeing continuous improvements over the last weeks, including a new bunch of changes that are backed by interesting proposals.

Interested in sponsoring Swift Weekly Brief? Learn more here.

Starter tasks

  • SR-11619 [Compiler] Spurious error “anonymous closure argument not contained in a closure” in #if false
  • SR-11636 [Compiler] Accessing covariant Self from stored property initializer in extension segfaults
  • TF-935 [Autodiff] Add -enable-experimental-forward-mode-differentiation flag

Podcasts

John Sundell and Josh Shaffer discuss SwiftUI, including the Swift features that power it.

News and community

Swift 5.1.1 was released.

Pavel Yaskevich wrote a blog post about the architectural improvements they’ve been making to get better type error messages, and goes into detail as to how this works under the hood.

Sahin Yort added initial support for SwiftPM to Bazel!

With the release of async-http-client 1.0.0, there now is a stable release!

Along async-http-client, SwiftNIO shipped version 2.9.0.

Alex Lorenz announced llvm-project, a monorepository for all LLVM-related Swift projects.

Ian Partridge wrote a blog post on doing Swift development in Docker using Visual Studio Code Remote.

Commits and pull requests

David Smith merged a pull request improving the performance for decoding data into ASCII… by a factor of 500! 🤯

Accepted proposals

SE-0263: Add a String Initializer with Access to Uninitialized Storage was accepted.

The proposal has been accepted.

Well, that’s as clear as it gets. :)

SE-0266: Synthesized Comparable conformance for enum types was accepted.

The proposal was received positively, and removes boilerplate code for many common cases.

Concern was raised by some reviewers regarding the implicit reliance on source ordering to synthesize the conformance. The core team feels this behavior is justified based on several factors:

  • other features currently expose source ordering for enums (CaseIterable order and rawValue assignment).
  • < implementations on enums can easily be written in a way where adding new cases may not break compilation; comparable conformance must always be considered when adding cases even today.
  • conforming a type to a protocol requires an understanding of what that conformance means and how it is being fulfilled. “It compiles” is never enough.

Much of the review was occupied with discussion of adding support for structs and raw-representable enums. The core team would welcome further pitches weighing the pros and cons of these additions.

Returned proposals

SE-0264: Standard Library Preview Package was returned for revision.

The review for SE-0264 has concluded. We had a uniformly positive response to the idea of previewing standard library content, but many people had questions and concerns about how the process and versioning was supposed to work. Then John McCall advanced the idea of having separately-versioned packages for each proposal. Several people were favorably disposed, and we didn’t hear any objections from anyone including the proposers. The core team is, by and large, persuaded that this approach has many advantages and would like to see a revised proposal that includes it.

Thanks to all for your engagement, and for making this process work. Even when a review goes back for revision, we are still making progress, together, on the evolution of Swift.

The last sentence also being a good reminder that any input into the evolution process is greatly appreciated.

Proposals in review

SE-0267: where clauses on contextually generic declarations is under review.

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.

SE-0268: Refine didSet Semantics is under review.

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.

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.

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:

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..<items.count {
      items[index] = index + 1
    }
  }
}

var container = Container()
container.update()

This will create 100 copies of the array to provide the oldValue, even though they’re not used at all.

Swift Forums

Joe Groff shared some thoughts on improving the representation of polymorphic interfaces in SIL with “substituted function types”.

Swift Intermediate Language (SIL) has a lot of incidental complexity arising from representational issues in how we handle the types of polymorphically-callable functions such as protocol witnesses, class methods, and closures. Here is a proposed design for substituted function types, which tries to address some of these issues.

To implement a polymorphic interface, such as a protocol method or class method, the different implementations have to share a common generic machine-level calling convention, even though each implementation has a different concrete function type derived from the specific parent type it provides an implementation for.

Nate Cook pitched a proposal to add RangeSet and related Collection operations.

We can use a range to address a single range of consecutive elements in a collection, but the standard library doesn’t currently provide a way to access discontiguous elements. This proposes the addition of a RangeSet type that can store the location of any number of collections indices, along with collection operations that let us use a range set to access or modify the collection. In addition, because these operations depend on mutable collection algorithms that we’ve long wanted in the standard library, this proposal includes those too.

Ted Kremenek shared some thoughts on evaluating adding Automatic Differentiation to Swift.

One of the efforts of the Swift for TensorFlow project has been to explore adding features like Automatic Differentiation to the Swift language. This is a powerful capability that can significantly enrich Swift’s potential as a programming language for scientific computing, numerics, and machine learning.

Speaking on behalf of the Swift Core Team, we are interested in evaluating incorporating this capability directly into the Swift language.

[..]

To summarize:

  • An implementation of Automatic differentiation will be added to the compiler, guarded under a flag (or flags) to indicate it is an experimental feature. This will be done directly on master.

Once implementations are ready, each component of the feature will go through the Swift Evolution process. Until that time, the experimental feature will not be included in official Swift releases.

Jordan Rose pitched a proposal allowing backwards-deployable conformances.

Let’s say it’s suddenly vitally important that Int conforms to Collection.

extension Int: Collection {
  public var startIndex: Int { 0 }
  public var endIndex: Int { self.bitWidth }
  public subscript(index: Int) -> Int { (self >> index) & 1 }
}

What’s the problem? Well, to start, none of these accessors exist in iOS 13, which means that if someone tries to use this new conformance in iOS, their app will just crash when running on iOS 13. That’s a limitation of shipping Swift as part of the OS.

There are two ways to resolve this today: @available with a set of minimum OS versions, or @_alwaysEmitIntoClient. But that still doesn’t stop someone from writing this code…

let someInt = 42
myArray.append(contentsOf: someInt)

…and trying to run that on iOS 13, where the conformance doesn’t exist. If the append(contentsOf:) is not optimized away, this will cause problems.

In theory, we have the same two options for how to deal with this: restrict the use of the conformance with availability, or emit the conformance into client binaries. I’ll discuss each of those in turn and then a secret third option that I think is the best choice.

You can read the full proposal pitch here.

Lantua asked for clarification on source compatability, and Brent Royal-Gordon gave a great answer:

There are two kinds of Swift releases:

  • “Non-breaking release”: Language changes that would break any existing Swift code are not permitted. Any code that was valid before should continue to build and function the same way.

  • “Source-breaking release”: Some language changes may affect existing, valid code, either making it invalid or changing its behavior. A source-breaking release will add a new version to the compiler’s -swift-version flag; specifying the previous version will cause it to emulate the old compiler’s behavior in situations where it’s changed, and usually emit deprecation warnings telling the user what to update for the new version.*

However, things are not quite as black-and-white as this. A non-source-breaking release may sometimes include very small changes—bug fixes, new overloads—that could theoretically break existing code, as long as we believe the breakage will be very small or nonexistent in practice and the benefit is worth the risk. This is very much a judgement call and the core team is typically involved with these decisions.

You can read the full answer here.

Frederick Kellison-Linn pitched a proposal to add suppport for instance-level polymorphism.

I’ve seen some similar ideas discussed on the forums (notably Polymorphic methods in enums) which are focused around the idea of improving the experience of attaching case-specific behavior to enums without having a totally fragmented implementation. I encountered this issue yet again yesterday and wanted to solicit feedback on some thoughts I had about potential solutions.

Finally

Together, you can make everything less horrible. Teamwork is great!