Files
swift-mirror/test/stdlib/KeyPath32Bit.swift
Max Desiatov 5b97cb7c1f Fix KeyPath with 16-byte-aligned subscript traps on 32-bit targets (#88725)
- **Explanation**: Fixes a crash with key paths on 32-bit platforms reproducible for types that have 16-byte alignment.
The intended bit layout of `ComputedArgumentSize` in `KeyPath` on 32-bit is:
```
 ┌───────┬───────────┐
 │ bits  │   field   │
 ├───────┼───────────┤
 │ 0–27  │ size      │
 ├───────┼───────────┤
 │ 28–29 │ padding   │
 ├───────┼───────────┤
 │ 30–31 │ alignment │
 └───────┴───────────┘
```
Currently, `alignmentMask = 0x6000_0000`, i.e. bits 29–30, not 30–31. It overlaps paddingMask (bits 28–29) at bit 29, meaning that alignment and padding unintentionally share a bit. With `alignmentShift = 30`, storing `shift = 2 << 30` places 1 at bit 31, which the mask doesn't cover.

Correct value is `0xC000_0000` covers bits 30–31, which matches `alignmentShift = 30` so both `shift = 1` and `shift = 2` round-trip, and it does not overlap with `paddingMask = 0x3000_0000` (bits 28–29). It also mirrors the 64-bit layout (top bits of the word reserved for alignment, just 2 of them instead of 1).
- **Scope**: Limited to 32-bit platforms.
- **Issues**: rdar://175799967
- **Risk**: Low due to increased test coverage.
- **Testing**: Previously crashing on 32-bit platforms sample code is now added to the test suite.
2026-04-29 20:25:16 -07:00

53 lines
1.8 KiB
Swift

// RUN: %target-run-simple-swift
// RUN: %target-run-simple-swift(-O)
// REQUIRES: executable_test
// UNSUPPORTED: use_os_stdlib
// UNSUPPORTED: back_deployment_runtime
// Regression test for a 32-bit-only mask-overlap bug in `ComputedArgumentSize`
// (`stdlib/public/core/KeyPath.swift`).
import StdlibUnittest
let tests = TestSuite("KeyPath32Bit")
struct Inner {
let base: Int
subscript(idx: SIMD4<Int32>) -> Int {
return base + Int(idx[0]) + Int(idx[1]) + Int(idx[2]) + Int(idx[3])
}
}
struct Outer {
var inner: Inner
}
tests.test("appended keypath with 16-byte-aligned subscript argument") {
// Precondition: SIMD4<Int32> must have 16-byte alignment on this target.
// If a future stdlib change demotes the alignment, this test would stop
// exercising the bug's codepath — assert loudly instead of silently.
expectEqual(16, MemoryLayout<SIMD4<Int32>>.alignment)
expectEqual(16, MemoryLayout<SIMD4<Int32>>.size)
let arg = SIMD4<Int32>(1, 2, 3, 4)
// The leaf keypath captures a 16-aligned argument.
let leaf: KeyPath<Inner, Int> = \Inner.[arg]
// `appending(path:)` forces the buggy `calculateAppendedKeyPathSize` /
// `_storeInto` paths that read `.alignment` back from the leaf component's
// header. Under the buggy mask, the read returns 0; the appended buffer is
// under-allocated; projection traps inside the stdlib.
let composed: KeyPath<Outer, Int> = (\Outer.inner).appending(path: leaf)
// Normal keypath projection. On a correct stdlib this returns the expected
// value. On a buggy stdlib it traps before returning, which lit records as
// a non-zero exit code (test FAIL).
let observed = Outer(inner: Inner(base: 100))[keyPath: composed]
// 100 (base) + 1 + 2 + 3 + 4 = 110.
expectEqual(110, observed)
}
runAllTests()