# Compiler issue: Escaping closure captures non-escaping parameter 'block'

**URL:** <https://forums.swift.org/t/compiler-issue-escaping-closure-captures-non-escaping-parameter-block/83578>\
**Category:** Using Swift\
**Tags:** closure, escapingclosure\
**Created:** [December 5, 2025, 11:33am UTC](https://forums.swift.org/t/compiler-issue-escaping-closure-captures-non-escaping-parameter-block/83578 "2025-12-05T11:33:48Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![blazej.sleboda](https://avatars.discourse-cdn.com/v4/letter/b/22d042/32.png) [@blazej.sleboda](https://forums.swift.org/u/blazej.sleboda)\
**Post date:** [December 5, 2025, 11:33am UTC](https://forums.swift.org/t/compiler-issue-escaping-closure-captures-non-escaping-parameter-block/83578/1 "2025-12-05T11:33:48Z")

</div>

How can we explain why func executeNOK doesn’t compile but execute1 and executeOk does?

```swift
    func executeOk(block: () -> Void) {
        { block() }()
    }

    func execute1(block: () -> Void) {
        let closure = { block() }
    }

    func executeNOK(block: () -> Void) {
        let closure = { block() } // compiler issue: Escaping closure captures non-escaping parameter 'block'
        closure()
    }

```

Xcode Toolchain 26.1, Swift 6.2.1

---

<div class="post-metadata">

**Author:** ![jamieQ](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jamieq/32/5117_2.png) [@jamieQ](https://forums.swift.org/u/jamieQ)\
**Post date:** [December 5, 2025, 12:47pm UTC](https://forums.swift.org/t/compiler-issue-escaping-closure-captures-non-escaping-parameter-block/83578/2 "2025-12-05T12:47:56Z")

</div>

my best current guess:

```swift
func executeOk(block: () -> Void) {
    { block() }()
}

```

this one compiles because the wrapping closure is also inferred to be non-escaping. this is visible in the AST dump output (see [this](https://swift.godbolt.org/z/9djW5fhY8), and note how there is no `escaping` tag on the corresponding `closure_expr`).

```swift
 func execute1(block: () -> Void) {
    let closure = { block() }
}

```

since the closure is stored in this case, the closure's function type is escaping (variables like this currently are always escaping AFAIK – see discussion [here](https://forums.swift.org/t/is-a-closure-considered-as-non-escaping-if-its-stored-in-local-variable-and-not-passed-to-other-functions/81279/2)). but this compiles because the diagnostics check for an 'interesting' use of the escaped value, and there aren't any here.

```swift
func executeNOK(block: () -> Void) {
    let closure = { block() } // compiler issue: Escaping closure captures non-escaping parameter 'block'
    closure()
}

```

this one is similar to `execute1` but fails to compile because there is a use of the escaping closure that captures the non-escaping parameter. that's treated as a violation of the escaping rules even though in your example it's clearly not actually escaping.

* * *

IIUC, the diagnostic you hit in the final case is implemented in [DiagnoseInvalidEscapingCaptures.cpp](https://github.com/swiftlang/swift/blob/5aba778b01dd6767c85cd69f15153af0315f8687/lib/SILOptimizer/Mandatory/DiagnoseInvalidEscapingCaptures.cpp) if you're interested in the particulars. @Slava_Pestov will hopefully correct any errors in this assessment.

---

<div class="post-metadata">

**Author:** ![jamieQ](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jamieq/32/5117_2.png) [@jamieQ](https://forums.swift.org/u/jamieQ)\
**Post date:** [December 5, 2025, 12:59pm UTC](https://forums.swift.org/t/compiler-issue-escaping-closure-captures-non-escaping-parameter-block/83578/3 "2025-12-05T12:59:56Z")

</div>

ah, and a thing i forgot to mention – if you do want to get cases like `executeNOK` to build, you can use `withoutActuallyEscaping` to temporarily treat the non-escaping closure as if it were escaping:

```swift
func executeNOK(block: () -> Void) {
  withoutActuallyEscaping(block) { block in
    let closure = { block() } // ✅
    closure()
  }
}

```

however, it's then up to you to actually ensure that condition is upheld. as the docs say:

> The escapable copy of `closure` passed to `body` is only valid during the call to `withoutActuallyEscaping(_:do:)` . It is undefined behavior for the escapable closure to be stored, referenced, or executed after the function returns.

---

<div class="post-metadata">

**Author:** ![blazej.sleboda](https://avatars.discourse-cdn.com/v4/letter/b/22d042/32.png) [@blazej.sleboda](https://forums.swift.org/u/blazej.sleboda)\
**Post date:** [December 5, 2025, 2:09pm UTC](https://forums.swift.org/t/compiler-issue-escaping-closure-captures-non-escaping-parameter-block/83578/4 "2025-12-05T14:09:15Z")

</div>

Thank you @jamieQ for the link to withoutActuallyEscaping. My code which lead me to the question on the forum, was exactly as the second example of code snippet in documentation of the withoutActuallyEscaping.

I have tested these variants with withoutActuallyEscaping to get an idea how it works:

```swift
    func execute(block: () -> Void) {
        withoutActuallyEscaping(block) { block in
            myExecuteLaterX(block)
        }
    }

    func myExecuteLater1(_ block: @escaping () -> ()) {
        block()
    }

    func myExecuteLater2(_ block: @escaping () -> ()) {
        sleep(2)
        block()
    }

    func myExecuteLater3(_ block: @escaping () -> ()) {
        DispatchQueue.main.async { block() }
    }

    func myExecuteLater4(_ block: @escaping () -> ()) {
        DispatchQueue.main.asyncAfter(deadline: .now() + 100) {
            block()
        }
    }

```

myExecuteLater1 and 2 execute without crashing.  
myExecuteLater3 crashed at runtime. I tested myExecuteLater4 to check whether the crash happens when passing the closure to the @escaping argument or when the closure is executed later (just to confirm the crash message). The result is that it crashes immediately, at the moment the closure is passed as an argument.

The crash message is:

> The crash is: "Thread 1: closure argument was escaped in withoutActuallyEscaping block:"

**Now, why do we need withoutActuallyEscaping?**

withoutActuallyEscaping is essentially a hack — an unsafe, developer-driven manual decision that can have consequences at runtime.

It seems like the compiler could resolve this automatically, but implementing the logic to determine whether an escaping closure ultimately behaves as non-escaping would require too much complexity. Am I right?

---

<div class="post-metadata">

**Author:** ![jamieQ](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jamieq/32/5117_2.png) [@jamieQ](https://forums.swift.org/u/jamieQ)\
**Post date:** [December 5, 2025, 2:32pm UTC](https://forums.swift.org/t/compiler-issue-escaping-closure-captures-non-escaping-parameter-block/83578/5 "2025-12-05T14:32:34Z")

</div>

> [@blazej.sleboda](#):
>
> The result is that it crashes immediately, at the moment the closure is passed as an argument.

IIRC from looking into this in the [past](https://forums.swift.org/t/question-about-using-withoutactuallyescaping-and-withcheckedcontinuation-together/81974/2), i think there is a runtime check/assert of the refcount of the closure argument at exit of the `withoutActuallyEscaping` function, so i would guess that's the point at which you'd generally expect to see a runtime error (though with optimizations on, idk maybe it moves around or isn't 100% reliable).

> [@blazej.sleboda](#):
>
> why do we need withoutActuallyEscaping?

as i understand it, it's just a tradeoff. in your initial example, the compiler's analysis is conservative and it won't let you express in-actuality-safe code. rather than that just being the end of the story, Swift gives you an out, but it's a 'Spider Man' sort of deal – you take on the responsibility of ensuring things don't escape (well, the runtime tries to 'help' you too, by crashing if you've failed to uphold your end of the bargain).

i would expect as the ownership model and stuff like `~Escapable` types becomes more fully built-out, we will get more precise control over this sort of thing. e.g. if you could just declare that your local variable is `~Escapable` in some manner, then i think your initial example would work as desired without need for 'opting out' of the static safety checks. mabye this is possible today and i'm just unaware of the syntax you'd need to do it? sounds like an somewhat entertaining exercise for the reader 🙂

---

<div class="post-metadata">

**Author:** ![NotTheNHK](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/notthenhk/32/43199_2.png) [@NotTheNHK](https://forums.swift.org/u/NotTheNHK)\
**Post date:** [December 5, 2025, 2:51pm UTC](https://forums.swift.org/t/compiler-issue-escaping-closure-captures-non-escaping-parameter-block/83578/6 "2025-12-05T14:51:23Z")

</div>

This was my exact thought. With the new ownership system, we should be able to more precisely check the validity of such code. And my guess is, that we will see exactly these improvements in future versions, if we look at the evolution of Sendable checking.

---

<div class="post-metadata">

**Author:** ![sveinhal](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/sveinhal/32/3992_2.png) [@sveinhal](https://forums.swift.org/u/sveinhal)\
**Post date:** [December 8, 2025, 1:35pm UTC](https://forums.swift.org/t/compiler-issue-escaping-closure-captures-non-escaping-parameter-block/83578/7 "2025-12-08T13:35:29Z")

</div>

> [@blazej.sleboda](#):
>
> Now, why do we need withoutActuallyEscaping?

Here's a seven year old blog post I wrote, back when I thought I might write a blog:

> **[Without actually escaping](https://sveinhal.github.io/2018/07/02/without-actually-escaping/)**
>
> When dealing with lazy sequences and generic functions, there are subtle pitfalls you need to beaware of. You could easily negate the laziness and end up drastically slowing down your algorithms.

It provides at least one answer for why one might want this escape hatch.

---

<div class="post-metadata">

**Author:** ![blazej.sleboda](https://avatars.discourse-cdn.com/v4/letter/b/22d042/32.png) [@blazej.sleboda](https://forums.swift.org/u/blazej.sleboda)\
**Post date:** [December 8, 2025, 5:19pm UTC](https://forums.swift.org/t/compiler-issue-escaping-closure-captures-non-escaping-parameter-block/83578/8 "2025-12-08T17:19:21Z")

</div>

@sveinhal, I tried your code, and I didn’t get the same behaviour. I also tested older Swift versions (your post was written in 2018), such as 4.2.4, to see whether the compiler behaviour had changed, but the result was the same.

I created an example intended to reproduce the behaviour described in your blog post:

```swift
func myFunc(_ predicate: () -> Void) -> Bool {
  _ = predicate()
  print("non escaping function signature")
  return true
}

func myFunc(_ predicate: @escaping () -> Void) -> String {
  _ = predicate()
  print("escaping function signature")
  return ""
}

func test() {
    withoutActuallyEscaping({}) { _ = myFunc($0) }
    _ = myFunc({})
}

test()

```

After reading your blog, I was expecting the following output:

> "escaping function signature"  
> "non escaping function signature"

But I got:

> "escaping function signature"  
> "escaping function signature"

Here is a SwiftFiddle playground with the code sample: [SwiftFiddle - Swift Online Playground](https://swiftfiddle.com/2zf5lqyll5alvituskahe7qtke)

---

<div class="post-metadata">

**Author:** ![sveinhal](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/sveinhal/32/3992_2.png) [@sveinhal](https://forums.swift.org/u/sveinhal)\
**Post date:** [December 9, 2025, 9:06am UTC](https://forums.swift.org/t/compiler-issue-escaping-closure-captures-non-escaping-parameter-block/83578/9 "2025-12-09T09:06:10Z")

</div>

```swift
func myFunc(_ predicate: () -> Void) -> Bool {
    _ = predicate()
    print("non escaping function signature")
    return true
}

func myFunc(_ predicate: @escaping () -> Void) -> String {
    _ = predicate()
    print("escaping function signature")
    return ""
}

func test(_ nonEscapingClosure: () -> Void) {
    withoutActuallyEscaping(nonEscapingClosure) { _ = myFunc($0) }
    _ = myFunc(nonEscapingClosure)
}

test({})

```

outputs

```swift
escaping function signature
non escaping function signature

```

---

<div class="post-metadata">

**Author:** ![sveinhal](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/sveinhal/32/3992_2.png) [@sveinhal](https://forums.swift.org/u/sveinhal)\
**Post date:** [December 9, 2025, 9:12am UTC](https://forums.swift.org/t/compiler-issue-escaping-closure-captures-non-escaping-parameter-block/83578/10 "2025-12-09T09:12:54Z")

</div>

```swift
extension Sequence {
    var first: Element? {
        var iter = makeIterator()
        return iter.next()
    }
    func firstMatch(_ predicate: (Element) -> Bool) -> Element? {
        self.lazy.filter(predicate).first
    }
    func firstMatchWithoutActuallyEscaping(_ predicate: (Element) -> Bool) -> Element? {
        withoutActuallyEscaping(predicate) { escapingClosure in
            self.lazy.filter(escapingClosure).first
        }
    }
}

let _ = (0..<1_000_000).firstMatch { candidate in
    print("evaluating \(candidate)")
    return true
}

```

prints:

```swift
evaluating 0
evaluating 1
evaluating 2
evaluating 3
...
evaluating 999999

```

```swift
let _ = (0..<1_000_000).firstMatchWithoutActuallyEscaping { candidate in
    print("evaluating \(candidate)")
    return true
}

```

prints

```swift
evaluating 0

```

---

<div class="post-metadata">

**Author:** ![blazej.sleboda](https://avatars.discourse-cdn.com/v4/letter/b/22d042/32.png) [@blazej.sleboda](https://forums.swift.org/u/blazej.sleboda)\
**Post date:** [December 9, 2025, 12:02pm UTC](https://forums.swift.org/t/compiler-issue-escaping-closure-captures-non-escaping-parameter-block/83578/11 "2025-12-09T12:02:52Z")

</div>

Thank you for the example. In your blog post, you used compactMap. Is there any particular reason you switched to filter?

---

<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:** [December 9, 2025, 3:33pm UTC](https://forums.swift.org/t/compiler-issue-escaping-closure-captures-non-escaping-parameter-block/83578/12 "2025-12-09T15:33:03Z")

</div>

> [@jamieQ](#):
>
> It is undefined behavior for the escapable closure to be stored, referenced, or executed after the function returns.

With the new languages features like `~Copyable` or `~Escapable` could this be improved upon to cause a compilation error if user attempts to do anything like that?

---

<div class="post-metadata">

**Author:** ![jamieQ](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jamieq/32/5117_2.png) [@jamieQ](https://forums.swift.org/u/jamieQ)\
**Post date:** [December 9, 2025, 4:09pm UTC](https://forums.swift.org/t/compiler-issue-escaping-closure-captures-non-escaping-parameter-block/83578/13 "2025-12-09T16:09:48Z")

</div>

> [@tera](#):
>
> With the new languages features like `~Copyable` or `~Escapable` could this be improved upon to cause a compilation error if user attempts to do anything like that?

maybe? though this API strikes me as a carve out for circumstances in which you can't easily (or possibly ever) encode the true nature of 'non-escaping-ness' into the type system, but nothing actually escapes at runtime. IMO the dispatch examples from the docs are a good illustration – if you're submitting things async to a concurrent queue, the work item blocks are escaping – there's no real way around that. but if you know you will wait on a barrier block before returning, they're not going to escape the caller's execution. not sure there's really a way for the compiler to necessarily be able to determine that sort of thing statically.

---

<div class="post-metadata">

**Author:** ![sveinhal](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/sveinhal/32/3992_2.png) [@sveinhal](https://forums.swift.org/u/sveinhal)\
**Post date:** [December 10, 2025, 11:22am UTC](https://forums.swift.org/t/compiler-issue-escaping-closure-captures-non-escaping-parameter-block/83578/14 "2025-12-10T11:22:57Z")

</div>

> [@blazej.sleboda](#):
>
> Thank you for the example. In your blog post, you used compactMap. Is there any particular reason you switched to filter?

I wrote that several years ago, and I think it was based on a concrete use case from these forums. However, when I just checked in a playground-doc just now, I used filter just for simplicity.

I'm not sure what has changes in collection/sequence-implementation in the standard library, or in the escaping-analysis in the compiler, since 2018.

One problem I encountered now, was that `first` isn't available on `Sequence`, so calling `first` (without the implementation I provided), caused the type resolver to instead choose a `filter` that returned a `Collection`. That is, the `[Element]`-returning one was chosen, again negating the `.lazy`.
