Is it OK for a GAIT's nonisolated member to access its mutable isolated member?

Does anyone know if the code is valid or not? It compiles in Swift 6.1.2 and Swift 6.2-snapshot-2025-06-17.

@MainActor
struct S1 {
    var value: Int = 0
    nonisolated var id: Int { value } // This compiles. Should it?
}

I thought it's invalid because I was able to to demonstrate a data race:

// This compiles in Swift 6.1.2
nonisolated func test1() {
    var s = S1()
    // 1) modifying the value in MainActor
    Task { @MainActor  in
        s.value += 1
    }
    // 2) reading the value from caller's isolation domain currently
    print(s.id)
}

Then I found that Swift 6.2-snapshot-2025-06-17 caught the issue in test1(), although I worked out a slightly different version to trigger the date race again:

// This compiles in Swift 6.2-snapshot-2025-06-17
@MainActor
func test2() {
    var s = S1()
    // 1) modifying the value in MainActor
    Task { @MainActor  in
        s.value += 1
    }
    // 2) reading the value on global executors currently
    Task { @concurrent in
        print(s.id)
    }
}

But that made me to think. I originally thought that there were two issues, which are orthogonal and should be fixed separately:

  1. A GAIT's nonisolated member shouldn't be allowed to access its mutable isolated member
  2. Region based isolation analysis in test1() and test2() are broken in Swift 6.1.2 and Swift 6.2-snapshot-2025-06-17, respectively.

Now I'm not sure about issue 1. Given the entire value of a S1 instance is isolated, maybe it's impossible to trigger a data race other than leverage region based isolation bugs? (if so, then issue 1 is a non-issue).

I wonder if this is covered in any SE proposal?

PS: In my understanding Region based isolation is typically used to send non-sendable values in disconnected region of non-isolated types to another isolation domain. It's a bit weird to send a value of explict MainActor isolated type, but it works.

PS2: I filed #82230 when I thought issue 1 was a bug. I ask here because I'm not sure about it now. I think #82259 is also a similar issue.

1 Like

I agree issue 1 does not stand. If we change struct S1 into class S1, there will be diagnostics.

For sturcts, because they are copied when being transferred across domains, there cannot be concurrent accesses to the same object later. Both test1 and test2 captured that object by reference, so it's a different story.

FYI, I think test1() is related to a long-standing bug that just got fixed recently. And test2() uses a new syntax which I cannot find in any official documentation, so I'm not sure where this one will go.

FWIW, the behavior was defined in SE-0434:

First, let's consider the stored properties of struct s isolated to global actors. let properties of such types are implicitly treated as nonisolated within the current module if they have Sendable type, but var properties are not.
...
However, there is nothing unsafe about treating x as nonisolated . ... So, first off, there's no reason for Swift to require (unsafe) when marking x nonisolated .
...
We can do better than that, though. It should be possible to treat a var stored property of a global-actor-isolated value type as implicitly nonisolated under the same conditions that a let property can be.

A simpler example:

@MainActor struct S {
    var value: Int = 1

    nonisolated mutating func increase() {
        value += 1
    }
}