Files
swift-mirror/test/stdlib/KeyPathMultiModule.swift
Joe Groff 02e1f2ea15 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.
2024-04-25 10:36:13 -07:00

239 lines
11 KiB
Swift

// -- Test with resilience enabled
// RUN: %empty-directory(%t)
// RUN: %target-build-swift -whole-module-optimization -enable-library-evolution -module-name KeyPathMultiModule_b -c -o %t/KeyPathMultiModule_b.o -emit-module-path %t/KeyPathMultiModule_b.swiftmodule -parse-as-library %S/Inputs/KeyPathMultiModule_b.swift
// RUN: %target-build-swift -g %t/KeyPathMultiModule_b.o -I %t %s -o %t/a.out.resilient
// RUN: %target-codesign %t/a.out.resilient
// RUN: %target-run %t/a.out.resilient
// -- Test again with resilience disabled, which changes the circumstances under
// which we emit and use property descriptors
// RUN: %empty-directory(%t)
// RUN: %target-build-swift -whole-module-optimization -module-name KeyPathMultiModule_b -c -o %t/KeyPathMultiModule_b.o -emit-module-path %t/KeyPathMultiModule_b.swiftmodule -parse-as-library %S/Inputs/KeyPathMultiModule_b.swift
// RUN: %target-build-swift %t/KeyPathMultiModule_b.o -I %t %s -o %t/a.out.fragile
// RUN: %target-codesign %t/a.out.fragile
// RUN: %target-run %t/a.out.fragile
// REQUIRES: executable_test
// Freestanding stdlib is built with -experimental-hermetic-seal-at-link which doesn't allow -enable-library-evolution
// UNSUPPORTED: freestanding
import KeyPathMultiModule_b
import StdlibUnittest
var keyPathMultiModule = TestSuite("key paths across multiple modules")
func expectEqualWithHashes<T: Hashable>(_ x: T, _ y: T,
file: String = #file,
line: UInt = #line) {
expectEqual(x, y, file: file, line: line)
expectEqual(x.hashValue, y.hashValue, file: file, line: line)
}
class LocalSub: ResilientSub {
override var virtual: String {
get {
return "bas"
}
set {
}
}
final var storedD: String = "zim"
final var storedE: String = "zang"
var localSubA: String {
get { return "zung" }
}
var localSubB: String {
get { return "zipiti" }
}
}
keyPathMultiModule.test("identity across multiple modules") {
// Do this twice, to ensure that fully constant key paths remain stable
// after one-time instantiation of the object
for _ in 1...2 {
expectEqualWithHashes(A_x_keypath(), \A.x)
expectEqualWithHashes(A_y_keypath(), \A.y)
expectEqualWithHashes(A_z_keypath(), \A.z)
expectEqualWithHashes(A_w_keypath(), \A.w)
expectEqualWithHashes(A_v_keypath(), \A.v)
expectEqualWithHashes(A_immutable_keypath(), \A.immutable)
expectEqualWithHashes(A_subscript_withGeneric_keypath(index: 0), \A.[withGeneric: 0])
expectEqualWithHashes(A_subscript_withGeneric_keypath(index: "butt"),
\A.[withGeneric: "butt"])
expectEqualWithHashes(A_subscript_withGeneric_butt_keypath(),
\A.[withGeneric: "pomeranian's big butt"])
expectEqualWithHashes(A_subscript_withGenericSettable_keypath(index: 0),
\A.[withGenericSettable: 0])
expectEqualWithHashes(A_subscript_withGenericSettable_keypath(index: "butt"),
\A.[withGenericSettable: "butt"])
expectEqualWithHashes(A_subscript_withGenericPrivateSet_keypath(index: 0),
\A.[withGenericPrivateSet: 0])
expectEqualWithHashes(A_subscript_withGenericPrivateSet_keypath(index: "butt"),
\A.[withGenericPrivateSet: "butt"])
do {
let lifetimeTracker = LifetimeTracked(679)
expectEqualWithHashes(A_subscript_withGeneric_keypath(index: lifetimeTracker),
\A.[withGeneric: lifetimeTracker])
expectEqualWithHashes(A_subscript_withGenericSettable_keypath(index: lifetimeTracker),
\A.[withGenericSettable: lifetimeTracker])
expectEqualWithHashes(A_subscript_withGenericPrivateSet_keypath(index: lifetimeTracker),
\A.[withGenericPrivateSet: lifetimeTracker])
expectEqualWithHashes(\A.[withGeneric: lifetimeTracker].appendTest,
(\A.[withGeneric: lifetimeTracker])
.appending(path: \LifetimeTracked.appendTest))
expectEqualWithHashes(\A.[withGenericSettable: lifetimeTracker].appendTest,
(\A.[withGenericSettable: lifetimeTracker])
.appending(path: \LifetimeTracked.appendTest))
expectEqualWithHashes(\A.[withGenericPrivateSet: lifetimeTracker].appendTest,
(\A.[withGenericPrivateSet: lifetimeTracker])
.appending(path: \LifetimeTracked.appendTest))
}
expectEqualWithHashes(B_x_keypath(Double.self), \B<Double>.x)
expectEqualWithHashes(B_Int_x_keypath(), \B<Int>.x)
expectEqualWithHashes(B_y_keypath(Double.self), \B<Double>.y)
expectEqualWithHashes(B_Int_y_keypath(), \B<Int>.y)
expectEqualWithHashes(B_z_keypath(Double.self), \B<Double>.z)
expectEqualWithHashes(B_Int_z_keypath(), \B<Int>.z)
expectEqualWithHashes(B_subscript_withInt_keypath(Double.self, index: 1738),
\B<Double>.[withInt: 1738])
expectEqualWithHashes(B_subscript_withInt_keypath(Double.self, index: 679),
\B<Double>.[withInt: 679])
expectEqualWithHashes(B_Double_subscript_withInt_0_keypath(),
\B<Double>.[withInt: 0])
expectEqualWithHashes(B_subscript_withGeneric_keypath(Double.self, index: "buttt"),
\B<Double>.[withGeneric: "buttt"])
expectEqualWithHashes(B_Double_subscript_withGeneric_butt_keypath(),
\B<Double>.[withGeneric: "Never is the universal butt type"])
expectEqualWithHashes(A_storedA_keypath(), \A.storedA)
expectEqualWithHashes(A_storedA_storedB_keypath(), \A.storedA.storedB)
expectEqualWithHashes(A_storedB_keypath(), \A.storedB)
expectEqualWithHashes(B_storedA_keypath(Double.self), \B<Double>.storedA)
expectEqualWithHashes(B_storedB_keypath(Double.self), \B<Double>.storedB)
expectEqualWithHashes(B_Int_storedA_keypath(), \B<Int>.storedA)
expectEqualWithHashes(B_Int_storedB_keypath(), \B<Int>.storedB)
func testInGenericContext<X, Y: Hashable, Z>(x: X, y: Y,
appending: KeyPath<Y, Z>) {
expectEqualWithHashes(A_subscript_withGeneric_keypath(index: y),
\A.[withGeneric: y])
expectEqualWithHashes(A_subscript_withGenericSettable_keypath(index: y),
\A.[withGenericSettable: y])
expectEqualWithHashes(A_subscript_withGenericPrivateSet_keypath(index: y),
\A.[withGenericPrivateSet: y])
_ = (\A.[withGeneric: y]).appending(path: appending)
_ = (\A.[withGenericSettable: y]).appending(path: appending)
_ = (\A.[withGenericPrivateSet: y]).appending(path: appending)
expectEqualWithHashes(B_x_keypath(X.self), \B<X>.x)
expectEqualWithHashes(B_y_keypath(X.self), \B<X>.y)
expectEqualWithHashes(B_z_keypath(X.self), \B<X>.z)
expectEqualWithHashes(B_subscript_withInt_keypath(X.self, index: 0),
\B<X>.[withInt: 0])
expectEqualWithHashes(B_subscript_withGeneric_keypath(X.self, index: y),
\B<X>.[withGeneric: y])
expectEqualWithHashes(B_subscript_withGenericSettable_keypath(X.self, index: y),
\B<X>.[withGenericSettable: y])
expectEqualWithHashes(B_subscript_withGenericPrivateSet_keypath(X.self, index: y),
\B<X>.[withGenericPrivateSet: y])
_ = (\B<X>.[withGeneric: y]).appending(path: appending)
_ = (\B<X>.[withGenericSettable: y]).appending(path: appending)
_ = (\B<X>.[withGenericPrivateSet: y]).appending(path: appending)
expectEqualWithHashes(B_storedA_keypath(X.self), \B<X>.storedA)
expectEqualWithHashes(B_storedB_keypath(X.self), \B<X>.storedB)
}
testInGenericContext(x: 0.0, y: 42, appending: \Int.appendTest)
testInGenericContext(x: "pomeranian", y: "big butt",
appending: \String.appendTest)
testInGenericContext(x: LifetimeTracked(17), y: LifetimeTracked(38),
appending: \LifetimeTracked.appendTest)
expectEqualWithHashes(ResilientRoot_storedA_keypath(),
\ResilientRoot.storedA)
expectEqualWithHashes(ResilientRoot_storedB_keypath(),
\ResilientRoot.storedB)
expectEqualWithHashes(ResilientRoot_storedLet_keypath(),
\ResilientRoot.storedLet)
expectEqualWithHashes(ResilientRoot_virtual_keypath(),
\ResilientRoot.virtual)
expectEqualWithHashes(ResilientRoot_virtualRO_keypath(),
\ResilientRoot.virtualRO)
expectEqualWithHashes(ResilientRoot_final_keypath(),
\ResilientRoot.final)
expectEqualWithHashes(ResilientSub_storedA_keypath(),
\ResilientSub.storedA)
expectEqualWithHashes(ResilientSub_storedB_keypath(),
\ResilientSub.storedB)
expectEqualWithHashes(ResilientSub_storedC_keypath(),
\ResilientSub.storedC)
expectEqualWithHashes(ResilientSub_virtual_keypath(),
\ResilientSub.virtual)
expectEqualWithHashes(ResilientSub_virtualRO_keypath(),
\ResilientSub.virtualRO)
expectEqualWithHashes(ResilientSub_final_keypath(),
\ResilientSub.final)
expectEqualWithHashes(ResilientSub_sub_keypath(),
\ResilientSub.sub)
expectEqualWithHashes(ResilientSub_subRO_keypath(),
\ResilientSub.subRO)
// Ensure that we can instantiate key paths on a local subclass of a
// resilient class, and that they have distinct values.
let kps: [PartialKeyPath<LocalSub>] = [
\LocalSub.storedA,
\LocalSub.storedB,
\LocalSub.storedC,
\LocalSub.storedD,
\LocalSub.storedE,
\LocalSub.virtual,
\LocalSub.sub,
\LocalSub.localSubA,
\LocalSub.localSubB,
]
for i in kps.indices {
for j in kps.indices {
if i == j { continue }
expectNotEqual(kps[i], kps[j])
}
}
func testInGenericContext2<W: ResilientSubProto>(_: W.Type) {
expectEqualWithHashes(ResilientRootProto_root_keypath(W.self),
\W.root)
expectEqualWithHashes(ResilientSubProto_sub_keypath(W.self),
\W.sub)
}
testInGenericContext2(Int.self)
}
}
@inline(never) @_optimize(none)
func testGenericExternalPropertyKeyPath<A, B, C>(
a: A, b: B, c: C
) -> KeyPath<GenericExternalKeyPathTest<C>, String> {
return \GenericExternalKeyPathTest<C>.property
}
keyPathMultiModule.test("external generic property keypath accessed from different generic context") {
let kp = testGenericExternalPropertyKeyPath(a: 1, b: 1.0, c: "one")
expectEqual(GenericExternalKeyPathTest<String>()[keyPath: kp],
"\(String.self)")
}
runAllTests()