> On May 16, 2017, at 17:37, John McCall <[email protected]> wrote:
> 
>> 
>> On May 16, 2017, at 5:20 PM, Jordan Rose <[email protected] 
>> <mailto:[email protected]>> wrote:
>> 
>> 
>>> On May 15, 2017, at 20:29, John McCall <[email protected] 
>>> <mailto:[email protected]>> wrote:
>>> 
>>>> 
>>>> On May 15, 2017, at 9:00 PM, Jordan Rose <[email protected] 
>>>> <mailto:[email protected]>> wrote:
>>>> 
>>>> [Proposal: 
>>>> https://github.com/apple/swift-evolution/blob/master/proposals/0176-enforce-exclusive-access-to-memory.md
>>>>  
>>>> <https://github.com/apple/swift-evolution/blob/master/proposals/0176-enforce-exclusive-access-to-memory.md>]
>>>> 
>>>> I have severe concerns about these revisions because they make 
>>>> withoutActuallyEscaping harder to reason about.
>>>> 
>>>> +  - Programmers using ``withoutActuallyEscaping`` should take
>>>> +    care not to allow the result to be recursively invoked.
>>>> 
>>>> +[…] if they are certain that their code will
>>>> +not violate the NRR, use ``withoutActuallyEscaping`` to disable
>>>> +the NPCR check.
>>>> 
>>>> I think this constitutes a violation of the user model for 
>>>> withoutActuallyEscaping, because withoutActuallyEscaping hasn't 
>>>> historically meant withoutBeingReentrant. The proposal "should take care" 
>>>> is a very mild way of saying we're introducing a new rule under which the 
>>>> compiler can silently do something different than what you wrote (with no 
>>>> "unsafe" in sight). Similarly, using withoutActuallyEscaping to mean 
>>>> withoutActuallyViolatingExclusivity seems like it'll come back to haunt 
>>>> us, the kind of thing where people ask on Stack Overflow why the Swift 
>>>> people didn't design something that said what it did.
>>> 
>>> You're right that it's a silent change in the semantics of 
>>> withoutActuallyEscaping.  I'm comfortable with that because of the ways in 
>>> which I expect withoutActuallyEscaping to be used, but I can see why 
>>> someone wouldn't be.
>>> 
>>>> When I first brought this up on an Apple-internal list, John let me know 
>>>> that we could introduce checking for it. This helps assuage my concerns 
>>>> quite a bit, but I'm not sure how we would do so without modifying the 
>>>> original closure, and if we can modify the original closure I'm not sure 
>>>> we've saved anything over proper dynamic checking.
>>> 
>>> withoutActuallyEscaping does not actually promise to return exactly the 
>>> original closure (which is not a user-detectable property of the closure 
>>> value), just something semantically equivalent.  In particular, we can wrap 
>>> it in a thunk that (say) dynamically asserts that the closure is not called 
>>> re-entrantly.
>>> 
>>> We may need withoutActuallyEscaping to add a thunk anyway, because I don't 
>>> know that we actually want to guarantee that the context pointer of a 
>>> non-escaping closure is retainable; it costs us unnecessary set-up code in 
>>> the caller.
>> 
>> I thought of this too, but it doesn't handle the case where you use 
>> withoutActuallyEscaping and use the original closure directly from within 
>> the block. Maybe we can forbid that, though—it seems extra-rare. Does it 
>> make sense to add that as an extra rule in this proposal?
> 
> Well, you'd have to integrate the check with some sort of record-keeping done 
> by the original closures.  I'm not entirely sure what that record-keeping 
> would look like off-hand.
> 
> I'm not sure what you're proposing to disallow here.  I don't think we should 
> add any extra restrictions in order to avoid problems with unfortunate uses 
> of withoutActuallyEscaping, though.

Here's a small, currently-legal, re-entrant case that only calls the wrapped 
function once.

var global: (() -> Void)?
func problem(_ fn: () -> Void) {
  withoutActuallyEscaping(fn) { wrappedFn in
    global = wrappedFn
    fn() // note: not wrappedFn
  }
}
func test() {
  var local = 0
  problem() {
    local += 1
    if let callback = global {
      global = nil
      callback()
    }
    print(local)
  }
}
test()

We could prevent this by saying it's not legal to refer to 'fn' inside the 
callback for withoutActuallyEscaping, and you have to use 'wrappedFn' instead.

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

Reply via email to