`String.UnicodeScalarView` currently lacks proper index validation in its `index(after:)` and `index(before:)` methods, leading to out-of-bounds memory accesses when index navigation methods in this view are given invalid indices.
(Also see https://github.com/apple/swift/pull/41598 and https://github.com/apple/swift/pull/41417)
rdar://89498541
`String.index(before:)` (and methods that rely on it, such as `Substring.index(before:)`, `.distance(from:to:)` etc.) does not currently verify that the given index falls before the `endIndex` before aligning it to a scalar boundary. This allows an out-of-bounds memory access when the provided index points to a position beyond the end of `self`’s storage.
Additionally, the `i > startIndex` check needs to be done after scalar alignment, not before, as alignment can round the index down to `startIndex`.
rdar://89497074&89495373
`IndexDistance` is an artifact of ancient history — it used to be an associated type of Collection in the Swift 3 era. The Collection requirement got replaced with a deprecated typealias of Int in Swift 4, the same release that re-introduced the Collection conformance for String.
However, the typealias declaration for String.IndexDistance has fallen through the cracks. It was accidentally left un-deprecated and it’s still actively mentioned in String API declarations to this day. (These usages leak into the API docs, which can be a source of unnecessary confusion.)
Let’s put an end to this and add the missing deprecation attribute, replacing `IndexDistance` usages with `Int`.
While we’re here, also bring `String.index(_:offsetBy:)` and `String.index(_:offsetBy:limitedBy:)` in sync with their declarations in Collection by renaming the second argument from `n` to `distance`.
(These method declaration changes do get emitted into .swiftinterface files, but they aren’t breaking any client code — the argument names are only relevant to the docs (and the method implementation). They do not affect callers of these functions.)
Currently, the default implementations of the various `_failEarlyRangeCheck` forms contain several _precondition invocations, like this:
```
@inlinable
public func _failEarlyRangeCheck(_ index: Index, bounds: Range<Index>) {
_precondition(
bounds.lowerBound <= index,
"Out of bounds: index < startIndex")
_precondition(
index < bounds.upperBound,
"Out of bounds: index >= endIndex")
}
```
Each such precondition call generates a separate trap instruction, which seems like a waste — theoretically it would be helpful to know which condition was violated, but in practice, that information tends not to be surfaced anyway. Combining these will lead to a minuscule code size improvement.
(The separate messages do surface these in debug builds, but only if these generic defaults actually execute, which isn’t often. These are not public entry points, so concrete collection types tend ignore these and roll their own custom index validation instead. From what I’ve seen, custom code tends to combine the upper/lower checks into a single precondition check. These underscored requirements tend to be only called by the standard `Slice`; and it seems reasonable for that to follow suit.)
More interestingly, the range-in-range version of `_failEarlyRangeCheck` performs two times as many checks than are necessary:
```
@inlinable
public func _failEarlyRangeCheck(_ range: Range<Index>, bounds: Range<Index>) {
_precondition(
bounds.lowerBound <= range.lowerBound,
"Out of bounds: range begins before startIndex")
_precondition(
range.lowerBound <= bounds.upperBound,
"Out of bounds: range ends after endIndex")
_precondition(
bounds.lowerBound <= range.upperBound,
"Out of bounds: range ends before bounds.lowerBound")
_precondition(
range.upperBound <= bounds.upperBound,
"Out of bounds: range begins after bounds.upperBound")
}
```
It is safe to assume that `range.lowerBound <= range.upperBound`, so it’s enough to test that `bounds.lowerBound <= range.lowerBound && range.upperBound <= bounds.upperBound` — we don’t need to check each combination separately.
* [stdlib] relax stride check
- The stride check in `UnsafePointer.withMemoryRebound` makes less sense when rebinding memory for a single element.
- This skips the stride-matching portion of the `_debugPrecondition` in `withMemoryRebound` when `count == 1`.
* [test] UnsafePointer.withMemoryRebound with capacity 1
- The stride check in `UnsafePointer.withMemoryRebound` makes less sense when rebinding memory for a single element.
- This skips the stride-matching portion of the `_debugPrecondition` in `withMemoryRebound` when `count == 1`.
`Words.Iterator.next()` used to call `Int128(truncatingIfNeeded:)`, which in turn iterates over words, leading to an infinite recursion.
Implement half-width multiplication from scratch instead of masking off the full width results.
Having a direct initializer in the ABI is critically important — otherwise we wouldn’t be able to add back deployable conversion initializers in the future (without going through unnecessary overhead).
It is sometimes desirable to always perform (relatively cheap) runtime checks in the stdlib, even in configurations where we’d otherwise elide them.
Add a build-time option to the standard library (`SWIFT_STDLIB_ENABLE_DEBUG_PRECONDITIONS_IN_RELEASE`) to make `_debugPrecondition` work like `_precondition`, i.e., to enable it even in ReleaseAssert configurations.
This option will keep additional checks in the following places, even in release mode:
- Range checking in the subscript operations of all unsafe buffer pointer types.
- Nil checking in `Optional.unsafelyUnwrapped`
- Additional argument validation in `Unsafe[Mutable][Raw]Pointer`’s initialization/assignment/move/deinitialization/binding methods
- Protection against initializing `Unsafe[Mutable][Raw]Pointer` with invalid data. (Negative count, nil pointer for non-empty buffer, etc)
- Checks against index overflow in `Unsafe[Mutable]BufferPointer`’s index manipulation methods
- Checks against backward ranges in `Range(uncheckedBounds:)`, `ClosedRange(uncheckedBounds:)`
- Dynamic isa check in `unsafeDowncast(_:to:)`
- Additional [cheap] checks to catch invalid Sequence/Collection implementations in `Array.init<S:Sequence>(_:)` and elsewhere
- Checks against `Character` containing multiple grapheme clusters
- More index validation in `EmptyCollection`
(Additional cases will get added as the stdlib evolves.)
The option is disabled by default — so `_debugPrecondition`s continue to be disabled in optimized builds, even after this change lands, unless someone specifically builds a stdlib that enables them.
rdar://89118585
The requirement machine does not allow 'where' clauses to reference
typealiases defined in protocol extensions.
These should probably be removed entirely since they're not useful
anymore, but I'd rather that decision was made by the stdlib folks.