Proposal: Extend the &x -> UnsafePointer behavior to work with immutable values


(Lily Ballard) #1

# Introduction

Swift allows you to pass a &x ref to a function taking Unsafe[Mutable]Pointer, as long as x is mutable.

# Problem

There's no way to pass an immutable UnsafePointer that points to immutable data. Any such immutable data has to be copied into a mutable variable before the &x ref works.

# Solution

Allow for using &x with immutable values if and only if the reference is passed as a parameter to a function expecting UnsafePointer

# Drawbacks

It's easy to cast an UnsafePointer to an UnsafeMutablePointer and then mutate it. This problem exists in C and C++ as well. But UnsafePointer/UnsafeMutablePointer can already be used to bypass many of the safety mechanisms of the language, so I don't think this is a big deal. Any such mutations will either crash (because it's mutating something that's not in a writeable page), or it will invoke undefined behavior, but that shouldn't surprise anyone.

# Alternatives

Do nothing and hope that at some point Swift introduces some form of first-class support for explicit immutable references. That's probably not happening any time soon.

-Kevin Ballard


(Joe Groff) #2

# Introduction

Swift allows you to pass a &x ref to a function taking Unsafe[Mutable]Pointer, as long as x is mutable.

# Problem

There's no way to pass an immutable UnsafePointer that points to immutable data. Any such immutable data has to be copied into a mutable variable before the &x ref works.

This isn't strictly true; you can pass [x].

# Solution

Allow for using &x with immutable values if and only if the reference is passed as a parameter to a function expecting UnsafePointer

Seems reasonable. As I mentioned on swift-dev, '&x' in Swift indicates "this call mutates x with inout semantics", not the C sense of "I'm passing a pointer", so doing the conversion without an '&' seems more Swift-ish to me. The semantics of the implicit const pointer conversion also only allow the C call to treat the parameter as if it were an immutable value operand; any capture of or mutation through the variable is UB.

-Joe

···

On Dec 16, 2015, at 11:44 AM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:


(Jacob Bandes-Storch) #3

+1.

···

On Wed, Dec 16, 2015 at 11:44 AM, Kevin Ballard via swift-evolution < swift-evolution@swift.org> wrote:

# Alternatives

Do nothing and hope that at some point Swift introduces some form of
first-class support for explicit immutable references. That's probably not
happening any time soon.

What are you envisioning?

Jacob Bandes-Storch


(Lily Ballard) #4

>
> # Introduction
>
> Swift allows you to pass a &x ref to a function taking Unsafe[Mutable]Pointer, as long as x is mutable.
>
> # Problem
>
> There's no way to pass an immutable UnsafePointer that points to immutable data. Any such immutable data has to be copied into a mutable variable before the &x ref works.

This isn't strictly true; you can pass [x].

Really? I had no idea you could pass arrays to functions expecting pointers. I assume it still creates an intermediate Array object though (and copies the value to the heap).

I know you can pass a String to a function expecting UnsafePointer<Int8/UInt8>. And now I know about Array. Are there any other types that can be passed like this?

> # Solution
>
> Allow for using &x with immutable values if and only if the reference is passed as a parameter to a function expecting UnsafePointer

Seems reasonable. As I mentioned on swift-dev, '&x' in Swift indicates "this call mutates x with inout semantics", not the C sense of "I'm passing a pointer", so doing the conversion without an '&' seems more Swift-ish to me. The semantics of the implicit const pointer conversion also only allow the C call to treat the parameter as if it were an immutable value operand; any capture of or mutation through the variable is UB.

Fair enough. I'm still leaning towards keeping the &, because it feels a little odd to require foo(&x) for UnsafeMutablePointer and allow foo(x) for UnsafePointer, but I admit that may just be because &x looks like the C address-of operator. One benefit of dropping the & is it then becomes reasonable to say foo(42), whereas foo(&42) feels a little odd (but maybe no more odd than C's &(int){42}).

-Kevin Ballard

···

On Wed, Dec 16, 2015, at 11:54 AM, Joe Groff wrote:

> On Dec 16, 2015, at 11:44 AM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:


(Chris Lattner) #5

# Introduction

Swift allows you to pass a &x ref to a function taking Unsafe[Mutable]Pointer, as long as x is mutable.

# Solution

Allow for using &x with immutable values if and only if the reference is passed as a parameter to a function expecting UnsafePointer

+1 from me on the feature.

Seems reasonable. As I mentioned on swift-dev, '&x' in Swift indicates "this call mutates x with inout semantics", not the C sense of "I'm passing a pointer", so doing the conversion without an '&' seems more Swift-ish to me.

