Reason for call only non-escaping parameter


(Dimitri Racordon) #1

Hi everyone,

I failed to find the reason why Swift does not allows a non-escaping parameter to be assigned to a local variable. Here is a minimal example:

func f(_ closure: () -> Int) {
    let a = closure
}

I do understand that assigning a non-escaping closure to a variable whose lifetime exceeds that of the function would (by definition) violate the non-escaping property. For instance, doing that is understandably illegal:

var global = { 0 }
func f(_ closure: () -> Int) {
    global = closure
}

But in my first example, since `a` is stack allocated, there’s no risk that `closure` escapes the scope of `f`.

Is there some use case I’m missing, where such assignment could be problematic?
Or is this a limitation of the compiler, which wouldn't go all the way to check whether the lifetime of the assignee is compatible with that of the non-escaping parameter may exceed that of the variable it is assigned to?

Thank you very much for your time and your answer.

Best,
Dimitri Racordon


(John McCall) #2

Examples like yours, where a non-escaping closure parameter has a new constant name bound to it, are supportable but rather pointless — as a programmer, why have two names for the same value? Examples that would be more useful, like assigning the closure into a local variable or allowing it to be used in a more complex expression (like ? :), complicate the analysis for non-escaping closures in a way that would significantly subvert their purpose.

John.

···

On May 31, 2017, at 12:21 PM, Dimitri Racordon via swift-dev <swift-dev@swift.org> wrote:
Hi everyone,

I failed to find the reason why Swift does not allows a non-escaping parameter to be assigned to a local variable. Here is a minimal example:

func f(_ closure: () -> Int) {
    let a = closure
}

I do understand that assigning a non-escaping closure to a variable whose lifetime exceeds that of the function would (by definition) violate the non-escaping property. For instance, doing that is understandably illegal:

var global = { 0 }
func f(_ closure: () -> Int) {
    global = closure
}

But in my first example, since `a` is stack allocated, there’s no risk that `closure` escapes the scope of `f`.

Is there some use case I’m missing, where such assignment could be problematic?
Or is this a limitation of the compiler, which wouldn't go all the way to check whether the lifetime of the assignee is compatible with that of the non-escaping parameter may exceed that of the variable it is assigned to?

Thank you very much for your time and your answer.


(Dimitri Racordon) #3

Thanks for your answer.

I agree that it may not be the most useful feature (although I’m sure we could find not-so-contrived yet useful use-cases). Anyway, I guess that discussion would rather belong to the evolution list :slight_smile:

I was more wondering if there were situations where such local assignments would have to be disallowed.

Best,
Dimitri

···

On 31 May 2017, at 22:10, John McCall <rjmccall@apple.com<mailto:rjmccall@apple.com>> wrote:

On May 31, 2017, at 12:21 PM, Dimitri Racordon via swift-dev <swift-dev@swift.org<mailto:swift-dev@swift.org>> wrote:
Hi everyone,

I failed to find the reason why Swift does not allows a non-escaping parameter to be assigned to a local variable. Here is a minimal example:

func f(_ closure: () -> Int) {
    let a = closure
}

I do understand that assigning a non-escaping closure to a variable whose lifetime exceeds that of the function would (by definition) violate the non-escaping property. For instance, doing that is understandably illegal:

var global = { 0 }
func f(_ closure: () -> Int) {
    global = closure
}

But in my first example, since `a` is stack allocated, there’s no risk that `closure` escapes the scope of `f`.

Is there some use case I’m missing, where such assignment could be problematic?
Or is this a limitation of the compiler, which wouldn't go all the way to check whether the lifetime of the assignee is compatible with that of the non-escaping parameter may exceed that of the variable it is assigned to?

Thank you very much for your time and your answer.

Examples like yours, where a non-escaping closure parameter has a new constant name bound to it, are supportable but rather pointless — as a programmer, why have two names for the same value? Examples that would be more useful, like assigning the closure into a local variable or allowing it to be used in a more complex expression (like ? :), complicate the analysis for non-escaping closures in a way that would significantly subvert their purpose.

John.


(John McCall) #4

Thanks for your answer.

I agree that it may not be the most useful feature (although I’m sure we could find not-so-contrived yet useful use-cases). Anyway, I guess that discussion would rather belong to the evolution list :slight_smile:

I was more wondering if there were situations where such local assignments would have to be disallowed.

I was trying to answer that question; perhaps I did a poor job of explaining, and some examples would help.

This should always be fine, and in principle we could allow it specifically:
  let function = someParameter

This would probably also be okay:
  let function = { someClosure }

This would be a problem:
  var function = someParameter

As would this:
  let function = (someCondition ? someParameter : someOtherParameter)

John.

···

