Skip to content

Commit

Permalink
Implement the final approved syntax for SE-227 identity key paths.
Browse files Browse the repository at this point in the history
`\.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.
  • Loading branch information
jckarter committed Sep 19, 2018
1 parent 86e11c5 commit 93b5de6
Show file tree
Hide file tree
Showing 21 changed files with 115 additions and 110 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,20 @@ CHANGELOG
Swift 5.0
---------

* [SE-0227][]:

Key paths now support the `\.self` keypath, which is a `WritableKeyPath`
that refers to its entire input value:

```swift
let id = \Int.self

var x = 2
print(x[keyPath: id]) // prints 2
x[keyPath: id] = 3
print(x[keyPath: id]) // prints 3
```

* [SE-0214][]:

Renamed the `DictionaryLiteral` type to `KeyValuePairs`.
Expand Down
5 changes: 0 additions & 5 deletions include/swift/AST/Builtins.def
Original file line number Diff line number Diff line change
Expand Up @@ -373,11 +373,6 @@ BUILTIN_SIL_OPERATION(AllocWithTailElems, "allocWithTailElems", Special)
/// Projects the first tail-allocated element of type E from a class C.
BUILTIN_SIL_OPERATION(ProjectTailElems, "projectTailElems", Special)

/// identityKeyPath : <T> () -> WritableKeyPath<T, T>
///
/// Creates an identity key path object. (TODO: replace with proper syntax)
BUILTIN_SIL_OPERATION(IdentityKeyPath, "identityKeyPath", Special)

#undef BUILTIN_SIL_OPERATION

// BUILTIN_RUNTIME_CALL - A call into a runtime function.
Expand Down
33 changes: 25 additions & 8 deletions include/swift/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -4773,7 +4773,8 @@ class KeyPathExpr : public Expr {
Subscript,
OptionalForce,
OptionalChain,
OptionalWrap
OptionalWrap,
Identity,
};

private:
Expand All @@ -4787,9 +4788,11 @@ class KeyPathExpr : public Expr {
} Decl;


llvm::PointerIntPair<Expr *, 3, Kind> SubscriptIndexExprAndKind;
ArrayRef<Identifier> SubscriptLabels;
ArrayRef<ProtocolConformanceRef> SubscriptHashableConformances;
Expr *SubscriptIndexExpr;
const Identifier *SubscriptLabelsData;
const ProtocolConformanceRef *SubscriptHashableConformancesData;
unsigned SubscriptSize;
Kind KindValue;
Type ComponentType;
SourceLoc Loc;

Expand Down Expand Up @@ -4914,12 +4917,18 @@ class KeyPathExpr : public Expr {
SourceLoc());
}

static Component forIdentity(SourceLoc selfLoc) {
return Component(nullptr, {}, nullptr, {}, {},
Kind::Identity, Type(),
selfLoc);
}

SourceLoc getLoc() const {
return Loc;
}

Kind getKind() const {
return SubscriptIndexExprAndKind.getInt();
return KindValue;
}

bool isValid() const {
Expand All @@ -4936,6 +4945,7 @@ class KeyPathExpr : public Expr {
case Kind::OptionalWrap:
case Kind::OptionalForce:
case Kind::Property:
case Kind::Identity:
return true;

case Kind::UnresolvedSubscript:
Expand All @@ -4950,14 +4960,15 @@ class KeyPathExpr : public Expr {
switch (getKind()) {
case Kind::Subscript:
case Kind::UnresolvedSubscript:
return SubscriptIndexExprAndKind.getPointer();
return SubscriptIndexExpr;

case Kind::Invalid:
case Kind::OptionalChain:
case Kind::OptionalWrap:
case Kind::OptionalForce:
case Kind::UnresolvedProperty:
case Kind::Property:
case Kind::Identity:
return nullptr;
}
llvm_unreachable("unhandled kind");
Expand All @@ -4967,14 +4978,15 @@ class KeyPathExpr : public Expr {
switch (getKind()) {
case Kind::Subscript:
case Kind::UnresolvedSubscript:
return SubscriptLabels;
return {SubscriptLabelsData, (size_t)SubscriptSize};

case Kind::Invalid:
case Kind::OptionalChain:
case Kind::OptionalWrap:
case Kind::OptionalForce:
case Kind::UnresolvedProperty:
case Kind::Property:
case Kind::Identity:
llvm_unreachable("no subscript labels for this kind");
}
llvm_unreachable("unhandled kind");
Expand All @@ -4984,7 +4996,9 @@ class KeyPathExpr : public Expr {
getSubscriptIndexHashableConformances() const {
switch (getKind()) {
case Kind::Subscript:
return SubscriptHashableConformances;
if (!SubscriptHashableConformancesData)
return {};
return {SubscriptHashableConformancesData, (size_t)SubscriptSize};

case Kind::UnresolvedSubscript:
case Kind::Invalid:
Expand All @@ -4993,6 +5007,7 @@ class KeyPathExpr : public Expr {
case Kind::OptionalForce:
case Kind::UnresolvedProperty:
case Kind::Property:
case Kind::Identity:
return {};
}
llvm_unreachable("unhandled kind");
Expand All @@ -5013,6 +5028,7 @@ class KeyPathExpr : public Expr {
case Kind::OptionalWrap:
case Kind::OptionalForce:
case Kind::Property:
case Kind::Identity:
llvm_unreachable("no unresolved name for this kind");
}
llvm_unreachable("unhandled kind");
Expand All @@ -5030,6 +5046,7 @@ class KeyPathExpr : public Expr {
case Kind::OptionalChain:
case Kind::OptionalWrap:
case Kind::OptionalForce:
case Kind::Identity:
llvm_unreachable("no decl ref for this kind");
}
llvm_unreachable("unhandled kind");
Expand Down
4 changes: 4 additions & 0 deletions lib/AST/ASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2597,6 +2597,10 @@ class PrintExpr : public ExprVisitor<PrintExpr> {
component.getIndexExpr()->print(OS, Indent + 4);
OS.indent(Indent + 4);
break;
case KeyPathExpr::Component::Kind::Identity:
OS << "identity";
OS << '\n';
break;
}
OS << "type=";
component.getComponentType().print(OS);
Expand Down
1 change: 1 addition & 0 deletions lib/AST/ASTWalker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1034,6 +1034,7 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
case KeyPathExpr::Component::Kind::Property:
case KeyPathExpr::Component::Kind::UnresolvedProperty:
case KeyPathExpr::Component::Kind::Invalid:
case KeyPathExpr::Component::Kind::Identity:
// No subexpr to visit.
break;
}
Expand Down
14 changes: 1 addition & 13 deletions lib/AST/Builtins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -991,15 +991,6 @@ static ValueDecl *getTypeJoinMetaOperation(ASTContext &Context, Identifier Id) {
return builder.build(Id);
}

static ValueDecl *getIdentityKeyPathOperation(ASTContext &Context,
Identifier Id) {
BuiltinGenericSignatureBuilder builder(Context, 1);
auto arg = makeGenericParam();
builder.setResult(makeBoundGenericType(Context.getWritableKeyPathDecl(),
arg, arg));
return builder.build(Id);
}

static ValueDecl *getCanBeObjCClassOperation(ASTContext &Context,
Identifier Id) {
// <T> T.Type -> Builtin.Int8
Expand Down Expand Up @@ -1879,10 +1870,7 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) {
return getTypeJoinInoutOperation(Context, Id);

case BuiltinValueKind::TypeJoinMeta:
return getTypeJoinMetaOperation(Context, Id);

case BuiltinValueKind::IdentityKeyPath:
return getIdentityKeyPathOperation(Context, Id);
return getTypeJoinMetaOperation(Context, Id);
}

llvm_unreachable("bad builtin value!");
Expand Down
22 changes: 14 additions & 8 deletions lib/AST/Expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2135,13 +2135,16 @@ KeyPathExpr::Component::Component(ASTContext *ctxForCopyingLabels,
Kind kind,
Type type,
SourceLoc loc)
: Decl(decl), SubscriptIndexExprAndKind(indexExpr, kind),
SubscriptLabels(subscriptLabels.empty()
? subscriptLabels
: ctxForCopyingLabels->AllocateCopy(subscriptLabels)),
SubscriptHashableConformances(indexHashables),
: Decl(decl), SubscriptIndexExpr(indexExpr), KindValue(kind),
ComponentType(type), Loc(loc)
{}
{
assert(subscriptLabels.size() == indexHashables.size()
|| indexHashables.empty());
SubscriptLabelsData = subscriptLabels.data();
SubscriptHashableConformancesData = indexHashables.empty()
? nullptr : indexHashables.data();
SubscriptSize = subscriptLabels.size();
}

KeyPathExpr::Component
KeyPathExpr::Component::forSubscriptWithPrebuiltIndexExpr(
Expand All @@ -2157,8 +2160,10 @@ void KeyPathExpr::Component::setSubscriptIndexHashableConformances(
ArrayRef<ProtocolConformanceRef> hashables) {
switch (getKind()) {
case Kind::Subscript:
SubscriptHashableConformances = getComponentType()->getASTContext()
.AllocateCopy(hashables);
assert(hashables.size() == SubscriptSize);
SubscriptHashableConformancesData = getComponentType()->getASTContext()
.AllocateCopy(hashables)
.data();
return;

case Kind::UnresolvedSubscript:
Expand All @@ -2168,6 +2173,7 @@ void KeyPathExpr::Component::setSubscriptIndexHashableConformances(
case Kind::OptionalForce:
case Kind::UnresolvedProperty:
case Kind::Property:
case Kind::Identity:
llvm_unreachable("no hashable conformances for this kind");
}
}
Expand Down
1 change: 1 addition & 0 deletions lib/IDE/SourceEntityWalker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,7 @@ std::pair<bool, Expr *> SemaAnnotator::walkToExprPre(Expr *E) {
case KeyPathExpr::Component::Kind::OptionalChain:
case KeyPathExpr::Component::Kind::OptionalWrap:
case KeyPathExpr::Component::Kind::OptionalForce:
case KeyPathExpr::Component::Kind::Identity:
break;
}
}
Expand Down
52 changes: 0 additions & 52 deletions lib/SILGen/SILGenBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -970,58 +970,6 @@ static ManagedValue emitBuiltinProjectTailElems(SILGenFunction &SGF,
return ManagedValue::forUnmanaged(result);
}

static ManagedValue emitBuiltinIdentityKeyPath(SILGenFunction &SGF,
SILLocation loc,
SubstitutionMap subs,
ArrayRef<ManagedValue> args,
SGFContext C) {
assert(subs.getReplacementTypes().size() == 1 &&
"identityKeyPath should have one substitution");
assert(args.size() == 0 &&
"identityKeyPath should have no args");

auto identityTy = subs.getReplacementTypes()[0]->getCanonicalType();

// The `self` key can be used for identity in Cocoa KVC as well.
StringRef objcString = SGF.getASTContext().LangOpts.EnableObjCInterop
? "self" : "";

// The key path pattern has to capture some generic context if the type is
// dependent on this generic context. We only need the specific type, though,
// not the entire generic environment.
bool isDependent = identityTy->hasArchetype();
CanType identityPatternTy = identityTy;
CanGenericSignature patternSig = nullptr;
SubstitutionMap patternSubs;
if (isDependent) {
auto param = GenericTypeParamType::get(0, 0, SGF.getASTContext());
identityPatternTy = param->getCanonicalType();
patternSig = GenericSignature::get(param, {})->getCanonicalSignature();
patternSubs = SubstitutionMap::get(patternSig,
llvm::makeArrayRef((Type)identityTy),
{});
}

auto identityPattern = KeyPathPattern::get(SGF.SGM.M,
patternSig,
identityPatternTy,
identityPatternTy,
{},
objcString);

auto kpTy = BoundGenericType::get(SGF.getASTContext().getWritableKeyPathDecl(),
Type(),
{identityTy, identityTy})
->getCanonicalType();

auto keyPath = SGF.B.createKeyPath(loc, identityPattern,
patternSubs,
{},
SILType::getPrimitiveObjectType(kpTy));
return SGF.emitManagedRValueWithCleanup(keyPath);
}


/// Specialized emitter for type traits.
template<TypeTraitResult (TypeBase::*Trait)(),
BuiltinValueKind Kind>
Expand Down
3 changes: 3 additions & 0 deletions lib/SILGen/SILGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3849,6 +3849,9 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) {
KeyPathPatternComponent::forOptional(loweredKind, baseTy));
break;
}

case KeyPathExpr::Component::Kind::Identity:
continue;

case KeyPathExpr::Component::Kind::Invalid:
case KeyPathExpr::Component::Kind::UnresolvedProperty:
Expand Down
18 changes: 16 additions & 2 deletions lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,11 @@ static bool buildObjCKeyPathString(KeyPathExpr *E,
case KeyPathExpr::Component::Kind::OptionalWrap:
// KVC propagates nulls, so these don't affect the key path string.
continue;

case KeyPathExpr::Component::Kind::Identity:
// The identity component can be elided from the KVC string (unless it's
// the only component, in which case we use @"self").
continue;

case KeyPathExpr::Component::Kind::Property: {
// Property references must be to @objc properties.
// TODO: If we added special properties matching KVC operators like '@sum',
Expand Down Expand Up @@ -307,6 +311,12 @@ static bool buildObjCKeyPathString(KeyPathExpr *E,
}
}

// If there are no non-identity components, this is the "self" key.
if (buf.empty()) {
auto self = StringRef("self");
buf.append(self.begin(), self.end());
}

return true;
}

Expand Down Expand Up @@ -4514,7 +4524,11 @@ namespace {
baseTy = component.getComponentType();
resolvedComponents.push_back(component);
break;

case KeyPathExpr::Component::Kind::Identity:
component = origComponent;
component.setComponentType(baseTy);
resolvedComponents.push_back(component);
break;
case KeyPathExpr::Component::Kind::Property:
case KeyPathExpr::Component::Kind::Subscript:
case KeyPathExpr::Component::Kind::OptionalWrap:
Expand Down
1 change: 1 addition & 0 deletions lib/Sema/CSDiag.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6635,6 +6635,7 @@ static bool diagnoseKeyPathComponents(ConstraintSystem &CS, KeyPathExpr *KPE,
break;

case KeyPathExpr::Component::Kind::Invalid:
case KeyPathExpr::Component::Kind::Identity:
case KeyPathExpr::Component::Kind::OptionalChain:
case KeyPathExpr::Component::Kind::OptionalForce:
// FIXME: Diagnose optional chaining and forcing properly.
Expand Down
2 changes: 2 additions & 0 deletions lib/Sema/CSGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3067,6 +3067,8 @@ namespace {
base = OptionalType::get(base);
break;
}
case KeyPathExpr::Component::Kind::Identity:
continue;
}
}

Expand Down
2 changes: 2 additions & 0 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,7 @@ getCalleeDeclAndArgs(ConstraintSystem &cs,
case KeyPathExpr::Component::Kind::OptionalForce:
case KeyPathExpr::Component::Kind::OptionalChain:
case KeyPathExpr::Component::Kind::OptionalWrap:
case KeyPathExpr::Component::Kind::Identity:
return std::make_tuple(nullptr, 0, argLabels, hasTrailingClosure);
}

Expand Down Expand Up @@ -4137,6 +4138,7 @@ ConstraintSystem::simplifyKeyPathConstraint(Type keyPathTy,

switch (component.getKind()) {
case KeyPathExpr::Component::Kind::Invalid:
case KeyPathExpr::Component::Kind::Identity:
break;

case KeyPathExpr::Component::Kind::Property:
Expand Down
Loading

0 comments on commit 93b5de6

Please sign in to comment.