Thanks for refusing to let your pitch die. Something should eventually be done 
here and it's good to get feedback. The only reason to bring this up in Swift 4 
is if we decide to outlaw some code pattern that's already in use. If this ends 
up just an additive convenience request, than it can be a bug report for now 
and come back in Swift 5. There have been a couple +1's already but I don't 
know whether it's a serious problem or just an annoyance. 

Side note: don’t worry, simply declaring everything ‘var’ and passing them 
‘inout’ shouldn’t break exclusive memory access. If you pass ‘inout’ to a 
non-mutable pointer, it’s just considered a “read” access.

I'll speculate a bit and make some suggestions...

> On Apr 20, 2017, at 12:10 PM, Anders Kierulf via swift-evolution 
> <[email protected]> wrote:
> 
> Summary: Currently, only mutable values can be passed to UnsafeRawPointer, 
> except for a special case for arrays. That special case should be 
> generalized, allowing any values to be passed to UnsafeRawPointer without 
> using &.
> 
> The following code shows the inconsistency in passing values to 
> UnsafeRawPointer:
> 
> var varArray = [Int](repeating: 0, count: 6)
> var varTuple = (0, 0, 0, 0, 0, 0)
> 
> let letArray = [Int](repeating: 0, count: 6)
> let letTuple = (0, 0, 0, 0, 0, 0)
> 
> func get(_ pointer: UnsafeRawPointer, at index: Int) -> Int {
>    let a = pointer.bindMemory(to: Int.self, capacity: 6)
>    return a[index]
> }
> 
> // Array can be passed directly, automatically takes address.
> _ = get(varArray, at: 2) // okay
> _ = get(letArray, at: 2) // okay

This works out because a special implicit conversion is doing the work for us. 
I think the special case is all about lightweight C interop with Swift Array.

Let's not change this.

> // When explicitly taking address, can only pass mutable variables.
> _ = get(&varArray, at: 2) // okay, but seems inconsistent
> _ = get(&letArray, at: 2) // fails: & not allowed

I'll speculate that the type checker has a special case for `&varArray` for two 
reasons:

1. To prevent users from accidentally exposing the Array struct rather than its 
storage as a C pointer. That would be confusing to debug.

2. We need a mutable analog to the non-inout immutable case above, for 
lightweight C interop.

We allow implicit UnsafeMutablePointer to UnsafePointer, so, I don't think 
there's anything inconsistent about `&varArray` here.

I also think it's fine that we don't allow `&letArray`.

Let's not change this.

> // Passing tuple instead of array fails.
> _ = get(varTuple, at: 2) // fails: wrong type
> _ = get(letTuple, at: 2) // fails: wrong type

Refusing to compile these cases would probably seem normal if we didn't support 
the first non-inout array case.

I think we should allow both cases as a new, additive type system feature.

> // Adding & to pass a tuple only works for mutable values. Having to
> // pass the address using & means that any methods calling this must
> // be mutating, even though they don't mutate.
> _ = get(&varTuple, at: 2) // okay, but forces mutating
> _ = get(&letTuple, at: 2) // fails: cannot pass immutable value

Well, the `&varTuple` case only works by accident for raw pointers, but typed 
pointers will have the wrong type!

See [SR-3590] Implicitly convert &Tuple to UnsafePointer<Tuple.Element>.

The only reason I haven't pushed for this in Swift 4, along with several other 
important usability issues with UnsafePointer, is that (I thought) it's 
additive and I haven't wanted to compete for review bandwidth in this release 
cycle.

However, come to think of it, this will break some very unlikely code:

func foo(_ p: UnsafeMutablePointer<(Int, Int, Int)>) {
  p[0].0 = 42
}

var a = (0, 1, 2)
foo(&a)
print(a)

I think that's fair if migration is provided.

> Passing a value to an UnsafeRawPointer parameter should not require use of &, 
> as that forces all code calling it to be mutating. Having a special case for 
> array also doesn't make sense, as the idea of UnsafeRawPointer is that we're 
> interpreting that memory content as whatever we want. Logged as SR-4649.

Splitting hairs: the array case is really a special case and it does make 
sense. I'm *also* proposing a similar special case for homogeneous tuples.

> Proposal:
> - Never require & when passing value to UnsafeRawPointer.
> - Always require & when passing value to UnsafeMutableRawPointer.

+1 to your basic proposal. But I think each type system change needs to be more 
clearly spelled out. Probably with a separate bug for each.

- Support non-inout homogeneous tuple argument conversion to 
UnsafePointer<Element>.
(additive)

- Support non-homgenous non-inout tuple argument conversion to UnsafeRawPointer.
(additive)

- Support inout homogeneous tuple argument conversion to 
UnsafeMutablePointer<Element>.
See [SR-3590] Implicitly convert &Tuple to UnsafePointer<Tuple.Element>.
(source breaking in ridiculous cases)

- [SR-1956] `withUnsafePointer` shouldn't take its argument as `inout`
(additive... we don't need to ban the inout syntax)

The problem I have with [SR-4649] "Don't require & to pass value to 
UnsafeRawPointer", is that it seeks to create inconsistency between raw and 
non-raw pointers. That might make sense for non-homogenous tupes, but otherwise 
I don't think it's desirable. 

> 
> (Fixed size arrays are still needed, but with this tweak, workarounds can be 
> made to work.)

Yes, they very much are needed to get people out of unsafe territory.

-Andy


_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to