On May 31, 2017, at 1:17 PM, Dimitri Racordon via swift-dev <swift-dev@swift.org> wrote:

Best,
Dimitri

On 31 May 2017, at 22:10, John McCall <rjmccall@apple.com <mailto:rjmccall@apple.com>> wrote:

On May 31, 2017, at 12:21 PM, Dimitri Racordon via swift-dev <swift-dev@swift.org <mailto:swift-dev@swift.org>> wrote:
Hi everyone,

I failed to find the reason why Swift does not allows a non-escaping parameter to be assigned to a local variable. Here is a minimal example:

func f(_ closure: () -> Int) {
    let a = closure
}

I do understand that assigning a non-escaping closure to a variable whose lifetime exceeds that of the function would (by definition) violate the non-escaping property. For instance, doing that is understandably illegal:

var global = { 0 }
func f(_ closure: () -> Int) {
    global = closure
}

But in my first example, since `a` is stack allocated, there’s no risk that `closure` escapes the scope of `f`.

Is there some use case I’m missing, where such assignment could be problematic?
Or is this a limitation of the compiler, which wouldn't go all the way to check whether the lifetime of the assignee is compatible with that of the non-escaping parameter may exceed that of the variable it is assigned to?

Thank you very much for your time and your answer.

Examples like yours, where a non-escaping closure parameter has a new constant name bound to it, are supportable but rather pointless — as a programmer, why have two names for the same value? Examples that would be more useful, like assigning the closure into a local variable or allowing it to be used in a more complex expression (like ? :), complicate the analysis for non-escaping closures in a way that would significantly subvert their purpose.

John.

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


(Dimitri Racordon) #5

Sorry, your answer was perfectly fine. I’m the one who did a poor job acknowledging it :slight_smile:

I totally see how semantic analysis of such lifetimes may represent a complex challenge. In fact, if I’m not mistaking, the “non-escapability” of `f` in `let f = c ? nonEscParam : escParam` isn’t even statically decidable. One simple (but maybe too naïve?) solution might be to consider `f` as non-escaping as soon as there’s one path in the CFG where it’s assigned to an non-escaping closure. That way we could still disallow things like:

let f = c ? nonEscParam : escParam
self.callback = f // <- assignment to possibly non-escaping closure

But even so, the added complexity to the escape analysis is undeniable.

Once again, thanks for your answer.

Best,
Dimitri

···

On 31 May 2017, at 22:20, John McCall <rjmccall@apple.com<mailto:rjmccall@apple.com>> wrote:

On May 31, 2017, at 1:17 PM, Dimitri Racordon via swift-dev <swift-dev@swift.org<mailto:swift-dev@swift.org>> wrote:
Thanks for your answer.

I agree that it may not be the most useful feature (although I’m sure we could find not-so-contrived yet useful use-cases). Anyway, I guess that discussion would rather belong to the evolution list :slight_smile:

I was more wondering if there were situations where such local assignments would have to be disallowed.

I was trying to answer that question; perhaps I did a poor job of explaining, and some examples would help.

This should always be fine, and in principle we could allow it specifically:
  let function = someParameter

This would probably also be okay:
  let function = { someClosure }

This would be a problem:
  var function = someParameter

As would this:
  let function = (someCondition ? someParameter : someOtherParameter)

John.

Best,
Dimitri

On 31 May 2017, at 22:10, John McCall <rjmccall@apple.com<mailto:rjmccall@apple.com>> wrote:

On May 31, 2017, at 12:21 PM, Dimitri Racordon via swift-dev <swift-dev@swift.org<mailto:swift-dev@swift.org>> wrote:
Hi everyone,

I failed to find the reason why Swift does not allows a non-escaping parameter to be assigned to a local variable. Here is a minimal example:

func f(_ closure: () -> Int) {
    let a = closure
}

I do understand that assigning a non-escaping closure to a variable whose lifetime exceeds that of the function would (by definition) violate the non-escaping property. For instance, doing that is understandably illegal:

var global = { 0 }
func f(_ closure: () -> Int) {
    global = closure
}

But in my first example, since `a` is stack allocated, there’s no risk that `closure` escapes the scope of `f`.

Is there some use case I’m missing, where such assignment could be problematic?
Or is this a limitation of the compiler, which wouldn't go all the way to check whether the lifetime of the assignee is compatible with that of the non-escaping parameter may exceed that of the variable it is assigned to?

Thank you very much for your time and your answer.

Examples like yours, where a non-escaping closure parameter has a new constant name bound to it, are supportable but rather pointless — as a programmer, why have two names for the same value? Examples that would be more useful, like assigning the closure into a local variable or allowing it to be used in a more complex expression (like ? :), complicate the analysis for non-escaping closures in a way that would significantly subvert their purpose.

John.

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