[swift-evolution] [swift-evolution-announce] [Review] SE-0176: Enforce Exclusive Access to Memory

Jordan Rose jordan_rose at apple.com
Tue May 16 19:50:41 CDT 2017


> On May 16, 2017, at 17:37, John McCall <rjmccall at apple.com> wrote:
> 
>> 
>> On May 16, 2017, at 5:20 PM, Jordan Rose <jordan_rose at apple.com <mailto:jordan_rose at apple.com>> wrote:
>> 
>> 
>>> On May 15, 2017, at 20:29, John McCall <rjmccall at apple.com <mailto:rjmccall at apple.com>> wrote:
>>> 
>>>> 
>>>> On May 15, 2017, at 9:00 PM, Jordan Rose <jordan_rose at apple.com <mailto:jordan_rose at apple.com>> 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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170516/6b708484/attachment.html>


More information about the swift-evolution mailing list