Skip to content

Commit b6cc118

Browse files
author
Gabor Horvath
committed
[cxx-interop] Explicit conversions between Swift and C++ spans
A first step towards creating safe overloads for C++ APIs using span (rdar://139074571). Note that we need to mark span as owned because it the libc++ implementation was mistakenly recognized as owned and might now rely on span methods like `data` being renamed as `__dataUnsafe`. We will change it under a new interop version. But for the time being, we want consistent behavior across stdlib versions.
1 parent 1eb0ad2 commit b6cc118

File tree

6 files changed

+135
-64
lines changed

6 files changed

+135
-64
lines changed

stdlib/public/Cxx/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ add_swift_target_library(swiftCxx STATIC NO_LINK_NAME IS_STDLIB IS_SWIFT_ONLY
2121

2222
SWIFT_COMPILE_FLAGS ${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS}
2323
-cxx-interoperability-mode=default
24+
-enable-experimental-feature Span
25+
-enable-experimental-feature BuiltinModule
2426
# This module should not pull in the C++ standard library, so we disable it explicitly.
2527
# For functionality that depends on the C++ stdlib, use C++ stdlib overlay (`swiftstd` module).
2628
-Xcc -nostdinc++

stdlib/public/Cxx/CxxSpan.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@
99
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
1010
//
1111
//===----------------------------------------------------------------------===//
12+
import Builtin
13+
14+
@usableFromInline
15+
internal func unsafeBitCast<T: ~Escapable, U>(
16+
_ x: T, to type: U.Type
17+
) -> U {
18+
Builtin.reinterpretCast(x)
19+
}
1220

1321
/// A C++ type that is an object that can refer to a contiguous sequence of objects.
1422
///
@@ -19,6 +27,9 @@ public protocol CxxSpan<Element> {
1927

2028
init()
2129
init(_ unsafePointer : UnsafePointer<Element>, _ count: Size)
30+
31+
func size() -> Size
32+
func __dataUnsafe() -> UnsafePointer<Element>?
2233
}
2334

2435
extension CxxSpan {
@@ -36,6 +47,28 @@ extension CxxSpan {
3647
"UnsafeMutableBufferPointer should not point to nil")
3748
self.init(unsafeMutableBufferPointer.baseAddress!, Size(unsafeMutableBufferPointer.count))
3849
}
50+
51+
@available(SwiftStdlib 6.1, *)
52+
@inlinable
53+
@unsafe
54+
public init(_ span: Span<Element>) {
55+
let (p, c) = unsafeBitCast(span, to: (UnsafeRawPointer?, Int).self)
56+
precondition(p != nil, "Span should not point to nil")
57+
let binding = p!.bindMemory(to: Element.self, capacity: c)
58+
self.init(binding, Size(c))
59+
}
60+
}
61+
62+
@available(SwiftStdlib 6.1, *)
63+
extension Span {
64+
@_alwaysEmitIntoClient
65+
@lifetime(immortal)
66+
@unsafe
67+
public init<T: CxxSpan<Element>>(
68+
_unsafeCxxSpan span: T,
69+
) {
70+
self.init(_unsafeElements: .init(start: span.__dataUnsafe(), count: Int(span.size())))
71+
}
3972
}
4073

4174
public protocol CxxMutableSpan<Element> {

stdlib/public/Cxx/std/std.apinotes

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,7 @@ Namespaces:
1313
SwiftImportAs: owned
1414
- Name: vector
1515
SwiftImportAs: owned
16+
- Name: span
17+
Methods:
18+
- Name: data
19+
SwiftName: __dataUnsafe()

test/Interop/Cxx/stdlib/Inputs/std-span.h

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,53 +5,51 @@
55
#include <string>
66
#include <span>
77

8-
using ConstSpan = std::span<const int>;
9-
using Span = std::span<int>;
8+
using ConstSpanOfInt = std::span<const int>;
9+
using SpanOfInt = std::span<int>;
1010
using ConstSpanOfString = std::span<const std::string>;
1111
using SpanOfString = std::span<std::string>;
1212

1313
static int iarray[]{1, 2, 3};
1414
static std::string sarray[]{"", "ab", "abc"};
15-
static ConstSpan icspan = {iarray};
16-
static Span ispan = {iarray};
15+
static ConstSpanOfInt icspan = {iarray};
16+
static SpanOfInt ispan = {iarray};
1717
static ConstSpanOfString scspan = {sarray};
1818
static SpanOfString sspan = {sarray};
1919

2020
struct SpanBox {
21-
ConstSpan icspan;
22-
Span ispan;
21+
ConstSpanOfInt icspan;
22+
SpanOfInt ispan;
2323
ConstSpanOfString scspan;
2424
SpanOfString sspan;
2525
};
2626

2727
class CppApi {
2828
public:
29-
ConstSpan getConstSpan();
30-
Span getSpan();
29+
ConstSpanOfInt getConstSpan();
30+
SpanOfInt getSpan();
3131
};
3232

33-
ConstSpan CppApi::getConstSpan() {
34-
ConstSpan sp{new int[2], 2};
33+
ConstSpanOfInt CppApi::getConstSpan() {
34+
ConstSpanOfInt sp{new int[2], 2};
3535
return sp;
3636
}
3737

38-
Span CppApi::getSpan() {
39-
Span sp{new int[2], 2};
38+
SpanOfInt CppApi::getSpan() {
39+
SpanOfInt sp{new int[2], 2};
4040
return sp;
4141
}
4242

43-
inline ConstSpan initConstSpan() {
44-
const int a[]{1, 2, 3};
45-
return ConstSpan(a);
43+
inline ConstSpanOfInt initConstSpan() {
44+
return ConstSpanOfInt(iarray);
4645
}
4746

48-
inline Span initSpan() {
49-
int a[]{1, 2, 3};
50-
return Span(a);
47+
inline SpanOfInt initSpan() {
48+
return SpanOfInt(iarray);
5149
}
5250

53-
inline Span initSpan(int arr[], size_t size) {
54-
return Span(arr, size);
51+
inline SpanOfInt initSpan(int arr[], size_t size) {
52+
return SpanOfInt(arr, size);
5553
}
5654

5755
inline struct SpanBox getStructSpanBox() { return {iarray, iarray, sarray, sarray}; }

test/Interop/Cxx/stdlib/use-std-span-typechecker.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import StdSpan
77

88
let arr: [Int32] = [1, 2, 3]
99
arr.withUnsafeBufferPointer { ubpointer in
10-
let _ = ConstSpan(ubpointer) // okay
11-
let _ = ConstSpan(ubpointer.baseAddress!, ubpointer.count)
10+
let _ = ConstSpanOfInt(ubpointer) // okay
11+
let _ = ConstSpanOfInt(ubpointer.baseAddress!, ubpointer.count)
1212
// expected-warning@-1 {{'init(_:_:)' is deprecated: use 'init(_:)' instead.}}
1313
}

0 commit comments

Comments
 (0)