Swift 3 (Xcode 8 GM) issue with @escaping


(Shawn Erickson) #1

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)
}


(Shawn Erickson) #2

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@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)
}


(Jon Shier) #3

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@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@gmail.com <mailto:shawnce@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Shawn Erickson) #4

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@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@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@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Shawn Erickson) #5

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@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@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@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@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Michael Ilseman) #6

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.

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@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@gmail.com <mailto:shawnce@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@jonshier.com <mailto:jon@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@swift.org <mailto:swift-users@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@gmail.com <mailto:shawnce@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@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Jacob Bandes-Storch) #7

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>.)

···

On Wed, Sep 7, 2016 at 5:54 PM, Michael Ilseman via swift-users < swift-users@swift.org> wrote:

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@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@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@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@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@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________

swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Shawn Erickson) #8

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@gmail.com> wrote:

On Wed, Sep 7, 2016 at 5:54 PM, Michael Ilseman via swift-users < > swift-users@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@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@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@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@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@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________

swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Michael Ilseman) #9

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>.)

Yes it is possible (but *not* in time for Swift 3) to address this. Optional at the very least would be the highest bang for our buck, along with other single-type aggregate structures. A general solution would require some kind of aggregate liveness solution, but we can get something very good with a bit less: we could express such things with the ‘@escaping’ still being at the immediate function parameter position, and applying it throughout aggregate types in a principled fashion. E.g.:

func foo(_ a: @escaping ((Int) -> Int)?) {}
func foo(_ a: @escaping (Int, (Int) -> Int)) {}
func foo(_ a: @escaping ((Int) -> Int, (Int) -> Int)) {} // both are escaping
func foo(_ a: @escaping [(Int) -> Int]? {} // they are all escaping

Struct aggregate members and protocol associated types would not be impacted by this, as their member types are not listed in their signature, only tuples and generic type parameters (modulo sugar).

This would re-enforce that it’s about the parameter position specifically being a special case. Also, I don’t think there’s any practical benefit to finer grained escapability, e.g. some tuple elements are escaping and some are not. Though, at this point, maybe it makes more sense being a parameter attribute, rather than a type attribute…

Any solution should be discussed further on swift-evolution.

···

On Sep 7, 2016, at 6:50 PM, Jacob Bandes-Storch <jtbandes@gmail.com> wrote:
On Wed, Sep 7, 2016 at 5:54 PM, Michael Ilseman via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

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@swift.org <mailto:swift-users@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@gmail.com <mailto:shawnce@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@jonshier.com <mailto:jon@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@swift.org <mailto:swift-users@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@gmail.com <mailto:shawnce@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@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users


(Shawn Erickson) #10

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@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@gmail.com> > wrote:

On Wed, Sep 7, 2016 at 5:54 PM, Michael Ilseman via swift-users < >> swift-users@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@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@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@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@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@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________

swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Shawn Erickson) #11

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@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@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@gmail.com> >> wrote:

On Wed, Sep 7, 2016 at 5:54 PM, Michael Ilseman via swift-users < >>> swift-users@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@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@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@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@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@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________

swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Shawn Erickson) #12

Oh I also meant to state that original version of this visitor was marked
as @noescape since it doesn't in the implementation. It looks like I have
lost that ability with the @escaping change since appears to assume it is
escaping in this situation now?

-Shawn

···

On Wed, Sep 7, 2016 at 9:16 PM Shawn Erickson <shawnce@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@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@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@gmail.com> >>> wrote:

On Wed, Sep 7, 2016 at 5:54 PM, Michael Ilseman via swift-users < >>>> swift-users@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@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@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@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@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@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________

swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Shawn Erickson) #13

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@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@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@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@gmail.com> >>> wrote:

On Wed, Sep 7, 2016 at 5:54 PM, Michael Ilseman via swift-users < >>>> swift-users@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@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@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@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@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@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________

swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users