Swift Evolution News

Like my posts? Consider buying a book or two. Thanks!

Accepted Proposals

SE-0044: Import as member provides a mechanism for C API authors to specify the capability of importing functions and variables as members on imported Swift types. It also seeks to provide an automatic inference option for APIs that follow a consistent, disciplined naming convention. Discussion

SE-0043: Declare variables in ‘case’ labels with multiple patterns removes errors when a pattern declares the multiple variables with the same name and types. This change reduces repetitive code, and therefore reduces mistakes. It’s consistent with multi-pattern matching when variables aren’t defined.

SE-0042: Flattening the function type of unapplied method references makes unapplied method references more useful and consistent with idiomatic Swift and workable for mutating methods, by changing them to produce a function with a flat function type.

SE-0047: Defaulting non-Void functions so they warn on unused results flips Swift’s default behavior. Unused results are more likely to indicate programmer error than confusion between mutating and non-mutating function pairs. This proposal makes “warn on unused result” the default behavior for Swift methods and functions. Developers must override this default to enable the compiler to ignore unconsumed values. (Preliminary revision per Core Team feedback adds default migration behavior section for the Clang importer.)

In Review

SE-0053: Remove explicit use of let from Function Parameters In review to 3/27

SE-0055: Make unsafe pointer nullability explicit using Optional makes UnsafePointer<Int> represent a non-nullable pointer, and UnsafePointer<Int>? a nullable pointer. This also allows us to preserve information about pointer nullability available in header files for imported C and Objective-C APIs. In review to 3/27

SE-0048: Generic Type Aliases aims to add generic typealiases to Swift In review to 3/29

Scheduled Reviews

Design Discussion

Core Team Feedback on Pull Requests

Make pointer nullability explicit using Optional

Biggest open issue is what to do with UnsafeBufferPointer which has a base address and a count of the number of elements at that address. The most common use is to do fast things with an array. The problem is when you have an empty array.

We have a statically initialized empty array, so this doesn’t apply to array. But slices and Cocoa arrays can do it.

Half of the use cases are subscripting off of the buffer, so they don’t actually use the base address. They can’t actually subscript an empty array, but it’s not a syntax error — the loop is run zero times, so it doesn’t matter. The other half pass the pointers down to a C API that takes an address and count.

Someone might expect that the base address doesn’t change when something is initialized.

We can’t easily use the zero pointer because SIL already uses it for nil. But there are issues with using the same representation as C to avoid bridging costs.

We’re mapping two things in C onto one thing in Swift. In C, the buffer pointer would be __nullable long * and the length is ulong.

Given everything else in the system, it’s more like pointer. We didn’t call it a buffer because that tends to imply ownership.

Sketching out the state space:

Pointer Length Static type
null 0 UBP?
valid >= 0 UBP
valid < 0 X
vull != 0 ???

This issue would go away if we got rid of the base address on UnsafeBufferPointer, but that would get rid of a number of valid C operations like calling memcopy.

It seems like withUnsafeBufferPointer should never produce nil. With that in mind, why should UnsafeBufferPointer need to?

We do need a properly-aligned “valid” invalid pointer. LLVM makes assumptions about things being aligned.

Dominant feedback on the list has been for people want something that round trips cleanly. Making the base address non-optional adds overhead and removes the ability to round trip.

It’s unfortunate that we don’t have a way to represent in the type system a buffer pointer that isn’t nullable, from within withUnsafeBufferPointer which wouldn’t even call its closure if the buffer has a null base address.

Allow Swift types to provide custom Objective-C representations

The associated type could be AnyObject rather than NSObject. The use case for a non-subclass of NSObject is very narrow, but it’s not a needed restriction.

The unconditionalyBridgeFromObjectiveC function can probably go away. Calling initializers from the downcasting infrastructure is horrible. If we need a function, they

This doesn’t break the ability of the optimizer to reason about what a dynamic cast can do. We require that the bridgeable conformance must be in the same module as where the type is defined, and we have a white list of things that don’t follow that. Ok… but do we want people to expand casting this way? If we say no, we should take it away from array and string and dictionary too.

You shouldn’t need implicit conversions — the use case is very narrow, and we would rather have things use explicit conversions. The APIs come in with the right type; the implementation of the bridged type has to do conversion, but its clients don’t have to see that. From the Swift point of view, there won’t be any APIs that take the reference type.

Implicit conversions. In this proposals, you don’t get implicit conversions. Have a separate discussion about whether we can get rid of the four types that have implicit conversion. We see the existing ones as deeply problematic.

Dynamic casts. For example, AnyObject to a bridged value type. The whole reason for the dynamic cast infrastructure is to make the reference types irrelevant. Should this be using cast syntax or should we have a different type of function? It’s hard to describe what as does. It’s magic because you are casting AnyObject to a struct — calling it AnyObject doesn’t make a lot of sense.

If we have reference counted existentials, we could merge Any and AnyObject.

Resilience concern: you can not add this protocol after the type has been published.

SE-0054: Abolish ImplicitlyUnwrappedOptional type

IUO as a type is bad; it should become conceptually a decl attribute instead. If we import things that can be nil, their formal type is Optional but we can add an attribute that says when you form a rvalue to that thing, you get implicit unwrapping. The typechecker inserts a diamond that lets you convert it to T? or T. Meaning T! never enters the type system — although it does still appear in user-facing decl descriptions.

For example:

let y = P       // y is of type T?
let y: T! = P   // y is of type T!
let x = [P]     // x is of type [T?]

One issue is that we don’t have a good way to mark a function that takes a block pointer, when that block has not been audited.

void foo(C *(*fp)(C* x)) {}
func foo (fp: ((C?) -> C?)!) {}
func foo (@IUO fp: (C?) -> C?) {}

That is a regression from our current behavior. It would have to be a type attribute because they can chain. This will show up in places where you return (and then call) a block.

For example, you can no longer express the type let y: (T?, U!).

We don’t need to have a diamond for currying, only for rvalue access and application.

x.foo()         // T!
Type.foo(x)()   // T? return type

A source breaking change here is that if extract something into an intermediate variable could change type in a very important way. That’s already somewhat true because of overload resolution, but this makes it much more visible.

Does this gloss over the audit problem? We’ve gotten most of the really important stuff audited, but there are lots of unaudited things such as most of the SDK on Linux. This means we don’t propagate the IUO stuff up, meaning we end up with more explicit forces in code when you are using unaudited API.

Deferred initialization is still a good argument for why we need the feature. This approach locks down propagation and makes IUO more predictable: if the expression can typecheck as optional it will, otherwise it will force.

Awaiting Scheduling

Comments are closed.