I don’t think that "more Swift’ish” is a good enough rationalization here :-)

The semantics of the implicit const pointer conversion also only allow the C call to treat the parameter as if it were an immutable value operand; any capture of or mutation through the variable is UB.

& is a sigil foisted onto the caller, in order to make the semantics of the function more clear, there is no implementation reason to require it. I suppose what you’re really claiming is that the caller shouldn’t have to use & here, since no mutation is possible and nothing for the caller to need to think about.

I’m not sure about that. Consider that this is our current behavior:

  func f(a : UnsafePointer<Int>) {}
  var a = 42
  f(&a) // ok
  f(a) // error: cannot convert value of type 'Int' to expected argument type 'UnsafePointer<Int>’

It seems weird to me that we’d change this behavior for var, or make UnsafePointer be inconsistent with UnsafeMutablePointer. Further, UnsafePointer should *also* accept an array as it currently does, and it would be weird to either take a scalar or an array.

-Chris

···

On Dec 16, 2015, at 11:54 AM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Dec 16, 2015, at 11:44 AM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:


(Lily Ballard) #6

+1.

# Alternatives

Do nothing and hope that at some point Swift introduces some form of first-
class support for explicit immutable references. That's probably not
happening any time soon.

What are you envisioning?

Not really envisioning much at the moment. Adding unsafe references
would not be very good (and would be basically what we already have with
UnsafePointer anyway). Personally I'm a big fan of Rust's lifetime
system which allows for safe compile-time-checked references, but it
does have a bit of a learning curve and is probably the most confusing
part of Rust for newcomers, which is why I'm not proposing adding it to
Swift as it would be incompatible with using Swift as a teaching
language (I'm not sure how important that use-case is to the Swift core
team but I know there's a lot of community interest there).

-Kevin Ballard

···

On Wed, Dec 16, 2015, at 02:09 PM, Jacob Bandes-Storch wrote:

On Wed, Dec 16, 2015 at 11:44 AM, Kevin Ballard via swift-evolution > <swift-evolution@swift.org> wrote:


(Joe Groff) #7

# Introduction

Swift allows you to pass a &x ref to a function taking Unsafe[Mutable]Pointer, as long as x is mutable.

# Problem

There's no way to pass an immutable UnsafePointer that points to immutable data. Any such immutable data has to be copied into a mutable variable before the &x ref works.

This isn't strictly true; you can pass [x].

Really? I had no idea you could pass arrays to functions expecting pointers. I assume it still creates an intermediate Array object though (and copies the value to the heap).

True. Nadav's team is in the process of implementing stack promotion for literal arrays, which should cut the cost down; this is also something SILGen could reasonably peephole (and similarly, passing a string to a C function should just pass a static string and not bridge through String).

I know you can pass a String to a function expecting UnsafePointer<Int8/UInt8>. And now I know about Array. Are there any other types that can be passed like this?

I think that's it—String, Array, and vars are the things that are implicitly bridged to C pointers.

-Joe

···

On Dec 16, 2015, at 1:18 PM, Kevin Ballard <kevin@sb.org> wrote:
On Wed, Dec 16, 2015, at 11:54 AM, Joe Groff wrote:

On Dec 16, 2015, at 11:44 AM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

# Solution

Allow for using &x with immutable values if and only if the reference is passed as a parameter to a function expecting UnsafePointer

Seems reasonable. As I mentioned on swift-dev, '&x' in Swift indicates "this call mutates x with inout semantics", not the C sense of "I'm passing a pointer", so doing the conversion without an '&' seems more Swift-ish to me. The semantics of the implicit const pointer conversion also only allow the C call to treat the parameter as if it were an immutable value operand; any capture of or mutation through the variable is UB.

Fair enough. I'm still leaning towards keeping the &, because it feels a little odd to require foo(&x) for UnsafeMutablePointer and allow foo(x) for UnsafePointer, but I admit that may just be because &x looks like the C address-of operator. One benefit of dropping the & is it then becomes reasonable to say foo(42), whereas foo(&42) feels a little odd (but maybe no more odd than C's &(int){42}).

-Kevin Ballard


(Joe Groff) #8

# Introduction

Swift allows you to pass a &x ref to a function taking Unsafe[Mutable]Pointer, as long as x is mutable.

# Solution

Allow for using &x with immutable values if and only if the reference is passed as a parameter to a function expecting UnsafePointer

+1 from me on the feature.

Seems reasonable. As I mentioned on swift-dev, '&x' in Swift indicates "this call mutates x with inout semantics", not the C sense of "I'm passing a pointer", so doing the conversion without an '&' seems more Swift-ish to me.

