Skip to content

Commit 93b5de6

Browse files
committed
Implement the final approved syntax for SE-227 identity key paths.
`\.self` is the final chosen syntax. Implement support for this syntax, and remove the stopgap builtin and `WritableKeyPath._identity` property that were in place before.
1 parent 86e11c5 commit 93b5de6

21 files changed

+115
-110
lines changed

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,20 @@ CHANGELOG
2424
Swift 5.0
2525
---------
2626

27+
* [SE-0227][]:
28+
29+
Key paths now support the `\.self` keypath, which is a `WritableKeyPath`
30+
that refers to its entire input value:
31+
32+
```swift
33+
let id = \Int.self
34+
35+
var x = 2
36+
print(x[keyPath: id]) // prints 2
37+
x[keyPath: id] = 3
38+
print(x[keyPath: id]) // prints 3
39+
```
40+
2741
* [SE-0214][]:
2842

2943
Renamed the `DictionaryLiteral` type to `KeyValuePairs`.

include/swift/AST/Builtins.def

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -373,11 +373,6 @@ BUILTIN_SIL_OPERATION(AllocWithTailElems, "allocWithTailElems", Special)
373373
/// Projects the first tail-allocated element of type E from a class C.
374374
BUILTIN_SIL_OPERATION(ProjectTailElems, "projectTailElems", Special)
375375

376-
/// identityKeyPath : <T> () -> WritableKeyPath<T, T>
377-
///
378-
/// Creates an identity key path object. (TODO: replace with proper syntax)
379-
BUILTIN_SIL_OPERATION(IdentityKeyPath, "identityKeyPath", Special)
380-
381376
#undef BUILTIN_SIL_OPERATION
382377

383378
// BUILTIN_RUNTIME_CALL - A call into a runtime function.

include/swift/AST/Expr.h

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4773,7 +4773,8 @@ class KeyPathExpr : public Expr {
47734773
Subscript,
47744774
OptionalForce,
47754775
OptionalChain,
4776-
OptionalWrap
4776+
OptionalWrap,
4777+
Identity,
47774778
};
47784779

47794780
private:
@@ -4787,9 +4788,11 @@ class KeyPathExpr : public Expr {
47874788
} Decl;
47884789

47894790

4790-
llvm::PointerIntPair<Expr *, 3, Kind> SubscriptIndexExprAndKind;
4791-
ArrayRef<Identifier> SubscriptLabels;
4792-
ArrayRef<ProtocolConformanceRef> SubscriptHashableConformances;
4791+
Expr *SubscriptIndexExpr;
4792+
const Identifier *SubscriptLabelsData;
4793+
const ProtocolConformanceRef *SubscriptHashableConformancesData;
4794+
unsigned SubscriptSize;
4795+
Kind KindValue;
47934796
Type ComponentType;
47944797
SourceLoc Loc;
47954798

@@ -4914,12 +4917,18 @@ class KeyPathExpr : public Expr {
49144917
SourceLoc());
49154918
}
49164919

4920+
static Component forIdentity(SourceLoc selfLoc) {
4921+
return Component(nullptr, {}, nullptr, {}, {},
4922+
Kind::Identity, Type(),
4923+
selfLoc);
4924+
}
4925+
49174926
SourceLoc getLoc() const {
49184927
return Loc;
49194928
}
49204929

49214930
Kind getKind() const {
4922-
return SubscriptIndexExprAndKind.getInt();
4931+
return KindValue;
49234932
}
49244933

