Files
swift-mirror/stdlib/public/Cxx/CxxSpan.swift
Henrik G. Olsson 87f5309144 [Swiftify] enable mutable span (#80387)
* [Swiftify] Emit Mutable[Raw]Span when possible

Previously wrappers would use UnsafeMutable[Raw]Pointer for mutable
pointers, and Span for non-const std::span, to prevent the compiler from
complaining that MutableSpan didn't exist.

Now that MutableSpan has landed we can finally emit MutableSpan without
causing compilation errors. While we had (disabled) support for MutableSpan
syntax already, some unexpected semantic errors required additional
changes:
 - Mutable[Raw]Span parameters need to be inout (for mutation)
 - inout ~Escapable paramters need explicit lifetime annotations
 - MutableSpan cannot be directly bitcast to std::span, because it is
   ~Copyable, so they need unwrapping to UnsafeMutableBufferPointer

rdar://147883022

* [Swiftify] Wrap if-expressions in Immediately Called Closures

When parameters in swiftified wrapper functions are nullable, we use
separate branches for the nil and nonnil cases, because
`withUnsafeBufferPointer` (and similar) cannot be called on nil.
If-expressions have some limitations on where they are allowed in the
grammar, and cannot be passed as arguments to a function. As such, when
the return value is also swiftified, we get an error when trying to
pass the if-expression to the UnsafeBufferPointer/Span constructor.
While it isn't pretty, the best way forward seems to be by wrapping the
if-expressions in Immediately Called Closures.

The closures have the side-effect of acting as a barrier for 'unsafe':
unsafe keywords outside the closure do not "reach" unsafe expressions
inside the closure. We therefore have to emit "unsafe" where unsafe
expressions are used, rather than just when returning.

rdar://148153063
2025-03-29 05:05:01 -07:00

154 lines
5.3 KiB
Swift

//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import Builtin
@usableFromInline
internal func unsafeBitCast<T: ~Escapable, U>(
_ x: T, to type: U.Type
) -> U {
Builtin.reinterpretCast(x)
}
/// Unsafely discard any lifetime dependency on the `dependent` argument. Return
/// a value identical to `dependent` with a lifetime dependency on the caller's
/// borrow scope of the `source` argument.
///
/// This mimics the stdlib definition. It is public for use with import macros.
@unsafe
@_unsafeNonescapableResult
@_alwaysEmitIntoClient
@_transparent
@lifetime(borrow source)
public func _cxxOverrideLifetime<
T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable
>(
_ dependent: consuming T, borrowing source: borrowing U
) -> T {
// TODO: Remove @_unsafeNonescapableResult. Instead, the unsafe dependence
// should be expressed by a builtin that is hidden within the function body.
dependent
}
/// Unsafely discard any lifetime dependency on the `dependent` argument. Return
/// a value identical to `dependent` that inherits all lifetime dependencies from
/// the `source` argument.
///
/// This mimics the stdlib definition. It is public for use with import macros.
@unsafe
@_unsafeNonescapableResult
@_alwaysEmitIntoClient
@_transparent
@lifetime(copy source)
public func _cxxOverrideLifetime<
T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable
>(
_ dependent: consuming T, copying source: borrowing U
) -> T {
// TODO: Remove @_unsafeNonescapableResult. Instead, the unsafe dependence
// should be expressed by a builtin that is hidden within the function body.
dependent
}
/// A C++ type that is an object that can refer to a contiguous sequence of objects.
///
/// C++ standard library type `std::span` conforms to this protocol.
public protocol CxxSpan<Element> {
associatedtype Element
associatedtype Size: BinaryInteger
init()
init(_ unsafePointer : UnsafePointer<Element>, _ count: Size)
func size() -> Size
func __dataUnsafe() -> UnsafePointer<Element>?
}
extension CxxSpan {
/// Creates a C++ span from a Swift UnsafeBufferPointer
@inlinable
public init(_ unsafeBufferPointer: UnsafeBufferPointer<Element>) {
unsafe precondition(unsafeBufferPointer.baseAddress != nil,
"UnsafeBufferPointer should not point to nil")
unsafe self.init(unsafeBufferPointer.baseAddress!, Size(unsafeBufferPointer.count))
}
@inlinable
public init(_ unsafeMutableBufferPointer: UnsafeMutableBufferPointer<Element>) {
unsafe precondition(unsafeMutableBufferPointer.baseAddress != nil,
"UnsafeMutableBufferPointer should not point to nil")
unsafe self.init(unsafeMutableBufferPointer.baseAddress!, Size(unsafeMutableBufferPointer.count))
}
@available(SwiftStdlib 6.2, *)
@inlinable
@unsafe
public init(_ span: Span<Element>) {
let (p, c) = unsafe unsafeBitCast(span, to: (UnsafeRawPointer?, Int).self)
unsafe precondition(p != nil, "Span should not point to nil")
let binding = unsafe p!.bindMemory(to: Element.self, capacity: c)
unsafe self.init(binding, Size(c))
}
}
@available(SwiftStdlib 6.2, *)
extension Span {
@_alwaysEmitIntoClient
@unsafe
@_unsafeNonescapableResult
@lifetime(borrow span)
public init<T: CxxSpan<Element>>(
_unsafeCxxSpan span: borrowing T,
) {
let buffer = unsafe UnsafeBufferPointer(start: span.__dataUnsafe(), count: Int(span.size()))
let newSpan = unsafe Span(_unsafeElements: buffer)
// 'self' is limited to the caller's scope of the variable passed to the 'span' argument.
self = unsafe _cxxOverrideLifetime(newSpan, borrowing: span)
}
}
@available(SwiftStdlib 6.2, *)
extension MutableSpan {
@_alwaysEmitIntoClient
@unsafe
@_unsafeNonescapableResult
@lifetime(borrow span)
public init<T: CxxMutableSpan<Element>>(
_unsafeCxxSpan span: borrowing T,
) {
let buffer = unsafe UnsafeMutableBufferPointer(start: span.__dataUnsafe(), count: Int(span.size()))
let newSpan = unsafe MutableSpan(_unsafeElements: buffer)
// 'self' is limited to the caller's scope of the variable passed to the 'span' argument.
self = unsafe _cxxOverrideLifetime(newSpan, borrowing: span)
}
}
public protocol CxxMutableSpan<Element> {
associatedtype Element
associatedtype Size: BinaryInteger
init()
init(_ unsafeMutablePointer : UnsafeMutablePointer<Element>, _ count: Size)
func size() -> Size
func __dataUnsafe() -> UnsafeMutablePointer<Element>
}
extension CxxMutableSpan {
/// Creates a C++ span from a Swift UnsafeMutableBufferPointer
@inlinable
public init(_ unsafeMutableBufferPointer: UnsafeMutableBufferPointer<Element>) {
unsafe precondition(unsafeMutableBufferPointer.baseAddress != nil,
"UnsafeMutableBufferPointer should not point to nil")
unsafe self.init(unsafeMutableBufferPointer.baseAddress!, Size(unsafeMutableBufferPointer.count))
}
}