Why is 'MemoryLayout.size(ofValue: array)' considered an overlapping access?

I am trying to access raw memory to a tuple-field of a struct, but when the inner access uses MemoryLayout.size(ofValue:) the compiler complains about overlapping access.

Error: Overlapping accesses to 'self.array', but modification requires exclusive access; consider copying to a local variable

public subscript (index: Int) -> NodeId {
     set {
                withUnsafeMutablePointer(to: &self.array) {
                    $0.withMemoryRebound(to: NodeId.self,
                                         capacity: MemoryLayout.size(ofValue: array) / MemoryLayout<Element>.stride) {
                        /* code goes here */
                    }
                }

If I replace MemoryLayout.size(ofValue: array) with MemoryLayout< Int>.size it compiles just fine. I would have thought MemoryLayout.size(ofValue: array) should be evaluated at compile time. The value is a tuple of the struct and can't possibly change at run-time. Why would this be a run-time issue?

Update: Compiler Explorer seems to show the size can be calculated at compile time. Why is the compiler unhappy saying it's an overlapping access?

MemoryLayout.size(ofValue:), like most everything in the standard library, is just an ordinary Swift function. There is no feature which indicates that a Swift function is to be evaluated at compile time, only compiler optimizations which are optional.

It sounds to me like you’re saying that this particular function could have a semantic guarantee that it can be evaluated at compile time for certain values. However, that is not expressible in Swift; it is just an ordinary function for the purposes of the law of exclusivity.

1 Like

Would this be the kind of thing covered by
[Pitch] Compile-Time Constant Values
?

I don't think the design of any compile-time-eval feature has really been fleshed out fully enough to answer that question. Determining how the law of exclusivity interacts with such expressions certainly seems to me to be in-scope for discussion in a pitch thread, though.

Can't you just move the size calculation outside of the withUnsafeMutablePointer closure?

public subscript (index: Int) -> NodeId {
    set {
        let capacity = MemoryLayout.size(ofValue: array) / MemoryLayout<Element>.stride
        withUnsafeMutablePointer(to: &self.array) {
            $0.withMemoryRebound(to: NodeId.self, capacity: capacity) {
                /* code goes here */
            }
        }

I have a work-around that is acceptable. I just calculated the size, based on what I know the type is, rather than using the variable.

My interest in this is that using the variable itself feel like it's more maintainable because if the variable changes, the result would automatically change. But if this could be known at compile time, it should not cause a problem with exclusivity. In my opinion, it's currently being needlessly restrictive.

There may be factors that I don't understand that make this hard or even impossible to accomplish, but anything that can could reasonably be done at compile time should be used to remove restrictions where possible.