Swift 5.2.3 repairs property wrapper mutation and Linux linking
The maintenance release includes a regression test for observed wrapped arrays and changes the Linux runtime build to avoid libatomic.
An assignment through an array subscript can exercise more compiler machinery than its syntax suggests. If the array belongs to a wrapped property with observers, the compiler has to preserve the wrapper's access behavior and invoke the observers around the mutation. The Swift 5.2.3 release, published on April 29, includes a regression test for exactly that combination. Its changes against 5.2.2 also address Linux linking and generated accessors.
I value Swift's collections because their value semantics make mutation easier to reason about. That expectation extends to properties using wrappers. A programmer should be able to understand an element assignment through the property's declared behavior. Maintenance work on the generated access paths matters because several independently useful language facilities meet there.
An element assignment must preserve observers
The new property wrapper observer test defines a wrapper holding an integer array. Its wrappedValue has an ordinary getter and setter. A class uses the wrapper on an array property and attaches willSet and didSet observers. The program then changes the first array element.
The expected output gives a concrete specification. Here, willSet sees the updated array, and didSet sees the original array. The following abbreviated version preserves the structure of that test:
@propertyWrapper
struct Stored {
private var storage: [Int]
init(wrappedValue: [Int]) {
storage = wrappedValue
}
var wrappedValue: [Int] {
get { storage }
set { storage = newValue }
}
}
class Model {
@Stored var values = [1, 2, 3] {
willSet { print(newValue) }
didSet { print(oldValue) }
}
}
let model = Model()
model.values[0] = 4
The observers should print [4, 2, 3] followed by [1, 2, 3]. The source-level operation changes one element, but the property's surrounding access behavior still applies. An optimization that exposes storage for modification has to preserve that behavior, including the old value made available to didSet.
The compiler changes distinguish a wrapped property with observers from one whose read-write access can use the modify path directly. The new hasObservers helper also replaces repeated checks for the two observer kinds. This is a correction at the boundary between generated accessors and storage behavior. It does not require application authors to invent a different syntax for mutating an array.
Generated members need to exist across files
Another change forces synthesis of projected wrapper storage accessors when the compiler emits class members. The code comment identifies dynamically dispatched accessors as the reason. The accompanying multifile test defines a class with a wrapped property, reads and writes its projected value, then exercises that behavior through a subclass declared in a separate file.
A single-file example can miss this kind of defect. The compiler's knowledge and the generated entry points have to remain consistent when declarations are compiled across file boundaries. The test checks that the inherited behavior can actually run, rather than merely accepting the declarations during parsing.
The patch also improves recovery from invalid wrapper backing declarations. That distinction is useful when reading a maintenance diff. Some changes preserve the runtime behavior of valid code, while others let the compiler handle invalid code without making further incorrect assumptions. Both affect the editing experience, but they solve different problems.
For a wrapper author, projected access deserves separate consideration from wrapped access. The test uses the projected value as an assignable property even though the wrapped value is read-only in its wrapper definition. That combination explains why generating only the ordinary wrapped accessors cannot cover every operation the class exposes.
Linux changes the atomic dependency
The Linux build changes remove atomic from several link-library lists. For x86-64 C compilation, the build adds -march=core2, with an explicit comment connecting that choice to support for sixteen-byte compare-and-swap. The stated aim is to avoid a dependency on libatomic. The runtime unit-test build receives a corresponding architecture option.
This connects the instruction set assumed by the build with the libraries required by the resulting binaries. Removing a link flag alone would not explain how atomic operations remain available. The architecture setting is the other half of the change, and it is important when interpreting the packaging implications for Linux.
The driver also changes when it adds -pie. The condition now concerns ELF executable output and excludes a static executable request. Static executable link argument files lose -latomic, and their ICU library names change to the Swift-specific names. These edits concern the generated link invocation and runtime dependencies, rather than the grammar of a Swift application.
I find this kind of patch more useful when its effects are described at the level where they occur. A property-wrapper fix affects generated program behavior. A link change affects how the program is assembled and what its deployment environment must provide. Calling both a compiler update is accurate, but it leaves out the information needed to decide which part of an application build is affected.
The release tag identifies the source revision for this toolchain. The added tests identify specific behaviors maintainers intend to preserve, including observed element mutation and inherited projected access across files. Those are concrete examples of the interaction cases a small patch release can repair without introducing a new source-language feature.