Skip to content

Commit 02e1f2e

Browse files
committed
IRGen: Fix key path generic environment marshalling for external property descriptors.
The layout of a computed key path component carries an argument buffer for captures, which has the following layout: ``` --- captured values (subscript indices) --- generic arguments --- ``` When we reference an externally-defined public property or subscript from a key path, and the external declaration has a property descriptor, then the generic arguments for the external declaration get appended to the end of this buffer, giving: ``` --- captured values (subscript indices) --- generic arguments --- external property's generic arguments --- ``` The convention for key path accessors to bind their generic environment is thus to unpack them from the end of the argument buffer, so that the external keypath's accessors can find the arguments to bind the external generic environment while still allowing the enclosing key path to save the original captured generic environment (which may be necessary to capture the appropriate conditional and/or retroactive `Equatable` and `Hashable` conformances for subscript indices). However, our code generation for binding the generic arguments out of the argument buffer contained a flawed optimization: for a property, we know there are never any captured values, so I had assumed that the generic parameters could always be bound from the beginning of the argument buffer, assuming that the generic parameters make up the totality of the buffer. This falls over for external property descriptor references when the key path itself captures a generic environment, since the external property's expected generic environment appears after the key path's original generic environment. We can fix this by removing the conditional entirely, and always adjusting the offset we load the generic environment from to look at the end of the buffer. Fixes rdar://125886333.
1 parent cb5f1e2 commit 02e1f2e

File tree

4 files changed

+32
-8
lines changed

4 files changed

+32
-8
lines changed

lib/IRGen/GenKeyPath.cpp

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -83,14 +83,12 @@ irgen::bindPolymorphicArgumentsFromComponentIndices(IRGenFunction &IGF,
8383
// The generic environment is marshaled into the end of the component
8484
// argument area inside the instance. Bind the generic information out of
8585
// the buffer.
86-
if (hasSubscriptIndices) {
87-
auto genericArgsSize = llvm::ConstantInt::get(IGF.IGM.SizeTy,
88-
requirements.size() * IGF.IGM.getPointerSize().getValue());
86+
auto genericArgsSize = llvm::ConstantInt::get(IGF.IGM.SizeTy,
87+
requirements.size() * IGF.IGM.getPointerSize().getValue());
8988

90-
auto genericArgsOffset = IGF.Builder.CreateSub(size, genericArgsSize);
91-
args =
92-
IGF.Builder.CreateInBoundsGEP(IGF.IGM.Int8Ty, args, genericArgsOffset);
93-
}
89+
auto genericArgsOffset = IGF.Builder.CreateSub(size, genericArgsSize);
90+
args =
91+
IGF.Builder.CreateInBoundsGEP(IGF.IGM.Int8Ty, args, genericArgsOffset);
9492

9593
bindFromGenericRequirementsBuffer(
9694
IGF, requirements,

stdlib/public/core/KeyPath.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2787,7 +2787,6 @@ internal func _getTypeByMangledNameInEnvironmentOrContext(
27872787
genericEnvironmentOrContext: UnsafeRawPointer?,
27882788
genericArguments: UnsafeRawPointer?)
27892789
-> Any.Type? {
2790-
27912790
let taggedPointer = UInt(bitPattern: genericEnvironmentOrContext)
27922791
if taggedPointer & 1 == 0 {
27932792
return _getTypeByMangledNameInEnvironment(name, nameLength,

test/stdlib/Inputs/KeyPathMultiModule_b.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
import StdlibUnittest
22

3+
// rdar://125886333
4+
public struct GenericExternalKeyPathTest<E> {
5+
public private(set) var property: String {
6+
get {
7+
return "\(E.self)"
8+
}
9+
set {
10+
}
11+
}
12+
13+
public init() {}
14+
}
15+
316
public struct A {
417
public var x: Int { return 0 }
518

test/stdlib/KeyPathMultiModule.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,4 +221,18 @@ keyPathMultiModule.test("identity across multiple modules") {
221221
}
222222
}
223223

224+
@inline(never) @_optimize(none)
225+
func testGenericExternalPropertyKeyPath<A, B, C>(
226+
a: A, b: B, c: C
227+
) -> KeyPath<GenericExternalKeyPathTest<C>, String> {
228+
return \GenericExternalKeyPathTest<C>.property
229+
}
230+
231+
keyPathMultiModule.test("external generic property keypath accessed from different generic context") {
232+
let kp = testGenericExternalPropertyKeyPath(a: 1, b: 1.0, c: "one")
233+
234+
expectEqual(GenericExternalKeyPathTest<String>()[keyPath: kp],
235+
"\(String.self)")
236+
}
237+
224238
runAllTests()

0 commit comments

Comments
 (0)