I don’t think that "more Swift’ish” is a good enough rationalization here :slight_smile:

The semantics of the implicit const pointer conversion also only allow the C call to treat the parameter as if it were an immutable value operand; any capture of or mutation through the variable is UB.

& is a sigil foisted onto the caller, in order to make the semantics of the function more clear, there is no implementation reason to require it. I suppose what you’re really claiming is that the caller shouldn’t have to use & here, since no mutation is possible and nothing for the caller to need to think about.

I’m not sure about that. Consider that this is our current behavior:

  func f(a : UnsafePointer<Int>) {}
  var a = 42
  f(&a) // ok
  f(a) // error: cannot convert value of type 'Int' to expected argument type 'UnsafePointer<Int>’

It seems weird to me that we’d change this behavior for var, or make UnsafePointer be inconsistent with UnsafeMutablePointer.

The UnsafePointer and UnsafeMutablePointer conversions have different semantics—the former can't change the variable, but the latter can—so it seems reasonable to me that they look different.

Further, UnsafePointer should *also* accept an array as it currently does, and it would be weird to either take a scalar or an array.

We already do this for UnsafeMutablePointer, which can take &scalar or &array. The point of the conversions is similar to IUO—we don't know whether the C API is intended to behave as an 'in' scalar, 'in' array, or significant pointer parameter purely from its declaration, so we try to make all of the common possibilities work.

-Joe

-Joe

···

On Dec 16, 2015, at 2:24 PM, Chris Lattner <clattner@apple.com> wrote:
On Dec 16, 2015, at 11:54 AM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Dec 16, 2015, at 11:44 AM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:


(Michael Gottesman) #9

+1.

# Alternatives

Do nothing and hope that at some point Swift introduces some form of first-class support for explicit immutable references. That's probably not happening any time soon.

What are you envisioning?

Not really envisioning much at the moment. Adding unsafe references would not be very good (and would be basically what we already have with UnsafePointer anyway). Personally I'm a big fan of Rust's lifetime system which allows for safe compile-time-checked references, but it does have a bit of a learning curve and is probably the most confusing part of Rust for newcomers, which is why I'm not proposing adding it to Swift as it would be incompatible with using Swift as a teaching language (I'm not sure how important that use-case is to the Swift core team but I know there's a lot of community interest there).

We have in the past spoken about having that be an opt in feature. But that would be down the line. It has some interesting properties such as guaranteeing that a value is thread local or that a value has a lifetime that is subsumed by a different lifetime so it does not need reference counting.

Michael

···

On Dec 16, 2015, at 4:30 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:
On Wed, Dec 16, 2015, at 02:09 PM, Jacob Bandes-Storch wrote:

On Wed, Dec 16, 2015 at 11:44 AM, Kevin Ballard via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

-Kevin Ballard

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Michael Gottesman) #10

+1.

# Alternatives

Do nothing and hope that at some point Swift introduces some form of first-class support for explicit immutable references. That's probably not happening any time soon.

What are you envisioning?

Not really envisioning much at the moment. Adding unsafe references would not be very good (and would be basically what we already have with UnsafePointer anyway). Personally I'm a big fan of Rust's lifetime system which allows for safe compile-time-checked references, but it does have a bit of a learning curve and is probably the most confusing part of Rust for newcomers, which is why I'm not proposing adding it to Swift as it would be incompatible with using Swift as a teaching language (I'm not sure how important that use-case is to the Swift core team but I know there's a lot of community interest there).

We have in the past spoken about having that be an opt in feature. But that would be down the line. It has some interesting properties such as guaranteeing that a value is thread local or that a value has a lifetime that is subsumed by a different lifetime so it does not need reference counting.

(And keep in mind, it is not clear if it will happen, i.e. this is not a promise/speculation. I am just saying it has come up in conversations).

···

On Dec 16, 2015, at 4:39 PM, Michael Gottesman via swift-evolution <swift-evolution@swift.org> wrote:

On Dec 16, 2015, at 4:30 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
On Wed, Dec 16, 2015, at 02:09 PM, Jacob Bandes-Storch wrote:

On Wed, Dec 16, 2015 at 11:44 AM, Kevin Ballard via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Michael

-Kevin Ballard

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Matthew Johnson) #11

This is a bit of an aside, but I think a compelling use of an ownership system like Rust’s is the compiler guarantee that no code can unexpectedly store a reference to a mutable object (and potentially do something nasty with it later).

···

On Dec 16, 2015, at 4:39 PM, Michael Gottesman via swift-evolution <swift-evolution@swift.org> wrote:

