Skip to content

Commit a59af98

Browse files
authored
Merge pull request #12725 from jckarter/key-path-optional-chain-out-of-bounds
KeyPath: Fix out-of-bounds access when instantiating keypaths with optional chaining components.
2 parents adea06f + 993d795 commit a59af98

File tree

2 files changed

+29
-3
lines changed

2 files changed

+29
-3
lines changed

stdlib/public/core/KeyPath.swift

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2199,13 +2199,14 @@ internal func _getKeyPathClassAndInstanceSizeFromPattern(
21992199
// Scan the pattern to figure out the dynamic capability of the key path.
22002200
// Start off assuming the key path is writable.
22012201
var capability: KeyPathKind = .value
2202+
var didChain = false
22022203

22032204
let bufferPtr = pattern.advanced(by: keyPathObjectHeaderSize)
22042205
var buffer = KeyPathBuffer(base: bufferPtr)
22052206
var size = buffer.data.count + MemoryLayout<Int>.size
22062207
var alignmentMask = MemoryLayout<Int>.alignment - 1
22072208

2208-
scanComponents: while true {
2209+
while true {
22092210
let header = buffer.pop(RawKeyPathComponent.Header.self)
22102211

22112212
func popOffset() {
@@ -2290,8 +2291,8 @@ internal func _getKeyPathClassAndInstanceSizeFromPattern(
22902291
case .optionalChain,
22912292
.optionalWrap:
22922293
// Chaining always renders the whole key path read-only.
2293-
capability = .readOnly
2294-
break scanComponents
2294+
didChain = true
2295+
break
22952296

22962297
case .optionalForce:
22972298
// No effect.
@@ -2306,6 +2307,11 @@ internal func _getKeyPathClassAndInstanceSizeFromPattern(
23062307
alignment: MemoryLayout<Int>.alignment)
23072308
}
23082309

2310+
// Chaining always renders the whole key path read-only.
2311+
if didChain {
2312+
capability = .readOnly
2313+
}
2314+
23092315
// Grab the class object for the key path type we'll end up with.
23102316
func openRoot<Root>(_: Root.Type) -> AnyKeyPath.Type {
23112317
func openLeaf<Leaf>(_: Leaf.Type) -> AnyKeyPath.Type {

test/stdlib/KeyPath.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,5 +666,25 @@ keyPath.test("subscripts") {
666666
expectEqual(base[keyPath: ints_be], (17 + 38).bigEndian)
667667
}
668668

669+
// SR-6096
670+
671+
protocol Protocol6096 {}
672+
struct Value6096<ValueType> {}
673+
extension Protocol6096 {
674+
var asString: String? {
675+
return self as? String
676+
}
677+
}
678+
extension Value6096 where ValueType: Protocol6096 {
679+
func doSomething() {
680+
_ = \ValueType.asString?.endIndex
681+
}
682+
}
683+
extension Int: Protocol6096 {}
684+
685+
keyPath.test("optional chaining component that needs generic instantiation") {
686+
Value6096<Int>().doSomething()
687+
}
688+
669689
runAllTests()
670690

0 commit comments

Comments
 (0)