Replace C++ implementation of swift_beginAccess and swift_endAccess with (almost) pure Swift implementation. Helpers remain in C++ for TLS, getting return addresses, and raising a fatal error on violations.
This change also moves the exclusivity access set head from the shared SwiftTLSContext structure to a dedicated TLS key. This improves performance, which is important for exclusivity checking. This is particularly the case where we can inline TLS access with a constant key, as on Darwin ARM64.
The code that bridges exclusivity tracking into Concurrency remains in C++. The new Swift implementation exposes a few helpers for it to use as a replacement for directly manipulating the C++ implementation.
rdar://161122309
We should expose the demangle functionality; It's been widely used by
calling into internal _swift_demangle and instead we should offer a real
API. It's also already used in the Runtime module already when forming
backtraces.
[Previous
discussions](https://forums.swift.org/t/demangle-function/25416/15)
happened between 2019 and 2024, and just never materialized in a
complete implementation and proposal.
Right now, even more tools are in need of this API, as we are building
continious profiling solutions etc, so it is a good time to revisit this
topic.
This PR is roughly based off @Azoy's
https://github.com/swiftlang/swift/pull/25314/files#diff-fd379a721cc9a1c9ef6486eae713e945da842b42170d4d069029a57334371eba
from 2019, however it brings it over to the new Runtime module which is
a great place to put this functionality - even Backtrace had to recently
reinvent calling the demangling infra in this module.
Pending SE review, [proposal
here](https://github.com/swiftlang/swift-evolution/pull/2989).
cc @azoy @al45tair
---------
Co-authored-by: Alastair Houghton <alastair@alastairs-place.net>
Now accepted as
[SE-0489](https://github.com/ZevEisenberg/swift-evolution/blob/main/proposals/0489-codable-error-printing.md).
# To Do
- [x] confirm which version of Swift to use for the availability
annotations. Probably 6.3 at time of writing.
# Context
Re: [Swift forum
post](https://forums.swift.org/t/the-future-of-serialization-deserialization-apis/78585/77),
where a discussion about future serialization tools in Swift prompted
Kevin Perry to suggest that some proposed changes could actually be made
in today's stdlib.
# Summary of Changes
Conforms `EncodingError` and `DecodingError` to
`CustomDebugStringConvertible` and adds a more-readable
`debugDescription`.
# Future Directions
This is a pared-down version of some experiments I did in
[UsefulDecode](https://github.com/ZevEisenberg/UsefulDecode). The
changes in this PR are the best I could do without changing the public
interface of `DecodingError` and `EncodingError`, and without modifying
the way the `JSON`/`PropertyList` `Encoder`/`Decoder` in Foundation
generate their errors' debug descriptions.
In the above-linked
[UsefulDecode](https://github.com/ZevEisenberg/UsefulDecode) repo, when
JSON decoding fails, I go back and re-decode the JSON using
`JSONSerialization` in order to provide more context about what failed,
and why. I didn't attempt to make such a change here, but I'd like to
discuss what may be possible.
# Examples
To illustrate the effect of the changes in this PR, I removed my changes
to stdlib/public/core/Codable.swift and ran my new test cases again.
Here are the resulting diffs.
##
`test_encodingError_invalidValue_nonEmptyCodingPath_nilUnderlyingError`
### Before
`invalidValue(234, Swift.EncodingError.Context(codingPath:
[GenericCodingKey(stringValue: "first", intValue: nil),
GenericCodingKey(stringValue: "second", intValue: nil),
GenericCodingKey(stringValue: "2", intValue: 2)], debugDescription: "You
cannot do that!", underlyingError: nil))`
### After
`EncodingError.invalidValue: 234 (Int). Path: first.second[2]. Debug
description: You cannot do that!`
## `test_decodingError_valueNotFound_nilUnderlyingError`
### Before
`valueNotFound(Swift.String, Swift.DecodingError.Context(codingPath:
[GenericCodingKey(stringValue: "0", intValue: 0),
GenericCodingKey(stringValue: "firstName", intValue: nil)],
debugDescription: "Description for debugging purposes", underlyingError:
nil))`
### After
`DecodingError.valueNotFound: Expected value of type String but found
null instead. Path: [0].firstName. Debug description: Description for
debugging purposes`
## `test_decodingError_keyNotFound_nonNilUnderlyingError`
### Before
`keyNotFound(GenericCodingKey(stringValue: "name", intValue: nil),
Swift.DecodingError.Context(codingPath: [GenericCodingKey(stringValue:
"0", intValue: 0), GenericCodingKey(stringValue: "address", intValue:
nil), GenericCodingKey(stringValue: "city", intValue: nil)],
debugDescription: "Just some info to help you out", underlyingError:
Optional(main.GenericError(name: "hey, who turned out the lights?"))))`
### After
`DecodingError.keyNotFound: Key \'name\' not found in keyed decoding
container. Path: [0].address.city. Debug description: Just some info to
help you out. Underlying error: GenericError(name: "hey, who turned out
the lights?")`
## `test_decodingError_typeMismatch_nilUnderlyingError`
### Before
`typeMismatch(Swift.String, Swift.DecodingError.Context(codingPath:
[GenericCodingKey(stringValue: "0", intValue: 0),
GenericCodingKey(stringValue: "address", intValue: nil),
GenericCodingKey(stringValue: "city", intValue: nil),
GenericCodingKey(stringValue: "birds", intValue: nil),
GenericCodingKey(stringValue: "1", intValue: 1),
GenericCodingKey(stringValue: "name", intValue: nil)], debugDescription:
"This is where the debug description goes", underlyingError: nil))`
### After
`DecodingError.typeMismatch: expected value of type String. Path:
[0].address.city.birds[1].name. Debug description: This is where the
debug description goes`
## `test_decodingError_dataCorrupted_nonEmptyCodingPath`
### Before
`dataCorrupted(Swift.DecodingError.Context(codingPath:
[GenericCodingKey(stringValue: "first", intValue: nil),
GenericCodingKey(stringValue: "second", intValue: nil),
GenericCodingKey(stringValue: "2", intValue: 2)], debugDescription:
"There was apparently some data corruption!", underlyingError:
Optional(main.GenericError(name: "This data corruption is getting out of
hand"))))`
### After
`DecodingError.dataCorrupted: Data was corrupted. Path: first.second[2].
Debug description: There was apparently some data corruption!.
Underlying error: GenericError(name: "This data corruption is getting
out of hand")`
## `test_decodingError_valueNotFound_nonNilUnderlyingError`
### Before
`valueNotFound(Swift.Int, Swift.DecodingError.Context(codingPath:
[GenericCodingKey(stringValue: "0", intValue: 0),
GenericCodingKey(stringValue: "population", intValue: nil)],
debugDescription: "Here is the debug description for value-not-found",
underlyingError: Optional(main.GenericError(name: "these aren\\\'t the
droids you\\\'re looking for"))))`
### After
`DecodingError.valueNotFound: Expected value of type Int but found null
instead. Path: [0].population. Debug description: Here is the debug
description for value-not-found. Underlying error: GenericError(name:
"these aren\\\'t the droids you\\\'re looking for")`
##
`test_encodingError_invalidValue_emptyCodingPath_nonNilUnderlyingError`
### Before
`invalidValue(345, Swift.EncodingError.Context(codingPath: [],
debugDescription: "You cannot do that!", underlyingError:
Optional(main.GenericError(name: "You really cannot do that"))))`
### After
`EncodingError.invalidValue: 345 (Int). Debug description: You cannot do
that!. Underlying error: GenericError(name: "You really cannot do
that")`
## `test_decodingError_typeMismatch_nonNilUnderlyingError`
### Before
`typeMismatch(Swift.String, Swift.DecodingError.Context(codingPath:
[GenericCodingKey(stringValue: "0", intValue: 0),
GenericCodingKey(stringValue: "address", intValue: nil),
GenericCodingKey(stringValue: "1", intValue: 1),
GenericCodingKey(stringValue: "street", intValue: nil)],
debugDescription: "Some debug description", underlyingError:
Optional(main.GenericError(name: "some generic error goes here"))))`
### After
`DecodingError.typeMismatch: expected value of type String. Path:
[0].address[1].street. Debug description: Some debug description.
Underlying error: GenericError(name: "some generic error goes here")`
## `test_encodingError_invalidValue_emptyCodingPath_nilUnderlyingError`
### Before
`invalidValue(123, Swift.EncodingError.Context(codingPath: [],
debugDescription: "You cannot do that!", underlyingError: nil))`
### After
`EncodingError.invalidValue: 123 (Int). Debug description: You cannot do
that!`
## `test_decodingError_keyNotFound_nilUnderlyingError`
### Before
`keyNotFound(GenericCodingKey(stringValue: "name", intValue: nil),
Swift.DecodingError.Context(codingPath: [GenericCodingKey(stringValue:
"0", intValue: 0), GenericCodingKey(stringValue: "address", intValue:
nil), GenericCodingKey(stringValue: "city", intValue: nil)],
debugDescription: "How would you describe your relationship with your
debugger?", underlyingError: nil))`
### After
`DecodingError.keyNotFound: Key \'name\' not found in keyed decoding
container. Path: [0]address.city. Debug description: How would you
describe your relationship with your debugger?`
## `test_decodingError_dataCorrupted_emptyCodingPath`
### Before
`dataCorrupted(Swift.DecodingError.Context(codingPath: [],
debugDescription: "The given data was not valid JSON", underlyingError:
Optional(main.GenericError(name: "just some data corruption"))))`
### After
`DecodingError.dataCorrupted: Data was corrupted. Debug description: The
given data was not valid JSON. Underlying error: GenericError(name:
"just some data corruption")`
##
`test_encodingError_invalidValue_nonEmptyCodingPath_nonNilUnderlyingError`
### Before
`invalidValue(456, Swift.EncodingError.Context(codingPath:
[GenericCodingKey(stringValue: "first", intValue: nil),
GenericCodingKey(stringValue: "second", intValue: nil),
GenericCodingKey(stringValue: "2", intValue: 2)], debugDescription: "You
cannot do that!", underlyingError: Optional(main.GenericError(name: "You
really cannot do that"))))`
### After
`EncodingError.invalidValue: 456 (Int). Path: first.second[2]. Debug
description: You cannot do that!. Underlying error: GenericError(name:
"You really cannot do that")`
Before this change, we were sometimes using introductory platform version as the starting version where
a symbol is considered belonging to the original binary. However, this is insufficient
because backdeploying to a platform version prior to a symbol's introductory version can happen. When it
happens, we should expect the moved symbol to be found in the moved-from library, not the moved-to one. Expecting
the symbol to be found in the moved-to library can lead to crasher.
Resolves: rdar://159408187
Add SWIFT_DEBUG_ENABLE_TASK_SLAB_ALLOCATOR, which is on by default. When turned off, async stack allocations call through to malloc/free. This allows memory debugging tools to be used on async stack allocations.
Instead of using weak definitions of swift_retain_preservemost and swift_release_preservemost, use weak references and explicitly check them for NULL.
Embedded Swift uses weak definitions for its runtime symbols. If swiftCore is able to override the weak definitions of the _preservemost functions, it will also override the Embedded symbols. They are not compatible, so this ends poorly.
We scan the target's initial allocation pool, and all 16kB heap allocations. We check each pointer-aligned offset within those areas, and try to read it as Swift metadata and get a name from it. If that fails, quietly move on. It's very unlikely for some random memory to look enough like Swift metadata for this to produce a name, so this works very well to print the generic metadata instantiated in the remote process without requiring `SWIFT_DEBUG_ENABLE_METADATA_ALLOCATION_ITERATION`.
rdar://161120936
Carefully overhaul our word breaking implementation to follow the recommendations of Unicode Annex #29. Start exposing the core primitives (as well as `String`-level interfaces), so that folks can prototype proper API for these concepts.
- Fix `_wordIndex(after:)` to always advance forward. It now requires its input index to be on a word boundary. Remove the `@_spi` attribute, exposing it as a (hidden, but) public entry point.
- The old SPIs `_wordIndex(before:)` and `_nearestWordIndex(atOrBelow:)` were irredemably broken; follow the Unicode recommendation for implementing random-access text segmentation and replace them both with a new public `_wordIndex(somewhereAtOrBefore:)` entry pont.
- Expose handcrafted low-level state machines for detecting word boundaries (_WordRecognizer`, `_RandomAccessWordRecognizer`), following the design of `_CharacterRecognizer`.
- Add tests to reliably validate that the two state machine flavors always produce consistent results.
rdar://155482680
https://github.com/swiftlang/swift/pull/80821 added `@isolated(any)`
which is an ABI breaking change but since we are not planning to keep
this API around, let's use `@_silgen_name` to bring the old name back
for now.
Resolves: rdar://152178221
Key paths can't reference non-escapable or non-copyable storage declarations,
so we don't need to refer to them resiliently, and can elide their property
descriptors.
However, declarations may still be conditionally Copyable and Escapable, and
if so, then they still need a property descriptor for resilient key path
references. When a property or subscript can be used in a context where it
is fully Copyable and Escapable, emit the property descriptor in a generic
environment constrained by the necessary conditional constraints.
Fixes rdar://151628396.