Lifetime dependence is lost when an address-only ~Escapable value is stored into an Optional · Issue #93414 · swiftlang/swift · GitHub
Skip to content

Lifetime dependence is lost when an address-only ~Escapable value is stored into an Optional #93414

Open

Description

@hassila

Description

Steps: save the file below as repro.swift, then run

swiftc -enable-experimental-feature Lifetimes repro.swift -o repro && ./repro
swiftc -enable-experimental-feature Lifetimes -DNEGATIVE repro.swift

Actual: the first command compiles at -Onone and -O; at -Onone it prints 1, then 9 after the owner is mutated, then the contents of a freed buffer (0 here). The second command refuses the two direct shapes as expected (direct, and the optional parameter assigned straight to the optional variable), so an unwrapped and re-wrapped optional gets through where the plain assignment doesn't.

The shape that matters in practice: a non-frozen public ~Escapable type from a library built with -enable-library-evolution is address-only in its clients, and owner.withView { view in kept = view } compiles there whether or not the client enables the feature; against a @frozen type, or the same library built without library evolution, it's refused. In the SIL of the accepted closure the store goes through alloc_stack $Optional<View>, init_enum_data_addr, copy_addr, inject_enum_addr and a copy_addr [take] into the captured box, where the refused direct store is a single copy_addr.

References: SE-0446 Nonescapable Types (https://github.com/swiftlang/swift-evolution/blob/main/proposals/0446-non-escapable.md) and the lifetime dependency annotations proposal, swift-evolution PR #2305 (swiftlang/swift-evolution#2305), as implemented behind the Lifetimes feature.

Reproduction

// Swift 6.4 (swiftlang-6.4.0.34.1): an address-only ~Escapable value escapes its borrow through an Optional.
// swiftc -enable-experimental-feature Lifetimes repro.swift -o repro && ./repro
// swiftc -enable-experimental-feature Lifetimes -DNEGATIVE repro.swift     (the shapes the compiler refuses)

struct View: ~Escapable {
    let span: Span<UInt8>
    let tag: Any = ()   // address-only, as a non-frozen public type from a library-evolution build is in its clients; drop this line and every shape below is refused
    @_lifetime(copy span) init(_ span: Span<UInt8>) { self.span = span }
    var first: UInt8 { span[0] }
}

struct Owner {
    var bytes: [UInt8] = [1, 2, 3, 4]
    var view: View { @_lifetime(borrow self) get { View(bytes.span) } }
    func withView(_ body: (View) -> Void) { body(view) }
    func withOptionalView(_ body: (View?) -> Void) { body(view) }
}

func escape() {
    var owner = Owner()
    var kept: View? = nil
    owner.withView { view in kept = view }                          // compiles: the view outlives the lend
    print("kept view after the lend:", kept!.first)                 // 1
    owner.bytes[0] = 9                                              // the owner is mutated while the view is held
    print("kept view after the owner changed:", kept!.first)        // 9: the view still aliases the owner
    owner.withOptionalView { view in if let view { kept = view } }  // compiles as well
    do { let local = Owner(); kept = local.view }                   // compiles as well, no closure involved
    print("kept view of a dead owner:", kept!.first)                // reads freed memory
}
escape()

#if NEGATIVE
func refused(_ owner: Owner) {
    var direct: View = owner.view
    owner.withView { view in direct = view }        // error: lifetime-dependent variable 'direct' escapes its scope
    var kept: View? = nil
    owner.withOptionalView { view in kept = view }  // error: lifetime-dependent variable 'kept' escapes its scope
    print(direct.first, kept!.first)
}
#endif

Expected behavior

Expected: the three stores into kept in escape() are refused with error: lifetime-dependent variable 'kept' escapes its scope, as they are when the tag field is removed from View (a loadable type) or when kept is a non-optional View.

Environment

Toolchain: swiftlang-6.4.0.34.1 (Swift 6.4, Xcode 27.0 27A266a), macOS 27 arm64, -enable-experimental-feature Lifetimes.

Additional information

No response

Activity

  1. added theissue type on Oct 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    triage neededThis issue needs more specific labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions