This patch removes as much checked math as I was able to find from the
unsafe buffer pointers types. In particular, I removed two kinds of
checked math:
1. Checked math that was provably unable to trap due to prior operations
done in the same function, or in a function that must have been
called before the current one. For example, in
index(_:offsetBy:limitedBy:), when we do the final index addition we
have already validated that it must be in-bounds. Therefore, it
cannot overflow.
2. Checked math that can only fail if the user is misusing the indices.
As previously discussed with Karoy and Andrew, the unsafe raw buffer
types are not obligated to crash if you misuse their indices, they
are free to invoke undefined behaviour. In these cases, I added
defensive overflow checks in debug mode.
The result of this change should be reductions in code size in almost
all usage sites of the raw buffer types.
* [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.
`Slice` does not technically guarantee that its indices are valid in its base, so these initializers accidentally allowed the creation of obviously out-of-bounds buffers.
Every other Unsafe(Mutable)BufferPointer precondition in this file is debug-mode only.
This information is already part of the documentation for this function, and it's really hard to get rid of these branches and traps otherwise.
UnsafeRawBufferPointer was not made self-slicing, but is instead sliced
by Slice<UnsafeRawBufferPointer>. When working with
UnsafeRawBufferPointer objects it is quite common to want to transform
back into the underlying collection, which is why the
UnsafeRawBufferPointer provides a constructor from its subsequence:
UnsafeRawBufferPointer.init(rebasing:).
Unfortunately for an initializer on this extremely low-level type, this
initializer is surprisingly expensive. When disassembled on Linux it
emits 7 separate traps and 11 branches. This relative heft means this
method often gets outlined, which is a shame, as several of the branches
could often be eliminated due to checks elsewhere in functions where
this initializer is used.
Almost all of these branches and almost all of the traps are
unnecessary. We can remove them by noting that it is impossible to
construct a Slice whose endIndex is earlier than its startIndex. All
Slice inits are constructed with bounds expressed as Range, and Range
enforces ordering on its endpoints.
For this reason, we can do unchecked arithmetic on the start and end
index of the slice, and remove 5 traps in one fell swoop. This greatly
cheapens the cost of the initializer, improving its odds of being
inlined and having even more of its branches optimised away.
For what it's worth, I also considered trying to remove the last two
preconditions. Unfortunately I concluded I couldn't confidently do that.
I wanted to remove them based on the premise that a valid Slice must
have indices that were in-bounds for its parent Collection, and so we
could safely assume that if the parent base address was nil the count
would have to be zero, and that adding start index to the base address
would definitely not overflow. However, the existence of the
Slice.init(base:bounds:) constructor led me to be a bit suspicions of
removing those checks. Given that
UnsafeRawBufferPointer.init(start:count:) enforces those invariants, I
decided it was safest for us to continue to do that.
These include the pointer-to-pointer and pointer-to-buffer-pointer
initialiser parameters amongst a couple of others, such as
`Unmanaged.fromOpaque`, and the source for the `move[...]` family of
methods.
This fix updates various initializers to handle incoming empty buffers
that happen to have a nil base. They should simply create another
buffer with nil base rather than crashing!
It is valid for an Unsafe[Raw]BufferPointer can have a nil base
address. This allows round-tripping with C code that takes a
pointer/length pair and uses `0` as the pointer value.
The original design wrongly assumed that we would use a sentinel value
for empty buffers and was never updated for or tested with the current
design.
Fixes <rdar://problem/47946984> Regression in Foundation.Data's
UnsafeBufferPointer constructor.
It is unfortunate that `Unsafe[Raw]BufferPointer._pointer` and
`baseAddress` are declared Optional. This leaves extra runtime checks
in the code in the most performance critical paths. Contrast this with
Array, which uses an sentinal pointer for the empty representation.
This forces us to use _unsafelyUnwrappedUnchecked whenever we just
want to dereference a non-empty buffer pointer.
Fixes SR-9809: swiftc appears to make some sub-optimal optimization choices
This fixes the Windows platform, where the aligned allocation path is
not malloc-compatible. It won't have any observable difference on
Darwin or Linux, aside from manually allocated memory on Linux now
being consistently 16-byte aligned (heap objects will still be 8-byte
aligned on Linux).
It is unfortunate that we can't guarantee Swift-allocated memory via
Unsafe*Pointer is malloc compatible on Windows. It would have been
nice for that to be a cross platform guarantee since it's normal to
allocate in C and deallocate in Swift or vice-versa. Now we have to
tell developers to always use _aligned_malloc/_aligned_free when
transitioning between Swift/C if they expect their code to work on
Windows.
Even though this fix isn't required today on Darwin/Linux, it makes
good sense to guarantee that the allocation/deallocation paths are
consistent.
This is done by specifying a constant that stdlib can use to round up
alignment, _swift_MinAllocationAlignment. The runtime asserts that
this constant is greater than MALLOC_ALIGN_MASK for all platforms.
This way, manually allocated buffers will always use the aligned
allocation path. If users specify an alignment less than m
round up so users don't need
to pass the same alignment to deallocate the buffer). This constant
does not need to be ABI.
Alternatives are:
1. Require users of Unsafe*Pointer to specify the same alignment
during deallocation. This is obviously madness.
2. Introduce new runtime entry points:
swift_alignedAlloc/swift_alignedDealloc, introduce corresponding
new builtins, and have Unsafe*Pointer always call those. This would
make the runtime API a little more obvious but would introduce
complexity in other areas of the compiler and it doesn't have any
other significant benefit. Less than 16-byte alignment of manually
allocated buffers on Linux is a non-goal.
Add a use an unchecked subscript on UnsafeBufferPointer, which skips
debugPrecondition checks (in case we're not inlined) as well as a
force-unwrap check.
This switches the standard library's sort algorithm from an in-place
introsort to use a modified timsort, a stable, adaptive sort that
merges runs using a temporary buffer. This implementation performs
straight merges instead of adopting timsort's galloping strategy.
In addition to maintaining the relative order of equal/non-comparable
elements, this algorithm outperforms the introsort on data with any
intrinsic structure, such as runs of ascending or descending elements
or a significant number of equality collisions.
This saves a few instructions for some operations, like getting the count.
Also, it avoids the check for unwrapping the optional end pointer. For example, iterating over an unsafe buffer now has no overhead.
Also remove the _unboundedStartingAt initializer, which is not needed anymore.
* Make Range conditionally a Collection
* Convert ClosedRange to conditionally a collection
* De-gyb Range/ClosedRange, refactoring some methods.
* Remove use of Countable{Closed}Range from stdlib
* Remove Countable use from Foundation
* Fix test errors and warnings resulting from Range/CountableRange collapse
* fix prespecialize test for new mangling
* Update CoreAudio use of CountableRange
* Update SwiftSyntax use of CountableRange
* Restore ClosedRange.Index: Hashable conformance
* Move fixed typechecker slowness test for array-of-ranges from slow to fast, yay
* Apply Doug's patch to loosen test to just check for error
- Revise Equatable and Hashable for synthesized requirements
- Complete Strideable and stride(from:...:by:) documentation
- Revise DoubleWidth type docs
- Add complexity notes for Set.index(of:) and .contains(_:)
- Fix typos in Set.formUnion docs
- Add missing axioms for SetAlgebra (SR-6319)
- Improve guidance for description and debugDescription
- Add note about the result of passing duplicate keys to
Dictionary(uniqueKeysWithValues:)
- Fix typo in BinaryInteger docs
- Update Substring docs with better conversion example
- Improve docs for withMemoryRebound and isKnownUniquelyReferenced
- Add missing docs not propagated from protocols
* Nest various top-level Iterator and Index types, and flatten extensions.
* Fix tests from nesting iterator
* Nest Unsafe*BufferPointer.Iterator, extensionify UnsafeBufferPointer
* Degyb LazyCollection
* Nest Flatten iterator and index
* Eradicate IndexDistance associated type, replacing with Int everywhere
* Consistently use Int for ExistentialCollection’s IndexDistance type.
* Fix test for IndexDistance removal
* Remove a handful of no-longer-needed explicit types
* Add compatibility shims for non-Int index distances
* Test compatibility shim
* Move IndexDistance typealias into the Collection protocol
* Refactor Indices and Slice to use conditional conformance
* Replace ReversedRandomAccessCollection with a conditional extension
* Refactor some types into struct+extensions
* Revise Slice documentation
* Fix test cases for adoption of conditional conformances.
* [RangeReplaceableCollection] Eliminate unnecessary slicing subscript operator.
* Add -enable-experimental-conditional-conformances to test.
* Gruesome workaround for crasher in MutableSlice tests
- Revisions to unsafeDowncast and withVaList
- Fix the Int64/UInt64 discussion
- Buffer pointer revisions
- Fix Optional example to use new integer methods
- Revise and correct some UnsafeRawBufferPointer docs
- Fix symmetricDifference examples
- Fix wording in FloatingPoint.nextDown
- Update ImplicitlyUnwrappedOptional
- Clarify elementsEqual
- Minor integer doc fixes
- Comment for _AppendKeyPath
- Clarification re collection indices
- Revise RangeExpression.relative(to:)
- Codable revisions