From e694f861fd21a946a143eb9c5d4b63dcdd879ef0 Mon Sep 17 00:00:00 2001 From: Rick van Voorden Date: Thu, 16 Oct 2025 14:42:38 -0700 Subject: [PATCH 1/6] [stdlib] string identical --- benchmark/single-source/StringTests.swift | 14 +++ benchmark/single-source/SubstringTest.swift | 9 ++ stdlib/public/core/String.swift | 40 +++++- stdlib/public/core/Substring.swift | 41 ++++++ test/stdlib/StringAPI.swift | 37 ++++++ test/stdlib/subString.swift | 132 ++++++++++++++++++++ 6 files changed, 272 insertions(+), 1 deletion(-) diff --git a/benchmark/single-source/StringTests.swift b/benchmark/single-source/StringTests.swift index bcc43a2633777..4940e8ce31967 100644 --- a/benchmark/single-source/StringTests.swift +++ b/benchmark/single-source/StringTests.swift @@ -40,6 +40,10 @@ public var benchmarks: [BenchmarkInfo] { runFunction: run_StringHasSuffixUnicode, tags: [.validation, .api, .String], legacyFactor: 1000), + BenchmarkInfo( + name: "StringIdentical", + runFunction: run_StringIdentical, + tags: [.validation, .api, .String]), ] if #available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) { @@ -1676,3 +1680,13 @@ public func run_iterateWords(_ n: Int) { blackHole(swiftOrgHTML._words) } } + +public func run_StringIdentical(_ n: Int) { + let str1 = "The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. " + let str2 = str1 + for _ in 0 ..< n { + for _ in 0 ..< 100_000 { + check(str1.isTriviallyIdentical(to: str2)) + } + } +} diff --git a/benchmark/single-source/SubstringTest.swift b/benchmark/single-source/SubstringTest.swift index 71ef0c774f26f..8ccb340b3ce39 100644 --- a/benchmark/single-source/SubstringTest.swift +++ b/benchmark/single-source/SubstringTest.swift @@ -30,6 +30,7 @@ public let benchmarks = [ BenchmarkInfo(name: "SubstringFromLongString2", runFunction: run_SubstringFromLongString, tags: [.validation, .api, .String]), BenchmarkInfo(name: "SubstringFromLongStringGeneric2", runFunction: run_SubstringFromLongStringGeneric, tags: [.validation, .api, .String]), BenchmarkInfo(name: "SubstringTrimmingASCIIWhitespace", runFunction: run_SubstringTrimmingASCIIWhitespace, tags: [.validation, .api, .String]), + BenchmarkInfo(name: "SubstringIdentical", runFunction: run_SubstringIdentical, tags: [.validation, .String]), ] // A string that doesn't fit in small string storage and doesn't fit in Latin-1 @@ -332,3 +333,11 @@ public func run _LessSubstringSubstringGenericStringProtocol(_ n: Int) { } } */ + +@inline(never) +public func run_SubstringIdentical(_ n: Int) { + let (a, b) = (ss1, ss1) + for _ in 1...n*500 { + blackHole(a.isTriviallyIdentical(to: b)) + } +} diff --git a/stdlib/public/core/String.swift b/stdlib/public/core/String.swift index e6715e91cc6ed..bab421dbacd2b 100644 --- a/stdlib/public/core/String.swift +++ b/stdlib/public/core/String.swift @@ -1112,4 +1112,42 @@ extension String { } } - +extension String { + /// Returns a boolean value indicating whether this string is identical to + /// `other`. + /// + /// Two string values are identical if there is no way to distinguish between + /// them. + /// + /// For any values `a`, `b`, and `c`: + /// + /// - `a.isTriviallyIdentical(to: a)` is always `true`. (Reflexivity) + /// - `a.isTriviallyIdentical(to: b)` implies `b.isTriviallyIdentical(to: a)`. + /// (Symmetry) + /// - If `a.isTriviallyIdentical(to: b)` and `b.isTriviallyIdentical(to: c)` + /// are both `true`, then `a.isTriviallyIdentical(to: c)` is also `true`. + /// (Transitivity) + /// - `a.isTriviallyIdentical(b)` implies `a == b` + /// - `a == b` does not imply `a.isTriviallyIdentical(b)` + /// + /// Values produced by copying the same value, with no intervening mutations, + /// will compare identical: + /// + /// ```swift + /// let d = c + /// print(c.isTriviallyIdentical(to: d)) + /// // Prints true + /// ``` + /// + /// Comparing strings this way includes comparing (normally) hidden + /// implementation details such as the memory location of any underlying + /// string storage object. Therefore, identical strings are guaranteed to + /// compare equal with `==`, but not all equal strings are considered + /// identical. + /// + /// - Performance: O(1) + @_alwaysEmitIntoClient + public func isTriviallyIdentical(to other: Self) -> Bool { + self._guts.rawBits == other._guts.rawBits + } +} diff --git a/stdlib/public/core/Substring.swift b/stdlib/public/core/Substring.swift index e4e91b6a02a4c..a69a08b90a4b5 100644 --- a/stdlib/public/core/Substring.swift +++ b/stdlib/public/core/Substring.swift @@ -1385,3 +1385,44 @@ extension Substring { return Substring(_unchecked: Slice(base: base, bounds: r)) } } + +extension Substring { + /// Returns a boolean value indicating whether this substring is identical to + /// `other`. + /// + /// Two substring values are identical if there is no way to distinguish + /// between them. + /// + /// For any values `a`, `b`, and `c`: + /// + /// - `a.isTriviallyIdentical(to: a)` is always `true`. (Reflexivity) + /// - `a.isTriviallyIdentical(to: b)` implies `b.isTriviallyIdentical(to: a)`. + /// (Symmetry) + /// - If `a.isTriviallyIdentical(to: b)` and `b.isTriviallyIdentical(to: c)` + /// are both `true`, then `a.isTriviallyIdentical(to: c)` is also `true`. + /// (Transitivity) + /// - `a.isTriviallyIdentical(b)` implies `a == b` + /// - `a == b` does not imply `a.isTriviallyIdentical(b)` + /// + /// Values produced by copying the same value, with no intervening mutations, + /// will compare identical: + /// + /// ```swift + /// let d = c + /// print(c.isTriviallyIdentical(to: d)) + /// // Prints true + /// ``` + /// + /// Comparing substrings this way includes comparing (normally) hidden + /// implementation details such as the memory location of any underlying + /// substring storage object. Therefore, identical substrings are guaranteed + /// to compare equal with `==`, but not all equal substrings are considered + /// identical. + /// + /// - Performance: O(1) + @_alwaysEmitIntoClient + public func isTriviallyIdentical(to other: Self) -> Bool { + self._wholeGuts.rawBits == other._wholeGuts.rawBits && + self._offsetRange == other._offsetRange + } +} diff --git a/test/stdlib/StringAPI.swift b/test/stdlib/StringAPI.swift index fb33db9b53ee7..d85cfb872fb74 100644 --- a/test/stdlib/StringAPI.swift +++ b/test/stdlib/StringAPI.swift @@ -533,4 +533,41 @@ StringTests.test("hasPrefix/hasSuffix vs Character boundaries") { expectFalse(s2.hasSuffix("\n")) } +StringTests.test("isTriviallyIdentical(to:) small ascii") { + let a = "Hello" + let b = "Hello" + + precondition(a == b) + + expectTrue(a.isTriviallyIdentical(to: a)) + expectTrue(b.isTriviallyIdentical(to: b)) + expectTrue(a.isTriviallyIdentical(to: b)) // Both small ASCII strings + expectTrue(b.isTriviallyIdentical(to: a)) +} + +StringTests.test("isTriviallyIdentical(to:) small unicode") { + let a = "Cafe\u{301}" + let b = "Cafe\u{301}" + let c = "Café" + + precondition(a == b) + precondition(b == c) + + expectTrue(a.isTriviallyIdentical(to: b)) + expectTrue(b.isTriviallyIdentical(to: a)) + expectFalse(a.isTriviallyIdentical(to: c)) + expectFalse(b.isTriviallyIdentical(to: c)) +} + +StringTests.test("isTriviallyIdentical(to:) large ascii") { + let a = String(repeating: "foo", count: 1000) + let b = String(repeating: "foo", count: 1000) + + precondition(a == b) + + expectFalse(a.isTriviallyIdentical(to: b)) // Two large, distinct native strings + expectTrue(a.isTriviallyIdentical(to: a)) + expectTrue(b.isTriviallyIdentical(to: b)) +} + runAllTests() diff --git a/test/stdlib/subString.swift b/test/stdlib/subString.swift index 2a7b9e58db2d8..78c6d8cb612e3 100644 --- a/test/stdlib/subString.swift +++ b/test/stdlib/subString.swift @@ -31,6 +31,41 @@ func checkHasContiguousStorageSubstring(_ x: Substring.UTF8View) { expectTrue(hasStorage) } +fileprivate func slices( + _ s: String, + from: Int, + to: Int +) -> ( + Substring, + Substring, + Substring +) { + let s1 = s[s.index(s.startIndex, offsetBy: from) ..< + s.index(s.startIndex, offsetBy: to)] + let s2 = s1[s1.startIndex.. Bool { + s.allSatisfy { $0.isEmpty == false } +} + +fileprivate func allEqual( + _ s: Substring... +) -> Bool { + for i in 0.. Date: Thu, 16 Oct 2025 15:58:33 -0700 Subject: [PATCH 2/6] [stdlib] string identical Co-authored-by: Karoy Lorentey --- stdlib/public/core/String.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/public/core/String.swift b/stdlib/public/core/String.swift index bab421dbacd2b..cb2ddbfb15929 100644 --- a/stdlib/public/core/String.swift +++ b/stdlib/public/core/String.swift @@ -1146,7 +1146,7 @@ extension String { /// identical. /// /// - Performance: O(1) - @_alwaysEmitIntoClient + @available(SwiftStdlib 6.3, *) public func isTriviallyIdentical(to other: Self) -> Bool { self._guts.rawBits == other._guts.rawBits } From 45c37b5df98c0715a93a721410dd809281496d09 Mon Sep 17 00:00:00 2001 From: Rick van Voorden Date: Thu, 16 Oct 2025 15:58:43 -0700 Subject: [PATCH 3/6] [stdlib] string identical Co-authored-by: Karoy Lorentey --- stdlib/public/core/Substring.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/public/core/Substring.swift b/stdlib/public/core/Substring.swift index a69a08b90a4b5..7b61664863f1c 100644 --- a/stdlib/public/core/Substring.swift +++ b/stdlib/public/core/Substring.swift @@ -1420,7 +1420,7 @@ extension Substring { /// identical. /// /// - Performance: O(1) - @_alwaysEmitIntoClient + @available(SwiftStdlib 6.3, *) public func isTriviallyIdentical(to other: Self) -> Bool { self._wholeGuts.rawBits == other._wholeGuts.rawBits && self._offsetRange == other._offsetRange From e2938ef744850600aa5737f6dc925ab6035bd8fd Mon Sep 17 00:00:00 2001 From: Rick van Voorden Date: Mon, 20 Oct 2025 11:22:36 -0700 Subject: [PATCH 4/6] [stdlib] string identical tests --- benchmark/single-source/StringTests.swift | 13 +++-- benchmark/single-source/SubstringTest.swift | 49 ++++++++++------- test/stdlib/StringAPI.swift | 27 ++++++++-- test/stdlib/subString.swift | 58 +++++++++++++++++---- 4 files changed, 110 insertions(+), 37 deletions(-) diff --git a/benchmark/single-source/StringTests.swift b/benchmark/single-source/StringTests.swift index 4940e8ce31967..579ac6f3d3ce8 100644 --- a/benchmark/single-source/StringTests.swift +++ b/benchmark/single-source/StringTests.swift @@ -40,10 +40,6 @@ public var benchmarks: [BenchmarkInfo] { runFunction: run_StringHasSuffixUnicode, tags: [.validation, .api, .String], legacyFactor: 1000), - BenchmarkInfo( - name: "StringIdentical", - runFunction: run_StringIdentical, - tags: [.validation, .api, .String]), ] if #available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) { @@ -53,6 +49,14 @@ public var benchmarks: [BenchmarkInfo] { runFunction: run_iterateWords, tags: [.validation, .String])) } + + if #available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, visionOS 9999, *) { + result.append( + BenchmarkInfo( + name: "StringIdentical", + runFunction: run_StringIdentical, + tags: [.validation, .api, .String])) + } return result } @@ -1681,6 +1685,7 @@ public func run_iterateWords(_ n: Int) { } } +@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, visionOS 9999, *) public func run_StringIdentical(_ n: Int) { let str1 = "The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. " let str2 = str1 diff --git a/benchmark/single-source/SubstringTest.swift b/benchmark/single-source/SubstringTest.swift index 8ccb340b3ce39..a7652ff1064bc 100644 --- a/benchmark/single-source/SubstringTest.swift +++ b/benchmark/single-source/SubstringTest.swift @@ -12,26 +12,34 @@ import TestsUtils -public let benchmarks = [ - BenchmarkInfo(name: "EqualStringSubstring", runFunction: run_EqualStringSubstring, tags: [.validation, .api, .String]), - BenchmarkInfo(name: "EqualSubstringString", runFunction: run_EqualSubstringString, tags: [.validation, .api, .String]), - BenchmarkInfo(name: "EqualSubstringSubstring", runFunction: run_EqualSubstringSubstring, tags: [.validation, .api, .String]), - BenchmarkInfo(name: "EqualSubstringSubstringGenericEquatable", runFunction: run_EqualSubstringSubstringGenericEquatable, tags: [.validation, .api, .String]), - BenchmarkInfo(name: "SubstringRemoveFirst1", runFunction: run_SubstringRemoveFirst1, tags: [.validation, .api, .String]), - BenchmarkInfo(name: "SubstringRemoveLast1", runFunction: run_SubstringRemoveLast1, tags: [.validation, .api, .String]), - BenchmarkInfo(name: "LessSubstringSubstring", runFunction: run_LessSubstringSubstring, tags: [.validation, .api, .String]), - BenchmarkInfo(name: "LessSubstringSubstringGenericComparable", runFunction: run_LessSubstringSubstringGenericComparable, tags: [.validation, .api, .String]), - BenchmarkInfo(name: "StringFromLongWholeSubstring", runFunction: run_StringFromLongWholeSubstring, tags: [.validation, .api, .String]), - BenchmarkInfo(name: "StringFromLongWholeSubstringGeneric", runFunction: run_StringFromLongWholeSubstringGeneric, tags: [.validation, .api, .String]), - BenchmarkInfo(name: "SubstringComparable", runFunction: run_SubstringComparable, tags: [.validation, .api, .String], - setUpFunction: { blackHole(_comparison) }), - BenchmarkInfo(name: "SubstringEqualString", runFunction: run_SubstringEqualString, tags: [.validation, .api, .String]), - BenchmarkInfo(name: "SubstringEquatable", runFunction: run_SubstringEquatable, tags: [.validation, .api, .String]), - BenchmarkInfo(name: "SubstringFromLongString2", runFunction: run_SubstringFromLongString, tags: [.validation, .api, .String]), - BenchmarkInfo(name: "SubstringFromLongStringGeneric2", runFunction: run_SubstringFromLongStringGeneric, tags: [.validation, .api, .String]), - BenchmarkInfo(name: "SubstringTrimmingASCIIWhitespace", runFunction: run_SubstringTrimmingASCIIWhitespace, tags: [.validation, .api, .String]), - BenchmarkInfo(name: "SubstringIdentical", runFunction: run_SubstringIdentical, tags: [.validation, .String]), -] +public var benchmarks: [BenchmarkInfo] { + var result = [ + BenchmarkInfo(name: "EqualStringSubstring", runFunction: run_EqualStringSubstring, tags: [.validation, .api, .String]), + BenchmarkInfo(name: "EqualSubstringString", runFunction: run_EqualSubstringString, tags: [.validation, .api, .String]), + BenchmarkInfo(name: "EqualSubstringSubstring", runFunction: run_EqualSubstringSubstring, tags: [.validation, .api, .String]), + BenchmarkInfo(name: "EqualSubstringSubstringGenericEquatable", runFunction: run_EqualSubstringSubstringGenericEquatable, tags: [.validation, .api, .String]), + BenchmarkInfo(name: "SubstringRemoveFirst1", runFunction: run_SubstringRemoveFirst1, tags: [.validation, .api, .String]), + BenchmarkInfo(name: "SubstringRemoveLast1", runFunction: run_SubstringRemoveLast1, tags: [.validation, .api, .String]), + BenchmarkInfo(name: "LessSubstringSubstring", runFunction: run_LessSubstringSubstring, tags: [.validation, .api, .String]), + BenchmarkInfo(name: "LessSubstringSubstringGenericComparable", runFunction: run_LessSubstringSubstringGenericComparable, tags: [.validation, .api, .String]), + BenchmarkInfo(name: "StringFromLongWholeSubstring", runFunction: run_StringFromLongWholeSubstring, tags: [.validation, .api, .String]), + BenchmarkInfo(name: "StringFromLongWholeSubstringGeneric", runFunction: run_StringFromLongWholeSubstringGeneric, tags: [.validation, .api, .String]), + BenchmarkInfo(name: "SubstringComparable", runFunction: run_SubstringComparable, tags: [.validation, .api, .String], + setUpFunction: { blackHole(_comparison) }), + BenchmarkInfo(name: "SubstringEqualString", runFunction: run_SubstringEqualString, tags: [.validation, .api, .String]), + BenchmarkInfo(name: "SubstringEquatable", runFunction: run_SubstringEquatable, tags: [.validation, .api, .String]), + BenchmarkInfo(name: "SubstringFromLongString2", runFunction: run_SubstringFromLongString, tags: [.validation, .api, .String]), + BenchmarkInfo(name: "SubstringFromLongStringGeneric2", runFunction: run_SubstringFromLongStringGeneric, tags: [.validation, .api, .String]), + BenchmarkInfo(name: "SubstringTrimmingASCIIWhitespace", runFunction: run_SubstringTrimmingASCIIWhitespace, tags: [.validation, .api, .String]), + ] + + if #available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, visionOS 9999, *) { + result.append( + BenchmarkInfo(name: "SubstringIdentical", runFunction: run_SubstringIdentical, tags: [.validation, .String]), + ) + } + return result +} // A string that doesn't fit in small string storage and doesn't fit in Latin-1 let longWide = "fὢasὢodὢijὢadὢolὢsjὢalὢsdὢjlὢasὢdfὢijὢliὢsdὢjøὢslὢdiὢalὢiὢ" @@ -335,6 +343,7 @@ public func run _LessSubstringSubstringGenericStringProtocol(_ n: Int) { */ @inline(never) +@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, visionOS 9999, *) public func run_SubstringIdentical(_ n: Int) { let (a, b) = (ss1, ss1) for _ in 1...n*500 { diff --git a/test/stdlib/StringAPI.swift b/test/stdlib/StringAPI.swift index d85cfb872fb74..bb3efc4292215 100644 --- a/test/stdlib/StringAPI.swift +++ b/test/stdlib/StringAPI.swift @@ -533,7 +533,14 @@ StringTests.test("hasPrefix/hasSuffix vs Character boundaries") { expectFalse(s2.hasSuffix("\n")) } -StringTests.test("isTriviallyIdentical(to:) small ascii") { +StringTests.test("isTriviallyIdentical(to:) small ascii") +.skip(.custom( + { if #available(SwiftStdlib 6.3, *) { false } else { true } }, + reason: "Requires Swift 6.3's standard library" +)) +.code { + guard #available(SwiftStdlib 6.3, *) else { return } + let a = "Hello" let b = "Hello" @@ -545,7 +552,14 @@ StringTests.test("isTriviallyIdentical(to:) small ascii") { expectTrue(b.isTriviallyIdentical(to: a)) } -StringTests.test("isTriviallyIdentical(to:) small unicode") { +StringTests.test("isTriviallyIdentical(to:) small unicode") +.skip(.custom( + { if #available(SwiftStdlib 6.3, *) { false } else { true } }, + reason: "Requires Swift 6.3's standard library" +)) +.code { + guard #available(SwiftStdlib 6.3, *) else { return } + let a = "Cafe\u{301}" let b = "Cafe\u{301}" let c = "Café" @@ -559,7 +573,14 @@ StringTests.test("isTriviallyIdentical(to:) small unicode") { expectFalse(b.isTriviallyIdentical(to: c)) } -StringTests.test("isTriviallyIdentical(to:) large ascii") { +StringTests.test("isTriviallyIdentical(to:) large ascii") +.skip(.custom( + { if #available(SwiftStdlib 6.3, *) { false } else { true } }, + reason: "Requires Swift 6.3's standard library" +)) +.code { + guard #available(SwiftStdlib 6.3, *) else { return } + let a = String(repeating: "foo", count: 1000) let b = String(repeating: "foo", count: 1000) diff --git a/test/stdlib/subString.swift b/test/stdlib/subString.swift index 78c6d8cb612e3..cfac2ec43bb2a 100644 --- a/test/stdlib/subString.swift +++ b/test/stdlib/subString.swift @@ -317,9 +317,21 @@ SubstringTests.test("Substring.base") { } } -SubstringTests.test("isTriviallyIdentical(to:) small ascii") { - let (a1, a2, a3) = slices("Hello", from: 2, to: 4) - let (b1, b2, b3) = slices("Hello", from: 2, to: 4) +SubstringTests.test("isTriviallyIdentical(to:) small ascii") +.skip(.custom( + { if #available(SwiftStdlib 6.3, *) { false } else { true } }, + reason: "Requires Swift 6.3's standard library" +)) +.code { + guard #available(SwiftStdlib 6.3, *) else { return } + + let a = "Hello" + let b = "Hello" + + precondition(a == b) + + let (a1, a2, a3) = slices(a, from: 2, to: 4) + let (b1, b2, b3) = slices(b, from: 2, to: 4) precondition(allNotEmpty(a1, a2, a3, b1, b2, b3)) precondition(allEqual(a1, a2, a3, b1, b2, b3)) @@ -346,10 +358,24 @@ SubstringTests.test("isTriviallyIdentical(to:) small ascii") { expectTrue(a3.isTriviallyIdentical(to: b3)) } -SubstringTests.test("isTriviallyIdentical(to:) small unicode") { - let (a1, a2, a3) = slices("Hello Cafe\u{301}", from: 2, to: 4) - let (b1, b2, b3) = slices("Hello Cafe\u{301}", from: 2, to: 4) - let (c1, c2, c3) = slices("Hello Café", from: 2, to: 4) +SubstringTests.test("isTriviallyIdentical(to:) small unicode") +.skip(.custom( + { if #available(SwiftStdlib 6.3, *) { false } else { true } }, + reason: "Requires Swift 6.3's standard library" +)) +.code { + guard #available(SwiftStdlib 6.3, *) else { return } + + let a = "Cafe\u{301}" + let b = "Cafe\u{301}" + let c = "Café" + + precondition(a == b) + precondition(b == c) + + let (a1, a2, a3) = slices(a, from: 2, to: 4) + let (b1, b2, b3) = slices(b, from: 2, to: 4) + let (c1, c2, c3) = slices(c, from: 2, to: 4) precondition(allNotEmpty(a1, a2, a3, b1, b2, b3, c1, c2, c3)) precondition(allEqual(a1, a2, a3, b1, b2, b3, c1, c2, c3)) @@ -385,9 +411,21 @@ SubstringTests.test("isTriviallyIdentical(to:) small unicode") { expectFalse(a3.isTriviallyIdentical(to: c3)) } -SubstringTests.test("isTriviallyIdentical(to:) large ascii") { - let (a1, a2, a3) = slices(String(repeating: "Hello", count: 1000), from: 2, to: 4) - let (b1, b2, b3) = slices(String(repeating: "Hello", count: 1000), from: 2, to: 4) +SubstringTests.test("isTriviallyIdentical(to:) large ascii") +.skip(.custom( + { if #available(SwiftStdlib 6.3, *) { false } else { true } }, + reason: "Requires Swift 6.3's standard library" +)) +.code { + guard #available(SwiftStdlib 6.3, *) else { return } + + let a = String(repeating: "foo", count: 1000) + let b = String(repeating: "foo", count: 1000) + + precondition(a == b) + + let (a1, a2, a3) = slices(a, from: 2, to: 4) + let (b1, b2, b3) = slices(b, from: 2, to: 4) precondition(allNotEmpty(a1, a2, a3, b1, b2, b3)) precondition(allEqual(a1, a2, a3, b1, b2, b3)) From 28a2bb3b9246b1bca3257fb4ef8d1ebad20a386c Mon Sep 17 00:00:00 2001 From: Rick van Voorden Date: Mon, 20 Oct 2025 12:17:27 -0700 Subject: [PATCH 5/6] [stdlib] string identical tests --- test/stdlib/subString.swift | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/test/stdlib/subString.swift b/test/stdlib/subString.swift index cfac2ec43bb2a..24cc04808e6c0 100644 --- a/test/stdlib/subString.swift +++ b/test/stdlib/subString.swift @@ -356,6 +356,24 @@ SubstringTests.test("isTriviallyIdentical(to:) small ascii") expectTrue(a3.isTriviallyIdentical(to: b1)) expectTrue(a3.isTriviallyIdentical(to: b2)) expectTrue(a3.isTriviallyIdentical(to: b3)) + + let c = "Hello" + + precondition(b == c) + + let (c1, c2, c3) = slices(c, from: 1, to: 3) + + expectFalse(a1.isTriviallyIdentical(to: c1)) + expectFalse(a1.isTriviallyIdentical(to: c2)) + expectFalse(a1.isTriviallyIdentical(to: c3)) + + expectFalse(a2.isTriviallyIdentical(to: c1)) + expectFalse(a2.isTriviallyIdentical(to: c2)) + expectFalse(a2.isTriviallyIdentical(to: c3)) + + expectFalse(a3.isTriviallyIdentical(to: c1)) + expectFalse(a3.isTriviallyIdentical(to: c2)) + expectFalse(a3.isTriviallyIdentical(to: c3)) } SubstringTests.test("isTriviallyIdentical(to:) small unicode") @@ -418,7 +436,7 @@ SubstringTests.test("isTriviallyIdentical(to:) large ascii") )) .code { guard #available(SwiftStdlib 6.3, *) else { return } - + let a = String(repeating: "foo", count: 1000) let b = String(repeating: "foo", count: 1000) From 6a68b0b8c40593e9b46dbb10dd36cc94718419aa Mon Sep 17 00:00:00 2001 From: Rick van Voorden Date: Mon, 20 Oct 2025 18:32:41 -0700 Subject: [PATCH 6/6] [stdlib] string identical --- stdlib/public/core/String.swift | 2 +- stdlib/public/core/Substring.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/public/core/String.swift b/stdlib/public/core/String.swift index cb2ddbfb15929..884357639a358 100644 --- a/stdlib/public/core/String.swift +++ b/stdlib/public/core/String.swift @@ -1145,7 +1145,7 @@ extension String { /// compare equal with `==`, but not all equal strings are considered /// identical. /// - /// - Performance: O(1) + /// - Complexity: O(1) @available(SwiftStdlib 6.3, *) public func isTriviallyIdentical(to other: Self) -> Bool { self._guts.rawBits == other._guts.rawBits diff --git a/stdlib/public/core/Substring.swift b/stdlib/public/core/Substring.swift index 7b61664863f1c..42e020197b8bc 100644 --- a/stdlib/public/core/Substring.swift +++ b/stdlib/public/core/Substring.swift @@ -1419,7 +1419,7 @@ extension Substring { /// to compare equal with `==`, but not all equal substrings are considered /// identical. /// - /// - Performance: O(1) + /// - Complexity: O(1) @available(SwiftStdlib 6.3, *) public func isTriviallyIdentical(to other: Self) -> Bool { self._wholeGuts.rawBits == other._wholeGuts.rawBits &&