Making capturing semantics of local functions explicit

I got bit again by a sneaky memory leak concerning local functions and would like to discuss a small language change. I vaguely remember this being discussed in the past, but can’t find the thread (if anybody could point me to it, I’d appreciate it). Basically, here’s an example of the leak:

class A {
    func foo() {
        func local() {
            bar()
        }
    
        methodWithEscapingClosure { [unowned self] _ in
            self.bar()
            local() // this leaks because local captures self
        }
    }
    
    func bar() {
    }
}

Its sneaky because local’s capturing of self is not obvious if you’ve trained your brain to watch out for calls prefixed with self. I would suggest having the compiler force users to make self capturing explicit, the same way it does for closures:

class A {
    func foo() {
        func local() {
            bar() // error: Call to method ‘bar' in function ‘local' requires explicit 'self.' to make capture semantics explicit
        }
    
  // ...
    }
}

What do you think?

David.

Works for me

- Dave Sweeris

···

On Oct 25, 2017, at 4:41 AM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

I got bit again by a sneaky memory leak concerning local functions and would like to discuss a small language change. I vaguely remember this being discussed in the past, but can’t find the thread (if anybody could point me to it, I’d appreciate it). Basically, here’s an example of the leak:

class A {
    func foo() {
        func local() {
            bar()
        }
    
        methodWithEscapingClosure { [unowned self] _ in
            self.bar()
            local() // this leaks because local captures self
        }
    }
    
    func bar() {
    }
}

Its sneaky because local’s capturing of self is not obvious if you’ve trained your brain to watch out for calls prefixed with self. I would suggest having the compiler force users to make self capturing explicit, the same way it does for closures:

class A {
    func foo() {
        func local() {
            bar() // error: Call to method ‘bar' in function ‘local' requires explicit 'self.' to make capture semantics explicit
        }
    
  // ...
    }
}

What do you think?

I got bit again by a sneaky memory leak concerning local functions and would like to discuss a small language change. I vaguely remember this being discussed in the past, but can’t find the thread (if anybody could point me to it, I’d appreciate it). Basically, here’s an example of the leak:

class A {
    func foo() {
        func local() {
            bar()
        }
    
        methodWithEscapingClosure { [unowned self] _ in
            self.bar()
            local() // this leaks because local captures self
        }
    }
    
    func bar() {
    }
}

Its sneaky because local’s capturing of self is not obvious if you’ve trained your brain to watch out for calls prefixed with self. I would suggest having the compiler force users to make self capturing explicit, the same way it does for closures:

I think this is a good idea. Ideally the proposal would also allow explicit capture lists in local functions.

John.

···

On Oct 25, 2017, at 7:41 AM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

class A {
    func foo() {
        func local() {
            bar() // error: Call to method ‘bar' in function ‘local' requires explicit 'self.' to make capture semantics explicit
        }
    
  // ...
    }
}

What do you think?

David.

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

i support this™

···

On Wed, Oct 25, 2017 at 6:41 AM, David Hart via swift-evolution < swift-evolution@swift.org> wrote:

I got bit again by a sneaky memory leak concerning local functions and
would like to discuss a small language change. I vaguely remember this
being discussed in the past, but can’t find the thread (if anybody could
point me to it, I’d appreciate it). Basically, here’s an example of the
leak:

