A closure can capture its own variable?

I ran into a logic issue today caused by a variable being used inside a closure used for initializing it. Is this supposed to be allowed?

import Foundation

let item: [String:Any] = [
    "date": "Not a date"
]
let a: Date? = (item["date"] as? String).flatMap {
    dump(a)
    return ISO8601DateFormatter().date(from: $0)
}
print(a?.description ?? "Did not parse date")

Apple Swift version 6.1.2 (swift-6.1.2-RELEASE)

▿ Optional(2001-01-01 00:00:00 +0000)
  ▿ some: 2001-01-01 00:00:00 +0000
    - timeIntervalSinceReferenceDate: 0.0
Did not parse date

I think this is a known issue with global variables. If you put this inside a function instead is it still allowed?

1 Like

The original issue was in a function in an actor. The test has different output when inside a function but still runs.

import Foundation

func parseDate() {
    let item: [String:Any] = [
        "date": "Not a date"
    ]
    let a: Date? = (item["date"] as? String).flatMap {
        dump(a)
        return ISO8601DateFormatter().date(from: $0)
    }
    print(a?.description ?? "Did not parse date")
}
parseDate()
▿ Optional()
  ▿ some: 
    - timeIntervalSinceReferenceDate: -nan(0x3fff9ffffb4b4)
Did not parse date
Program ended with exit code: 0

Edit: Only runs in Xcode. With the open source toolchain it shows an error when building with the Static Linux SDK, but appears to crash the compiler error when building for Mac.

Static Linux SDK

swift build --swift-sdk x86_64-swift-linux-musl
.../main.swift:7:54: error: closure captures 'a' before it is declared
 5 |         "date": "Not a date"
 6 |     ]
 7 |     let a: Date? = (item["date"] as? String).flatMap {
   |         |                                            `- error: closure captures 'a' before it is declared
   |         `- note: captured value declared here
 8 |         dump(a)
   |              `- note: captured here
 9 |         return ISO8601DateFormatter().date(from: $0)
10 |     }

Mac

swift build
error: compile command failed due to signal 6 (use -v to see invocation)
SIL memory lifetime failure in @$s18ClosureCaptureTest9parseDateyyF: memory is not initialized, but should be
memory location:   %31 = alloc_stack [lexical] [var_decl] $Optional<Date>, let, name "a" // users: %118, %117, %87, %71, %66
at instruction:   copy_addr %31 to [init] %65 : $*Optional<Date>  // id: %66

I will file an issue for that.

2 Likes

@bbrk24 is right here. This is a known issue with top level code.
I'm a bit surprised that this also reproduces within a function. I haven't seen an issue with that before so reporting that is a good idea.

In general you might want to switch to @main instead to get better diagnostics though.

Thank you. Filed #82639 with stack dumps with and without @main

1 Like