[swift-users] Swift 3 (Xcode 8 GM) issue with @escaping

Shawn Erickson shawnce at gmail.com
Thu Sep 8 19:55:43 CDT 2016


Michael any ideas about how to deal with the following? It seems like at
least a bug in the fix-it.

I assume I should open a bug on this?

-Shawn

On Wed, Sep 7, 2016 at 9:16 PM Shawn Erickson <shawnce at gmail.com> wrote:

> I seem to have hit a possibly related @escaping issue...
>
> The following code worked without issue under Swift 2.x and earlier
> variants of Swift 3 but after the @escaping change (fairly sure that is
> what is at fault) it no longer compiles.
>
> protocol Foo {
>     // Happy
>     func visitValues<T>(visitors: [(key: String, body: (T) throws ->
> Void)]) throws
>
>     // Happy
>     func visitValues<T>(throwOnWrongType: Bool, visitors: [(key: String,
> body: (T) throws -> Void)]) throws
> }
>
> protocol FooBar { // IGNORE ME - NOT PART OF ORIGINAL CODE
>     // Unhappy - this is appearntly expected given prior emails in this
> thread
>     // @escaping may only be applied to parameters of function type
>     func visitValues<T>(visitors: [(key: String, body: @escaping (T)
> throws -> Void)]) throws
>
>     // Unhappy - this is appearntly expected given prior emails in this
> thread
>     // @escaping may only be applied to parameters of function type
>     func visitValues<T>(throwOnWrongType: Bool, visitors: [(key: String,
> body: @escaping (T) throws -> Void)]) throws
> }
>
> // Provide a default implementation for the simple visitor version
> extension Foo {
>     // Happy
>     func visitValues<T>(visitors: [(String, body: (T) throws -> Void)])
> throws {
>         try self.visitValues(throwOnWrongType: false, visitors: visitors)
>     }
> }
>
> // Unhappy
> // error: type 'RealFoo' does not conform to protocol 'Foo'
> struct RealFoo : Foo {
>     // Happy
>     func visitValues<T>(throwOnWrongType: Bool, visitors: [(key: String,
> body: (T) throws -> Void)]) throws {
>         // do stuff...
>     }
>
>     // error: MyPlayground.playground:23:8: error: type 'RealFoo' does not
> conform to protocol 'Foo'
>     //struct RealFoo : Foo {
>     //    ^
>     //
>     //    MyPlayground.playground:16:10: note: candidate has non-matching
> type '<Self, T> (visitors: [(String, body: (T) throws -> Void)]) throws ->
> ()' (aka '<τ_0_0, τ_1_0> (visitors: Array<(String, body: (τ_1_0) throws ->
> ())>) throws -> ()')
>     //    func visitValues<T>(visitors: [(String, body: (T) throws ->
> Void)]) throws {
>
>     // The fix-it suggestion for "error: type 'RealFoo' does not conform
> to protocol 'Foo'" inserts the following which makes
>     // the "not conform" error go away however it doesn't compile because
> of @escaping
>     // Unhappy
>     // @escaping may only be applied to parameters of function type
>     internal func visitValues<T>(visitors: [(key: String, body: @escaping
> (T) throws -> Void)]) throws { // NOT PART OF ORIGINAL CODE
>     }
>     // The above shouldn't be needed since an implementation is provided
> via an extension on protocol Foo right?
> }
>
> On Wed, Sep 7, 2016 at 8:06 PM Shawn Erickson <shawnce at gmail.com> wrote:
>
>> To more completely state what I find it a little strange.
>>
>> The default was moved to be non-escaping since that is the better default
>> (for various reason) with @escaping being used to both inform the compiler
>> that something is intentionally escaping (yeah I meant to do that) and to
>> me it seem also helpful as a way to inform the user of the API that the
>> closure they are providing is escaping so they better understand the
>> requirements placed on that closure (e.g. it may not be called before the
>> called function returns, possible retain cycle issues, etc.).
>>
>> So when using APIs I would look for @escaping as a way to know I need to
>> think a little more about the behavior of the closure and the func taking
>> the closure that I am calling. If I don't see it I may incorrectly assume
>> it isn't escaping when intact it may have be implicitly escaping.
>>
>> Anyway my thoughts on this...
>>
>> -Shawn
>>
>>
>>
>> On Wed, Sep 7, 2016 at 7:08 PM Shawn Erickson <shawnce at gmail.com> wrote:
>>
>>> It just seems a little strange that @escaping only shows up once in the
>>> following example (real) function of mine yet both of them are escaping.
>>> This fixit message should maybe imply that @escaping isn't needed since the
>>> optional one is already implicitly considered escaping? ...it is kinda
>>> confusing when first jumping over to Swift if at one point you are told you
>>> need to added @escaping (the non-optional one) yet you are told @escaping
>>> isn't valid for the optional one despite it needing to be escaping (e.g.
>>> the fixit doesn't really explain that is already escaping).
>>>
>>>     func httpGet<T>(path: String, parameters: [String:String] = [:],
>>>                     needsAuth: Bool = true, queue: DispatchQueue =
>>> DispatchQueue.main,
>>>                     completion: ObjectFetchCallback<T>?, importer:
>>> @escaping HttpImporterCallback<T>)
>>>
>>> -Shawn
>>>
>>> On Wed, Sep 7, 2016 at 6:51 PM Jacob Bandes-Storch <jtbandes at gmail.com>
>>> wrote:
>>>
>>>> On Wed, Sep 7, 2016 at 5:54 PM, Michael Ilseman via swift-users <
>>>> swift-users at swift.org> wrote:
>>>>
>>>>> I implemented a better (i.e. correct) diagnostic message for this at
>>>>> https://github.com/apple/swift/pull/4670. I want to also do a better
>>>>> diagnostic specifically for aggregate parameters to functions (e.g.
>>>>> optional closures), but that requires more work in the type checker.
>>>>>
>>>>> Basically, @escaping is valid only on closures in function parameter
>>>>> position. The noescape-by-default rule only applies to these closures at
>>>>> function parameter position, otherwise they are escaping. Aggregates, such
>>>>> as enums with associated values (e.g. Optional), tuples, structs, etc., if
>>>>> they have closures, follow the default rules for closures that are not at
>>>>> function parameter position, i.e. they are escaping.
>>>>>
>>>>
>>>> Shouldn't it be possible to allow distinguishing @escaping/@noescape
>>>> for aggregates like these, at least for the simple case of Optional?  (I
>>>> handled optionals in https://github.com/apple/swift/pull/4438 for
>>>> imported function types; see comment
>>>> <https://github.com/apple/swift/pull/4438#issuecomment-243645367>.)
>>>>
>>>>
>>>>>
>>>>> It would be a post-Swift-3 addition to the language to be able to
>>>>> support more robust liveness tracking here. There may be interesting
>>>>> directions to take this, with optional closures being the most common
>>>>> beneficiary.
>>>>>
>>>>> On Sep 7, 2016, at 3:33 PM, Shawn Erickson via swift-users <
>>>>> swift-users at swift.org> wrote:
>>>>>
>>>>> I see https://bugs.swift.org/browse/SR-2324 and
>>>>> https://bugs.swift.org/browse/SR-2444 which looks related to this
>>>>> issue and may explain the error I saw on "the other side" of this.
>>>>>
>>>>>
>>>>>
>>>>> On Wed, Sep 7, 2016 at 3:28 PM Shawn Erickson <shawnce at gmail.com>
>>>>> wrote:
>>>>>
>>>>>> Yeah I actually have a few of those myself that I can no longer do.
>>>>>>
>>>>>> On Wed, Sep 7, 2016 at 3:26 PM Jon Shier <jon at jonshier.com> wrote:
>>>>>>
>>>>>>> Perhaps relatedly, it no longer seems possible to mark typealiased
>>>>>>> closures as @escaping. That was quite handy when you know that closures
>>>>>>> will always be used asynchronously.
>>>>>>>
>>>>>>>
>>>>>>> Jon
>>>>>>>
>>>>>>>
>>>>>>> On Sep 7, 2016, at 6:15 PM, Shawn Erickson via swift-users <
>>>>>>> swift-users at swift.org> wrote:
>>>>>>>
>>>>>>> I should note that this issue also appeared in an earlier variant of
>>>>>>> Swift after the addition of @escaping but I was on vacation so didn't get a
>>>>>>> chance to report it then. It isn't new with the Xcode 8 GM.
>>>>>>>
>>>>>>> On Wed, Sep 7, 2016 at 3:08 PM Shawn Erickson <shawnce at gmail.com>
>>>>>>> wrote:
>>>>>>>
>>>>>>>> I like and fully supported the change to @escaping away from
>>>>>>>> @noescape however in a body of code that I am porting to the latest Swift 3
>>>>>>>> variant (as found in Xcode 8 GM) I am hitting an issue for methods that
>>>>>>>> take an optional completion closure. If optional is involved I can't find a
>>>>>>>> way to apply @escape to the escaping closure. See the following for an
>>>>>>>> basic example...
>>>>>>>>
>>>>>>>> Is their a way to do what I need and/or is this an edge case in the
>>>>>>>> implementation of @escaping?
>>>>>>>>
>>>>>>>> typealias MyCallback = (String)->()
>>>>>>>>
>>>>>>>> // Happy
>>>>>>>> func foo1(bar: String, completion: ((String)->())) {
>>>>>>>>     completion(bar)
>>>>>>>> }
>>>>>>>>
>>>>>>>> // Happy
>>>>>>>> func foo2(bar: String, completion: MyCallback) {
>>>>>>>>     completion(bar)
>>>>>>>> }
>>>>>>>>
>>>>>>>> // Happy
>>>>>>>> func foo3(bar: String, completion: ((String)->())? = nil) {
>>>>>>>>     completion?(bar)
>>>>>>>> }
>>>>>>>>
>>>>>>>> // Happy
>>>>>>>> func foo4(bar: String, completion: MyCallback? = nil) {
>>>>>>>>     completion?(bar)
>>>>>>>> }
>>>>>>>>
>>>>>>>> // Happy
>>>>>>>> func foo5(bar: String, completion: Optional<MyCallback> = nil) {
>>>>>>>>     completion?(bar)
>>>>>>>> }
>>>>>>>>
>>>>>>>> // Happy
>>>>>>>> func foo6(bar: String, completion: @escaping (String)->()) {
>>>>>>>>     completion(bar)
>>>>>>>> }
>>>>>>>>
>>>>>>>> // Happy
>>>>>>>> func foo7(bar: String, completion: @escaping MyCallback) {
>>>>>>>>     completion(bar)
>>>>>>>> }
>>>>>>>>
>>>>>>>> // Unhappy...
>>>>>>>> // "@escaping attribute only applies to function types"
>>>>>>>> func foo8(bar: String, completion: @escaping ((String)->())? = nil)
>>>>>>>> {
>>>>>>>>     completion?(bar)
>>>>>>>> }
>>>>>>>>
>>>>>>>> // Unhappy...
>>>>>>>> // "@escaping attribute only applies to function types"
>>>>>>>> func foo9(bar: String, completion: @escaping MyCallback? = nil) {
>>>>>>>>     completion?(bar)
>>>>>>>> }
>>>>>>>>
>>>>>>>> // Unhappy...
>>>>>>>> // "@escaping attribute only applies to function types"
>>>>>>>> func foo10(bar: String, completion: (@escaping ((String)->()))? =
>>>>>>>> nil) {
>>>>>>>>     completion?(bar)
>>>>>>>> }
>>>>>>>>
>>>>>>>> // Unhappy...
>>>>>>>> // "@escaping attribute only applies to function types"
>>>>>>>> func foo11(bar: String, completion: (@escaping MyCallback)? = nil) {
>>>>>>>>     completion?(bar)
>>>>>>>> }
>>>>>>>>
>>>>>>>> // Unhappy...
>>>>>>>> // "@escaping attribute only applies to function types"
>>>>>>>> func foo12(bar: String, completion: Optional<@escaping MyCallback>
>>>>>>>> = nil) {
>>>>>>>>     completion?(bar)
>>>>>>>> }
>>>>>>>>
>>>>>>> _______________________________________________
>>>>>>> swift-users mailing list
>>>>>>> swift-users at swift.org
>>>>>>> https://lists.swift.org/mailman/listinfo/swift-users
>>>>>>>
>>>>>>>
>>>>>>> _______________________________________________
>>>>> swift-users mailing list
>>>>> swift-users at swift.org
>>>>> https://lists.swift.org/mailman/listinfo/swift-users
>>>>>
>>>>>
>>>>>
>>>>> _______________________________________________
>>>>> swift-users mailing list
>>>>> swift-users at swift.org
>>>>> https://lists.swift.org/mailman/listinfo/swift-users
>>>>>
>>>>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20160909/7b4c9c48/attachment.html>


More information about the swift-users mailing list