Notes from Swift core team 2016-03-23 design discussion


(Alex Martini) #1

To help keep proposals moving forward, the Swift core team has set aside some time specifically for design discussions of upcoming proposals. Below are some rough notes from the yesterday's discussion.

(This week, I want to point out that my notes for PR 219, the first discussion topic, are especially rough.)

These are informal comments, intended to guide the proposals in directions that draw constructive feedback. You are welcome to ignore the feedback, agree with it, or disagree with it. As always, the formal decision doesn't happen until after the review period ends.

Make pointer nullability explicit using Optional <file:///Users/alexmartini/DevPubs%20Git%20Repositories/Swift%20Language%20Review/_build/html/LR_MeetingNotes/2016-03-23.html#make-pointer-nullability-explicit-using-optional>
https://github.com/apple/swift-evolution/pull/219
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 <file:///Users/alexmartini/DevPubs%20Git%20Repositories/Swift%20Language%20Review/_build/html/LR_MeetingNotes/2016-03-23.html#allow-swift-types-to-provide-custom-objective-c-representations>
https://github.com/apple/swift-evolution/pull/198
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 <file:///Users/alexmartini/DevPubs%20Git%20Repositories/Swift%20Language%20Review/_build/html/LR_MeetingNotes/2016-03-23.html#se-0054-abolish-implicitlyunwrappedoptional-type>
https://github.com/apple/swift-evolution/blob/master/proposals/0054-abolish-iuo.md
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.


(Russ Bishop) #2

Allow Swift types to provide custom Objective-C representations <file:///Users/alexmartini/DevPubs%20Git%20Repositories/Swift%20Language%20Review/_build/html/LR_MeetingNotes/2016-03-23.html#allow-swift-types-to-provide-custom-objective-c-representations>
https://github.com/apple/swift-evolution/pull/198
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

Was there more to this line of thought? It looks like it got cut off.

I would like to unify this to either have the initializers or have the static functions but not both, but I don’t know which is preferred from an implementation perspective. The initializers feel more “Swifty” but moving back to static functions is perfectly workable.

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.

The casting was a late addition to allow the user to work around situations where the ObjC API is deficient and to keep the behavior consistent with how other types are bridged. It could be removed if desired but I agree that it should probably be removed from the existing types as well in that case.

Removing it would unify behavior: conversion happens through initializers, casting through as. That means the example would be more like “let x = SwiftType(SomeObjCType)”. Strings become “let x = String(anNSString)"

What is the team’s general approach to a situation like this? I feel like the casting discussion is a separate one but it does have an impact on the static function vs initializer discussion.

Russ

···

On Mar 24, 2016, at 10:26 AM, Alex Martini via swift-evolution <swift-evolution@swift.org> wrote


(Daniel Vollmer) #3

Hi Alex (and all other note-takers),

these notes are helpful and really appreciated. Thanks for making them available. :slight_smile:

  Daniel.


(Chris Lattner) #4

Allow Swift types to provide custom Objective-C representations <file:///Users/alexmartini/DevPubs%20Git%20Repositories/Swift%20Language%20Review/_build/html/LR_MeetingNotes/2016-03-23.html#allow-swift-types-to-provide-custom-objective-c-representations>
https://github.com/apple/swift-evolution/pull/198
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

Was there more to this line of thought? It looks like it got cut off.

I would like to unify this to either have the initializers or have the static functions but not both, but I don’t know which is preferred from an implementation perspective. The initializers feel more “Swifty” but moving back to static functions is perfectly workable.

The preference was to just have the initializers, since that is the preferred way to express conversions.

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.

The casting was a late addition to allow the user to work around situations where the ObjC API is deficient and to keep the behavior consistent with how other types are bridged. It could be removed if desired but I agree that it should probably be removed from the existing types as well in that case.

Removing it would unify behavior: conversion happens through initializers, casting through as. That means the example would be more like “let x = SwiftType(SomeObjCType)”. Strings become “let x = String(anNSString)”

Many of us would prefer to reduce the implicit conversions we have today in various ways (e.g. I’m a fan of disabling T -> T? promotion in operator argument contexts, which would solve a number of weird ?? issues). The existing implicit bridging conversions fall into the same category: it isn’t clear if we can eliminate them, but if we could, that would be great for predictability and for type checker performance.

Upshot of this is that there doesn’t seem to be a reason for this new feature to add new implicit conversions: doing an explicit conversion with “as” seems fine.

-Chris

···

On Mar 24, 2016, at 11:57 AM, Russ Bishop via swift-evolution <swift-evolution@swift.org> wrote:

On Mar 24, 2016, at 10:26 AM, Alex Martini via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote


(Alex Martini) #5

Sorry, I didn't have anything else here. It looks like I didn't finish the sentence while typing during the meeting.

If I remember right, the thought was that we can make a function (or maybe a closure) that calls the initializer if the surrounding code needs a function.

— Alex

···

On Mar 24, 2016, at 11:57 AM, Russ Bishop <xenadu@gmail.com> wrote:

On Mar 24, 2016, at 10:26 AM, Alex Martini via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote
Allow Swift types to provide custom Objective-C representations <file:///Users/alexmartini/DevPubs%20Git%20Repositories/Swift%20Language%20Review/_build/html/LR_MeetingNotes/2016-03-23.html#allow-swift-types-to-provide-custom-objective-c-representations>
https://github.com/apple/swift-evolution/pull/198
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

Was there more to this line of thought? It looks like it got cut off.


(Russ Bishop) #6

Sounds good to me; I updated the proposal to use initializers.

Russ

···

On Mar 24, 2016, at 1:26 PM, Chris Lattner <clattner@apple.com> wrote:

On Mar 24, 2016, at 11:57 AM, Russ Bishop via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Mar 24, 2016, at 10:26 AM, Alex Martini via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote
Allow Swift types to provide custom Objective-C representations <file:///Users/alexmartini/DevPubs%20Git%20Repositories/Swift%20Language%20Review/_build/html/LR_MeetingNotes/2016-03-23.html#allow-swift-types-to-provide-custom-objective-c-representations>
https://github.com/apple/swift-evolution/pull/198
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

Was there more to this line of thought? It looks like it got cut off.

I would like to unify this to either have the initializers or have the static functions but not both, but I don’t know which is preferred from an implementation perspective. The initializers feel more “Swifty” but moving back to static functions is perfectly workable.

The preference was to just have the initializers, since that is the preferred way to express conversions.