Skip to content

Commit c29d47c

Browse files
committed
Merge remote-tracking branch 'origin/master' into master-rebranch
2 parents 025338e + ca4c221 commit c29d47c

File tree

3 files changed

+117
-37
lines changed

3 files changed

+117
-37
lines changed

docs/SIL.rst

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3484,13 +3484,14 @@ has an escaping function type (not ``[on_stack]``) the closure context will be
34843484
allocated with retain count 1 and initialized to contain the values ``%1``,
34853485
``%2``, etc. The closed-over values will not be retained; that must be done
34863486
separately before the ``partial_apply``. The closure does however take ownership
3487-
of the partially applied arguments; when the closure reference count reaches
3488-
zero, the contained values will be destroyed. If the ``partial_apply`` has a
3489-
``@noescape`` function type (``partial_apply [on_stack]``) the closure context
3490-
is allocated on the stack and initialized to contain the closed-over values. The
3491-
closed-over values are not retained, lifetime of the closed-over values must be
3492-
managed separately. The lifetime of the stack context of a ``partial_apply
3493-
[on_stack]`` must be terminated with a ``dealloc_stack``.
3487+
of the partially applied arguments (except for ``@inout_aliasable`` parameters);
3488+
when the closure reference count reaches zero, the contained values will be
3489+
destroyed. If the ``partial_apply`` has a ``@noescape`` function type
3490+
(``partial_apply [on_stack]``) the closure context is allocated on the stack and
3491+
initialized to contain the closed-over values. The closed-over values are not
3492+
retained, lifetime of the closed-over values must be managed separately. The
3493+
lifetime of the stack context of a ``partial_apply [on_stack]`` must be
3494+
terminated with a ``dealloc_stack``.
34943495

34953496
If the callee is generic, all of its generic parameters must be bound by the
34963497
given substitution list. The arguments are given with these generic
@@ -3499,6 +3500,11 @@ type with the given substitutions applied. The generic parameters themselves
34993500
cannot be partially applied; all of them must be bound. The result is always
35003501
a concrete function.
35013502

3503+
If an address argument has ``@inout_aliasable`` convention, the closure
3504+
obtained from ``partial_apply`` will not own its underlying value.
3505+
The ``@inout_aliasable`` parameter convention is used when a ``@noescape``
3506+
closure captures an ``inout`` argument.
3507+
35023508
TODO: The instruction, when applied to a generic function,
35033509
currently implicitly performs abstraction difference transformations enabled
35043510
by the given substitutions, such as promoting address-only arguments and returns

stdlib/public/core/Diffing.swift

Lines changed: 42 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
1616
extension CollectionDifference {
1717
fileprivate func _fastEnumeratedApply(
18-
_ consume: (Change) -> Void
19-
) {
18+
_ consume: (Change) throws -> Void
19+
) rethrows {
2020
let totalRemoves = removals.count
2121
let totalInserts = insertions.count
2222
var enumeratedRemoves = 0
@@ -41,7 +41,7 @@ extension CollectionDifference {
4141
preconditionFailure()
4242
}
4343

44-
consume(change)
44+
try consume(change)
4545

4646
switch change {
4747
case .remove(_, _, _):
@@ -53,6 +53,9 @@ extension CollectionDifference {
5353
}
5454
}
5555

56+
// Error type allows the use of throw to unroll state on application failure
57+
private enum _ApplicationError : Error { case failed }
58+
5659
extension RangeReplaceableCollection {
5760
/// Applies the given difference to this collection.
5861
///
@@ -66,45 +69,54 @@ extension RangeReplaceableCollection {
6669
/// number of changes contained by the parameter.
6770
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
6871
public func applying(_ difference: CollectionDifference<Element>) -> Self? {
69-
var result = Self()
70-
var enumeratedRemoves = 0
71-
var enumeratedInserts = 0
72-
var enumeratedOriginals = 0
73-
var currentIndex = self.startIndex
7472

7573
func append(
7674
into target: inout Self,
7775
contentsOf source: Self,
7876
from index: inout Self.Index, count: Int
79-
) {
77+
) throws {
8078
let start = index
81-
source.formIndex(&index, offsetBy: count)
79+
if !source.formIndex(&index, offsetBy: count, limitedBy: source.endIndex) {
80+
throw _ApplicationError.failed
81+
}
8282
target.append(contentsOf: source[start..<index])
8383
}
8484

85-
difference._fastEnumeratedApply { change in
86-
switch change {
87-
case .remove(offset: let offset, element: _, associatedWith: _):
88-
let origCount = offset - enumeratedOriginals
89-
append(into: &result, contentsOf: self, from: &currentIndex, count: origCount)
90-
enumeratedOriginals += origCount + 1 // Removal consumes an original element
91-
currentIndex = self.index(after: currentIndex)
92-
enumeratedRemoves += 1
93-
case .insert(offset: let offset, element: let element, associatedWith: _):
94-
let origCount = (offset + enumeratedRemoves - enumeratedInserts) - enumeratedOriginals
95-
append(into: &result, contentsOf: self, from: &currentIndex, count: origCount)
96-
result.append(element)
97-
enumeratedOriginals += origCount
98-
enumeratedInserts += 1
85+
var result = Self()
86+
do {
87+
var enumeratedRemoves = 0
88+
var enumeratedInserts = 0
89+
var enumeratedOriginals = 0
90+
var currentIndex = self.startIndex
91+
try difference._fastEnumeratedApply { change in
92+
switch change {
93+
case .remove(offset: let offset, element: _, associatedWith: _):
94+
let origCount = offset - enumeratedOriginals
95+
try append(into: &result, contentsOf: self, from: &currentIndex, count: origCount)
96+
if currentIndex == self.endIndex {
97+
// Removing nonexistent element off the end of the collection
98+
throw _ApplicationError.failed
99+
}
100+
enumeratedOriginals += origCount + 1 // Removal consumes an original element
101+
currentIndex = self.index(after: currentIndex)
102+
enumeratedRemoves += 1
103+
case .insert(offset: let offset, element: let element, associatedWith: _):
104+
let origCount = (offset + enumeratedRemoves - enumeratedInserts) - enumeratedOriginals
105+
try append(into: &result, contentsOf: self, from: &currentIndex, count: origCount)
106+
result.append(element)
107+
enumeratedOriginals += origCount
108+
enumeratedInserts += 1
109+
}
110+
_internalInvariant(enumeratedOriginals <= self.count)
111+
}
112+
if currentIndex < self.endIndex {
113+
result.append(contentsOf: self[currentIndex...])
99114
}
100-
_internalInvariant(enumeratedOriginals <= self.count)
115+
_internalInvariant(result.count == self.count + enumeratedInserts - enumeratedRemoves)
116+
} catch {
117+
return nil
101118
}
102-
let origCount = self.count - enumeratedOriginals
103-
append(into: &result, contentsOf: self, from: &currentIndex, count: origCount)
104119

105-
_internalInvariant(currentIndex == self.endIndex)
106-
_internalInvariant(enumeratedOriginals + origCount == self.count)
107-
_internalInvariant(result.count == self.count + enumeratedInserts - enumeratedRemoves)
108120
return result
109121
}
110122
}

test/stdlib/Diffing.swift

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,68 @@ if #available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) {
629629
}}}}}}
630630
}
631631

