Skip to content

Commit cae81ee

Browse files
authored
Fix issues with discontiguous-slice assignment (swiftlang#34708)
The mutating collection subscript for discontiguous slices assigns to the wrong group of indices. This fixes the behavior and adds test coverage. rdar://problem/70690643
1 parent 4b6dcf6 commit cae81ee

File tree

2 files changed

+51
-2
lines changed

2 files changed

+51
-2
lines changed

stdlib/public/core/DiscontiguousSlice.swift

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,25 @@ extension MutableCollection {
168168
DiscontiguousSlice(base: self, subranges: subranges)
169169
}
170170
set {
171-
for i in newValue.indices {
172-
self[i.base] = newValue[i]
171+
var indexOfReplacement = newValue.startIndex
172+
for range in subranges.ranges {
173+
_debugPrecondition(!range.isEmpty, "Empty range in a range set")
174+
175+
var indexToReplace = range.lowerBound
176+
repeat {
177+
_precondition(
178+
indexOfReplacement < newValue.endIndex,
179+
"Attempt to replace discontinuous slice with too few elements")
180+
181+
self[indexToReplace] = newValue[indexOfReplacement]
182+
self.formIndex(after: &indexToReplace)
183+
newValue.formIndex(after: &indexOfReplacement)
184+
} while indexToReplace < range.upperBound
173185
}
186+
187+
_precondition(
188+
indexOfReplacement == newValue.endIndex,
189+
"Attempt to replace discontinuous slice with too many elements")
174190
}
175191
}
176192
}

validation-test/StdlibUnittest/RangeSet.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,39 @@ if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) {
357357
}
358358
}
359359
}
360+
361+
RangeSetTests.test("DiscontiguousSliceMutating") {
362+
var initial = Array(1...20)
363+
let none = initial.subranges(where: { $0.isMultiple(of: 100) })
364+
initial[none] = initial[none]
365+
expectEqualSequence(1...20, initial)
366+
367+
let evens = initial.subranges(where: { $0.isMultiple(of: 2) })
368+
let odds = initial.subranges(where: { !$0.isMultiple(of: 2) })
369+
initial[evens] = initial[odds]
370+
expectEqualSequence(
371+
[1, 1, 3, 3, 5, 5, 7, 7, 9, 9, 11, 11, 13, 13, 15, 15, 17, 17, 19, 19],
372+
initial
373+
)
374+
}
375+
376+
RangeSetTests.test("DiscontiguousSliceMutating/TooLarge") {
377+
var initial = Array(1...21)
378+
let evens = initial.subranges(where: { $0.isMultiple(of: 2) })
379+
let odds = initial.subranges(where: { !$0.isMultiple(of: 2) })
380+
381+
expectCrashLater()
382+
initial[evens] = initial[odds]
383+
}
384+
385+
RangeSetTests.test("DiscontiguousSliceMutating/TooSmall") {
386+
var initial = Array(1...21)
387+
let evens = initial.subranges(where: { $0.isMultiple(of: 2) })
388+
let odds = initial.subranges(where: { !$0.isMultiple(of: 2) })
389+
390+
expectCrashLater()
391+
initial[odds] = initial[evens]
392+
}
360393
}
361394

362395
runAllTests()

0 commit comments

Comments
 (0)