# Why is implicit self allowed for weak self captures in non-escaping closures?

**URL:** <https://forums.swift.org/t/why-is-implicit-self-allowed-for-weak-self-captures-in-non-escaping-closures/60483>\
**Category:** Discussion\
**Created:** [September 24, 2022, 4:06pm UTC](https://forums.swift.org/t/why-is-implicit-self-allowed-for-weak-self-captures-in-non-escaping-closures/60483 "2022-09-24T16:06:49Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![cal](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/cal/32/35557_2.png) [@cal](https://forums.swift.org/u/cal)\
**Post date:** [September 24, 2022, 4:06pm UTC](https://forums.swift.org/t/why-is-implicit-self-allowed-for-weak-self-captures-in-non-escaping-closures/60483/1 "2022-09-24T16:06:49Z")

</div>

I've been working on landing the implementation of [SE-0365](https://github.com/apple/swift-evolution/blob/main/proposals/0365-implicit-self-weak-capture.md). I previously thought implicit self calls were never allowed for `weak self` captures, but have found that this is actually allowed in non-escaping closures (even when `self` is optional):

For example, this [compiles](https://swiftfiddle.com/tpqnc6t7hncxrpyjenei5qjg5y) in Swift 5.7:

```swift
class Object {
  func getSelf() -> Object {
    self
  }

  func test() {
    doVoidStuff { [weak self] in
      let getSelf: () -> Object = getSelf // refers to self.getSelf
      let nonoptionalSelf: Object = getSelf()
      let optionalSelf: Object? = self
    }
  }
}

func doVoidStuff(_ closure: () -> Void) { closure() }

```

This is surprising to me, considering `self` is optional here so, at a minimum, a reference to a function or property on `self` should have an optional type, right?

I haven't been able to construct an example where this is unsafe, since I think `self` is practically speaking always guaranteed to exist for the lifetime of a non-escaping closure (right?). However it seems incorrect to me based on my understanding of the user-facing model of how `weak self` works.

Does anyone know why this behavior exists? Is this intentional? There aren't any tests for this in the compiler's main test suite (and just a single example in the source compatibility suite) which makes me think this could potentially be a bug.

Even if this is a bug, I don't think we can remove support for it in Swift 5.8 (right?). Would it make sense to remove support for this in Swift 6?

---

<div class="post-metadata">

**Author:** ![cal](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/cal/32/35557_2.png) [@cal](https://forums.swift.org/u/cal)\
**Post date:** [September 24, 2022, 4:27pm UTC](https://forums.swift.org/t/why-is-implicit-self-allowed-for-weak-self-captures-in-non-escaping-closures/60483/2 "2022-09-24T16:27:01Z")

</div>

For reference, this [did not compile](https://swiftfiddle.com/zci34zfzszhhjnvohdhofl6xc4) in Swift 3.x but [_does compile_](https://swiftfiddle.com/nnyflkjf45eb5kyxg766b7pop4) in Swift 4.0 and later. I don't see a relevant evolution proposal from this time period, so I'm not sure why this change was made or if it was intentional.

If this is a bug, can we fix it in Swift 6 without running an evolution proposal?

---

<div class="post-metadata">

**Author:** ![Jumhyn](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jumhyn/32/3891_2.png) [@Jumhyn](https://forums.swift.org/u/Jumhyn)\
**Post date:** [September 24, 2022, 5:35pm UTC](https://forums.swift.org/t/why-is-implicit-self-allowed-for-weak-self-captures-in-non-escaping-closures/60483/3 "2022-09-24T17:35:02Z")

</div>

Hah, wow, good find. What does the usage of this behavior in the source compatibility suite look like? This behavior _seems_ like a straightforward bug to me, so I'd probably be in favor of deprecating it, either in the next Swift version or the next language mode if it seems like the potential for breakage is too high.

> [@cal](#):
>
> I haven't been able to construct an example where this is unsafe, since I think `self` is practically speaking always guaranteed to exist for the lifetime of a non-escaping closure (right?).

Here's a contrived example that exploits the fact that you can have `mutating` methods on classes via protocol extensions:

```swift
protocol P {
  init()
}

extension P {
  mutating func changeSelf() {
    self = Self()
  }
  mutating func runTest() where Self: AnyObject {
    doVoidStuff { [weak self] in
      print(ObjectIdentifier(self!))
      changeSelf()
      print(self as Any)
    }
  }
}

final class Object: P {
  deinit {
    print("deinit \(ObjectIdentifier(self))")
  }
}

func doVoidStuff(_ closure: () -> Void) { closure() }

var obj = Object()
obj.runTest()

```

This prints:

```swift
ObjectIdentifier(0x0000600001060590)
deinit ObjectIdentifier(0x0000600001060590)
nil

```

However, I still don't think there's a safety hole here—I _expect_ that when referencing non-weak `self` implicitly via `getSelf` the compiler will properly reference count this invisible capture. In fact, we can see this happen with the following modification (using your `getSelf` method):

```swift
mutating func runTest() where Self: AnyObject {
  doVoidStuff { [weak self] in
    let method = getSelf
    print(ObjectIdentifier(self!))
    changeSelf()
    print(self as Any)
    print(method())
  }
}

```

This prints:

```swift
ObjectIdentifier(0x0000600003ea4c20)
Optional(__lldb_expr_20.Object)
__lldb_expr_20.Object
deinit ObjectIdentifier(0x0000600003ea4c20)

```

---

<div class="post-metadata">

**Author:** ![cal](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/cal/32/35557_2.png) [@cal](https://forums.swift.org/u/cal)\
**Post date:** [September 25, 2022, 3:05pm UTC](https://forums.swift.org/t/why-is-implicit-self-allowed-for-weak-self-captures-in-non-escaping-closures/60483/4 "2022-09-25T15:05:46Z")

</div>

> [@Jumhyn](#):
>
> What does the usage of this behavior in the source compatibility suite look like?

Here's an example from CareKit ([link](https://github.com/carekit-apple/CareKit/blob/main/CareKit/CareKit/iOS/Contact/View%20Controllers/OCKContactViewController.swift#L226)), which is the only project in the source compatibility suite that does this:

```swift
open func didSelectContactView(_ contactView: UIView & OCKContactDisplayable) {
    handleThrowable(method: controller.initiateSystemContactLookup) { [weak self] contactViewController in
        contactViewController.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self,
                                                                                  action: #selector(dismissViewController))
        let navigationController = UINavigationController(rootViewController: contactViewController)
        present(navigationController, animated: true, completion: nil)
    }
}

```

The `#selector(dismissViewController)` and `present(navigationController, animated: true, completion: nil)` calls both refer to instance methods on `self`.

According to the behavior described in [SE-0365](https://github.com/apple/swift-evolution/blob/main/proposals/0365-implicit-self-weak-capture.md), using implicit self like this on a `weak self` capture is valid but _only_ after `self` has been unwrapped and is no longer an optional type.

The existing implementation of SE-0365 actually correctly (imo) rejects this code as invalid. I think the best thing to do here is probably to continue accept this code in the Swift 5 language mode (with a warning), and then reject it as an error in Swift 6. I can try to make this change as a part of landing SE-0365.

* * *

On the implementation side of things, I'm not _entirely sure_ it's possible to continue supporting this for non-escaping closures while also supporting the behavior described in SE-0365 for escaping closures. Supporting the new behavior requires changes to [AST lookup](https://github.com/apple/swift/pull/40702/files#diff-b1bc7eec55227e29ae2bdc9779550f6cb16d803cab5ecce6822fc5f93bc17921R393), which I'm not sure we can make conditional on whether or not the closure is non-escaping (since the closure itself isn't type-checked yet at this point, afaict). Continuing to investigate options here, though.

@Jumhyn -- if this bug blocks landing the implementation of SE-0365, do you think it would make sense to fix this bug in Swift 5.8 instead of Swift 6 (so we can land SE-0365 in Swift 5.8), or do you think we'd need to delay SE-0365 until Swift 6?

edit: good news, I was able to [update](https://github.com/apple/swift/pull/40702#issuecomment-1257286036) the implementation of SE-0365 to handle this in Swift 5.8 without breaking existing code (but not allow this in Swift 6). Hopefully we're able to land that soon!

---

<div class="post-metadata">

**Author:** ![Jumhyn](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jumhyn/32/3891_2.png) [@Jumhyn](https://forums.swift.org/u/Jumhyn)\
**Post date:** [September 25, 2022, 11:03pm UTC](https://forums.swift.org/t/why-is-implicit-self-allowed-for-weak-self-captures-in-non-escaping-closures/60483/5 "2022-09-25T23:03:21Z")

</div>

> [@cal](#):
>
> edit: good news, I was able to [update](https://github.com/apple/swift/pull/40702#issuecomment-1257286036) the implementation of SE-0365 to handle this in Swift 5.8 without breaking existing code (but not allow this in Swift 6). Hopefully we're able to land that soon!

Great, glad to hear it. I think warning in Swift 5.8 and erroring in Swift 6 seems like a reasonable approach.

Tangentially, I actually kind of feel like the `#selector` part here should work? We're not capturing `self` in that case (I hope? 😬) so the logic around diagnosing implicit `self` doesn't really make sense to me. But as you note it looks like we reject this for escaping closures and it's easy enough I suppose to write `self?.method` or `Self.method` so meh.

---

<div class="post-metadata">

**Author:** ![tera](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tera/32/38201_2.png) [@tera](https://forums.swift.org/u/tera)\
**Post date:** [September 25, 2022, 11:28pm UTC](https://forums.swift.org/t/why-is-implicit-self-allowed-for-weak-self-captures-in-non-escaping-closures/60483/6 "2022-09-25T23:28:29Z")

</div>

I wonder if the "doVoidStuff { [weak self] in" part itself should be allowed in case of non escaping closures. It's kind of a lie to begin with.

---

<div class="post-metadata">

**Author:** ![cal](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/cal/32/35557_2.png) [@cal](https://forums.swift.org/u/cal)\
**Post date:** [September 25, 2022, 11:29pm UTC](https://forums.swift.org/t/why-is-implicit-self-allowed-for-weak-self-captures-in-non-escaping-closures/60483/7 "2022-09-25T23:29:54Z")

</div>

> [@Jumhyn](#):
>
> Tangentially, I actually kind of feel like the `#selector` part here should work?

I agree in principle that it could be reasonable for the `#selector` pattern to work even when `self` is not unwrapped yet (since the instance member lookup is really just a side-effect of the spelling of this syntax). However, this isn't permitted in Swift 5.7 if the closure is `@escaping`, and I think it's probably best to be consistent with that.

> [@tera](#):
>
> I wonder if the "doVoidStuff { [weak self] in" part itself should be allowed in case of non escaping closures. It's kind of a lie to begin with.

Agreed -- is there any real reason to use a `weak self` capture in a non-escaping closure? I wonder if it would make sense to have a warning in this case (or disallow it completely in Swift 6).

---

<div class="post-metadata">

**Author:** ![Jumhyn](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jumhyn/32/3891_2.png) [@Jumhyn](https://forums.swift.org/u/Jumhyn)\
**Post date:** [September 25, 2022, 11:31pm UTC](https://forums.swift.org/t/why-is-implicit-self-allowed-for-weak-self-captures-in-non-escaping-closures/60483/8 "2022-09-25T23:31:16Z")

</div>

> [@cal](#):
>
> However, this isn't permitted in Swift 5.7 if the closure is `@escaping`, and I think it's probably best to be consistent with that.

Yup, agreed that this is totally orthogonal.

---

<div class="post-metadata">

**Author:** ![Jumhyn](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jumhyn/32/3891_2.png) [@Jumhyn](https://forums.swift.org/u/Jumhyn)\
**Post date:** [September 25, 2022, 11:36pm UTC](https://forums.swift.org/t/why-is-implicit-self-allowed-for-weak-self-captures-in-non-escaping-closures/60483/9 "2022-09-25T23:36:41Z")

</div>

> [@cal](#):
>
> > [@tera](#):
> >
> > I wonder if the "doVoidStuff { [weak self] in" part itself should be allowed in case of non escaping closures. It's kind of a lie to begin with.
> 
> Agreed -- is there any real reason to use a `weak self` capture in a non-escaping closure? I wonder if it would make sense to have a warning in this case (or disallow it completely in Swift 6).

It's not _really_ a lie since as noted above you can contrive a setup where the `weak`-ness of the capture actually does matter, but from within a non-`mutating` method that's actually defined on the class in question I can't think of any situation where this would realistically matter... maybe if we got more aggressive about optimizing lifetimes in a way that could cause `self` to go away before the end of a method?
