Skip to content

Commit 3789ce2

Browse files
committed
[stdlib] fix an accidental recursion bug involving MutableCollection
- add a default implementation of MutableCollection’s subscript(bounds: Range<_>) with the most general signature possible - it is marked unavailable in order to prevent the infinite recursion bug reported in SR-14848 - add a conditionally-available subscript(bounds: Range<_>) -> Slice<Self> only when Subsequence is Slice<Self>. This will supersede the unconditional extension that provides the same signature.
1 parent 50e7c98 commit 3789ce2

File tree

2 files changed

+54
-0
lines changed

2 files changed

+54
-0
lines changed

stdlib/public/core/MutableCollection.swift

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,19 @@ extension MutableCollection {
249249
}
250250
}
251251

252+
// This unavailable default implementation of `subscript(bounds: Range<_>)`
253+
// prevents incomplete MutableCollection implementations from satisfying the
254+
// protocol through the use of the generic convenience implementation
255+
// `subscript<R: RangeExpression>(r: R)`. If that were the case, at
256+
// runtime the generic implementation would call itself
257+
// in an infinite recursion due to the absence of a better option.
258+
@available(*, unavailable)
259+
@_alwaysEmitIntoClient
260+
public subscript(bounds: Range<Index>) -> SubSequence {
261+
get { fatalError() }
262+
set { fatalError() }
263+
}
264+
252265
/// Exchanges the values at the specified indices of the collection.
253266
///
254267
/// Both parameters must be valid indices of the collection that are not
@@ -269,6 +282,45 @@ extension MutableCollection {
269282
}
270283
}
271284

285+
@available(SwiftStdlib 5.5, *)
286+
extension MutableCollection where SubSequence == Slice<Self> {
287+
288+
/// Accesses a contiguous subrange of the collection's elements.
289+
///
290+
/// The accessed slice uses the same indices for the same elements as the
291+
/// original collection. Always use the slice's `startIndex` property
292+
/// instead of assuming that its indices start at a particular value.
293+
///
294+
/// This example demonstrates getting a slice of an array of strings, finding
295+
/// the index of one of the strings in the slice, and then using that index
296+
/// in the original array.
297+
///
298+
/// var streets = ["Adams", "Bryant", "Channing", "Douglas", "Evarts"]
299+
/// let streetsSlice = streets[2 ..< streets.endIndex]
300+
/// print(streetsSlice)
301+
/// // Prints "["Channing", "Douglas", "Evarts"]"
302+
///
303+
/// let index = streetsSlice.firstIndex(of: "Evarts") // 4
304+
/// streets[index!] = "Eustace"
305+
/// print(streets[index!])
306+
/// // Prints "Eustace"
307+
///
308+
/// - Parameter bounds: A range of the collection's indices. The bounds of
309+
/// the range must be valid indices of the collection.
310+
///
311+
/// - Complexity: O(1)
312+
@inlinable
313+
public subscript(bounds: Range<Index>) -> Slice<Self> {
314+
get {
315+
_failEarlyRangeCheck(bounds, bounds: startIndex..<endIndex)
316+
return Slice(base: self, bounds: bounds)
317+
}
318+
set {
319+
_writeBackMutableSlice(&self, bounds: bounds, slice: newValue)
320+
}
321+
}
322+
}
323+
272324
//===----------------------------------------------------------------------===//
273325
// _rotate(in:shiftingToStart:)
274326
//===----------------------------------------------------------------------===//

validation-test/stdlib/CollectionDiagnostics.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,8 @@ struct CollectionWithNonDefaultSubSequence: Collection {
154154
public subscript(position: Int) -> Int { position }
155155
}
156156

157+
// expected-error@+2 {{type 'MutableCollectionWithNonDefaultSubSequence' does not conform to protocol 'MutableCollection'}}
158+
// expected-error@+1 {{unavailable subscript 'subscript(_:)' was used to satisfy a requirement of protocol 'MutableCollection'}}
157159
struct MutableCollectionWithNonDefaultSubSequence: MutableCollection {
158160
public var startIndex: Int
159161
public var endIndex: Int

0 commit comments

Comments
 (0)