[swift-evolution] [Proposal] Make non-escaping closures the default

Trent Nadeau tanadeau at gmail.com
Thu Jun 9 21:42:26 CDT 2016

Thanks for the very interesting design discussion so far. It's exactly what
I was wanting to see :)

I'm going to incorporate some of this in the next draft of the proposal.
I'm also going to add that there should be an asUnsafeEscapingClosure(_:)
helper that will convert a default/noescape closure to an escaping one. For
the initial implementation it could use unsafeBitCast(_:to:) to perhaps be
changed later.

On Thu, Jun 9, 2016 at 8:29 PM, John McCall <rjmccall at apple.com> wrote:

> On Jun 9, 2016, at 4:15 PM, Jordan Rose <jordan_rose at apple.com> wrote:
> On Jun 9, 2016, at 16:10, John McCall <rjmccall at apple.com> wrote:
> On Jun 9, 2016, at 3:43 PM, Jordan Rose via swift-evolution <
> swift-evolution at swift.org> wrote:
> I'm against this for library evolution reasons: if someone releases a
> version of their library that has a non-escaping closure and later
> discovers it needs to be escaping, they can't change it.
> IIRC the counterpoint to this is that people were probably implicitly
> relying on it being non-escaping already, and that there aren't many cases
> where you'd want to do this anyway.
> Right.  APIs are already semantically constrained in how they're allowed
> to use their closure arguments.  Closure arguments inject arbitrary code,
> with arbitrary data access, into the callee; as a rule, the caller must
> know how the callee intends to use the closure, or its semantics will be
> grossly violated.  You can't re-implement an existing API that always
> synchronously sub-invokes a closure to instead call the closure
> asynchronously or concurrently because it is completely reasonable for the
> caller to pass a closure that relies on being called synchronously or from
> at most one thread at once and/or within a fixed range of time.  For
> example, the closure may modify a captured local variable, or it may it use
> a network connection that will be closed after the API returns.  APIs that
> want to do this sort of thing have to reserve the right to do that (and
> even then, they may have binary compatibility limitations), in which case
> it is totally reasonable to expect them to express that in the type.
> I don't buy this. If someone publishes an API that executes something on
> the current thread today and on a background queue tomorrow, that's totally
> fine if they never promised it would execute on a particular thread. If a
> client accidentally assumes an implementation detail is part of the
> interface, that's their fault, and always has been…though the library
> author might decide to continue supporting their use in the future in the
> interest of not making waves.
> Synchronous-but-off-thread is kind of a special case because it's only
> observable in very special ways, e.g. thread-local storage and the current
> thread ID.  Concurrent (e.g. calling an enumeration callback on multiple
> threads simultaneously) and asynchronous (even if it comes back to the
> current queue) are absolutely something you have to know about as a
> caller.  It is deeply unreasonable for an API to suddenly start invoking a
> closure asynchronously when it hasn't documented that it might do that
> (perhaps implicitly by obviously following some well-known pattern, e.g.
> calling the closure a completion handler); that would be a huge semantic
> and binary-compatibility problem.
> Another line of argument: flipping the default is a huge boon to static
> analysis because (1) closure execution becomes ordered by default and (2)
> an escaping closure becomes a much more meaningful hint.  For example,
> consider a use-after-free static analysis that sees this code:
>   func foo(ptr: UnsafeMutablePointer<Int>) {
>     bar { ptr[5] = 0 }
>     ptr.dealloc()
>   }
> This analysis is currently blocked by this abstraction unless it knows
> something specific about 'bar'.  If 'bar' marks its argument @noescape,
> then the analysis knows that this is safe; but if not, the analysis is
> unlikely to be willing to warn because it's quite likely that 'bar' is just
> missing an annotation and does not actually execute its closure
> asynchronously.  However, if the polarity is flipped, the analysis wins on
> both ends: it can prove correctness in many more cases by default, and the
> cases where the closure actually escapes become much more suspicious,
> probably enough to warn by default.
> John.

Trent Nadeau
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160609/84cac815/attachment.html>

More information about the swift-evolution mailing list