Being able to access first/last properties of a consumed array

I find that it's OK to access first and last properties of an array after it has been consumed. Is it a bug or a special feature? Acessing the array's count property or subscript produce errors as expected.

func test() {
    let values = [1, 2, 3]
    let newValues = consume values
    newValues.append(4)
    
    //print(values.count) // this doesn't compile (as expected)
    //print(values[0]) // this doesn't compile (as expected)
    print(values.first!) // output: 1
    print(values.last!) // output: 3
}
1 Like

This is definitely a bug.

Generalizing your example a bit, I find it's related to whether the matched method is from a protocol extension:

protocol P {
    func bar() 
}
extension P {
    func bar() {}
}

struct A: P {
    let x: String = ""

    func foo() {}
}

func test() {
    let a = A()
    _ = consume a

    a.foo() // error, as expected
    a.bar() // no error, not as expected
}
4 Likes

Thanks. I filed #83277. It's a bit hard for me to understand how a feature available since Swift 5.9 still has a so basic issue. I started reading ownership related documents yesterday and found the issue soon. Sometime I can't help wondering if it's because Swift has become so complex that it's hard to maintain it or if it's because few people actually use these features.

1 Like

Interesting. This is a regression: you can see on godbolt.org that Swift 6.1 and earlier correctly diagnoses the problem.

2 Likes

Perhaps you forgot to comment out a.foo() line when trying the above code in Swift 6.1 and Swift 6.0.3? The code compiles in those Swift releases too.

swift 6.1 result: Compiler Explorer
swift 6.03 result: Compiler Explorer

it seems it's not just protocol extensions, but generic functions of various flavors that exhibit this behavior (which is what protocol extensions in some sense are). the behavior also differs between local variable bindings that are consumed, and parameter bindings with consuming ownership, so perhaps there is some implicit copying issue at play (godbolt).

func consumeValue<V>(_ v: consuming V) { _ = consume v }
func borrowValue<V>(_ v: borrowing V) { _ = v }
func useValue<V>(_ v: V) { _ = v }

func consumeA(_ a: consuming A) { _ = consume a }
func borrowA(_ a: borrowing A) { _ = a }
func useA(_ a: A) { _ = a }

struct A {
    var prop = ""
}

func testLocalBindingUseAfterConsumeGenerics() {
    let a = A()
    _ = consume a
    // 👇 replacing the `_ = consume a` with this will cause no error
    // but if they are both present, the second consume will error
    // consumeValue(a)
    borrowValue(a)
    useValue(a)
}

func testLocalBindingUseAfterConsumeNoGenericFns() {
    let a = A() // error: 'a' used after consume
    _ = consume a   // note: consumed here

    // without the generic indirection, these all error:
    consumeA(a) // note: used here
    borrowA(a)  // note: used here
    useA(a)     // note: used here
}

func testParameterBinding(_ a: consuming A) { // error: 'a' used after consume
    _ = consume a   // note: consumed here
    // consumeValue(a) // consuming like this will also cause an error
    borrowValue(a)  // note: used here
    useValue(a)
}

I was referring to your original example:

func test() {
    let values = [1, 2, 3]
    var newValues = consume values
    newValues.append(4)
   
    print(values.first!)
    print(values.last!)
}
1 Like