Property initialization of not Sendable types in nonisolated init

Why is such initialization prohibited for classes and structs, but allowed for actors?

public struct NotSendableValue {}

@MainActor
class Klass {
    let value: NotSendableValue

    nonisolated init() {
        value = NotSendableValue() // error: Main actor-isolated property 'value' can not be mutated from a non-isolated context
    }
}

actor Aktor {
    let value: NotSendableValue

    init() { // implicitly nonisolated
        value = NotSendableValue() // fine
    }
}

This code should be thread-safe, so I assume that error is wrong.

1 Like

SE-0327 proposes to apply the same flow-sensitive actor isolation to nonisolated initializers of GAITs. This just means that while self has not become isolated, we can make sure statically that there won't be a data race within the initializer.

Under SE-0306, all properties defined on an actor implicitly conform to Sendable , and in your example I think value implicitly conforms to Sendable so its fine to access it in an actor's initializer.

However, for classes/structs I think that Sendable conformance has to be explicit, so you see this error because you're trying to assign to a globally-isolated property from a non-isolated context. And then, per SE-0327, such initializer will be equivalent to the Aktor's initializer.

So, the solution to the errors in nonisolated initializers of GAITs would be to conform the property to Sendable or to make the property nonisolated too.

1 Like

Thanks for your response!

The rule in SE-0327 seems reasonable. However, I'm wondering why it doesn't apply to classes or structures. In my example, no one accesses the value after it's been assigned. It seems like there shouldn't be any errors "Main actor-isolated property can not be mutated from a non-isolated context" until the object has fully initialized, since properties can't be changed concurrently because self can't be captured.

Klass is similar to the following class, which doesn't cause compilation errors:

@MainActor
class Klass2 {
    let value = NotSendableValue()

    nonisolated init() {}
}

Therefore, it would be logical for Klass to not have any errors either.

Can properties be Sendable? I thought only types could be. Or do you mean property types? (I suppose no, because a type can't be both Sendable and non-Sendable at the same time).

nonisolated/nonisolated(unsafe) doesn't help.

But let's assume that we want to keep the non-Sendable type and the isolated property. It seems like the compiler could be more intelligent in analyzing the class/struct initializers, similar to how it analyzes actor initializers.

1 Like

Can properties be Sendable? I thought only types could be. Or do you mean property types? (I suppose no, because a type can't be both Sendable and non-Sendable at the same time).

I meant property types. I just reread the part of the proposal that talks about Sendability, and actors are implicitly Sendable, but referencing their properties will only work if their types are explicitly Sendable. I misread this part earlier, sorry😅

But let's assume that we want to keep the non-Sendable type and the isolated property. It seems like the compiler could be more intelligent in analyzing the class/struct initializers, similar to how it analyzes actor initializers.

I agree, I ran into multiple such issues with initializers, but for now as far as I understand the only solution would be conforming the property type to Sendable. According to what I understand so far, I can't explain anymore how actor is different, given no inference of Sendable is happening behind the scenes.

This is an interesting question, I'll try to figure this out and come back to you🙂

1 Like

@kavon , as the author of SE-0327, could you please explain why actor initialization differs from GAIT initialization in this case?