Skip to content

Commit cdc7a5c

Browse files
committed
Support application of AnyKeyPath/PartialKeyPath.
rdar://problem/32237567
1 parent 9355354 commit cdc7a5c

File tree

9 files changed

+201
-46
lines changed

9 files changed

+201
-46
lines changed

include/swift/AST/KnownDecls.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ FUNC_DECL(GetErrorEmbeddedNSError, "_stdlib_getErrorEmbeddedNSError")
7070

7171
FUNC_DECL(UnsafeBitCast, "unsafeBitCast")
7272

73+
FUNC_DECL(ProjectKeyPathAny, "_projectKeyPathAny")
74+
FUNC_DECL(ProjectKeyPathPartial, "_projectKeyPathPartial")
7375
FUNC_DECL(ProjectKeyPathReadOnly, "_projectKeyPathReadOnly")
7476
FUNC_DECL(ProjectKeyPathWritable, "_projectKeyPathWritable")
7577
FUNC_DECL(ProjectKeyPathReferenceWritable, "_projectKeyPathReferenceWritable")

include/swift/AST/KnownStdlibTypes.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ KNOWN_STDLIB_TYPE_DECL(Dictionary, NominalTypeDecl, 2)
5151
KNOWN_STDLIB_TYPE_DECL(AnyHashable, NominalTypeDecl, 0)
5252
KNOWN_STDLIB_TYPE_DECL(MutableCollection, ProtocolDecl, 1)
5353

54+
KNOWN_STDLIB_TYPE_DECL(AnyKeyPath, NominalTypeDecl, 0)
5455
KNOWN_STDLIB_TYPE_DECL(PartialKeyPath, NominalTypeDecl, 1)
5556
KNOWN_STDLIB_TYPE_DECL(KeyPath, NominalTypeDecl, 2)
5657
KNOWN_STDLIB_TYPE_DECL(WritableKeyPath, NominalTypeDecl, 2)

