If the compiler can prove the access is safe, is it really safe?

Not limited to the following example

func balance(_ x: inout Int, _ y: inout Int) {
    let sum = x + y
    x = sum / 2
    y = sum - x
var holly = Player(name: "Holly", health: 10, energy: 10)
balance(&holly.health, &holly.energy)  // Error
func someFunc() {
    var playerInformation = (health: 10, energy: 20)
    balance(&playerInformation.health, &playerInformation.energy) // is it really safe
someFunc() // Success

I'm guessing this is some sort of bug. I would expect the tuple behavior to be the same whether it's at the global scope vs local to a function. I'm guessing the compiler is optimizing away the dynamic runtime checks when it's inside of someFun, but not emitting an error about overlapping access at compile time. Whereas global variables must always be dynamically checked. Although it's highly possible I'm misunderstanding the rule of exclusivity and the local overlapping access is actually safe.

cc @John_McCall

Actually, reading more into the exclusivity rule, I'm thinking the local overlapping accesses are okay because they can be proven to not modify other parts of the value. You can see this start to come out if you add some didSet to the Player properties, and you'll suddenly see a static compile time error when using Player locally in the someFunc context, because the compiler suddenly treats modifications of one of those properties as accessing the entire value, thus causing an unsafe overlap.

What error are you getting? I assume it is not related to access exclusivity. Check if they're mutable.

You are accessing the same enclosing object twice, yes, but only to further access a certain property. Eventually, in balance you can only modify and have access to the properties you referenced. Unless you passed references to the same property, it must be safe (here, safe means an exclusive access to something you can mutate) and shouldn't be considered overlapping access. I don't see how any of those examples can be unsafe unless there's some kind of very complex recursion that the compiler can't catch.

UDP Oh, it's a runtime error. Erik is likely right about it being a bug then.

The error is an exclusivity violation at runtime for the global case. I'm not quite sure why the global case access isn't being optimized away when the other case can statically determine it's safe. I'm guessing the dynamic checking is very conservative and is always emitted for globals? And then the dynamic checking doesn't know anything about the access to the rest of the value, so it just assumes it's a violation.

This is the intended behavior. For statically-analyzable cases, we allow simultaneous access to obviously independent parts of a value (fields of a tuple, stored properties of a struct), but aren't able to do that analysis dynamically, so accesses to global variables or class properties always use the entire value.


Right. You can argue that this ought be different when "global" means a top-level value in e.g. a playground, though.

1 Like

Why is a global variable (let it be a struct instance) not statically analyzable?

In a normal, non-playground use case, a global variable can be accessed from an arbitrary function (subject only to access control), so a static analysis would have to prove that such a function was not called during the access. Sometimes that's feasible, but in general it isn't for standard Rice's Theorem reasons, and so in the general case we have to use dynamic enforcement on such variables.

The top level of a playground is different from that, though — it functions a lot more like the local scope of a function than the global scope of any other file. In that case, we could reasonably apply the same sort of capture-based analysis that we would apply in a local scope: specifically, if the variable hasn't been "captured" in a top-level function yet, we can still use static enforcement on it, and we'd only have to fall back on dynamic enforcement after it's been "captured" in code that can be called in some infeasible-to-track way.


Thank you, I appreciate the explanation.