49254934
bool isValid() const {
@@ -4936,6 +4945,7 @@ class KeyPathExpr : public Expr {
49364945
case Kind::OptionalWrap:
49374946
case Kind::OptionalForce:
49384947
case Kind::Property:
4948+
case Kind::Identity:
49394949
return true;
49404950

49414951
case Kind::UnresolvedSubscript:
@@ -4950,14 +4960,15 @@ class KeyPathExpr : public Expr {
49504960
switch (getKind()) {
49514961
case Kind::Subscript:
49524962
case Kind::UnresolvedSubscript:
4953-
return SubscriptIndexExprAndKind.getPointer();
4963+
return SubscriptIndexExpr;
49544964

49554965
case Kind::Invalid:
49564966
case Kind::OptionalChain:
49574967
case Kind::OptionalWrap:
49584968
case Kind::OptionalForce:
49594969
case Kind::UnresolvedProperty:
49604970
case Kind::Property:
4971+
case Kind::Identity:
49614972
return nullptr;
49624973
}
49634974
llvm_unreachable("unhandled kind");
@@ -4967,14 +4978,15 @@ class KeyPathExpr : public Expr {
49674978
switch (getKind()) {
49684979
case Kind::Subscript:
49694980
case Kind::UnresolvedSubscript:
4970-
return SubscriptLabels;
4981+
return {SubscriptLabelsData, (size_t)SubscriptSize};
49714982

49724983
case Kind::Invalid:
49734984
case Kind::OptionalChain:
49744985
case Kind::OptionalWrap:
49754986
case Kind::OptionalForce:
49764987
case Kind::UnresolvedProperty:
49774988
case Kind::Property:
4989+
case Kind::Identity:
49784990
llvm_unreachable("no subscript labels for this kind");
49794991
}
49804992
llvm_unreachable("unhandled kind");
@@ -4984,7 +4996,9 @@ class KeyPathExpr : public Expr {
49844996
getSubscriptIndexHashableConformances() const {
49854997
switch (getKind()) {
49864998
case Kind::Subscript:
4987-
return SubscriptHashableConformances;
4999+
if (!SubscriptHashableConformancesData)
5000+
return {};
5001+
return {SubscriptHashableConformancesData, (size_t)SubscriptSize};
49885002

49895003
case Kind::UnresolvedSubscript:
49905004
case Kind::Invalid:
@@ -4993,6 +5007,7 @@ class KeyPathExpr : public Expr {
49935007
case Kind::OptionalForce:
49945008
case Kind::UnresolvedProperty:
49955009
case Kind::Property:
5010+
case Kind::Identity:
49965011
return {};
49975012
}
49985013
llvm_unreachable("unhandled kind");
@@ -5013,6 +5028,7 @@ class KeyPathExpr : public Expr {
50135028
case Kind::OptionalWrap:
50145029
case Kind::OptionalForce:
50155030
case Kind::Property:
5031+
case Kind::Identity:
50165032
llvm_unreachable("no unresolved name for this kind");
50175033
}
50185034
llvm_unreachable("unhandled kind");
@@ -5030,6 +5046,7 @@ class KeyPathExpr : public Expr {
50305046
case Kind::OptionalChain:
50315047
case Kind::OptionalWrap:
50325048
case Kind::OptionalForce:
5049+
case Kind::Identity:
50335050
llvm_unreachable("no decl ref for this kind");
50345051
}
50355052
llvm_unreachable("unhandled kind");

lib/AST/ASTDumper.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2597,6 +2597,10 @@ class PrintExpr : public ExprVisitor<PrintExpr> {
25972597
component.getIndexExpr()->print(OS, Indent + 4);
25982598
OS.indent(Indent + 4);
25992599
break;
2600+
case KeyPathExpr::Component::Kind::Identity:
2601+
OS << "identity";
2602+
OS << '\n';
2603+
break;
26002604
}
26012605
OS << "type=";
26022606
component.getComponentType().print(OS);

lib/AST/ASTWalker.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,6 +1034,7 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
10341034
case KeyPathExpr::Component::Kind::Property:
10351035
case KeyPathExpr::Component::Kind::UnresolvedProperty:
10361036
case KeyPathExpr::Component::Kind::Invalid:
1037+
case KeyPathExpr::Component::Kind::Identity:
10371038
// No subexpr to visit.
10381039
break;
10391040
}

lib/AST/Builtins.cpp

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -991,15 +991,6 @@ static ValueDecl *getTypeJoinMetaOperation(ASTContext &Context, Identifier Id) {
991991
return builder.build(Id);
992992
}
993993

994-
static ValueDecl *getIdentityKeyPathOperation(ASTContext &Context,
995-
Identifier Id) {
996-
BuiltinGenericSignatureBuilder builder(Context, 1);
997-
auto arg = makeGenericParam();
998-
builder.setResult(makeBoundGenericType(Context.getWritableKeyPathDecl(),
999-
arg, arg));
1000-
return builder.build(Id);
1001-
}
1002-
1003994
static ValueDecl *getCanBeObjCClassOperation(ASTContext &Context,
1004995
Identifier Id) {
1005996
// <T> T.Type -> Builtin.Int8
@@ -1879,10 +1870,7 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) {
18791870
return getTypeJoinInoutOperation(Context, Id);
18801871

18811872
case BuiltinValueKind::TypeJoinMeta:
1882-
return getTypeJoinMetaOperation(Context, Id);
1883-
1884-
case BuiltinValueKind::IdentityKeyPath:
1885-
return getIdentityKeyPathOperation(Context, Id);
1873+
return getTypeJoinMetaOperation(Context, Id);
18861874
}
18871875

18881876
llvm_unreachable("bad builtin value!");

lib/AST/Expr.cpp

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2135,13 +2135,16 @@ KeyPathExpr::Component::Component(ASTContext *ctxForCopyingLabels,
21352135
Kind kind,
21362136
Type type,
21372137
SourceLoc loc)
2138-
: Decl(decl), SubscriptIndexExprAndKind(indexExpr, kind),
2139-
SubscriptLabels(subscriptLabels.empty()
2140-
? subscriptLabels
2141-
: ctxForCopyingLabels->AllocateCopy(subscriptLabels)),
2142-
SubscriptHashableConformances(indexHashables),
2138+
: Decl(decl), SubscriptIndexExpr(indexExpr), KindValue(kind),
21432139
ComponentType(type), Loc(loc)
2144-
{}
2140+
{
2141+
assert(subscriptLabels.size() == indexHashables.size()
2142+
|| indexHashables.empty());
2143+
SubscriptLabelsData = subscriptLabels.data();
2144+
SubscriptHashableConformancesData = indexHashables.empty()
2145+
? nullptr : indexHashables.data();
2146+
SubscriptSize = subscriptLabels.size();
2147+
}
21452148

21462149
KeyPathExpr::Component
21472150
KeyPathExpr::Component::forSubscriptWithPrebuiltIndexExpr(
@@ -2157,8 +2160,10 @@ void KeyPathExpr::Component::setSubscriptIndexHashableConformances(
21572160
ArrayRef<ProtocolConformanceRef> hashables) {
21582161
switch (getKind()) {
21592162
case Kind::Subscript:
2160-
SubscriptHashableConformances = getComponentType()->getASTContext()
2161-
.AllocateCopy(hashables);
2163+
assert(hashables.size() == SubscriptSize);
2164+
SubscriptHashableConformancesData = getComponentType()->getASTContext()
2165+
.AllocateCopy(hashables)
2166+
.data();
21622167
return;
21632168

21642169
case Kind::UnresolvedSubscript:
@@ -2168,6 +2173,7 @@ void KeyPathExpr::Component::setSubscriptIndexHashableConformances(
21682173
case Kind::OptionalForce:
21692174
case Kind::UnresolvedProperty:
21702175
case Kind::Property:
2176+
case Kind::Identity:
21712177
llvm_unreachable("no hashable conformances for this kind");
21722178
}
21732179
}

lib/IDE/SourceEntityWalker.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,7 @@ std::pair<bool, Expr *> SemaAnnotator::walkToExprPre(Expr *E) {
367367
case KeyPathExpr::Component::Kind::OptionalChain:
368368
case KeyPathExpr::Component::Kind::OptionalWrap:
369369
case KeyPathExpr::Component::Kind::OptionalForce:
370+
case KeyPathExpr::Component::Kind::Identity:
370371
break;
371372
}
372373
}

lib/SILGen/SILGenBuiltin.cpp

Lines changed: 0 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -970,58 +970,6 @@ static ManagedValue emitBuiltinProjectTailElems(SILGenFunction &SGF,
970970
return ManagedValue::forUnmanaged(result);
971971
}
972972

973-
static ManagedValue emitBuiltinIdentityKeyPath(SILGenFunction &SGF,
974-
SILLocation loc,
975-
SubstitutionMap subs,
976-
ArrayRef<ManagedValue> args,
977-
SGFContext C) {
978-
assert(subs.getReplacementTypes().size() == 1 &&
979-
"identityKeyPath should have one substitution");
980-
assert(args.size() == 0 &&
981-
"identityKeyPath should have no args");
982-
983-
auto identityTy = subs.getReplacementTypes()[0]->getCanonicalType();
984-
985-
// The `self` key can be used for identity in Cocoa KVC as well.
986-
StringRef objcString = SGF.getASTContext().LangOpts.EnableObjCInterop
987-
? "self" : "";
988-
989-
// The key path pattern has to capture some generic context if the type is
990-
// dependent on this generic context. We only need the specific type, though,
991-
// not the entire generic environment.
992-
bool isDependent = identityTy->hasArchetype();
993-
CanType identityPatternTy = identityTy;
994-
CanGenericSignature patternSig = nullptr;
995-
SubstitutionMap patternSubs;
996-
if (isDependent) {
997-
auto param = GenericTypeParamType::get(0, 0, SGF.getASTContext());
998-
identityPatternTy = param->getCanonicalType();
999-
patternSig = GenericSignature::get(param, {})->getCanonicalSignature();
1000-
patternSubs = SubstitutionMap::get(patternSig,
1001-
llvm::makeArrayRef((Type)identityTy),
1002-
{});
1003-
}
1004-
1005-
auto identityPattern = KeyPathPattern::get(SGF.SGM.M,
1006-
patternSig,
1007-
identityPatternTy,
1008-
identityPatternTy,
1009-
{},
1010-
objcString);
1011-
1012-
auto kpTy = BoundGenericType::get(SGF.getASTContext().getWritableKeyPathDecl(),
1013-
Type(),
1014-
{identityTy, identityTy})
1015-
->getCanonicalType();
1016-
1017-
auto keyPath = SGF.B.createKeyPath(loc, identityPattern,
1018-
patternSubs,
1019-
{},
1020-
SILType::getPrimitiveObjectType(kpTy));
1021-
return SGF.emitManagedRValueWithCleanup(keyPath);
1022-
}
1023-
1024-
1025973
/// Specialized emitter for type traits.
1026974
template<TypeTraitResult (TypeBase::*Trait)(),
1027975
BuiltinValueKind Kind>

lib/SILGen/SILGenExpr.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3849,6 +3849,9 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) {
38493849
KeyPathPatternComponent::forOptional(loweredKind, baseTy));
38503850
break;
38513851
}
3852+
3853+
case KeyPathExpr::Component::Kind::Identity:
3854+
continue;
38523855

38533856
case KeyPathExpr::Component::Kind::Invalid:
38543857
case KeyPathExpr::Component::Kind::UnresolvedProperty:

lib/Sema/CSApply.cpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,11 @@ static bool buildObjCKeyPathString(KeyPathExpr *E,
276276
case KeyPathExpr::Component::Kind::OptionalWrap:
277277
// KVC propagates nulls, so these don't affect the key path string.
278278
continue;
279-
279+
case KeyPathExpr::Component::Kind::Identity:
280+
// The identity component can be elided from the KVC string (unless it's
281+
// the only component, in which case we use @"self").
282+
continue;
283+
280284
case KeyPathExpr::Component::Kind::Property: {
281285
// Property references must be to @objc properties.
282286
// TODO: If we added special properties matching KVC operators like '@sum',
@@ -307,6 +311,12 @@ static bool buildObjCKeyPathString(KeyPathExpr *E,
307311
}
308312
}
309313

314+
// If there are no non-identity components, this is the "self" key.
315+
if (buf.empty()) {
316+
auto self = StringRef("self");
317+
buf.append(self.begin(), self.end());
318+
}
319+
310320
return true;
311321
}
312322

@@ -4514,7 +4524,11 @@ namespace {
45144524
baseTy = component.getComponentType();
45154525
resolvedComponents.push_back(component);
45164526
break;
4517-
4527+
case KeyPathExpr::Component::Kind::Identity:
4528+
component = origComponent;
4529+
component.setComponentType(baseTy);
4530+
resolvedComponents.push_back(component);
4531+
break;
45184532
case KeyPathExpr::Component::Kind::Property:
45194533
case KeyPathExpr::Component::Kind::Subscript:
45204534
case KeyPathExpr::Component::Kind::OptionalWrap:

lib/Sema/CSDiag.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6635,6 +6635,7 @@ static bool diagnoseKeyPathComponents(ConstraintSystem &CS, KeyPathExpr *KPE,
66356635
break;
66366636

66376637
case KeyPathExpr::Component::Kind::Invalid:
6638+
case KeyPathExpr::Component::Kind::Identity:
66386639
case KeyPathExpr::Component::Kind::OptionalChain:
66396640
case KeyPathExpr::Component::Kind::OptionalForce:
66406641
// FIXME: Diagnose optional chaining and forcing properly.

lib/Sema/CSGen.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3067,6 +3067,8 @@ namespace {
30673067
base = OptionalType::get(base);
30683068
break;
30693069
}
3070+
case KeyPathExpr::Component::Kind::Identity:
3071+
continue;
30703072
}
30713073
}
30723074

lib/Sema/CSSimplify.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,7 @@ getCalleeDeclAndArgs(ConstraintSystem &cs,
638638
case KeyPathExpr::Component::Kind::OptionalForce:
639639
case KeyPathExpr::Component::Kind::OptionalChain:
640640
case KeyPathExpr::Component::Kind::OptionalWrap:
641+
case KeyPathExpr::Component::Kind::Identity:
641642
return std::make_tuple(nullptr, 0, argLabels, hasTrailingClosure);
642643
}
643644

@@ -4137,6 +4138,7 @@ ConstraintSystem::simplifyKeyPathConstraint(Type keyPathTy,
41374138

41384139
switch (component.getKind()) {
41394140
case KeyPathExpr::Component::Kind::Invalid:
4141+
case KeyPathExpr::Component::Kind::Identity:
41404142
break;
41414143

41424144
case KeyPathExpr::Component::Kind::Property:

0 commit comments

Comments
 (0)