lib/SILGen/SILGenExpr.cpp

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2789,34 +2789,49 @@ visitKeyPathApplicationExpr(KeyPathApplicationExpr *E, SGFContext C) {
27892789
auto root = SGF.emitMaterializedRValueAsOrig(E->getBase(),
27902790
AbstractionPattern::getOpaque());
27912791
auto keyPath = SGF.emitRValueAsSingleValue(E->getKeyPath());
2792-
2793-
// Get the root and leaf type from the key path type.
2794-
auto keyPathTy = E->getKeyPath()->getType()->castTo<BoundGenericType>();
27952792

2796-
// Upcast the keypath to KeyPath<T, U> if it isn't already.
2797-
if (keyPathTy->getDecl() != SGF.getASTContext().getKeyPathDecl()) {
2798-
auto castToTy = BoundGenericType::get(SGF.getASTContext().getKeyPathDecl(),
2793+
auto keyPathDecl = E->getKeyPath()->getType()->getAnyNominal();
2794+
FuncDecl *projectFn;
2795+
SmallVector<Substitution, 4> subs;
2796+
2797+
if (keyPathDecl == SGF.getASTContext().getAnyKeyPathDecl()) {
2798+
// Invoke projectKeyPathAny with the type of the base value.
2799+
// The result is always `Any?`.
2800+
projectFn = SGF.getASTContext().getProjectKeyPathAny(nullptr);
2801+
subs.push_back(Substitution(E->getBase()->getType(), {}));
2802+
} else {
2803+
auto keyPathTy = E->getKeyPath()->getType()->castTo<BoundGenericType>();
2804+
if (keyPathDecl == SGF.getASTContext().getPartialKeyPathDecl()) {
2805+
// Invoke projectKeyPathPartial with the type of the base value.
2806+
// The result is always `Any`.
2807+
projectFn = SGF.getASTContext().getProjectKeyPathPartial(nullptr);
2808+
subs.push_back(Substitution(keyPathTy->getGenericArgs()[0], {}));
2809+
} else {
2810+
projectFn = SGF.getASTContext().getProjectKeyPathReadOnly(nullptr);
2811+
// Get the root and leaf type from the key path type.
2812+
subs.push_back(Substitution(keyPathTy->getGenericArgs()[0], {}));
2813+
subs.push_back(Substitution(keyPathTy->getGenericArgs()[1], {}));
2814+
2815+
// Upcast the keypath to KeyPath<T, U> if it isn't already.
2816+
if (keyPathTy->getDecl() != SGF.getASTContext().getKeyPathDecl()) {
2817+
auto castToTy = BoundGenericType::get(
2818+
SGF.getASTContext().getKeyPathDecl(),
27992819
nullptr,
28002820
keyPathTy->getGenericArgs())
2801-
->getCanonicalType();
2802-
auto upcast = SGF.B.createUpcast(SILLocation(E),
2821+
->getCanonicalType();
2822+
auto upcast = SGF.B.createUpcast(SILLocation(E),
28032823
keyPath.forward(SGF),
28042824
SILType::getPrimitiveObjectType(castToTy));
2805-
keyPath = SGF.emitManagedRValueWithCleanup(upcast);
2825+
keyPath = SGF.emitManagedRValueWithCleanup(upcast);
2826+
}
2827+
}
28062828
}
28072829

2808-
auto projectFn = SGF.getASTContext().getProjectKeyPathReadOnly(nullptr);
2809-
Substitution genericArgs[] = {
2810-
Substitution(keyPathTy->getGenericArgs()[0], {}),
2811-
Substitution(keyPathTy->getGenericArgs()[1], {}),
2812-
};
2813-
28142830
auto genericArgsMap =
2815-
projectFn->getGenericSignature()->getSubstitutionMap(genericArgs);
2831+
projectFn->getGenericSignature()->getSubstitutionMap(subs);
28162832

28172833
return SGF.emitApplyOfLibraryIntrinsic(SILLocation(E),
2818-
SGF.getASTContext().getProjectKeyPathReadOnly(nullptr),
2819-
genericArgsMap, {root, keyPath}, C);
2834+
projectFn, genericArgsMap, {root, keyPath}, C);
28202835
}
28212836

28222837
RValue RValueEmitter::

lib/Sema/CSApply.cpp

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1306,24 +1306,53 @@ namespace {
13061306
// Apply a key path if we have one.
13071307
if (choice.getKind() == OverloadChoiceKind::KeyPathApplication) {
13081308
// The index argument should be (keyPath: KeyPath<Root, Value>).
1309-
auto keyPathTy = index->getType()->castTo<TupleType>()
1310-
->getElementType(0)->castTo<BoundGenericType>();
1311-
auto valueTy = keyPathTy->getGenericArgs()[1];
1309+
auto keyPathTTy = index->getType()->castTo<TupleType>()
1310+
->getElementType(0);
13121311

1313-
// The result may be an lvalue based on the base and key path kind.
1312+
Type valueTy;
13141313
bool resultIsLValue;
1315-
if (keyPathTy->getDecl() == cs.getASTContext().getKeyPathDecl()) {
1316-
resultIsLValue = false;
1317-
base = cs.coerceToRValue(base);
1318-
} else if (keyPathTy->getDecl() ==
1319-
cs.getASTContext().getWritableKeyPathDecl()) {
1320-
resultIsLValue = base->getType()->isLValueType();
1321-
} else if (keyPathTy->getDecl() ==
1322-
cs.getASTContext().getReferenceWritableKeyPathDecl()) {
1323-
resultIsLValue = true;
1324-
base = cs.coerceToRValue(base);
1314+
1315+
if (auto nom = keyPathTTy->getAs<NominalType>()) {
1316+
// AnyKeyPath is <T> rvalue T -> rvalue Any?
1317+
if (nom->getDecl() == cs.getASTContext().getAnyKeyPathDecl()) {
1318+
valueTy = ProtocolCompositionType::get(cs.getASTContext(), {},
1319+
/*explicit anyobject*/ false);
1320+
valueTy = OptionalType::get(valueTy);
1321+
resultIsLValue = false;
1322+
base = cs.coerceToRValue(base);
1323+
} else {
1324+
llvm_unreachable("unknown key path class!");
1325+
}
13251326
} else {
1326-
llvm_unreachable("unknown key path class!");
1327+
auto keyPathBGT = keyPathTTy->castTo<BoundGenericType>();
1328+
1329+
if (keyPathBGT->getDecl()
1330+
== cs.getASTContext().getPartialKeyPathDecl()) {
1331+
// PartialKeyPath<T> is rvalue T -> rvalue Any
1332+
valueTy = ProtocolCompositionType::get(cs.getASTContext(), {},
1333+
/*explicit anyobject*/ false);
1334+
resultIsLValue = false;
1335+
base = cs.coerceToRValue(base);
1336+
} else {
1337+
// *KeyPath<T, U> is T -> U, with rvalueness based on mutability
1338+
// of base and keypath
1339+
valueTy = keyPathBGT->getGenericArgs()[1];
1340+
1341+
// The result may be an lvalue based on the base and key path kind.
1342+
if (keyPathBGT->getDecl() == cs.getASTContext().getKeyPathDecl()) {
1343+
resultIsLValue = false;
1344+
base = cs.coerceToRValue(base);
1345+
} else if (keyPathBGT->getDecl() ==
1346+
cs.getASTContext().getWritableKeyPathDecl()) {
1347+
resultIsLValue = base->getType()->isLValueType();
1348+
} else if (keyPathBGT->getDecl() ==
1349+
cs.getASTContext().getReferenceWritableKeyPathDecl()) {
1350+
resultIsLValue = true;
1351+
base = cs.coerceToRValue(base);
1352+
} else {
1353+
llvm_unreachable("unknown key path class!");
1354+
}
1355+
}
13271356
}
13281357
if (resultIsLValue)
13291358
valueTy = LValueType::get(valueTy);

lib/Sema/CSSimplify.cpp

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3936,13 +3936,21 @@ ConstraintSystem::simplifyKeyPathApplicationConstraint(
39363936
return SolutionKind::Unsolved;
39373937
};
39383938

3939+
if (auto clas = keyPathTy->getAs<NominalType>()) {
3940+
if (clas->getDecl() == getASTContext().getAnyKeyPathDecl()) {
3941+
// Read-only keypath, whose projected value is upcast to `Any?`.
3942+
// The root type can be anything.
3943+
Type resultTy = ProtocolCompositionType::get(getASTContext(), {},
3944+
/*explicit AnyObject*/ false);
3945+
resultTy = OptionalType::get(resultTy);
3946+
return matchTypes(resultTy, valueTy, ConstraintKind::Bind,
3947+
subflags, locator);
3948+
}
3949+
}
3950+
39393951
if (auto bgt = keyPathTy->getAs<BoundGenericType>()) {
3940-
if (bgt->getGenericArgs().size() < 2)
3941-
return SolutionKind::Error;
3942-
39433952
// We have the key path type. Match it to the other ends of the constraint.
39443953
auto kpRootTy = bgt->getGenericArgs()[0];
3945-
auto kpValueTy = bgt->getGenericArgs()[1];
39463954

39473955
// Try to match the root type.
39483956
rootTy = getFixedTypeRecursive(rootTy, flags, /*wantRValue=*/false);
@@ -3957,7 +3965,19 @@ ConstraintSystem::simplifyKeyPathApplicationConstraint(
39573965
case SolutionKind::Unsolved:
39583966
llvm_unreachable("should have generated constraints");
39593967
}
3960-
3968+
3969+
if (bgt->getDecl() == getASTContext().getPartialKeyPathDecl()) {
3970+
// Read-only keypath, whose projected value is upcast to `Any`.
3971+
auto resultTy = ProtocolCompositionType::get(getASTContext(), {},
3972+
/*explicit AnyObject*/ false);
3973+
return matchTypes(resultTy, valueTy,
3974+
ConstraintKind::Bind, subflags, locator);
3975+
}
3976+
3977+
if (bgt->getGenericArgs().size() < 2)
3978+
return SolutionKind::Error;
3979+
auto kpValueTy = bgt->getGenericArgs()[1];
3980+
39613981
/// Solve for an rvalue base.
39623982
auto solveRValue = [&]() -> ConstraintSystem::SolutionKind {
39633983
return matchTypes(kpValueTy, valueTy,
@@ -3976,6 +3996,7 @@ ConstraintSystem::simplifyKeyPathApplicationConstraint(
39763996
return matchTypes(LValueType::get(kpValueTy), valueTy,
39773997
ConstraintKind::Bind, subflags, locator);
39783998
};
3999+
39794000

39804001
if (bgt->getDecl() == getASTContext().getKeyPathDecl()) {
39814002
// Read-only keypath.

stdlib/public/core/KeyPath.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,6 +1132,39 @@ internal struct KeyPathBuffer {
11321132

11331133
// MARK: Library intrinsics for projecting key paths.
11341134

1135+
@_inlineable
1136+
public // COMPILER_INTRINSIC
1137+
func _projectKeyPathPartial<Root>(
1138+
root: Root,
1139+
keyPath: PartialKeyPath<Root>
1140+
) -> Any {
1141+
func open<Value>(_: Value.Type) -> Any {
1142+
return _projectKeyPathReadOnly(root: root,
1143+
keyPath: unsafeDowncast(keyPath, to: KeyPath<Root, Value>.self))
1144+
}
1145+
return _openExistential(type(of: keyPath).valueType, do: open)
1146+
}
1147+
1148+
@_inlineable
1149+
public // COMPILER_INTRINSIC
1150+
func _projectKeyPathAny<RootValue>(
1151+
root: RootValue,
1152+
keyPath: AnyKeyPath
1153+
) -> Any? {
1154+
let (keyPathRoot, keyPathValue) = type(of: keyPath)._rootAndValueType
1155+
func openRoot<KeyPathRoot>(_: KeyPathRoot.Type) -> Any? {
1156+
guard let rootForKeyPath = root as? KeyPathRoot else {
1157+
return nil
1158+
}
1159+
func openValue<Value>(_: Value.Type) -> Any {
1160+
return _projectKeyPathReadOnly(root: rootForKeyPath,
1161+
keyPath: unsafeDowncast(keyPath, to: KeyPath<KeyPathRoot, Value>.self))
1162+
}
1163+
return _openExistential(keyPathValue, do: openValue)
1164+
}
1165+
return _openExistential(keyPathRoot, do: openRoot)
1166+
}
1167+
11351168
public // COMPILER_INTRINSIC
11361169
func _projectKeyPathReadOnly<Root, Value>(
11371170
root: Root,

test/SILGen/keypath_application.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,3 +220,24 @@ func reabstracted(readonly: @escaping () -> (),
220220
readonly[keyPath: rkp] = value
221221
writable[keyPath: rkp] = value
222222
}
223+
224+
// CHECK-LABEL: sil hidden @{{.*}}partial
225+
func partial<A>(valueA: A,
226+
valueB: Int,
227+
pkpA: PartialKeyPath<A>,
228+
pkpB: PartialKeyPath<Int>,
229+
akp: AnyKeyPath) {
230+
// CHECK: [[PROJECT:%.*]] = function_ref @{{.*}}projectKeyPathAny
231+
// CHECK: apply [[PROJECT]]<A>
232+
_ = valueA[keyPath: akp]
233+
// CHECK: [[PROJECT:%.*]] = function_ref @{{.*}}projectKeyPathPartial
234+
// CHECK: apply [[PROJECT]]<A>
235+
_ = valueA[keyPath: pkpA]
236+
237+
// CHECK: [[PROJECT:%.*]] = function_ref @{{.*}}projectKeyPathAny
238+
// CHECK: apply [[PROJECT]]<Int>
239+
_ = valueB[keyPath: akp]
240+
// CHECK: [[PROJECT:%.*]] = function_ref @{{.*}}projectKeyPathPartial
241+
// CHECK: apply [[PROJECT]]<Int>
242+
_ = valueB[keyPath: pkpB]
243+
}

test/expr/unary/keypath/keypath.swift

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -209,18 +209,15 @@ func testKeyPathSubscript(readonly: Z, writable: inout Z,
209209
readonly[keyPath: rkp] = sink
210210
writable[keyPath: rkp] = sink
211211

212-
// TODO: PartialKeyPath and AnyKeyPath application
213-
214-
/*
215212
let pkp: PartialKeyPath = rkp
216213

217214
var anySink1 = readonly[keyPath: pkp]
218215
expect(&anySink1, toHaveType: Exactly<Any>.self)
219216
var anySink2 = writable[keyPath: pkp]
220217
expect(&anySink2, toHaveType: Exactly<Any>.self)
221218

222-
readonly[keyPath: pkp] = anySink1 // e/xpected-error{{cannot assign to immutable}}
223-
writable[keyPath: pkp] = anySink2 // e/xpected-error{{cannot assign to immutable}}
219+
readonly[keyPath: pkp] = anySink1 // expected-error{{cannot assign to immutable}}
220+
writable[keyPath: pkp] = anySink2 // expected-error{{cannot assign to immutable}}
224221

225222
let akp: AnyKeyPath = pkp
226223

@@ -229,9 +226,8 @@ func testKeyPathSubscript(readonly: Z, writable: inout Z,
229226
var anyqSink2 = writable[keyPath: akp]
230227
expect(&anyqSink2, toHaveType: Exactly<Any?>.self)
231228

232-
readonly[keyPath: akp] = anyqSink1 // e/xpected-error{{cannot assign to immutable}}
233-
writable[keyPath: akp] = anyqSink2 // e/xpected-error{{cannot assign to immutable}}
234-
*/
229+
readonly[keyPath: akp] = anyqSink1 // expected-error{{cannot assign to immutable}}
230+
writable[keyPath: akp] = anyqSink2 // expected-error{{cannot assign to immutable}}
235231
}
236232

237233
func testKeyPathSubscriptMetatype(readonly: Z.Type, writable: inout Z.Type,

test/stdlib/KeyPath.swift

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,4 +277,41 @@ keyPath.test("computed properties") {
277277
}
278278
}
279279

280+
class AB {
281+
}
282+
class ABC: AB {
283+
var a = LifetimeTracked(1)
284+
var b = LifetimeTracked(2)
285+
var c = LifetimeTracked(3)
286+
}
287+
288+
keyPath.test("dynamically-typed application") {
289+
let cPaths = [\ABC.a, \ABC.b, \ABC.c]
290+
291+
let subject = ABC()
292+
293+
do {
294+
let fields = cPaths.map { subject[keyPath: $0] }
295+
expectTrue(fields[0] as! AnyObject === subject.a)
296+
expectTrue(fields[1] as! AnyObject === subject.b)
297+
expectTrue(fields[2] as! AnyObject === subject.c)
298+
}
299+
300+
let erasedSubject: AB = subject
301+
let erasedPaths: [AnyKeyPath] = cPaths
302+
let wrongSubject = AB()
303+
304+
do {
305+
let fields = erasedPaths.map { erasedSubject[keyPath: $0] }
306+
expectTrue(fields[0]! as! AnyObject === subject.a)
307+
expectTrue(fields[1]! as! AnyObject === subject.b)
308+
expectTrue(fields[2]! as! AnyObject === subject.c)
309+
310+
let wrongFields = erasedPaths.map { wrongSubject[keyPath: $0] }
311+
expectTrue(wrongFields[0] == nil)
312+
expectTrue(wrongFields[1] == nil)
313+
expectTrue(wrongFields[2] == nil)
314+
}
315+
}
316+
280317
runAllTests()

0 commit comments

Comments
 (0)