<div dir="ltr">[ shameless plug for <a href="https://github.com/apple/swift-evolution/blob/master/proposals/0012-add-noescape-to-public-library-api.md">https://github.com/apple/swift-evolution/blob/master/proposals/0012-add-noescape-to-public-library-api.md</a> ]<div class="gmail_extra">
<br><div class="gmail_quote">On Thu, Jan 28, 2016 at 1:46 PM, Kevin Ballard via swift-evolution <span dir="ltr"><<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><u></u>
<div><div>I'm generally in favor of this. My biggest concern is when using closures that are known not to escape the function but aren't marked as @noescape. Sometimes this is just due to the frameworks not annotating types properly, e.g. dispatch_sync()'s closure isn't @noescaping. Sometimes this is due to a function taking a closure that may escape sometimes, but not escape at other times (e.g. UIView animation methods usually don't escape the animation block, unless a non-zero delay is provided). And sometimes this is due to synchronization at a level the API doesn't know about, such as using dispatch_async + dispatch_group to wait until a block has executed.<br></div>
<div> </div>
<div>That said, if dispatch_sync() is fixed, then I think it's reasonable to require people to use manual workarounds for the remaining issues (e.g. writing a local copy themselves). Especially because capturing an inout parameter in a block is a relatively rare thing to do anyway (I don't think I've ever actually done that myself, which is why I wasn't even sure how Swift handled it).<br></div>
<div> </div>
<div>The idea of using a capture list of [inout x] is interesting, but ultimately confusing, because inout implies writeback semantics, when in fact the closure wouldn't be doing writeback but would in fact be closing over a shared mutable value (e.g. writeback implies there's a specific point where the modified value becomes visible to the enclosing scope, but that can't possibly work with non-@noescape closures because there's no single point where the compiler knows it's appropriate to insert the writeback). Based on that, I think the local copy version is the most appropriate workaround.<br></div>
<div> </div>
<div>So, given all that, +1 from me, but I really want to see dispatch_sync() fixed as soon as possible (I want it fixed anyway, but I think dispatch_sync() is the most likely candidate for unexpectedly hitting this proposed limitation in Swift). I recognize that dispatch_sync() isn't something the Swift team can fix (though Swift does have its own copy of libdispatch, I assume that changes made to that don't actually propagate upstream back to Apple's version), but since Swift core team members are Apple employees, perhaps they can put some pressure on the appropriate team to add the noescaping attribute to the appropriate libdispatch APIs.<br></div>
<div> </div>
<div>-Kevin Ballard<br></div><div><div class="h5">
<div> </div>
<div>On Thu, Jan 28, 2016, at 11:58 AM, Joe Groff via swift-evolution wrote:<br></div>
</div></div><blockquote type="cite"><div><div class="h5"><div>I think the time has come for us to limit the implicit capture of 'inout' parameters to @noescape closures. In the early days before @noescape, we designed a semantics for capturing "inout" parameters that attempted to balance the constraints on inout with the usability of higher-order functions. Inout parameters don't pass a first-class reference as in other languages, but instead provide a limited lease to mutate a property for the duration of a call; this means closures can't extend the lifetime of that lease, so closures instead capture the local mutable copy of the inout parameter. This gives the expected behavior as long as closures over an inout parameter never escape their original scope, but leads to surprises as soon as those closures escape. This unintuitive behavior has made several "Swift gotchas" lists. Now that we can explicitly mark closures as not escaping, I think we should prevent 'inout' parameters from being implicitly captured by escapable closures. There are several ways we can still support today's behavior:<br></div>
<div> </div>
<div>- Encourage people to write the local copy themselves, if that's what they want:<br></div>
<div> </div>
<blockquote style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:40px;border-top-style:none;border-right-style:none;border-bottom-style:none;border-left-style:none;border-top-width:initial;border-right-width:initial;border-bottom-width:initial;border-left-width:initial;border-top-color:initial;border-right-color:initial;border-bottom-color:initial;border-left-color:initial;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px"><div>func foo(inout x: Int) {<br></div>
<div> var localX = x; defer { x = localX }<br></div>
<div> </div>
<div> // Asynchronously mutate localX, then wait for it to finish<br></div>
<div> dispatch_async { mutate(&localX) }<br></div>
<div> dispatch_sync {}<br></div>
<div>}<br></div>
<div> </div>
</blockquote><div>- You can explicitly capture an immutable copy:<br></div>
<div> </div>
<blockquote style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:40px;border-top-style:none;border-right-style:none;border-bottom-style:none;border-left-style:none;border-top-width:initial;border-right-width:initial;border-bottom-width:initial;border-left-width:initial;border-top-color:initial;border-right-color:initial;border-bottom-color:initial;border-left-color:initial;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px"><div>func foo(inout x: Int) {<br></div>
<div> // We don't need to mutate or observe mutations on x in the async job<br></div>
<div> dispatch_async {[x] in doStuffWithoutMutating(x) }<br></div>
<div>}<br></div>
<div> </div>
</blockquote><div>and/or we could introduce a new explicit capture kind for the current behavior:<br></div>
<div><div> </div>
</div>
<blockquote style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:40px;border-top-style:none;border-right-style:none;border-bottom-style:none;border-left-style:none;border-top-width:initial;border-right-width:initial;border-bottom-width:initial;border-left-width:initial;border-top-color:initial;border-right-color:initial;border-bottom-color:initial;border-left-color:initial;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px"><div><div>func foo(inout x: Int) {<br></div>
</div>
<div><div> // Explicitly capture the 'inout' mutable shadow copy of x<br></div>
</div>
<div><div> dispatch_async {[inout x] in mutate(&x) }<br></div>
</div>
<div> dispatch_sync { }<br></div>
<div><div>}<br></div>
</div>
</blockquote><div> </div>
<div>Making it explicit should make the behavior less surprising when it occurs. We should able to provide fixits to migrate code that relies on the current behavior as well. What do you all think?<br></div>
<div> </div>
<div>-Joe<br></div>
</div></div><div><u>_______________________________________________</u><br></div>
<div>swift-evolution mailing list<br></div>
<div><a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a><br></div>
<div><a href="https://lists.swift.org/mailman/listinfo/swift-evolution" target="_blank">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br></div>
</blockquote></div>
<br>_______________________________________________<br>
swift-evolution mailing list<br>
<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a><br>
<a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br>
<br></blockquote></div><br></div></div>