632+
suite.test("Fast applicator error condition") {
633+
let bear = "bear"
634+
let bird = "bird"
635+
let bat = "bat"
636+
637+
let diff = bird.difference(from: bear)
638+
639+
expectEqual(nil, bat.applying(diff))
640+
}
641+
642+
suite.test("Fast applicator boundary condition remove last element") {
643+
let base = [1, 2, 3]
644+
645+
expectEqual([1, 2], base.applying(CollectionDifference<Int>([.remove(offset: base.count - 1, element: 3, associatedWith: nil)])!))
646+
}
647+
648+
suite.test("Fast applicator boundary condition append element") {
649+
let base = [1, 2, 3]
650+
651+
expectEqual([1, 2, 3, 4], base.applying(CollectionDifference<Int>([.insert(offset: base.count, element: 4, associatedWith: nil)])!))
652+
}
653+
654+
suite.test("Fast applicator error boundary condition remove at endIndex") {
655+
let base = [1, 2, 3]
656+
657+
expectEqual(nil, base.applying(CollectionDifference<Int>([.remove(offset: base.count, element: 4, associatedWith: nil)])!))
658+
}
659+
660+
suite.test("Fast applicator error boundary condition insert beyond end") {
661+
let base = [1, 2, 3]
662+
663+
expectEqual(nil, base.applying(CollectionDifference<Int>([.insert(offset: base.count + 1, element: 5, associatedWith: nil)])!))
664+
}
665+
666+
suite.test("Fast applicator boundary condition replace tail") {
667+
let base = [1, 2, 3]
668+
669+
expectEqual([1, 2, 4], base.applying(CollectionDifference<Int>([
670+
.remove(offset: base.count - 1, element: 3, associatedWith: nil),
671+
.insert(offset: base.count - 1, element: 4, associatedWith: nil)
672+
])!))
673+
}
674+
675+
suite.test("Fast applicator error boundary condition insert beyond end after tail removal") {
676+
let base = [1, 2, 3]
677+
678+
expectEqual(nil, base.applying(CollectionDifference<Int>([
679+
.remove(offset: base.count - 1, element: 3, associatedWith: nil),
680+
.insert(offset: base.count, element: 4, associatedWith: nil)
681+
])!))
682+
683+
}
684+
685+
suite.test("Fast applicator error boundary condition insert beyond end after non-tail removal") {
686+
let base = [1, 2, 3]
687+
688+
expectEqual(nil, base.applying(CollectionDifference<Int>([
689+
.remove(offset: base.count - 2, element: 2, associatedWith: nil),
690+
.insert(offset: base.count, element: 4, associatedWith: nil)
691+
])!))
692+
}
693+
632694
suite.test("Fast applicator fuzzer") {
633695
func makeArray() -> [Int] {
634696
var arr = [Int]()

0 commit comments

Comments
 (0)