Skip to content

Commit 2bd8684

Browse files
author
Gabor Horvath
committed
[cxx-interop] Add span accessors to std.string variants
rdar://146944876
1 parent a868d92 commit 2bd8684

File tree

6 files changed

+107
-4
lines changed

6 files changed

+107
-4
lines changed

Runtimes/Overlay/Cxx/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ target_compile_options(swiftCxx PRIVATE
3434
"$<$<COMPILE_LANGUAGE:Swift>:SHELL:-Xcc -nostdinc++>"
3535
"$<$<COMPILE_LANGUAGE:Swift>:SHELL:-enable-experimental-feature AllowUnsafeAttribute>"
3636
"$<$<COMPILE_LANGUAGE:Swift>:SHELL:-enable-experimental-feature BuiltinModule>"
37-
"$<$<COMPILE_LANGUAGE:Swift>:SHELL:-enable-experimental-feature Span>")
37+
"$<$<COMPILE_LANGUAGE:Swift>:SHELL:-enable-experimental-feature Lifetimes>")
3838
target_link_libraries(swiftCxx PRIVATE
3939
swiftCore)
4040

Runtimes/Overlay/Cxx/std/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ target_compile_options(swiftCxxStdlib PRIVATE
99
"-strict-memory-safety"
1010
"-cxx-interoperability-mode=default"
1111
"SHELL:-enable-experimental-feature AllowUnsafeAttribute"
12+
"SHELL:-enable-experimental-feature Lifetimes"
1213
# This flag is unnecessary when building with newer compilers that allow using
1314
# C++ symbols in resilient overlays (see f4204568).
1415
"SHELL:-enable-experimental-feature AssumeResilientCxxTypes"

stdlib/public/Cxx/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ 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
2524
-enable-experimental-feature BuiltinModule
2625
-enable-experimental-feature AllowUnsafeAttribute
26+
-enable-experimental-feature Lifetimes
2727
-strict-memory-safety
2828
# This module should not pull in the C++ standard library, so we disable it explicitly.
2929
# For functionality that depends on the C++ stdlib, use C++ stdlib overlay (`swiftstd` module).

stdlib/public/Cxx/std/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ add_swift_target_library(swiftCxxStdlib STATIC NO_LINK_NAME IS_STDLIB IS_SWIFT_O
4444
# This flag is unnecessary when building with newer compilers that allow
4545
# using C++ symbols in resilient overlays (see f4204568).
4646
-enable-experimental-feature AssumeResilientCxxTypes
47-
47+
-enable-experimental-feature Lifetimes
4848
-enable-experimental-feature AllowUnsafeAttribute
4949
-strict-memory-safety
5050

stdlib/public/Cxx/std/String.swift

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ extension std.string {
6060
self.init()
6161
return
6262
}
63-
self.init(str)
63+
unsafe self.init(str)
6464
}
6565
}
6666

