Skip to content

Fix MutableSpan exclusive access to unsafe pointers #82450

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 24, 2025

Conversation

atrick
Copy link
Contributor

@atrick atrick commented Jun 24, 2025

This fix enables exclusive access to a MutableSpan created from an UnsafeMutablePointer.

The compiler has a special case that allows MutableSpan to depend on a mutable
pointer without extending that pointer's access scope. That lets us implement
standard library code like this:

mutating public func extracting(droppingLast k: Int) -> Self {
  //...
  let newSpan = unsafe Self(_unchecked: _pointer, byteCount: newCount)
  return unsafe _overrideLifetime(newSpan, mutating: &self)

Refine this special case so that is does not apply to inout parameters where the
programmer has an expectation that the unsafe pointer is not copied when being
passed as an argument. Now, we safely get an exclusivity violation when creating
two mutable spans from the same pointer field:

@lifetime(&self)
mutating func getSpan() -> MutableSpan<T> {
  let span1 = makeMutableSpan(&self.pointer)
  let span2 = makeMutableSpan(&self.pointer) // ERROR: overlapping access
  return span1
}

If we don't fix this now, it will likely be source breaking in the future.

Fixes rdar://153745332 (Swift allows constructing two MutableSpans to the same underlying pointer)

This fix enables exclusive access to a MutableSpan created from an UnsafeMutablePointer.

The compiler has a special case that allows MutableSpan to depend on a mutable
pointer *without* extending that pointer's access scope. That lets us implement
standard library code like this:

    mutating public func extracting(droppingLast k: Int) -> Self {
      //...
      let newSpan = unsafe Self(_unchecked: _pointer, byteCount: newCount)
      return unsafe _overrideLifetime(newSpan, mutating: &self)

Refine this special case so that is does not apply to inout parameters where the
programmer has an expectation that the unsafe pointer is not copied when being
passed as an argument. Now, we safely get an exclusivity violation when creating
two mutable spans from the same pointer field:

    @Lifetime(&self)
    mutating func getSpan() -> MutableSpan<T> {
      let span1 = makeMutableSpan(&self.pointer)
      let span2 = makeMutableSpan(&self.pointer) // ERROR: overlapping access
      return span1
    }

If we don't fix this now, it will likely be source breaking in the future.

Fixes rdar://153745332 (Swift allows constructing two MutableSpans to the same underlying pointer)
@atrick atrick requested a review from eeckstein as a code owner June 24, 2025 07:11
atrick added a commit to atrick/swift that referenced this pull request Jun 24, 2025
This fix enables exclusive access to a MutableSpan created from an UnsafeMutablePointer.

The compiler has a special case that allows MutableSpan to depend on a mutable
pointer *without* extending that pointer's access scope. That lets us implement
standard library code like this:

    mutating public func extracting(droppingLast k: Int) -> Self {
      //...
      let newSpan = unsafe Self(_unchecked: _pointer, byteCount: newCount)
      return unsafe _overrideLifetime(newSpan, mutating: &self)

Refine this special case so that is does not apply to inout parameters where the
programmer has an expectation that the unsafe pointer is not copied when being
passed as an argument. Now, we safely get an exclusivity violation when creating
two mutable spans from the same pointer field:

    @Lifetime(&self)
    mutating func getSpan() -> MutableSpan<T> {
      let span1 = makeMutableSpan(&self.pointer)
      let span2 = makeMutableSpan(&self.pointer) // ERROR: overlapping access
      return span1
    }

If we don't fix this now, it will likely be source breaking in the future.

Fixes rdar://153745332 (Swift allows constructing two MutableSpans to
the same underlying pointer)

(cherry picked from commit 7c5d4b8)

--- CCC ---

Explanation: Fix MutableSpan exclusive access to unsafe pointers This
fix enables exclusive access to a MutableSpan created from an
UnsafeMutablePointer.

Scope: Affects users of MutableSpan when initializing them from an unsafe pointer.

Radar/SR Issue: rdar://153745332 (Swift allows constructing two
MutableSpans to the same underlying pointer)

main PR: swiftlang#82450

Risk: Low. This only affects users of an API that requires lifetime
dependence. Without using an experimental feature, this only applies
to the initializers of Mutable[Raw]Span.

Testing: Added source-level unit tests

Reviewer: TBD
@atrick atrick requested a review from meg-gupta June 24, 2025 07:20
@atrick
Copy link
Contributor Author

atrick commented Jun 24, 2025

@swift-ci test

@atrick atrick enabled auto-merge June 24, 2025 07:20
@atrick
Copy link
Contributor Author

atrick commented Jun 24, 2025

@swift-ci test macOS

@atrick
Copy link
Contributor Author

atrick commented Jun 24, 2025

@swift-ci test Windows

Copy link
Contributor

@meg-gupta meg-gupta left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@atrick atrick merged commit 05a9afe into swiftlang:main Jun 24, 2025
5 checks passed
@atrick atrick deleted the unsafe-mutablespan branch June 24, 2025 23:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants