Skip to content

Commit 7cbfed3

Browse files
committed
[stdlib] Explicitly declare Collection requirements on child protocols
Fixes: <rdar://problem/42408692> and SR-8022
1 parent b042215 commit 7cbfed3

File tree

2 files changed

+236
-9
lines changed

2 files changed

+236
-9
lines changed

stdlib/public/core/BidirectionalCollection.swift

Lines changed: 113 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,116 @@ where SubSequence: BidirectionalCollection, Indices: BidirectionalCollection {
6262
/// `startIndex`.
6363
func formIndex(before i: inout Index)
6464

65+
/// Returns the position immediately after the given index.
66+
///
67+
/// The successor of an index must be well defined. For an index `i` into a
68+
/// collection `c`, calling `c.index(after: i)` returns the same index every
69+
/// time.
70+
///
71+
/// - Parameter i: A valid index of the collection. `i` must be less than
72+
/// `endIndex`.
73+
/// - Returns: The index value immediately after `i`.
74+
func index(after i: Index) -> Index
75+
76+
/// Replaces the given index with its successor.
77+
///
78+
/// - Parameter i: A valid index of the collection. `i` must be less than
79+
/// `endIndex`.
80+
func formIndex(after i: inout Index)
81+
82+
/// Returns an index that is the specified distance from the given index.
83+
///
84+
/// The following example obtains an index advanced four positions from a
85+
/// string's starting index and then prints the character at that position.
86+
///
87+
/// let s = "Swift"
88+
/// let i = s.index(s.startIndex, offsetBy: 4)
89+
/// print(s[i])
90+
/// // Prints "t"
91+
///
92+
/// The value passed as `distance` must not offset `i` beyond the bounds of
93+
/// the collection.
94+
///
95+
/// - Parameters:
96+
/// - i: A valid index of the collection.
97+
/// - distance: The distance to offset `i`. `distance` must not be negative
98+
/// unless the collection conforms to the `BidirectionalCollection`
99+
/// protocol.
100+
/// - Returns: An index offset by `distance` from the index `i`. If
101+
/// `distance` is positive, this is the same value as the result of
102+
/// `distance` calls to `index(after:)`. If `distance` is negative, this
103+
/// is the same value as the result of `abs(distance)` calls to
104+
/// `index(before:)`.
105+
///
106+
/// - Complexity: O(1) if the collection conforms to
107+
/// `RandomAccessCollection`; otherwise, O(*k*), where *k* is the absolute
108+
/// value of `distance`.
109+
func index(_ i: Index, offsetBy distance: Int) -> Index
110+
111+
/// Returns an index that is the specified distance from the given index,
112+
/// unless that distance is beyond a given limiting index.
113+
///
114+
/// The following example obtains an index advanced four positions from a
115+
/// string's starting index and then prints the character at that position.
116+
/// The operation doesn't require going beyond the limiting `s.endIndex`
117+
/// value, so it succeeds.
118+
///
119+
/// let s = "Swift"
120+
/// if let i = s.index(s.startIndex, offsetBy: 4, limitedBy: s.endIndex) {
121+
/// print(s[i])
122+
/// }
123+
/// // Prints "t"
124+
///
125+
/// The next example attempts to retrieve an index six positions from
126+
/// `s.startIndex` but fails, because that distance is beyond the index
127+
/// passed as `limit`.
128+
///
129+
/// let j = s.index(s.startIndex, offsetBy: 6, limitedBy: s.endIndex)
130+
/// print(j)
131+
/// // Prints "nil"
132+
///
133+
/// The value passed as `distance` must not offset `i` beyond the bounds of
134+
/// the collection, unless the index passed as `limit` prevents offsetting
135+
/// beyond those bounds.
136+
///
137+
/// - Parameters:
138+
/// - i: A valid index of the collection.
139+
/// - distance: The distance to offset `i`. `distance` must not be negative
140+
/// unless the collection conforms to the `BidirectionalCollection`
141+
/// protocol.
142+
/// - limit: A valid index of the collection to use as a limit. If
143+
/// `distance > 0`, a limit that is less than `i` has no effect.
144+
/// Likewise, if `distance < 0`, a limit that is greater than `i` has no
145+
/// effect.
146+
/// - Returns: An index offset by `distance` from the index `i`, unless that
147+
/// index would be beyond `limit` in the direction of movement. In that
148+
/// case, the method returns `nil`.
149+
///
150+
/// - Complexity: O(1) if the collection conforms to
151+
/// `RandomAccessCollection`; otherwise, O(*k*), where *k* is the absolute
152+
/// value of `distance`.
153+
func index(
154+
_ i: Index, offsetBy distance: Int, limitedBy limit: Index
155+
) -> Index?
156+
157+
/// Returns the distance between two indices.
158+
///
159+
/// Unless the collection conforms to the `BidirectionalCollection` protocol,
160+
/// `start` must be less than or equal to `end`.
161+
///
162+
/// - Parameters:
163+
/// - start: A valid index of the collection.
164+
/// - end: Another valid index of the collection. If `end` is equal to
165+
/// `start`, the result is zero.
166+
/// - Returns: The distance between `start` and `end`. The result can be
167+
/// negative only if the collection conforms to the
168+
/// `BidirectionalCollection` protocol.
169+
///
170+
/// - Complexity: O(1) if the collection conforms to
171+
/// `RandomAccessCollection`; otherwise, O(*k*), where *k* is the
172+
/// resulting distance.
173+
func distance(from start: Index, to end: Index) -> Int
174+
65175
/// The indices that are valid for subscripting the collection, in ascending
66176
/// order.
67177
///
@@ -257,9 +367,7 @@ extension BidirectionalCollection where SubSequence == Self {
257367
_precondition(k >= 0, "Number of elements to remove should be non-negative")
258368
_precondition(count >= k,
259369
"Can't remove more items from a collection than it contains")
260-
// FIXME: using non-_'d `index` incorrectly calls the Collection one for
261-
// conditional conformances to BidirectionalCollections.
262-
self = self[startIndex..<_index(endIndex, offsetBy: -k)]
370+
self = self[startIndex..<index(endIndex, offsetBy: -k)]
263371
}
264372
}
265373

@@ -287,9 +395,7 @@ extension BidirectionalCollection {
287395
public func dropLast(_ k: Int) -> SubSequence {
288396
_precondition(
289397
k >= 0, "Can't drop a negative number of elements from a collection")
290-
// FIXME: using non-_'d `index` incorrectly calls the Collection one for
291-
// conditional conformances to BidirectionalCollections.
292-
let end = _index(
398+
let end = index(
293399
endIndex,
294400
offsetBy: -k,
295401
limitedBy: startIndex) ?? startIndex
@@ -321,9 +427,7 @@ extension BidirectionalCollection {
321427
_precondition(
322428
maxLength >= 0,
323429
"Can't take a suffix of negative length from a collection")
324-
// FIXME: using non-_'d `index` incorrectly calls the Collection one for
325-
// conditional conformances to BidirectionalCollections.
326-
let start = _index(
430+
let start = index(
327431
endIndex,
328432
offsetBy: -maxLength,
329433
limitedBy: startIndex) ?? startIndex

stdlib/public/core/RandomAccessCollection.swift

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,129 @@ where SubSequence: RandomAccessCollection, Indices: RandomAccessCollection
9797

9898
// FIXME(ABI): Associated type inference requires this.
9999
var endIndex: Index { get }
100+
101+
/// Returns the position immediately before the given index.
102+
///
103+
/// - Parameter i: A valid index of the collection. `i` must be greater than
104+
/// `startIndex`.
105+
/// - Returns: The index value immediately before `i`.
106+
func index(before i: Index) -> Index
107+
108+
/// Replaces the given index with its predecessor.
109+
///
110+
/// - Parameter i: A valid index of the collection. `i` must be greater than
111+
/// `startIndex`.
112+
func formIndex(before i: inout Index)
113+
114+
/// Returns the position immediately after the given index.
115+
///
116+
/// The successor of an index must be well defined. For an index `i` into a
117+
/// collection `c`, calling `c.index(after: i)` returns the same index every
118+
/// time.
119+
///
120+
/// - Parameter i: A valid index of the collection. `i` must be less than
121+
/// `endIndex`.
122+
/// - Returns: The index value immediately after `i`.
123+
func index(after i: Index) -> Index
124+
125+
/// Replaces the given index with its successor.
126+
///
127+
/// - Parameter i: A valid index of the collection. `i` must be less than
128+
/// `endIndex`.
129+
func formIndex(after i: inout Index)
130+
131+
/// Returns an index that is the specified distance from the given index.
132+
///
133+
/// The following example obtains an index advanced four positions from a
134+
/// string's starting index and then prints the character at that position.
135+
///
136+
/// let s = "Swift"
137+
/// let i = s.index(s.startIndex, offsetBy: 4)
138+
/// print(s[i])
139+
/// // Prints "t"
140+
///
141+
/// The value passed as `distance` must not offset `i` beyond the bounds of
142+
/// the collection.
143+
///
144+
/// - Parameters:
145+
/// - i: A valid index of the collection.
146+
/// - distance: The distance to offset `i`. `distance` must not be negative
147+
/// unless the collection conforms to the `BidirectionalCollection`
148+
/// protocol.
149+
/// - Returns: An index offset by `distance` from the index `i`. If
150+
/// `distance` is positive, this is the same value as the result of
151+
/// `distance` calls to `index(after:)`. If `distance` is negative, this
152+
/// is the same value as the result of `abs(distance)` calls to
153+
/// `index(before:)`.
154+
///
155+
/// - Complexity: O(1) if the collection conforms to
156+
/// `RandomAccessCollection`; otherwise, O(*k*), where *k* is the absolute
157+
/// value of `distance`.
158+
func index(_ i: Index, offsetBy distance: Int) -> Index
159+
160+
/// Returns an index that is the specified distance from the given index,
161+
/// unless that distance is beyond a given limiting index.
162+
///
163+
/// The following example obtains an index advanced four positions from a
164+
/// string's starting index and then prints the character at that position.
165+
/// The operation doesn't require going beyond the limiting `s.endIndex`
166+
/// value, so it succeeds.
167+
///
168+
/// let s = "Swift"
169+
/// if let i = s.index(s.startIndex, offsetBy: 4, limitedBy: s.endIndex) {
170+
/// print(s[i])
171+
/// }
172+
/// // Prints "t"
173+
///
174+
/// The next example attempts to retrieve an index six positions from
175+
/// `s.startIndex` but fails, because that distance is beyond the index
176+
/// passed as `limit`.
177+
///
178+
/// let j = s.index(s.startIndex, offsetBy: 6, limitedBy: s.endIndex)
179+
/// print(j)
180+
/// // Prints "nil"
181+
///
182+
/// The value passed as `distance` must not offset `i` beyond the bounds of
183+
/// the collection, unless the index passed as `limit` prevents offsetting
184+
/// beyond those bounds.
185+
///
186+
/// - Parameters:
187+
/// - i: A valid index of the collection.
188+
/// - distance: The distance to offset `i`. `distance` must not be negative
189+
/// unless the collection conforms to the `BidirectionalCollection`
190+
/// protocol.
191+
/// - limit: A valid index of the collection to use as a limit. If
192+
/// `distance > 0`, a limit that is less than `i` has no effect.
193+
/// Likewise, if `distance < 0`, a limit that is greater than `i` has no
194+
/// effect.
195+
/// - Returns: An index offset by `distance` from the index `i`, unless that
196+
/// index would be beyond `limit` in the direction of movement. In that
197+
/// case, the method returns `nil`.
198+
///
199+
/// - Complexity: O(1) if the collection conforms to
200+
/// `RandomAccessCollection`; otherwise, O(*k*), where *k* is the absolute
201+
/// value of `distance`.
202+
func index(
203+
_ i: Index, offsetBy distance: Int, limitedBy limit: Index
204+
) -> Index?
205+
206+
/// Returns the distance between two indices.
207+
///
208+
/// Unless the collection conforms to the `BidirectionalCollection` protocol,
209+
/// `start` must be less than or equal to `end`.
210+
///
211+
/// - Parameters:
212+
/// - start: A valid index of the collection.
213+
/// - end: Another valid index of the collection. If `end` is equal to
214+
/// `start`, the result is zero.
215+
/// - Returns: The distance between `start` and `end`. The result can be
216+
/// negative only if the collection conforms to the
217+
/// `BidirectionalCollection` protocol.
218+
///
219+
/// - Complexity: O(1) if the collection conforms to
220+
/// `RandomAccessCollection`; otherwise, O(*k*), where *k* is the
221+
/// resulting distance.
222+
func distance(from start: Index, to end: Index) -> Int
100223
}
101224

102225
// TODO: swift-3-indexing-model - (By creating an ambiguity?), try to

0 commit comments

Comments
 (0)