@@ -403,3 +403,62 @@ extension String {
403403
unsafe withExtendedLifetime(cxxU32StringView) {}
404404
}
405405
}
406+
407+
@available(SwiftCompatibilitySpan 5.0, *)
408+
extension std.string {
409+
public var span: Span<CChar> {
410+
@_lifetime(borrow self)
411+
@_alwaysEmitIntoClient
412+
borrowing get {
413+
let buffer = unsafe UnsafeBufferPointer(start: self.__dataUnsafe(), count: Int(self.size()))
414+
let span = unsafe Span(_unsafeElements: buffer)
415+
return unsafe _cxxOverrideLifetime(span, borrowing: self)
416+
}
417+
}
418+
}
419+
420+
@available(SwiftStdlib 6.2, *)
421+
extension std.string {
422+
public var utf8Span: UTF8Span? {
423+
@_lifetime(borrow self)
424+
@_alwaysEmitIntoClient
425+
borrowing get {
426+
let buffer = unsafe UnsafeBufferPointer(start: self.__dataUnsafe(), count: Int(self.size()))
427+
let rawBuffer = UnsafeRawBufferPointer(buffer)
428+
let bufferWithFixedType = unsafe rawBuffer.assumingMemoryBound(to: UInt8.self)
429+
let span = unsafe Span(_unsafeElements: bufferWithFixedType)
430+
let spanWithFixedLifetime = unsafe _cxxOverrideLifetime(span, borrowing: self)
431+
return try? UTF8Span(validating: spanWithFixedLifetime)
432+
}
433+
}
434+
}
435+
436+
@available(SwiftCompatibilitySpan 5.0, *)
437+
extension std.u16string {
438+
public var span: Span<UInt16> {
439+
@_lifetime(borrow self)
440+
@_alwaysEmitIntoClient
441+
borrowing get {
442+
let buffer = unsafe UnsafeBufferPointer(start: self.__dataUnsafe(), count: Int(self.size()))
443+
let rawBuffer = UnsafeRawBufferPointer(buffer)
444+
let bufferWithFixedType = unsafe rawBuffer.assumingMemoryBound(to: UInt16.self)
445+
let span = unsafe Span(_unsafeElements: bufferWithFixedType)
446+
return unsafe _cxxOverrideLifetime(span, borrowing: self)
447+
}
448+
}
449+
}
450+
451+
@available(SwiftCompatibilitySpan 5.0, *)
452+
extension std.u32string {
453+
public var span: Span<UInt32> {
454+
@_lifetime(borrow self)
455+
@_alwaysEmitIntoClient
456+
borrowing get {
457+
let buffer = unsafe UnsafeBufferPointer(start: self.__dataUnsafe(), count: Int(self.size()))
458+
let rawBuffer = UnsafeRawBufferPointer(buffer)
459+
let bufferWithFixedType = unsafe rawBuffer.assumingMemoryBound(to: UInt32.self)
460+
let span = unsafe Span(_unsafeElements: bufferWithFixedType)
461+
return unsafe _cxxOverrideLifetime(span, borrowing: self)
462+
}
463+
}
464+
}

test/Interop/Cxx/stdlib/use-std-string.swift

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,4 +507,47 @@ StdStringTestSuite.test("pass as a default argument") {
507507
}
508508
#endif
509509

510+
StdStringTestSuite.test("std.string to span").require(.stdlib_6_2).code {
511+
guard #available(SwiftStdlib 6.2, *) else { return }
512+
513+
let s = std.string("abc")
514+
// FIXME: remove once borrow checking is fixed.
515+
withExtendedLifetime(s) {
516+
let span = s.span
517+
expectEqual(span.count, 3)
518+
expectFalse(span.isEmpty)
519+
expectEqual(span[0], 97)
520+
expectEqual(span[1], 98)
521+
expectEqual(span[2], 99)
522+
523+
let utfspan = s.utf8Span!
524+
let str = String(copying: utfspan)
525+
expectEqual(str, "abc")
526+
}
527+
528+
let scalars: [UInt16] = [97, 55296, 99]
529+
var s_16 = std.u16string()
530+
for scalar: UInt16 in scalars {
531+
s_16.push_back(scalar)
532+
}
533+
// FIXME: remove once borrow checking is fixed.
534+
withExtendedLifetime(s_16) {
535+
let span_16 = s_16.span
536+
expectEqual(span_16.count, 3)
537+
expectFalse(span_16.isEmpty)
538+
for (n, c) in scalars.enumerated() {
539+
expectEqual(span_16[n], c)
540+
}
541+
}
542+
543+
let s_32 = std.u32string("abc")
544+
// FIXME: remove once borrow checking is fixed.
545+
withExtendedLifetime(s_32) {
546+
let span_32 = s_32.span
547+
for (n, c) in "abc".enumerated() {
548+
expectEqual(span_32[n], UInt32(c.asciiValue!))
549+
}
550+
}
551+
}
552+
510553
runAllTests()

0 commit comments

Comments
 (0)