diff --git a/stdlib/public/core/Array.swift b/stdlib/public/core/Array.swift index 2a03a8d64dc65..859e301e52867 100644 --- a/stdlib/public/core/Array.swift +++ b/stdlib/public/core/Array.swift @@ -1202,6 +1202,48 @@ extension Array: RangeReplaceableCollection { _appendElementAssumeUniqueAndCapacity(oldCount, newElement: newElement) _endMutation() } + + /// Adds the elements of a collection to the end of the array. + /// + /// Use this method to append the elements of a collection to the end of this + /// array. This example appends the elements of a `Range` instance + /// to an array of integers. + /// + /// var numbers = [1, 2, 3, 4, 5] + /// numbers.append(contentsOf: 10...15) + /// print(numbers) + /// // Prints "[1, 2, 3, 4, 5, 10, 11, 12, 13, 14, 15]" + /// + /// - Parameter newElements: The elements to append to the array. + /// + /// - Complexity: O(*m*) on average, where *m* is the length of + /// `newElements`, over many calls to `append(contentsOf:)` on the same + /// array. + @_alwaysEmitIntoClient + @_semantics("array.append_contentsOf") + @_effects(notEscaping self.value**) + public mutating func append(contentsOf newElements: __owned some Collection) { + let newElementsCount = newElements.count + // This check prevents a data race writing to _swiftEmptyArrayStorage + if newElementsCount == 0 { + return + } + defer { + _endMutation() + } + _reserveCapacityImpl(minimumCapacity: self.count + newElementsCount, + growForAppend: true) + + let oldCount = _buffer.mutableCount + let startNewElements = _buffer.mutableFirstElementAddress + oldCount + let buf = UnsafeMutableBufferPointer( + start: startNewElements, + count: newElementsCount) + _debugPrecondition(buf.endIndex <= _buffer.mutableCapacity) + let end = buf.initialize(fromContentsOf: newElements) + _precondition(end == buf.endIndex) + _buffer.mutableCount = _buffer.mutableCount + newElementsCount + } /// Adds the elements of a sequence to the end of the array. /// @@ -1225,6 +1267,14 @@ extension Array: RangeReplaceableCollection { public mutating func append(contentsOf newElements: __owned S) where S.Element == Element { + let wasContiguous = newElements.withContiguousStorageIfAvailable { + append(contentsOf: $0) + return true + } + if wasContiguous != nil { + return + } + defer { _endMutation() } diff --git a/stdlib/public/core/ArraySlice.swift b/stdlib/public/core/ArraySlice.swift index 6c213c31c3a26..9b58f06464bbe 100644 --- a/stdlib/public/core/ArraySlice.swift +++ b/stdlib/public/core/ArraySlice.swift @@ -925,6 +925,47 @@ extension ArraySlice: RangeReplaceableCollection { _appendElementAssumeUniqueAndCapacity(oldCount, newElement: newElement) _endMutation() } + + /// Adds the elements of a collection to the end of the array. + /// + /// Use this method to append the elements of a collection to the end of this + /// array. This example appends the elements of a `Range` instance + /// to an array of integers. + /// + /// var numbers = [1, 2, 3, 4, 5] + /// numbers.append(contentsOf: 10...15) + /// print(numbers) + /// // Prints "[1, 2, 3, 4, 5, 10, 11, 12, 13, 14, 15]" + /// + /// - Parameter newElements: The elements to append to the array. + /// + /// - Complexity: O(*m*) on average, where *m* is the length of + /// `newElements`, over many calls to `append(contentsOf:)` on the same + /// array. + @_alwaysEmitIntoClient + @_semantics("array.append_contentsOf") + public mutating func append(contentsOf newElements: __owned some Collection) { + let newElementsCount = newElements.count + // This check prevents a data race writing to _swiftEmptyArrayStorage + if newElementsCount == 0 { + return + } + defer { + _endMutation() + } + reserveCapacityForAppend(newElementsCount: newElementsCount) + _ = _buffer.beginCOWMutation() + + let oldCount = self.count + let startNewElements = _buffer.firstElementAddress + oldCount + let buf = UnsafeMutableBufferPointer( + start: startNewElements, + count: newElementsCount) + _debugPrecondition(buf.endIndex <= self.capacity) + let end = buf.initialize(fromContentsOf: newElements) + _precondition(end == buf.endIndex) + _buffer.count += newElementsCount + } /// Adds the elements of a sequence to the end of the array. /// @@ -947,6 +988,17 @@ extension ArraySlice: RangeReplaceableCollection { public mutating func append(contentsOf newElements: __owned S) where S.Element == Element { + let wasContiguous = newElements.withContiguousStorageIfAvailable { + append(contentsOf: $0) + return true + } + if wasContiguous != nil { + return + } + + defer { + _endMutation() + } let newElementsCount = newElements.underestimatedCount reserveCapacityForAppend(newElementsCount: newElementsCount) _ = _buffer.beginCOWMutation() @@ -975,7 +1027,6 @@ extension ArraySlice: RangeReplaceableCollection { // append them in slow sequence-only mode _buffer._arrayAppendSequence(IteratorSequence(remainder)) } - _endMutation() } @inlinable diff --git a/stdlib/public/core/ContiguousArray.swift b/stdlib/public/core/ContiguousArray.swift index 3c7cf01fea388..0c5ca1ec4c300 100644 --- a/stdlib/public/core/ContiguousArray.swift +++ b/stdlib/public/core/ContiguousArray.swift @@ -815,6 +815,50 @@ extension ContiguousArray: RangeReplaceableCollection { _appendElementAssumeUniqueAndCapacity(oldCount, newElement: newElement) _endMutation() } + + /// Adds the elements of a sequence to the end of the array. + /// + /// Use this method to append the elements of a sequence to the end of this + /// array. This example appends the elements of a `Range` instance + /// to an array of integers. + /// + /// var numbers = [1, 2, 3, 4, 5] + /// numbers.append(contentsOf: 10...15) + /// print(numbers) + /// // Prints "[1, 2, 3, 4, 5, 10, 11, 12, 13, 14, 15]" + /// + /// - Parameter newElements: The elements to append to the array. + /// + /// - Complexity: O(*m*) on average, where *m* is the length of + /// `newElements`, over many calls to `append(contentsOf:)` on the same + /// array. + @_alwaysEmitIntoClient + @_semantics("array.append_contentsOf") + public mutating func append( + contentsOf newElements: __owned some Collection + ) { + let newElementsCount = newElements.count + // This check prevents a data race writing to _swiftEmptyArrayStorage + if newElementsCount == 0 { + return + } + defer { + _endMutation() + } + + _reserveCapacityImpl(minimumCapacity: self.count + newElementsCount, + growForAppend: true) + + let oldCount = _buffer.mutableCount + let startNewElements = _buffer.mutableFirstElementAddress + oldCount + let buf = UnsafeMutableBufferPointer( + start: startNewElements, + count: newElementsCount) + _debugPrecondition(buf.endIndex <= _buffer.mutableCapacity) + let end = buf.initialize(fromContentsOf: newElements) + _precondition(end == buf.endIndex) + _buffer.count += newElementsCount + } /// Adds the elements of a sequence to the end of the array. /// @@ -836,6 +880,14 @@ extension ContiguousArray: RangeReplaceableCollection { @_semantics("array.append_contentsOf") public mutating func append(contentsOf newElements: __owned S) where S.Element == Element { + + let wasContiguous = newElements.withContiguousStorageIfAvailable { + append(contentsOf: $0) + return true + } + if wasContiguous != nil { + return + } defer { _endMutation() diff --git a/stdlib/public/core/ContiguousArrayBuffer.swift b/stdlib/public/core/ContiguousArrayBuffer.swift index 0796d97668071..3fe4882ca39f4 100644 --- a/stdlib/public/core/ContiguousArrayBuffer.swift +++ b/stdlib/public/core/ContiguousArrayBuffer.swift @@ -1063,6 +1063,12 @@ extension Sequence { internal func _copySequenceToContiguousArray< S: Sequence >(_ source: S) -> ContiguousArray { + let contigArray = source.withContiguousStorageIfAvailable { + _copyCollectionToContiguousArray($0) + } + if let contigArray { + return contigArray + } let initialCapacity = source.underestimatedCount var builder = _UnsafePartiallyInitializedContiguousArrayBuffer( diff --git a/test/SILOptimizer/array_contentof_opt.swift b/test/SILOptimizer/array_contentof_opt.swift index 4671fd11631ad..48e26633cc773 100644 --- a/test/SILOptimizer/array_contentof_opt.swift +++ b/test/SILOptimizer/array_contentof_opt.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -O -sil-verify-all -emit-sil -Xllvm '-sil-inline-never-functions=$sSa6appendyy' -Xllvm -sil-inline-never-function='$sSa6append10contentsOfyqd__n_t7ElementQyd__RszSTRd__lFSi_SaySiGTg5' %s | %FileCheck %s +// RUN: %target-swift-frontend -O -sil-verify-all -emit-sil -Xllvm '-sil-inline-never-functions=$sSa6appendyy' -Xllvm -sil-inline-never-function='$sSa6append10contentsOfyqd__n_t7ElementQyd__RszSlRd__lFSi_SaySiGTg5' %s | %FileCheck %s // REQUIRES: swift_stdlib_no_asserts,optimized_stdlib // This is an end-to-end test of the Array.append(contentsOf:) -> @@ -63,7 +63,7 @@ public func dontPropagateContiguousArray(_ a: inout ContiguousArray) { // Check if the specialized Array.append(contentsOf:) is reasonably optimized for Array. -// CHECK-LABEL: sil shared {{.*}}@$sSa6append10contentsOfyqd__n_t7ElementQyd__RszSTRd__lFSi_SaySiGTg5 +// CHECK-LABEL: sil shared {{.*}}@$sSa6append10contentsOfyqd__n_t7ElementQyd__RszSlRd__lFSi_SaySiGTg5 // There should only be a single call to _createNewBuffer or reserveCapacityForAppend/reserveCapacityImpl. @@ -72,5 +72,5 @@ public func dontPropagateContiguousArray(_ a: inout ContiguousArray) { // CHECK: apply [[F]] // CHECK-NOT: apply -// CHECK-LABEL: } // end sil function '$sSa6append10contentsOfyqd__n_t7ElementQyd__RszSTRd__lFSi_SaySiGTg5 +// CHECK-LABEL: } // end sil function '$sSa6append10contentsOfyqd__n_t7ElementQyd__RszSlRd__lFSi_SaySiGTg5