class A {
    func foo() {
        func local() {
            bar()
        }

        methodWithEscapingClosure { [unowned self] _ in
            self.bar()
            local() // this leaks because local captures self }
    }

    func bar() {
    }
}

Its sneaky because local’s capturing of self is not obvious if you’ve
trained your brain to watch out for calls prefixed with self. I would
suggest having the compiler force users to make self capturing explicit,
the same way it does for closures:

class A {
    func foo() {
        func local() {
            bar() // error: Call to method ‘bar' in function ‘local' requires explicit 'self.' to make capture semantics explicit
        }
      // ...
    }
}

What do you think?

David.

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

Does the compiler treat a nested function differently from a closure in any
meaningful way? (I suppose they might have symbols emitted in the symbol
table, whereas a closure assigned to a local variable wouldn't?)

It definitely seems odd that you can write two functionally equivalent
pieces of code (one with a closure, one with a nested function) and can
only address the capturing semantics in one of them. Unifying these seems
like a good idea.

···

On Wed, Oct 25, 2017 at 10:12 AM David Sweeris via swift-evolution < swift-evolution@swift.org> wrote:

On Oct 25, 2017, at 4:41 AM, David Hart via swift-evolution < > swift-evolution@swift.org> wrote:

I got bit again by a sneaky memory leak concerning local functions and
would like to discuss a small language change. I vaguely remember this
being discussed in the past, but can’t find the thread (if anybody could
point me to it, I’d appreciate it). Basically, here’s an example of the
leak:

class A {
    func foo() {
        func local() {
            bar()
        }

        methodWithEscapingClosure { [unowned self] _ in
            self.bar()
            local() // this leaks because local captures self }
    }

    func bar() {
    }
}

Its sneaky because local’s capturing of self is not obvious if you’ve
trained your brain to watch out for calls prefixed with self. I would
suggest having the compiler force users to make self capturing explicit,
the same way it does for closures:

class A {
    func foo() {
        func local() {
            bar() // error: Call to method ‘bar' in function ‘local' requires explicit 'self.' to make capture semantics explicit
        }
      // ...
    }
}

What do you think?

Works for me

- Dave Sweeris

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

I got bit again by a sneaky memory leak concerning local functions and would like to discuss a small language change. I vaguely remember this being discussed in the past, but can’t find the thread (if anybody could point me to it, I’d appreciate it). Basically, here’s an example of the leak:

class A {
    func foo() {
        func local() {
            bar()
        }
    
        methodWithEscapingClosure { [unowned self] _ in
            self.bar()
            local() // this leaks because local captures self
        }
    }
    
    func bar() {
    }
}

Its sneaky because local’s capturing of self is not obvious if you’ve trained your brain to watch out for calls prefixed with self. I would suggest having the compiler force users to make self capturing explicit, the same way it does for closures:

I think this is a good idea. Ideally the proposal would also allow explicit capture lists in local functions.

Ideally, yes. But the only sensible syntax I can come up for that seems odd in the context of functions:

class A {
    func foo() {
        func local() -> Int { [weak self] in
        }
    }
}

Don’t you think?

David.

···

On 25 Oct 2017, at 19:01, John McCall <rjmccall@apple.com> wrote:

On Oct 25, 2017, at 7:41 AM, David Hart via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

John.

class A {
    func foo() {
        func local() {
            bar() // error: Call to method ‘bar' in function ‘local' requires explicit 'self.' to make capture semantics explicit
        }
    
  // ...
    }
}

What do you think?

David.

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

Hi,

I got bit again by a sneaky memory leak concerning local functions and would like to discuss a small language change. I vaguely remember this being discussed in the past, but can’t find the thread (if anybody could point me to it, I’d appreciate it). Basically, here’s an example of the leak:

class A
{
    
func foo
() {
        
func local
() {
            
bar
()
        }
    
        methodWithEscapingClosure { [
unowned self] _ in

self.bar
()
            
local() // this leaks because local captures self
        }
    }
    
func bar
() {
    }
}

Its sneaky because local’s capturing of self is not obvious if you’ve trained your brain to watch out for calls prefixed with self. I would suggest having the compiler force users to make self capturing explicit, the same way it does for closures:

I think this is a good idea. Ideally the proposal would also allow explicit capture lists in local functions.

Yes, please. Optionally allowing exhaustive capture lists would be great. I think they work really well as compiler checked documentation too.
Sometimes a programmer makes sure that a given closure doesn't capture something and maybe even adds a comment explaining that and why this might be important. Still, it's fairly easy for another programmer to accidentally capture something without even noticing. And given that we mostly review on diffs with limited context it's even possible that this change slips though the review as the comment doesn't necessarily appear in the review.

With optional exhaustive capture lists, this problem would go away because a) a programmer is more likely to think about the consequences when changing the capture list b) it's obvious in code review.
That's especially true if capture lists are optional because the mere fact that there is a capture list suggests that the code might rely on the very captures only.

-- Johannes

···

On 25 Oct 2017, at 6:01 pm, John McCall via swift-evolution <swift-evolution@swift.org> wrote:

On Oct 25, 2017, at 7:41 AM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

John.

class A
{
    
func foo
() {
        
func local
() {
            
bar
() // error: Call to method ‘bar' in function ‘local' requires explicit 'self.' to make capture semantics explicit
        }
    
// ...
    }
}

What do you think?

David.

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

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

Yes, this issue (and variants of it) have come up lots of times (https://www.google.de/search?q=nested+function+noescape+site:lists.swift.org <https://www.google.de/search?q=nested+function+noescape+site:lists.swift.org>)

There’s also SR-2274 (https://bugs.swift.org/browse/SR-2274)

Personally, I consider it a bug and definitely support fixing it.

- Karl

···

On 25. Oct 2017, at 13:41, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

I got bit again by a sneaky memory leak concerning local functions and would like to discuss a small language change. I vaguely remember this being discussed in the past, but can’t find the thread (if anybody could point me to it, I’d appreciate it). Basically, here’s an example of the leak:

class A {
    func foo() {
        func local() {
            bar()
        }
    
        methodWithEscapingClosure { [unowned self] _ in
            self.bar()
            local() // this leaks because local captures self
        }
    }
    
    func bar() {
    }
}

Its sneaky because local’s capturing of self is not obvious if you’ve trained your brain to watch out for calls prefixed with self. I would suggest having the compiler force users to make self capturing explicit, the same way it does for closures:

class A {
    func foo() {
        func local() {
            bar() // error: Call to method ‘bar' in function ‘local' requires explicit 'self.' to make capture semantics explicit
        }
    
  // ...
    }
}

What do you think?

David.

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

class A {
    func foo() -> ()->String {
        func local() -> String { [weak self] in
            return "foo \(self.i)"
        }
        return local
    }
...

compares well with this...

class A {
    func foo() -> ()->String {
        let local : () -> String = { [weak self] in
            return "foo \(self?.i)"
        }
        return local
    }
    func foo2() -> ()->String {
        let local : () -> String = {
            return "foo2 \(self.i)"
        }
        return local
    }
    let i : Int
    init(_ i: Int) { self.i = i }
}
var a = A(2)
let b = a.foo()
a = A(4)
print(b()) // prints "foo nil"

let b2 = a.foo2()
a = A(6)
print(b2()) // prints "foo2 4"

···

--
C. Keith Ray

* https://leanpub.com/wepntk <- buy my book?
* http://www.thirdfoundationsw.com/keith_ray_resume_2014_long.pdf
* http://agilesolutionspace.blogspot.com/

On Oct 25, 2017, at 1:21 PM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

class A { func foo() { func local() -> Int { [weak self] in } } }

I got bit again by a sneaky memory leak concerning local functions and would like to discuss a small language change. I vaguely remember this being discussed in the past, but can’t find the thread (if anybody could point me to it, I’d appreciate it). Basically, here’s an example of the leak:

class A {
    func foo() {
        func local() {
            bar()
        }
    
        methodWithEscapingClosure { [unowned self] _ in
            self.bar()
            local() // this leaks because local captures self
        }
    }
    
    func bar() {
    }
}

Its sneaky because local’s capturing of self is not obvious if you’ve trained your brain to watch out for calls prefixed with self. I would suggest having the compiler force users to make self capturing explicit, the same way it does for closures:

I think this is a good idea. Ideally the proposal would also allow explicit capture lists in local functions.

Ideally, yes. But the only sensible syntax I can come up for that seems odd in the context of functions:

class A {
    func foo() {
        func local() -> Int { [weak self] in
        }
    }
}

Don’t you think?

You could leave the "in" off, but it's only a little weird to have it, and the inconsistency would probably be worse.

John.

···

On Oct 25, 2017, at 4:21 PM, David Hart <david@hartbit.com> wrote:

On 25 Oct 2017, at 19:01, John McCall <rjmccall@apple.com <mailto:rjmccall@apple.com>> wrote:

On Oct 25, 2017, at 7:41 AM, David Hart via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

David.

John.

class A {
    func foo() {
        func local() {
            bar() // error: Call to method ‘bar' in function ‘local' requires explicit 'self.' to make capture semantics explicit
        }
    
  // ...
    }
}

What do you think?

David.

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

I agree; I think the 'in' should be there. This should be fairly
straight-forward to implement right? It's just alternative syntax over
existing behavior. (thinking about the proposal now...)

···

On Wed, Oct 25, 2017 at 5:45 PM, John McCall via swift-evolution < swift-evolution@swift.org> wrote:

On Oct 25, 2017, at 4:21 PM, David Hart <david@hartbit.com> wrote:

On 25 Oct 2017, at 19:01, John McCall <rjmccall@apple.com> wrote:

On Oct 25, 2017, at 7:41 AM, David Hart via swift-evolution < > swift-evolution@swift.org> wrote:
I got bit again by a sneaky memory leak concerning local functions and
would like to discuss a small language change. I vaguely remember this
being discussed in the past, but can’t find the thread (if anybody could
point me to it, I’d appreciate it). Basically, here’s an example of the
leak:

class A {
    func foo() {
        func local() {
            bar()
        }

        methodWithEscapingClosure { [unowned self] _ in
            self.bar()
            local() // this leaks because local captures self }
    }

    func bar() {
    }
}

Its sneaky because local’s capturing of self is not obvious if you’ve
trained your brain to watch out for calls prefixed with self. I would
suggest having the compiler force users to make self capturing explicit,
the same way it does for closures:

I think this is a good idea. Ideally the proposal would also allow
explicit capture lists in local functions.

Ideally, yes. But the only sensible syntax I can come up for that seems
odd in the context of functions:

class A {
    func foo() {
        func local() -> Int { [weak self] in
        }
    }
}

Don’t you think?

You could leave the "in" off, but it's only a little weird to have it, and
the inconsistency would probably be worse.

John.

David.

John.

class A {
    func foo() {
        func local() {
            bar() // error: Call to method ‘bar' in function ‘local' requires explicit 'self.' to make capture semantics explicit
        }
      // ...
    }
}

What do you think?

David.

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

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