SE-0427: Noncopyable Generics

Ok, I'm confused here... SE-0398 says that this is legit:

struct ZipSequence<each S: Sequence>: Sequence

But with all the flags mentioned above turned on and using one of the new nightly Swift 6 builds this doesn't work:

public struct Tree<each T: ~Copyable>: ~Copyable

It gets: Cannot suppress '~Copyable' on type 'each T'. Is this the intended interaction between SE-0398 and SE-0427?

fwiw, the idea is to build a variadic version of:

struct Box<Wrapped: ~Copyable>: ~Copyable

such that I consume the variadic arguments and each variadically specified type is unique.

2 Likes

I'm a little concerned that we don't have all of the bases covered with respect to functions lifted into nominal types. Consider the following:

func identity<T: ~Copyable>(_ t: consuming T) -> T { t }
public struct FuncNC<A: ~Copyable, B: ~Copyable>: ~Copyable {
    let run: (borrowing A) -> B
    init(run: @escaping (borrowing A) -> B) { self.run = run }
    borrowing func callAsFunction(_ a: borrowing A) -> B { run(a) }

    consuming func map<C: ~Copyable>(_ f: @escaping (borrowing B) -> C) -> FuncNC<A, C> {
        .init { a in f(self(a)) }
    }
    consuming func flatMap<C: ~Copyable>(_ f: @escaping (borrowing B) -> FuncNC<A, C>) -> FuncNC<A, C> {
        .init { a in f(self(a))(a) }
    }
    consuming func join<C: ~Copyable>() -> FuncNC<A, C> where B == FuncNC<A, C> {
        self.flatMap(identity)
    }
}

as of the 6.0-DEVELOPMENT-SNAPSHOT-2024-03-26-a build, join on this type produces:

<unknown>:0: error: copy of noncopyable typed value. This is a compiler bug. Please file a bug with a small example of the bug

Is there something fundamentally wrong with join here?

There also seems to be problems with type inference and consuming. Consider:

public struct ContinuationNC<T: ~Copyable, R: ~Copyable>: ~Copyable {
    public let run: (@escaping (consuming T)  -> R)  -> R

    public init(_ run: @escaping (@escaping (consuming T)  -> R)  -> R) {
        self.run = run
    }
    func callAsFunction(_ callback: @escaping (consuming T)  -> R)  -> R {
         run(callback)
    }

    consuming func map<U>(
        _ transform: @escaping (consuming T)  -> U
    ) -> ContinuationNC<U, R> {
        .init { downstream in  self {
            t in  downstream(transform(t))
        } }
    }
}

In the map function this produces:

't' is borrowed and cannot be consumed

Shouldn't it infer that t is consuming?

And finally, I've raised this before, it is not possible, but it would be correct behavior on the ContinuationNC type to be able to mark callAsFunction as being consuming. That would be a great future direction to see in this pitch.

1 Like

It should raise an error. identity is a (consuming T) -> T function declaration, but flatMap takes a (borrowing B) -> FuncNC<A, C> parameter, and we can't convert from a function taking a consuming parameter to one that takes a borrowing parameter without a thunk that copies the parameter, which is probably tripping that internal diagnostic. What happens if you change flatMap's parameter's parameter to be consuming as well?

Yeah, that looks like a bug as well.

There shouldn't be any reason callAsFunction can't be consuming. These all sound like bugs to me not directly related to noncopyable generics.

3 Likes

Thanks @Joe_Groff !

I agree that much of the above is bug-related more than design-related, but the general difficulty of the topic has forced me to wonder if what I think are bugs really are bugs or if they are gaps in my understanding.

I think that of the examples above what I'm concerned most about for review purposes is the bit about variadics. Its not clear to me if the intent of the proposal is to allow protocol suppression to be specified in most places where you could allow protocol conformance to be specified. Or maybe not.

Variadics stress my understanding of the proposal. Should variadic suppression of copyability be allowed or not under this proposal?

More abstractly, I will say that, like others, I'm finding the syntax associated with specifying "make no assumptions about copyability" vs "instances of this type cannot be copied" to be very difficult to explain to colleagues. But I think this is restating what others have already said.

2 Likes

No, this should work. We're looking into it.

4 Likes

This thread was somehow left unresolved. For reference, the proposal got returned for revision back in May, with the notes below.

The proposal got amended accordingly, and a second review has started here:

1 Like