mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
This adds conformances for C++ string types (`std::string`, `std::u16string`, `std::u32string`) to `Swift.ExpressibleByStringInterpolation`. These conformances currently implicitly use `DefaultStringInterpolation`. In the future we can provide more performant interpolation mechanisms for C++ strings specifically that avoid the extra conversion between Swift String and C++ string types. rdar://147249169
494 lines
14 KiB
Swift
494 lines
14 KiB
Swift
// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -enable-experimental-cxx-interop)
|
||
// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -enable-experimental-cxx-interop -D USE_CUSTOM_STRING_API)
|
||
// RUN: %target-run-simple-swift(-I %S/Inputs -cxx-interoperability-mode=swift-6 -D USE_CUSTOM_STRING_API)
|
||
// RUN: %target-run-simple-swift(-I %S/Inputs -cxx-interoperability-mode=upcoming-swift -D USE_CUSTOM_STRING_API)
|
||
// RUN: %target-run-simple-swift(-I %S/Inputs -cxx-interoperability-mode=upcoming-swift -D USE_CUSTOM_STRING_API -Xcc -std=c++14)
|
||
// RUN: %target-run-simple-swift(-I %S/Inputs -cxx-interoperability-mode=upcoming-swift -D USE_CUSTOM_STRING_API -Xcc -std=c++17)
|
||
// RUN: %target-run-simple-swift(-I %S/Inputs -cxx-interoperability-mode=upcoming-swift -D USE_CUSTOM_STRING_API -Xcc -std=c++20)
|
||
//
|
||
// REQUIRES: executable_test
|
||
|
||
import StdlibUnittest
|
||
import CxxStdlib
|
||
#if USE_CUSTOM_STRING_API
|
||
import StdString
|
||
#endif
|
||
|
||
var StdStringTestSuite = TestSuite("StdString")
|
||
|
||
StdStringTestSuite.test("init") {
|
||
let s = std.string()
|
||
expectEqual(s.size(), 0)
|
||
expectTrue(s.empty())
|
||
}
|
||
|
||
StdStringTestSuite.test("push back") {
|
||
var s = std.string()
|
||
s.push_back(42)
|
||
expectEqual(s.size(), 1)
|
||
expectFalse(s.empty())
|
||
expectEqual(s[0], 42)
|
||
}
|
||
|
||
StdStringTestSuite.test("std::string <=> Swift.String") {
|
||
let cxx1 = std.string()
|
||
let swift1 = String(cxx1)
|
||
expectEqual(swift1, "")
|
||
|
||
let cxx2 = std.string("something123")
|
||
let swift2 = String(cxx2)
|
||
expectEqual(swift2, "something123")
|
||
|
||
let cxx3: std.string = "literal"
|
||
expectEqual(cxx3.size(), 7)
|
||
|
||
// Non-ASCII characters are represented by more than one CChar.
|
||
let cxx4: std.string = "тест"
|
||
expectEqual(cxx4.size(), 8)
|
||
let swift4 = String(cxx4)
|
||
expectEqual(swift4, "тест")
|
||
|
||
let cxx5: std.string = "emoji_🤖"
|
||
expectEqual(cxx5.size(), 10)
|
||
let swift5 = String(cxx5)
|
||
expectEqual(swift5, "emoji_🤖")
|
||
|
||
let cxx6 = std.string("xyz\0abc")
|
||
expectEqual(cxx6.size(), 7)
|
||
let swift6 = String(cxx6)
|
||
expectEqual(swift6, "xyz\0abc")
|
||
|
||
var cxx7 = std.string()
|
||
let bytes: [UInt8] = [0xE1, 0xC1, 0xAC]
|
||
for byte in bytes {
|
||
cxx7.push_back(CChar(bitPattern: byte))
|
||
}
|
||
let swift7 = String(cxx7)
|
||
expectEqual(swift7, "<EFBFBD><EFBFBD><EFBFBD>")
|
||
|
||
let cxxLiteral: std.string = "Hello"
|
||
let cxx8: std.string = "\(cxxLiteral), World!"
|
||
expectEqual(cxx8.size(), 13)
|
||
let swift8 = String(cxx8)
|
||
expectEqual(swift8, "Hello, World!")
|
||
}
|
||
|
||
StdStringTestSuite.test("std::string operators") {
|
||
var s1 = std.string("something")
|
||
let s2 = std.string("123")
|
||
let sum = s1 + s2
|
||
expectEqual(sum, std.string("something123"))
|
||
|
||
expectFalse(s1 == s2)
|
||
let s3 = std.string("something123")
|
||
expectFalse(s1 == s3)
|
||
expectFalse(s2 == s3)
|
||
|
||
s1 += s2
|
||
expectTrue(s1 == std.string("something123"))
|
||
expectTrue(s1 == s3)
|
||
|
||
// Make sure the operators work together with ExpressibleByStringLiteral conformance.
|
||
s1 += "literal"
|
||
expectTrue(s1 == "something123literal")
|
||
}
|
||
|
||
StdStringTestSuite.test("std::u16string operators") {
|
||
var s1 = std.u16string("something")
|
||
let s2 = std.u16string("123")
|
||
let sum = s1 + s2
|
||
expectEqual(sum, std.u16string("something123"))
|
||
|
||
expectFalse(s1 == s2)
|
||
let s3 = std.u16string("something123")
|
||
expectFalse(s1 == s3)
|
||
expectFalse(s2 == s3)
|
||
|
||
s1 += s2
|
||
expectTrue(s1 == std.u16string("something123"))
|
||
expectTrue(s1 == s3)
|
||
|
||
// Make sure the operators work together with ExpressibleByStringLiteral conformance.
|
||
s1 += "literal"
|
||
expectTrue(s1 == "something123literal")
|
||
}
|
||
|
||
StdStringTestSuite.test("std::u32string operators") {
|
||
var s1 = std.u32string("something")
|
||
let s2 = std.u32string("123")
|
||
let sum = s1 + s2
|
||
expectEqual(sum, std.u32string("something123"))
|
||
|
||
expectFalse(s1 == s2)
|
||
let s3 = std.u32string("something123")
|
||
expectFalse(s1 == s3)
|
||
expectFalse(s2 == s3)
|
||
|
||
s1 += s2
|
||
expectTrue(s1 == std.u32string("something123"))
|
||
expectTrue(s1 == s3)
|
||
|
||
// Make sure the operators work together with ExpressibleByStringLiteral conformance.
|
||
s1 += "literal"
|
||
expectTrue(s1 == "something123literal")
|
||
}
|
||
|
||
StdStringTestSuite.test("std::string::append") {
|
||
var s1 = std.string("0123")
|
||
let s2 = std.string("abc")
|
||
s1.append(s2)
|
||
expectEqual(s1, std.string("0123abc"))
|
||
}
|
||
|
||
StdStringTestSuite.test("std::u16string::append") {
|
||
var s1 = std.u16string("0123")
|
||
let s2 = std.u16string("abc")
|
||
s1.append(s2)
|
||
expectEqual(s1, std.u16string("0123abc"))
|
||
}
|
||
|
||
StdStringTestSuite.test("std::u32string::append") {
|
||
var s1 = std.u32string("0123")
|
||
let s2 = std.u32string("abc")
|
||
s1.append(s2)
|
||
expectEqual(s1, std.u32string("0123abc"))
|
||
}
|
||
|
||
StdStringTestSuite.test("std::string comparison") {
|
||
let s1 = std.string("abc")
|
||
let s2 = std.string("def")
|
||
let s3 = std.string("abc")
|
||
|
||
expectTrue(s1 < s2)
|
||
expectFalse(s2 < s1)
|
||
expectTrue(s1 <= s2)
|
||
expectFalse(s2 <= s1)
|
||
expectTrue(s2 > s1)
|
||
expectFalse(s1 > s2)
|
||
expectTrue(s2 >= s1)
|
||
expectFalse(s1 >= s2)
|
||
expectTrue(s1 == s3)
|
||
}
|
||
|
||
StdStringTestSuite.test("std::u16string comparison") {
|
||
let s1 = std.u16string("abc")
|
||
let s2 = std.u16string("def")
|
||
let s3 = std.u16string("abc")
|
||
|
||
expectTrue(s1 < s2)
|
||
expectFalse(s2 < s1)
|
||
expectTrue(s1 <= s2)
|
||
expectFalse(s2 <= s1)
|
||
expectTrue(s2 > s1)
|
||
expectFalse(s1 > s2)
|
||
expectTrue(s2 >= s1)
|
||
expectFalse(s1 >= s2)
|
||
expectTrue(s1 == s3)
|
||
}
|
||
|
||
StdStringTestSuite.test("std::u32string comparison") {
|
||
let s1 = std.u32string("abc")
|
||
let s2 = std.u32string("def")
|
||
let s3 = std.u32string("abc")
|
||
|
||
expectTrue(s1 < s2)
|
||
expectFalse(s2 < s1)
|
||
expectTrue(s1 <= s2)
|
||
expectFalse(s2 <= s1)
|
||
expectTrue(s2 > s1)
|
||
expectFalse(s1 > s2)
|
||
expectTrue(s2 >= s1)
|
||
expectFalse(s1 >= s2)
|
||
expectTrue(s1 == s3)
|
||
}
|
||
|
||
StdStringTestSuite.test("std::string as Hashable") {
|
||
let s0 = std.string()
|
||
let h0 = s0.hashValue
|
||
|
||
let s1 = std.string("something")
|
||
let h1 = s1.hashValue
|
||
|
||
let s2 = std.string("something123")
|
||
let h2 = s2.hashValue
|
||
|
||
let s3 = std.string("something")
|
||
let h3 = s3.hashValue
|
||
|
||
expectEqual(h1, h3)
|
||
expectNotEqual(h0, h1)
|
||
expectNotEqual(h0, h2)
|
||
expectNotEqual(h0, h3)
|
||
expectNotEqual(h1, h2)
|
||
expectNotEqual(h2, h3)
|
||
}
|
||
|
||
StdStringTestSuite.test("std::u16string as Hashable") {
|
||
let s0 = std.u16string()
|
||
let h0 = s0.hashValue
|
||
|
||
let s1 = std.u16string("something")
|
||
let h1 = s1.hashValue
|
||
|
||
let s2 = std.u16string("something123")
|
||
let h2 = s2.hashValue
|
||
|
||
let s3 = std.u16string("something")
|
||
let h3 = s3.hashValue
|
||
|
||
expectEqual(h1, h3)
|
||
expectNotEqual(h0, h1)
|
||
expectNotEqual(h0, h2)
|
||
expectNotEqual(h0, h3)
|
||
expectNotEqual(h1, h2)
|
||
expectNotEqual(h2, h3)
|
||
}
|
||
|
||
StdStringTestSuite.test("std::u32string as Hashable") {
|
||
let s0 = std.u32string()
|
||
let h0 = s0.hashValue
|
||
|
||
let s1 = std.u32string("something")
|
||
let h1 = s1.hashValue
|
||
|
||
let s2 = std.u32string("something123")
|
||
let h2 = s2.hashValue
|
||
|
||
let s3 = std.u32string("something")
|
||
let h3 = s3.hashValue
|
||
|
||
expectEqual(h1, h3)
|
||
expectNotEqual(h0, h1)
|
||
expectNotEqual(h0, h2)
|
||
expectNotEqual(h0, h3)
|
||
expectNotEqual(h1, h2)
|
||
expectNotEqual(h2, h3)
|
||
}
|
||
|
||
StdStringTestSuite.test("std::u16string <=> Swift.String") {
|
||
let cxx1 = std.u16string()
|
||
let swift1 = String(cxx1)
|
||
expectEqual(swift1, "")
|
||
|
||
let cxx2 = std.u16string("something123")
|
||
expectEqual(cxx2.size(), 12)
|
||
let swift2 = String(cxx2)
|
||
expectEqual(swift2, "something123")
|
||
|
||
let cxx3: std.u16string = "literal"
|
||
expectEqual(cxx3.size(), 7)
|
||
|
||
let cxx4: std.u16string = "тест"
|
||
expectEqual(cxx4.size(), 4)
|
||
let swift4 = String(cxx4)
|
||
expectEqual(swift4, "тест")
|
||
|
||
// Emojis are represented by more than one CWideChar.
|
||
let cxx5: std.u16string = "emoji_🤖"
|
||
expectEqual(cxx5.size(), 8)
|
||
let swift5 = String(cxx5)
|
||
expectEqual(swift5, "emoji_🤖")
|
||
|
||
let cxx6 = std.u16string("xyz\0abc")
|
||
expectEqual(cxx6.size(), 7)
|
||
let swift6 = String(cxx6)
|
||
expectEqual(swift6, "xyz\0abc")
|
||
|
||
let cxxLiteral: std.u16string = "Hello"
|
||
let cxx8: std.u16string = "\(cxxLiteral), World!"
|
||
expectEqual(cxx8.size(), 13)
|
||
let swift8 = String(cxx8)
|
||
expectEqual(swift8, "Hello, World!")
|
||
}
|
||
|
||
StdStringTestSuite.test("std::u32string <=> Swift.String") {
|
||
let cxx1 = std.u32string()
|
||
let swift1 = String(cxx1)
|
||
expectEqual(swift1, "")
|
||
|
||
let cxx2 = std.u32string("something123")
|
||
expectEqual(cxx2.size(), 12)
|
||
let swift2 = String(cxx2)
|
||
expectEqual(swift2, "something123")
|
||
|
||
let cxx3: std.u32string = "literal"
|
||
expectEqual(cxx3.size(), 7)
|
||
|
||
let cxx4: std.u32string = "тест"
|
||
expectEqual(cxx4.size(), 4)
|
||
let swift4 = String(cxx4)
|
||
expectEqual(swift4, "тест")
|
||
|
||
// Emojis are represented by more than one CWideChar.
|
||
let cxx5: std.u32string = "emoji_🤖"
|
||
expectEqual(cxx5.size(), 7)
|
||
let swift5 = String(cxx5)
|
||
expectEqual(swift5, "emoji_🤖")
|
||
|
||
let cxx6 = std.u32string("xyz\0abc")
|
||
expectEqual(cxx6.size(), 7)
|
||
let swift6 = String(cxx6)
|
||
expectEqual(swift6, "xyz\0abc")
|
||
|
||
let cxxLiteral: std.u32string = "Hello"
|
||
let cxx8: std.u32string = "\(cxxLiteral), World!"
|
||
expectEqual(cxx8.size(), 13)
|
||
let swift8 = String(cxx8)
|
||
expectEqual(swift8, "Hello, World!")
|
||
}
|
||
|
||
StdStringTestSuite.test("std::string as Swift.CustomDebugStringConvertible") {
|
||
let cxx1 = std.string()
|
||
expectEqual(cxx1.debugDescription, "std.string()")
|
||
|
||
let cxx2 = std.string("something123")
|
||
expectEqual(cxx2.debugDescription, "std.string(something123)")
|
||
|
||
let bytes: [UInt8] = [0xE1, 0xC1, 0xAC]
|
||
var cxx3 = std.string()
|
||
for byte in bytes {
|
||
cxx3.push_back(CChar(bitPattern: byte))
|
||
}
|
||
expectEqual(cxx3.debugDescription, "std.string(<28><><EFBFBD>)")
|
||
}
|
||
|
||
StdStringTestSuite.test("std::u16string as Swift.CustomDebugStringConvertible") {
|
||
let cxx1 = std.u16string()
|
||
expectEqual(cxx1.debugDescription, "std.u16string()")
|
||
|
||
let cxx2 = std.u16string("something123")
|
||
expectEqual(cxx2.debugDescription, "std.u16string(something123)")
|
||
|
||
let scalars: [UInt16] = [97, 55296, 99]
|
||
var cxx3 = std.u16string()
|
||
for scalar in scalars {
|
||
cxx3.push_back(scalar)
|
||
}
|
||
expectEqual(cxx3.debugDescription, "std.u16string(a<>c)")
|
||
}
|
||
|
||
StdStringTestSuite.test("std::u32string as Swift.CustomDebugStringConvertible") {
|
||
let cxx1 = std.u32string()
|
||
expectEqual(cxx1.debugDescription, "std.u32string()")
|
||
|
||
let cxx2 = std.u32string("something123")
|
||
expectEqual(cxx2.debugDescription, "std.u32string(something123)")
|
||
|
||
// Since std::u32string does not support pushing back UInt32 directly, we utilize UInt16 instead.
|
||
let scalars: [UInt16] = [97, 55296, 99]
|
||
var cxx3_16 = std.u16string()
|
||
for scalar: UInt16 in scalars {
|
||
cxx3_16.push_back(scalar)
|
||
}
|
||
let cxx3 = std.u32string(String(cxx3_16))
|
||
expectEqual(cxx3.debugDescription, "std.u32string(a<>c)")
|
||
}
|
||
|
||
StdStringTestSuite.test("std::string as Swift.Sequence") {
|
||
let cxx1 = std.string()
|
||
var iterated = false
|
||
for _ in cxx1 {
|
||
iterated = true
|
||
}
|
||
expectFalse(iterated)
|
||
|
||
let cxx2 = std.string("abc123")
|
||
var chars = 0
|
||
var sum = 0
|
||
for it in cxx2 {
|
||
chars += 1
|
||
sum += Int(it)
|
||
}
|
||
expectEqual(6, chars)
|
||
expectEqual(97 + 98 + 99 + 49 + 50 + 51, sum)
|
||
}
|
||
|
||
StdStringTestSuite.test("std::string as CustomStringConvertible") {
|
||
let cxx1 = std.string()
|
||
expectEqual(cxx1.description, "")
|
||
|
||
let cxx2 = std.string("something123")
|
||
expectEqual(cxx2.description, "something123")
|
||
|
||
let bytes: [UInt8] = [0xE1, 0xC1, 0xAC]
|
||
var cxx3 = std.string()
|
||
for byte in bytes {
|
||
cxx3.push_back(CChar(bitPattern: byte))
|
||
}
|
||
expectEqual(cxx3.description, "<EFBFBD><EFBFBD><EFBFBD>")
|
||
}
|
||
|
||
StdStringTestSuite.test("std::u16string as Swift.CustomStringConvertible") {
|
||
let cxx1 = std.u16string()
|
||
expectEqual(cxx1.description, "")
|
||
|
||
let cxx2 = std.u16string("something123")
|
||
expectEqual(cxx2.description, "something123")
|
||
|
||
let scalars: [UInt16] = [97, 55296, 99]
|
||
var cxx3 = std.u16string()
|
||
for scalar in scalars {
|
||
cxx3.push_back(scalar)
|
||
}
|
||
expectEqual(cxx3.description, "a<EFBFBD>c")
|
||
}
|
||
|
||
StdStringTestSuite.test("std::u32string as Swift.CustomStringConvertible") {
|
||
let cxx1 = std.u32string()
|
||
expectEqual(cxx1.description, "")
|
||
|
||
let cxx2 = std.u32string("something123")
|
||
expectEqual(cxx2.description, "something123")
|
||
|
||
// Since std::u32string does not support pushing back UInt32 directly, we utilize UInt16 instead.
|
||
let scalars: [UInt16] = [97, 55296, 99]
|
||
var cxx3_16 = std.u16string()
|
||
for scalar: UInt16 in scalars {
|
||
cxx3_16.push_back(scalar)
|
||
}
|
||
let cxx3 = std.u32string(String(cxx3_16))
|
||
expectEqual(cxx3.description, "a<EFBFBD>c")
|
||
|
||
// For `push_back`
|
||
let scalars2: [Unicode.Scalar] = [0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x2C, 0x20, 0x4E16, 0x754C]
|
||
.compactMap { Unicode.Scalar($0) }
|
||
var cxx4 = std.u32string()
|
||
for scalar: Unicode.Scalar in scalars2 {
|
||
cxx4.push_back(scalar)
|
||
}
|
||
expectEqual(cxx4.description, "Hello, 世界")
|
||
}
|
||
|
||
StdStringTestSuite.test("std::string from C string") {
|
||
let str = "abc".withCString { ptr in
|
||
std.string(ptr)
|
||
}
|
||
expectEqual(str, std.string("abc"))
|
||
}
|
||
|
||
#if USE_CUSTOM_STRING_API
|
||
StdStringTestSuite.test("get from a method") {
|
||
let box = HasMethodThatReturnsString()
|
||
let str = box.getString()
|
||
expectEqual(str.size(), 3)
|
||
expectEqual(str, std.string("111"))
|
||
}
|
||
|
||
StdStringTestSuite.test("pass as an argument") {
|
||
let s = std.string("a")
|
||
let res = takesStringWithDefaultArg(s)
|
||
expectEqual(res.size(), 1)
|
||
expectEqual(res[0], 97)
|
||
}
|
||
|
||
StdStringTestSuite.test("pass as a default argument") {
|
||
let res = takesStringWithDefaultArg()
|
||
expectEqual(res.size(), 3)
|
||
expectEqual(res[0], 97)
|
||
expectEqual(res[1], 98)
|
||
expectEqual(res[2], 99)
|
||
}
|
||
#endif
|
||
|
||
runAllTests()
|