Merge pull request #12775 from jckarter/key-path-optional-chain-out-of-bounds-4.0

[4.0] KeyPath: Fix out-of-bounds access when instantiating keypaths with optional chaining components.
diff --git a/stdlib/public/core/KeyPath.swift b/stdlib/public/core/KeyPath.swift
index 61e0a4d..faeccdd 100644
--- a/stdlib/public/core/KeyPath.swift
+++ b/stdlib/public/core/KeyPath.swift
@@ -1915,13 +1915,14 @@
   // Scan the pattern to figure out the dynamic capability of the key path.
   // Start off assuming the key path is writable.
   var capability: KeyPathKind = .value
+  var didChain = false
 
   let bufferPtr = pattern.advanced(by: keyPathObjectHeaderSize)
   var buffer = KeyPathBuffer(base: bufferPtr)
   var size = buffer.data.count + MemoryLayout<Int>.size
   var alignmentMask = MemoryLayout<Int>.alignment - 1
 
-  scanComponents: while true {
+  while true {
     let header = buffer.pop(RawKeyPathComponent.Header.self)
 
     func popOffset() {
@@ -2006,8 +2007,8 @@
     case .optionalChain,
          .optionalWrap:
       // Chaining always renders the whole key path read-only.
-      capability = .readOnly
-      break scanComponents
+      didChain = true
+      break
 
     case .optionalForce:
       // No effect.
@@ -2022,6 +2023,11 @@
                       alignment: MemoryLayout<Int>.alignment)
   }
 
+  // Chaining always renders the whole key path read-only.
+  if didChain {
+    capability = .readOnly
+  }
+
   // Grab the class object for the key path type we'll end up with.
   func openRoot<Root>(_: Root.Type) -> AnyKeyPath.Type {
     func openLeaf<Leaf>(_: Leaf.Type) -> AnyKeyPath.Type {
diff --git a/test/stdlib/KeyPath.swift b/test/stdlib/KeyPath.swift
index b1fb84d..5d5182b 100644
--- a/test/stdlib/KeyPath.swift
+++ b/test/stdlib/KeyPath.swift
@@ -666,5 +666,25 @@
   expectEqual(base[keyPath: ints_be], (17 + 38).bigEndian)
 }
 
+// SR-6096
+
+protocol Protocol6096 {}
+struct Value6096<ValueType> {}
+extension Protocol6096 {
+    var asString: String? {
+        return self as? String
+    }
+}
+extension Value6096 where ValueType: Protocol6096 {
+    func doSomething() {
+        _ = \ValueType.asString?.endIndex
+    }
+}
+extension Int: Protocol6096 {}
+
+keyPath.test("optional chaining component that needs generic instantiation") {
+  Value6096<Int>().doSomething()
+}
+
 runAllTests()