The law of exclusivity now allows us to remove the safety belt of swapping out self, getting rid of the need to create an empty array. (Which eliminates a swift_retain call.)
`_copyContents(initializing:)` is a core method of Sequence, and it is used surprisingly often to copy stuff out of sequences. Array’s internal types currently have explicit implementations of it that trap (to prevent a performance bug due to the default iterator-based implementation. This has proved a bad idea, as not all code paths that end up calling `_copyContents` have actually been expunged — so we replaced a performance bug with a catastrophic correctness bug. 😥
Rather than trying to play whack-a-mole with code paths that end up in `_copyContents`, replace the traps with (relatively) efficient implementations, based on the ancient `_copyContents(subRange:initializing)` methods that have already been there all this time.
This resolves https://bugs.swift.org/browse/SR-14663.
I expect specialization will make this fix deploy back to earlier OSes in most (but unfortunately not all) cases.
* [stdlib] Deprecate MutableCollection._withUnsafeMutableBufferPointerIfSupported
In Swift 5.0, [SE-0237] introduced the public `MutableCollection.withContiguousMutableStorageIfAvailable` method. It’s time we migrated off the old, underscored variant and deprecated it.
The default `MutableCollection.sort` and `.partition(by:)` implementations are currently calling this hidden method rather than the documented interface, preventing custom Collection implementations from achieving good performance, even if they have contiguous storage.
[SE-0237]: https://github.com/apple/swift-evolution/blob/master/proposals/0237-contiguous-collection.md
* [test] Update tests for stdlib behavior changes
* Update stdlib/private/StdlibCollectionUnittest/CheckMutableCollectionType.swift
Co-authored-by: Nate Cook <natecook@apple.com>
* Update stdlib/private/StdlibCollectionUnittest/CheckMutableCollectionType.swift
Co-authored-by: Nate Cook <natecook@apple.com>
* Apply suggestions from code review
Co-authored-by: Nate Cook <natecook@apple.com>
* [test] LoggingMutableCollection: Fix logging targets
* [stdlib] Fix warning by restoring original workaround
Co-authored-by: Nate Cook <natecook@apple.com>
Introduce checking of ConcurrentValue conformances:
- For structs, check that each stored property conforms to ConcurrentValue
- For enums, check that each associated value conforms to ConcurrentValue
- For classes, check that each stored property is immutable and conforms
to ConcurrentValue
Because all of the stored properties / associated values need to be
visible for this check to work, limit ConcurrentValue conformances to
be in the same source file as the type definition.
This checking can be disabled by conforming to a new marker protocol,
UnsafeConcurrentValue, that refines ConcurrentValue.
UnsafeConcurrentValue otherwise his no specific meaning. This allows
both "I know what I'm doing" for types that manage concurrent access
themselves as well as enabling retroactive conformance, both of which
are fundamentally unsafe but also quite necessary.
The bulk of this change ended up being to the standard library, because
all conformances of standard library types to the ConcurrentValue
protocol needed to be sunk down into the standard library so they
would benefit from the checking above. There were numerous little
mistakes in the initial pass through the stsandard library types that
have now been corrected.
Due to a couple of unfortunate circumstances, appending an NSArray instance to an Array instance does not actually append any elements.
The cause is https://github.com/apple/swift/pull/29220, which accidentally optimized away the actual loop that appends the elements in this particular case. (And only this particular case, which is why this wasn’t detected by the test suite.)
When the argument to `Array.append(contentsOf:)` is of type NSArray, the `newElements is [Element]` expression is compiled into a runtime check that returns true, eliminating the subsequent loop over the remaining items of the iterator. Sadly, NSArray.underestimatedCount` currently returns 0, so the earlier _copyContents call is a noop, so no elements get added to `self` at all.
Turning the `is` test into a direct equality check between the metatype instances resolves the issue.
Use the new builtins for COW representation in Array, ContiguousArray and ArraySlice.
The basic idea is to strictly separate code which mutates an array buffer from code which reads from an array.
The concept is explained in more detail in docs/SIL.rst, section "Copy-on-Write Representation".
The main change is to use beginCOWMutation() instead of isUniquelyReferenced() and insert endCOWMutation() at the end of all mutating functions. Also, reading from the array buffer must be done differently, depending on if the buffer is in a mutable or immutable state.
All the required invariants are enforced by runtime checks - but only in an assert-build of the library: a bit in the buffer object side-table indicates if the buffer is mutable or not.
Along with the library changes, also two optimizations needed to be updated: COWArrayOpt and ObjectOutliner.
This has two advantages:
1. It does not force the Array in memory (to pass it as inout self to the non-inlinable _createNewBuffer).
2. The new _consumeAndCreateNew is annotated to consume self. This helps to reduce unnecessary retains/releases.
The change applies for Array and ContiguousArray.
Confirmed this change on using the swift-5.2-DEVELOPMENT-SNAPSHOT-2020-01-22-a
toolchain, Apple Swift version 5.2-dev (Swift d3f0448a4c).
Fixes rdar://problem/58717942
This additional check lets the optimizer eliminate most of the append-code in specializations where the appended sequence is also an Array.
For example, when "adding" arrays, e.g. arr += other_arr
This reverts commit 3e932c075d.
The compiler does not support @_alwaysEmitIntoClient properties
specially wrt property descriptors. The revert commit would introduce an
ABI incompatability when a keypath to Array.first is formed:
let greetings = ["hello", "hola"]
let count = greetings[keyPath: \[String].first?.count]
Runmning on an older runtime would lead to linker errors against $sSa5firstxSgvpMV
the property descriptor for Array.first.
rdar://58484319
All mutating Array functions must be annotated with semantics, because otherwise some high level optimizations get confused.
The semantic attributes prevent inlining those functions in high-level-sil.
This is need so that the optimizer sees that the Array is taken as inout and can reason that it's modified.
This restriction is not needed anymore when we’ll have COW representation in SIL.
rdar://problem/58478089
Just copy the buffer if it's not unique.
This also implies that if there is a copy-on-write in remove, "shrink" the capacity of the new buffer to the required amount of elements (instead of copying the capacity of the original buffer).
This makes Array.first much small and more efficient.
Without this, Array.first compiled down to RandomAccessCollection.first, which ended up in pretty unefficient code.
rdar://problem/46291397
A previous commit [1] identified and fixed a benign race on
`_swiftEmptyArrayStorage`. Unfortunately, we have some code duplication
and the fix was not applied to all the necessary places. Essentially,
we need to ensure that the "empty array storage" object that backs many
"array like types" is never written to.
I tried to improve the test to capture this, however, it is very
finicky. Currently, it does not go red on my system even when I remove
the check to avoid the benign race in Release mode.
Relevant classes: Array, ArraySlice, ContiguousArray, ArrayBuffer,
ContiguousArrayBuffer, SliceBuffer.
[1] b9b4c789f3
rdar://55161564
[stdlib] Make unsafe array initializer public
This implements SE-0245. The public versions of this initializer call
into the existing, underscored version, which avoids the need for
availability constraints.
This fixes a major perform bug involving array initialization from any
contiguously stored collection. This is not a recent regression. This fix
results in a 10,000X speedup (that's 4 zeros) for this code path:
func initializeFromSlice(_ a: [Int]) -> [Int] {
return Array<Int>(a[...])
}
A benchmark is included.
Also add overloads for these operators to an extension of Array.
This allows us to typecheck array concatenation quickly with
designated type support enabled and the remaining type checker hacks
disabled.
These should be audited since some might not actually need to be
@inlinable, but for now:
- Anything public and @inline(__always) is now also @inlinable
- Anything @usableFromInline and @inline(__always) is now @inlinable