In the recent Swift 4.2 branch, the existing Swift 4.1 warning about ‘overlapping accesses’ is now an error in Swift 4 mode. This means that projects with this warning will fail to build with the Swift 4.2 compiler.
The warning typically arises when a mutating method that modifies a variable is passed a non-escaping closure that reads from the same variable. For example:
var buffer = Data(count: Int(INET6_ADDRSTRLEN))
// Warning: overlapping accesses to 'buffer', but modification
// requires exclusive access; consider copying to a local variable
let ptr = buffer.withUnsafeMutableBytes {
// Note: conflicting access is here
inet_ntop(AF_INET, &sin.sin_addr, $0, socklen_t(buffer.count))
}
Here withUnsafeMutableBytes()
is a mutating method on buffer
and so conceptually modifies the entire variable for the duration of the method. The compiler warns on the call to count
, which is a computed property that reads from buffer
while the modification is in progress — a violation of Swift’s rules for exclusive access.
The best way to address this warning is by reading the buffer count outside of the closure and copying it to a captured local variable:
var buffer = Data(count: Int(INET6_ADDRSTRLEN))
let count = buffer.count
let ptr = buffer.withUnsafeMutableBytes {
inet_ntop(AF_INET, &sin.sin_addr, $0, socklen_t(count))
}
Sometimes copying the value outside of the closure doesn’t make sense because you want to observe the mutating effects of the called method inside the closure. For example, the following struct provides a helpful method to push a path component onto a path, execute a closure, and then pop the component.
struct MyFilePath {
var components: [String] = []
mutating
func withAppended(_ path: String, _ closure: () -> Void) {
components.append(path)
closure()
components.removeLast()
}
}
However, calling this method on a path results in an exclusivity violation when the closure also refers to the path via a capture:
var p = MyFilePath(components: ["usr", "local"])
p.withAppended("bin") {
// Warning: overlapping accesses to ‘p'
print("The path is \(p)")
}
To address this exclusivity violation, you can change the helper method to explicitly provide the mutated path as an inout parameter to the closure:
mutating
func withAppended(_ path: String, _ closure: (inout MyFilePath) -> Void) {
components.append(path)
closure(&self)
components.removeLast()
}
Then callers of the method can safely refer to the mutated path via the closure parameter:
var p = MyFilePath(components: ["usr", "local"])
p.withAppended("bin") {
print("The path is \($0)")
}
If you want to try out the newly-upgraded error on your own code, you can download a Swift 4.2 snapshot (Xcode, Linux) from swift.org. If you are willing, please post back to this thread with your experiences.