Skip to content

Commit 59b7593

Browse files
committed
[ConstraintSystem] Extract logic to determine key path literal capabilities
1 parent a4bda00 commit 59b7593

File tree

2 files changed

+121
-0
lines changed

2 files changed

+121
-0
lines changed

include/swift/Sema/ConstraintSystem.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,12 @@ enum TypeVariableOptions {
339339
TVO_PackExpansion = 0x40,
340340
};
341341

342+
enum class KeyPathCapability : uint8_t {
343+
ReadOnly,
344+
Writable,
345+
ReferenceWritable
346+
};
347+
342348
/// The implementation object for a type variable used within the
343349
/// constraint-solving type checker.
344350
///
@@ -5513,6 +5519,16 @@ class ConstraintSystem {
55135519
bool isMemberAvailableOnExistential(Type baseTy,
55145520
const ValueDecl *member) const;
55155521

5522+
/// Attempts to infer a capability of a key path (i.e. whether it
5523+
/// is read-only, writable, etc.) based on the referenced members.
5524+
///
5525+
/// \param keyPathType The type variable that represents the key path literal.
5526+
///
5527+
/// \returns Capability if key path is sufficiently resolved and None
5528+
/// otherwise.
5529+
llvm::Optional<KeyPathCapability>
5530+
inferKeyPathLiteralCapability(TypeVariableType *keyPathType);
5531+
55165532
SWIFT_DEBUG_DUMP;
55175533
SWIFT_DEBUG_DUMPER(dump(Expr *));
55185534

lib/Sema/ConstraintSystem.cpp

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7379,6 +7379,111 @@ ConstraintSystem::lookupConformance(Type type, ProtocolDecl *protocol) {
73797379
return conformance;
73807380
}
73817381

7382+
llvm::Optional<KeyPathCapability>
7383+
ConstraintSystem::inferKeyPathLiteralCapability(TypeVariableType *keyPathType) {
7384+
auto *typeLocator = keyPathType->getImpl().getLocator();
7385+
assert(typeLocator->isLastElement<LocatorPathElt::KeyPathType>());
7386+
7387+
auto *keyPath = castToExpr<KeyPathExpr>(typeLocator->getAnchor());
7388+
7389+
auto capability = KeyPathCapability::Writable;
7390+
7391+
bool didOptionalChain = false;
7392+
7393+
for (unsigned i : indices(keyPath->getComponents())) {
7394+
auto &component = keyPath->getComponents()[i];
7395+
7396+
switch (component.getKind()) {
7397+
case KeyPathExpr::Component::Kind::Invalid:
7398+
case KeyPathExpr::Component::Kind::Identity:
7399+
break;
7400+
7401+
case KeyPathExpr::Component::Kind::CodeCompletion: {
7402+
capability = KeyPathCapability::ReadOnly;
7403+
break;
7404+
}
7405+
case KeyPathExpr::Component::Kind::Property:
7406+
case KeyPathExpr::Component::Kind::Subscript:
7407+
case KeyPathExpr::Component::Kind::UnresolvedProperty:
7408+
case KeyPathExpr::Component::Kind::UnresolvedSubscript: {
7409+
auto *componentLoc =
7410+
getConstraintLocator(keyPath, LocatorPathElt::KeyPathComponent(i));
7411+
auto *calleeLoc = getCalleeLocator(componentLoc);
7412+
auto overload = findSelectedOverloadFor(calleeLoc);
7413+
if (!overload)
7414+
return llvm::None;
7415+
7416+
// tuple elements do not change the capability of the key path
7417+
auto choice = overload->choice;
7418+
if (choice.getKind() == OverloadChoiceKind::TupleIndex) {
7419+
continue;
7420+
}
7421+
7422+
// Discarded unsupported non-decl member lookups.
7423+
if (!choice.isDecl())
7424+
return llvm::None;
7425+
7426+
auto storage = dyn_cast<AbstractStorageDecl>(choice.getDecl());
7427+
7428+
if (hasFixFor(calleeLoc, FixKind::AllowInvalidRefInKeyPath)) {
7429+
if (!shouldAttemptFixes())
7430+
return llvm::None;
7431+
7432+
// If this was a method reference let's mark it as read-only.
7433+
if (!storage) {
7434+
capability = KeyPathCapability::ReadOnly;
7435+
continue;
7436+
}
7437+
}
7438+
7439+
if (!storage)
7440+
return llvm::None;
7441+
7442+
if (isReadOnlyKeyPathComponent(storage, component.getLoc())) {
7443+
capability = KeyPathCapability::ReadOnly;
7444+
continue;
7445+
}
7446+
7447+
// A nonmutating setter indicates a reference-writable base.
7448+
if (!storage->isSetterMutating()) {
7449+
capability = KeyPathCapability::ReferenceWritable;
7450+
continue;
7451+
}
7452+
7453+
// Otherwise, the key path maintains its current capability.
7454+
break;
7455+
}
7456+
7457+
case KeyPathExpr::Component::Kind::OptionalChain:
7458+
didOptionalChain = true;
7459+
break;
7460+
7461+
case KeyPathExpr::Component::Kind::OptionalForce:
7462+
// Forcing an optional preserves its lvalue-ness.
7463+
break;
7464+
7465+
case KeyPathExpr::Component::Kind::OptionalWrap:
7466+
// An optional chain should already have been recorded.
7467+
assert(didOptionalChain);
7468+
break;
7469+
7470+
case KeyPathExpr::Component::Kind::TupleElement:
7471+
llvm_unreachable("not implemented");
7472+
break;
7473+
7474+
case KeyPathExpr::Component::Kind::DictionaryKey:
7475+
llvm_unreachable("DictionaryKey only valid in #keyPath");
7476+
break;
7477+
}
7478+
}
7479+
7480+
// Optional chains force the entire key path to be read-only.
7481+
if (didOptionalChain)
7482+
capability = KeyPathCapability::ReadOnly;
7483+
7484+
return capability;
7485+
}
7486+
73827487
TypeVarBindingProducer::TypeVarBindingProducer(BindingSet &bindings)
73837488
: BindingProducer(bindings.getConstraintSystem(),
73847489
bindings.getTypeVariable()->getImpl().getLocator()),

0 commit comments

Comments
 (0)