Skip to content

Commit 1c1b2b9

Browse files
moiseevairspeedswift
authored andcommitted
[stdlib] String : RangeReplaceableCollection & BidirectionalCollection (swiftlang#8921)
* [stdlib] String : RangeReplaceableCollection & BidirectionalCollection * Add source compatibility hack for Swift.max * Add source compatibility hack for Swift.min * Remove redundant conformance in benchmarks * Fix stupid typo I thought I'd already pushed * XFAIL testing now-redundant conformance * XFAIL an IDE test for now
1 parent 463c6e4 commit 1c1b2b9

14 files changed

+78
-83
lines changed

benchmark/single-source/StringEdits.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,6 @@ var editWords: [String] = [
2323
"gastroperiodynia",
2424
]
2525

26-
// FIXME: remove when String is a Collection
27-
extension String: RangeReplaceableCollection { }
28-
2926
let alphabet = "abcdefghijklmnopqrstuvwxyz"
3027
/// All edits that are one edit away from `word`
3128
func edits(_ word: String) -> Set<String> {

benchmark/single-source/StringMatch.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@ import Glibc
1717
import Darwin
1818
#endif
1919

20-
// FIXME: remove when String is a Collection
21-
extension String: Collection { }
22-
2320
/* match: search for regexp anywhere in text */
2421
func match(regexp: String, text: String) -> Bool {
2522
if regexp.first == "^" {

stdlib/public/SDK/Foundation/NSStringAPI.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -616,7 +616,7 @@ extension String {
616616
return _withOptionalOutParameter(leftover) {
617617
self._ns.getBytes(
618618
&buffer,
619-
maxLength: min(buffer.count, maxBufferCount),
619+
maxLength: Swift.min(buffer.count, maxBufferCount),
620620
usedLength: usedBufferCount,
621621
encoding: encoding.rawValue,
622622
options: options,
@@ -636,7 +636,8 @@ extension String {
636636
public func getCString(
637637
_ buffer: inout [CChar], maxLength: Int, encoding: Encoding
638638
) -> Bool {
639-
return _ns.getCString(&buffer, maxLength: min(buffer.count, maxLength),
639+
return _ns.getCString(&buffer,
640+
maxLength: Swift.min(buffer.count, maxLength),
640641
encoding: encoding.rawValue)
641642
}
642643

@@ -652,7 +653,7 @@ extension String {
652653
public func getFileSystemRepresentation(
653654
_ buffer: inout [CChar], maxLength: Int) -> Bool {
654655
return _ns.getFileSystemRepresentation(
655-
&buffer, maxLength: min(buffer.count, maxLength))
656+
&buffer, maxLength: Swift.min(buffer.count, maxLength))
656657
}
657658

658659
// - (void)

stdlib/public/core/StringComparable.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ extension String {
5555
func _compareASCII(_ rhs: String) -> Int {
5656
var compare = Int(extendingOrTruncating: _swift_stdlib_memcmp(
5757
self._core.startASCII, rhs._core.startASCII,
58-
min(self._core.count, rhs._core.count)))
58+
Swift.min(self._core.count, rhs._core.count)))
5959
if compare == 0 {
6060
compare = self._core.count - rhs._core.count
6161
}

stdlib/public/core/StringLegacy.swift

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,6 @@
1313
import SwiftShims
1414

1515
extension String {
16-
/// Creates a string representing the given character repeated the specified
17-
/// number of times.
18-
///
19-
/// For example, use this initializer to create a string with ten `"0"`
20-
/// characters in a row.
21-
///
22-
/// let zeroes = String("0" as Character, count: 10)
23-
/// print(zeroes)
24-
/// // Prints "0000000000"
25-
@available(*, unavailable, message: "Replaced by init(repeating: String, count: Int)")
26-
public init(repeating repeatedValue: Character, count: Int) {
27-
Builtin.unreachable()
28-
}
29-
3016
/// Creates a string representing the given Unicode scalar repeated the
3117
/// specified number of times.
3218
///

stdlib/public/core/StringRangeReplaceableCollection.swift.gyb

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,7 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
// `String` does not conform to `RangeReplaceableCollection`, but provides a
14-
// similar API.
15-
16-
extension String {
13+
extension String : RangeReplaceableCollection, BidirectionalCollection {
1714
/// The index type for subscripting a string.
1815
public typealias Index = CharacterView.Index
1916

@@ -24,6 +21,31 @@ extension String {
2421
/// the other through zero or more applications of `index(after:)`.
2522
public typealias IndexDistance = CharacterView.IndexDistance
2623

24+
/// Creates a string representing the given character repeated the specified
25+
/// number of times.
26+
///
27+
/// For example, use this initializer to create a string with ten `"0"`
28+
/// characters in a row.
29+
///
30+
/// let zeroes = String("0" as Character, count: 10)
31+
/// print(zeroes)
32+
/// // Prints "0000000000"
33+
public init(repeating repeatedValue: Character, count: Int) {
34+
self.init(repeating: String(repeatedValue), count: count)
35+
}
36+
37+
// Now that String conforms to Collection, we need to disambiguate between:
38+
// - init<T>(_ value: T) where T : LosslessStringConvertible
39+
// - init<S>(_ characters: S) where S : Sequence, S.Iterator.Element == Character
40+
// Cannot simply do init(_: String) as that would itself be ambiguous with
41+
// init?(_ description: String)
42+
public init<
43+
T : LosslessStringConvertible & Sequence
44+
>(_ other: T)
45+
where T.Iterator.Element == Character {
46+
self = other.description
47+
}
48+
2749
/// The position of the first character in a nonempty string.
2850
///
2951
/// In an empty string, `startIndex` is equal to `endIndex`.
@@ -386,3 +408,33 @@ extension String {
386408
}
387409
}
388410

411+
extension String {
412+
// This is needed because of the issue described in SR-4660 which causes
413+
// source compatibility issues when String becomes a collection
414+
@_transparent
415+
public func max<T : Comparable>(_ x: T, _ y: T) -> T {
416+
return Swift.max(x,y)
417+
}
418+
419+
// This is needed because of the issue described in SR-4660 which causes
420+
// source compatibility issues when String becomes a collection
421+
@_transparent
422+
public func min<T : Comparable>(_ x: T, _ y: T) -> T {
423+
return Swift.min(x,y)
424+
}
425+
}
426+
427+
extension String {
428+
@available(*, unavailable, message: "Operator '+' cannot be used to append a String to a seqeunce of strings")
429+
public static func + <S : Sequence>(lhs: S, rhs: String) -> Never
430+
where S.Iterator.Element == String {
431+
fatalError()
432+
}
433+
434+
@available(*, unavailable, message: "Operator '+' cannot be used to append a String to a seqeunce of strings")
435+
public static func + <S : Sequence>(lhs: String, rhs: S) -> Never
436+
where S.Iterator.Element == String {
437+
fatalError()
438+
}
439+
}
440+

stdlib/public/core/UnavailableStringAPIs.swift.gyb

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -69,45 +69,6 @@ ${stringSubscriptComment}
6969
Builtin.unreachable()
7070
}
7171
% end
72-
/// The unavailable `String.count` API.
73-
///
74-
/// The concept of "the number of characters in a string" has
75-
/// different interpretations in different libraries and system
76-
/// components. The correct interpretation should be selected
77-
/// according to the use case and the APIs involved, so `String`
78-
/// does not have a `count` property, since there is no universal
79-
/// answer to the question about the number of characters in a
80-
/// given string.
81-
///
82-
/// Swift provides several different ways to access the character
83-
/// data stored inside strings. To access the number of data units
84-
/// in each representation you can use the following APIs.
85-
///
86-
/// - `String.utf8.count` property returns the number of UTF-8 code
87-
/// units in the string. Use this API when converting the string
88-
/// to UTF-8. Most POSIX APIs process strings in terms of UTF-8
89-
/// code units.
90-
///
91-
/// - `String.utf16.count` property returns the number of UTF-16
92-
/// code units in the string. Most Cocoa and Cocoa touch APIs
93-
/// process strings in terms of UTF-16 code units. For example,
94-
/// instances of `NSRange` used with `NSAttributedString` and
95-
/// `NSRegularExpression` store substring offsets and lengths in
96-
/// terms of UTF-16 code units.
97-
///
98-
/// - `String.unicodeScalars.count` property returns the number of
99-
/// Unicode scalars in the string. Use this API when you are
100-
/// performing low-level manipulation of character data.
101-
///
102-
/// - `String.characters.count` property returns the number of
103-
/// extended grapheme clusters. Use this API to count the
104-
/// number of user-perceived characters in the string.
105-
@available(
106-
*, unavailable,
107-
message: "there is no universally good answer, see the documentation comment for discussion")
108-
public var count: Int {
109-
Builtin.unreachable()
110-
}
11172
}
11273

11374
% for View in ['UTF8View', 'UTF16View', 'UnicodeScalarView', 'CharacterView']:

test/Parse/recovery.swift

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -705,9 +705,16 @@ protocol B23086402 {
705705
var c: [String] { get }
706706
}
707707
708-
// <rdar://problem/23550816> QoI: Poor diagnostic in argument list of "print" (varargs related)
709708
func test23086402(a: A23086402) {
710-
print(a.b.c + "") // expected-error {{binary operator '+' cannot be applied to operands of type '[String]' and 'String'}} expected-note {{expected an argument list of type '(String, String)'}}
709+
print(a.b.c + "") // should not crash but: expected-error {{}}
710+
}
711+
712+
// <rdar://problem/23550816> QoI: Poor diagnostic in argument list of "print" (varargs related)
713+
// The situation has changed. String now conforms to the RangeReplaceableCollection protocol
714+
// and `ss + s` becomes ambiguous. Diambiguation is provided with the unavailable overload
715+
// in order to produce a meaningful diagnostics. (Related: <rdar://problem/31763930>)
716+
func test23550816(ss: [String], s: String) {
717+
print(ss + s) // expected-error {{'+' is unavailable: Operator '+' cannot be used to append a String to a seqeunce of strings}}
711718
}
712719
713720
// <rdar://problem/23719432> [practicalswift] Compiler crashes on &(Int:_)

test/stdlib/Renames.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,7 @@ func _StringAppend(s: inout String, u: UnicodeScalar) {
517517
func _StringLegacy(c: Character, u: UnicodeScalar) {
518518
_ = String(count: 1, repeatedValue: c) // expected-error {{'init(count:repeatedValue:)' is unavailable: Renamed to init(repeating:count:) and reordered parameters}} {{none}}
519519
_ = String(count: 1, repeatedValue: u) // expected-error {{'init(count:repeatedValue:)' is unavailable: Renamed to init(repeating:count:) and reordered parameters}} {{none}}
520-
_ = String(repeating: c, count: 1) // expected-error {{'init(repeating:count:)' is unavailable: Replaced by init(repeating: String, count: Int)}} {{none}}
520+
_ = String(repeating: c, count: 1) // no more error, since String conforms to BidirectionalCollection
521521
_ = String(repeating: u, count: 1) // expected-error {{'init(repeating:count:)' is unavailable: Replaced by init(repeating: String, count: Int)}} {{none}}
522522
}
523523

test/stdlib/StringAPI.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,10 @@ StringTests.test("CompareStringsWithUnpairedSurrogates")
361361
)
362362
}
363363

364+
StringTests.test("String.init(_:String)") {
365+
let s: String = String("" as String) // should compile without ambiguities
366+
}
367+
364368
var CStringTests = TestSuite("CStringTests")
365369

366370
func getNullUTF8() -> UnsafeMutablePointer<UInt8>? {

0 commit comments

Comments
 (0)