Swift 6: Bug with Codable and @preconcurrency

If a MainActor isolated type is marked as Encodable, the automatically generated implementation of the Decodable protocol does not play well with @preconcurrency.
I would expect this to compile:

@MainActor
struct MyStruct : @preconcurrency Encodable {
    var myString = "123"
}

But compiling this fails with the following error:

Main actor-isolated property 'myString' can not be referenced from a non-isolated context

We found out that the reason for this is the "nonisolated" keyword which the implicitly generated Encodable implementation includes, like this:

@MainActor
struct MyStruct : @preconcurrency Encodable {
    var myString = "123"
    
    private enum CodingKeys: CodingKey {
        case myString
    }
    
    nonisolated func encode(to encoder: any Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(self.myString, forKey: .myString)
    }
}

If I explicitly implement Encodable like this without "nonisolated", the error disappears:

@MainActor
struct MyStruct : @preconcurrency Encodable {
    var myString = "123"
    
    private enum CodingKeys: CodingKey {
        case myString
    }
    
    func encode(to encoder: any Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(self.myString, forKey: .myString)
    }
}
1 Like

Why do you need to make Codable struct isolated to main actor after all? By nature it would be Sendable, with encoding and decoding being run on the inherited executor since they are synchronous.

It is just a shortened example to illustrate the bug. Just imagine the struct is not meant to be sendable because it contains a reference to a class object. Or make the struct a class. The bug is still the same.

Just for reference: I've also filed this bug under FB13896211.