Skip to content

Commit 9a01358

Browse files
authored
[C++ Interop] Deprecate Optional argument overloads of std.string initializer in Swift’s C++ Stdlib Overlay (#83223)
This patch addresses potential ambiguity and unsafe behavior by deprecating initializer overloads for `std.string` that accept optional arguments (`string?`). These overloads previously allowed implicit initialization from optional pointers (`UnsafePointer<CChar>?`), causing unclear or unintended behavior. - Deprecated: `init(_ string: UnsafePointer<CChar>?)`, guiding users toward the explicit, non-optional initializer. - Unavailable: `init(_ string: String?)`, explicitly preventing misuse from optional Swift strings. rdar://148041893
1 parent eff13b7 commit 9a01358

File tree

3 files changed

+47
-8
lines changed

3 files changed

+47
-8
lines changed

stdlib/public/Cxx/std/String.swift

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,34 @@ extension std.string {
3333
}
3434
}
3535

36+
3637
@_alwaysEmitIntoClient
37-
public init(_ string: UnsafePointer<CChar>?) {
38-
if let str = unsafe string {
39-
#if os(Windows)
38+
@available(*, unavailable, message: "initializing std::string with an optional String is not supported; unwrap the optional value before passing it to std.string()")
39+
public init(_ string: String?) {
40+
fatalError("This initializer is unavailable and should never be called.")
41+
}
42+
43+
@_alwaysEmitIntoClient
44+
public init(_ string: UnsafePointer<CChar>) {
45+
#if os(Windows)
4046
// Use the 2 parameter constructor.
4147
// The MSVC standard library has a enable_if template guard
4248
// on the 3 parameter constructor, and thus it's not imported into Swift.
43-
unsafe self.init(str, UTF8._nullCodeUnitOffset(in: str))
44-
#else
45-
unsafe self.init(str, UTF8._nullCodeUnitOffset(in: str), .init())
46-
#endif
47-
} else {
49+
unsafe self.init(string, UTF8._nullCodeUnitOffset(in: string))
50+
#else
51+
unsafe self.init(string, UTF8._nullCodeUnitOffset(in: string), .init())
52+
#endif
53+
}
54+
55+
@_alwaysEmitIntoClient
56+
@_disfavoredOverload
57+
@available(*, deprecated, message: "unwrap the optional value and use init(_ string: UnsafePointer<CChar>) instead")
58+
public init(_ string: UnsafePointer<CChar>?) {
59+
guard let str = unsafe string else {
4860
self.init()
61+
return
4962
}
63+
self.init(str)
5064
}
5165
}
5266

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// RUN: %target-typecheck-verify-swift -I %S/Inputs -cxx-interoperability-mode=default
2+
import CxxStdlib
3+
4+
let tmpOpt: String? = "üüüüüüü"
5+
let cppString1 = std.string(tmpOpt) // expected-warning {{'init(_:)' is deprecated: unwrap the optional value and use init(_ string: UnsafePointer<CChar>) instead}}
6+
7+
let tmpNonOpt: String = "üüüüüüü"
8+
let cppString2 = std.string(tmpNonOpt)

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,23 @@ StdStringTestSuite.test("std::string <=> Swift.String") {
7373
expectEqual(swift8, "Hello, World!")
7474
}
7575

76+
StdStringTestSuite.test("std::string <=> Optional<String>") {
77+
let ascii: String? = "aaaaaaa"
78+
let nonAscii: String? = "üüüüüüü"
79+
let nilString: String? = nil
80+
let emptyString: String? = ""
81+
82+
let s1 = std.string(ascii!)
83+
let s2 = std.string(nonAscii!)
84+
let s3 = std.string(nilString ?? "")
85+
let s4 = std.string(emptyString!)
86+
87+
expectEqual(String(s1), "aaaaaaa")
88+
expectEqual(String(s2), "üüüüüüü")
89+
expectEqual(String(s3), "")
90+
expectEqual(String(s4), "")
91+
}
92+
7693
StdStringTestSuite.test("std::string operators") {
7794
var s1 = std.string("something")
7895
let s2 = std.string("123")

0 commit comments

Comments
 (0)