On Dec 16, 2015, at 4:30 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Wed, Dec 16, 2015, at 02:09 PM, Jacob Bandes-Storch wrote:

+1.

On Wed, Dec 16, 2015 at 11:44 AM, Kevin Ballard via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
# Alternatives

Do nothing and hope that at some point Swift introduces some form of first-class support for explicit immutable references. That's probably not happening any time soon.

What are you envisioning?

Not really envisioning much at the moment. Adding unsafe references would not be very good (and would be basically what we already have with UnsafePointer anyway). Personally I'm a big fan of Rust's lifetime system which allows for safe compile-time-checked references, but it does have a bit of a learning curve and is probably the most confusing part of Rust for newcomers, which is why I'm not proposing adding it to Swift as it would be incompatible with using Swift as a teaching language (I'm not sure how important that use-case is to the Swift core team but I know there's a lot of community interest there).

We have in the past spoken about having that be an opt in feature. But that would be down the line. It has some interesting properties such as guaranteeing that a value is thread local or that a value has a lifetime that is subsumed by a different lifetime so it does not need reference counting.


(Joe Groff) #12

Some of these guarantees also arise naturally from value semantics; variables of pure value types can't be influenced outside their scope, and an 'inout' parameter can't capture a permanent reference and has a limited opportunity to mutate the original value.

-Joe

···

On Dec 16, 2015, at 7:48 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

On Dec 16, 2015, at 4:39 PM, Michael Gottesman via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Dec 16, 2015, at 4:30 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Wed, Dec 16, 2015, at 02:09 PM, Jacob Bandes-Storch wrote:

+1.

On Wed, Dec 16, 2015 at 11:44 AM, Kevin Ballard via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
# Alternatives

Do nothing and hope that at some point Swift introduces some form of first-class support for explicit immutable references. That's probably not happening any time soon.

What are you envisioning?

Not really envisioning much at the moment. Adding unsafe references would not be very good (and would be basically what we already have with UnsafePointer anyway). Personally I'm a big fan of Rust's lifetime system which allows for safe compile-time-checked references, but it does have a bit of a learning curve and is probably the most confusing part of Rust for newcomers, which is why I'm not proposing adding it to Swift as it would be incompatible with using Swift as a teaching language (I'm not sure how important that use-case is to the Swift core team but I know there's a lot of community interest there).

We have in the past spoken about having that be an opt in feature. But that would be down the line. It has some interesting properties such as guaranteeing that a value is thread local or that a value has a lifetime that is subsumed by a different lifetime so it does not need reference counting.

This is a bit of an aside, but I think a compelling use of an ownership system like Rust’s is the compiler guarantee that no code can unexpectedly store a reference to a mutable object (and potentially do something nasty with it later).


(Matthew Johnson) #13

Yes of course. I was referring to cases where a reference type is necessary, but sharing ownership is neither necessary nor desired.

···

On Dec 16, 2015, at 10:42 PM, Joe Groff <jgroff@apple.com> wrote:

On Dec 16, 2015, at 7:48 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Dec 16, 2015, at 4:39 PM, Michael Gottesman via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Dec 16, 2015, at 4:30 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Wed, Dec 16, 2015, at 02:09 PM, Jacob Bandes-Storch wrote:

+1.

On Wed, Dec 16, 2015 at 11:44 AM, Kevin Ballard via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
# Alternatives

Do nothing and hope that at some point Swift introduces some form of first-class support for explicit immutable references. That's probably not happening any time soon.

What are you envisioning?

Not really envisioning much at the moment. Adding unsafe references would not be very good (and would be basically what we already have with UnsafePointer anyway). Personally I'm a big fan of Rust's lifetime system which allows for safe compile-time-checked references, but it does have a bit of a learning curve and is probably the most confusing part of Rust for newcomers, which is why I'm not proposing adding it to Swift as it would be incompatible with using Swift as a teaching language (I'm not sure how important that use-case is to the Swift core team but I know there's a lot of community interest there).

We have in the past spoken about having that be an opt in feature. But that would be down the line. It has some interesting properties such as guaranteeing that a value is thread local or that a value has a lifetime that is subsumed by a different lifetime so it does not need reference counting.

This is a bit of an aside, but I think a compelling use of an ownership system like Rust’s is the compiler guarantee that no code can unexpectedly store a reference to a mutable object (and potentially do something nasty with it later).

Some of these guarantees also arise naturally from value semantics; variables of pure value types can't be influenced outside their scope, and an 'inout' parameter can't capture a permanent reference and has a limited opportunity to mutate the original value.