Loosing type info depending on iteration mode

UPDATE: See the first comment by @tera about what the issue really is. Changed the topic title from "Loosing type info in enums with associated values" to "Loosing type info depending on iteration mode".

In the following code with the enum case indirect case sequence(any Sequence<MyEnumeration>) the
MyEnumeration type seems to "get lost" (see case .sequence(let sequence). Why is this the case?

enum MyEnumeration {
    case number(Int)
    case text(String)
    indirect case array([MyEnumeration])
    indirect case sequence(any Sequence<MyEnumeration>)
}

let sequence: (any Sequence<MyEnumeration>)? = nil

sequence?.forEach { value in
    doSomething(with: value) // OK, value is of type MyEnumeration
}

func doSomething(with values: MyEnumeration...) {
    values.forEach { value in
        switch value {
        case .number(let number):
            print("number \(number)")
        case .text(let text):
            print("text \(text)")
        case .array(let array):
            for value in array { doSomething(with: value) }
        case .sequence(let sequence):
            for value in sequence { doSomething(with: value) } // error: "Cannot convert value of type 'Any' to expected argument type 'MyEnumeration'"
        }
    }
}

You have forEach in the first place and for in the second place. The quintessential example:

var values: any Sequence<Int> = [1, 2, 3]
values.forEach { value in
    let x: Int = value // ok, unused variable warning
}
for value in values {
    let x: Int = value // 🛑 Cannot convert value of type 'Any' to specified type 'Int'
}

Why this difference – I don't know.

BTW, indirect is not needed in your example.

Thanks, the issue is now clearer. Strange.

2 Likes

Thanks for the link.

“Should”, indeed.

2 Likes

As the linked thread shows this doesn't work either:

var values: any Sequence<Int> = [1, 2, 3]
var iterator = values.makeIterator() // Error
while let value = iterator.next() {
    let x: Int = value
}

Changing makeIterator to a custom makeIteratorWrapper does work though, where:

struct IteratorWrapper<T> {
    var iterator: any IteratorProtocol<T>
    mutating func next() -> T? {
        iterator.next()
    }
}

extension Sequence {
    func makeIteratorWrapper() -> IteratorWrapper<Element> {
        IteratorWrapper(iterator: makeIterator())
    }
}

I think that's because Iterator is an associatedtype of Sequence, therefore you cannot access it from a Sequence existential.

There were some explanations above about what hasn‘t been implemented yet so it cannot work yet, but from a user‘s standpoint it just seems that “something goes wrong”, so even if it is not really a bug, it seems at least like a major omission. Therefore I filed a bug report (using some of the code of @tera above).

The issue is fixed in the current 5.9 snapshot.

2 Likes