Skip to content

Commit 9bf9d54

Browse files
committed
[ConstraintSystem] Key path capability inference should indicate whether key path is invalid
This flag makes it easier to determine what binding to produce from the default. In cases where some of the member references are invalid it's better to produce a placeholder for a key path type instead of letting the solver to attempt to fix more contextual problems for a broken key path.
1 parent 62d15cd commit 9bf9d54

File tree

4 files changed

+49
-29
lines changed

4 files changed

+49
-29
lines changed

include/swift/Sema/ConstraintSystem.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5524,9 +5524,9 @@ class ConstraintSystem {
55245524
///
55255525
/// \param keyPathType The type variable that represents the key path literal.
55265526
///
5527-
/// \returns Capability if key path is sufficiently resolved and None
5528-
/// otherwise.
5529-
llvm::Optional<KeyPathCapability>
5527+
/// \returns `bool` to indicate whether key path is valid or not,
5528+
/// and capability if it could be determined.
5529+
std::pair</*isValid=*/bool, llvm::Optional<KeyPathCapability>>
55305530
inferKeyPathLiteralCapability(TypeVariableType *keyPathType);
55315531

55325532
SWIFT_DEBUG_DUMP;

lib/Sema/CSBindings.cpp

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -889,14 +889,32 @@ void BindingSet::addDefault(Constraint *constraint) {
889889

890890
if (TypeVar->getImpl().isKeyPathType() && Bindings.empty()) {
891891
if (constraint->getKind() == ConstraintKind::FallbackType) {
892-
if (auto capability = CS.inferKeyPathLiteralCapability(TypeVar)) {
892+
auto &ctx = CS.getASTContext();
893+
894+
bool isValid;
895+
llvm::Optional<KeyPathCapability> capability;
896+
897+
std::tie(isValid, capability) = CS.inferKeyPathLiteralCapability(TypeVar);
898+
899+
if (!isValid) {
900+
// If one of the references in a key path is invalid let's add
901+
// a placeholder binding in diagnostic mode to indicate that
902+
// the key path cannot be properly resolved.
903+
if (CS.shouldAttemptFixes()) {
904+
addBinding({PlaceholderType::get(ctx, TypeVar),
905+
AllowedBindingKind::Exact, constraint});
906+
}
907+
908+
// During normal solving the set has to stay empty.
909+
return;
910+
}
911+
912+
if (capability) {
893913
auto *keyPathType = defaultTy->castTo<BoundGenericType>();
894914

895915
auto root = keyPathType->getGenericArgs()[0];
896916
auto value = keyPathType->getGenericArgs()[1];
897917

898-
auto &ctx = CS.getASTContext();
899-
900918
switch (*capability) {
901919
case KeyPathCapability::ReadOnly:
902920
break;
@@ -914,8 +932,11 @@ void BindingSet::addDefault(Constraint *constraint) {
914932
}
915933

916934
addBinding({keyPathType, AllowedBindingKind::Exact, constraint});
917-
return;
918935
}
936+
937+
// If key path is not yet sufficiently resolved, don't add any
938+
// bindings.
939+
return;
919940
}
920941
}
921942

lib/Sema/CSSimplify.cpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11736,13 +11736,6 @@ bool ConstraintSystem::resolveKeyPath(TypeVariableType *typeVar,
1173611736
contextualType = BoundGenericType::get(
1173711737
keyPathTy->getDecl(), keyPathTy->getParent(), {root, value});
1173811738
}
11739-
} else if (contextualType->isPlaceholder()) {
11740-
auto root = simplifyType(getKeyPathRootType(keyPath));
11741-
if (!(root->isTypeVariableOrMember() || root->isPlaceholder())) {
11742-
auto value = getKeyPathValueType(keyPath);
11743-
contextualType =
11744-
BoundGenericType::get(ctx.getKeyPathDecl(), Type(), {root, value});
11745-
}
1174611739
}
1174711740
}
1174811741

lib/Sema/ConstraintSystem.cpp

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

7382-
llvm::Optional<KeyPathCapability>
7382+
std::pair<bool, llvm::Optional<KeyPathCapability>>
73837383
ConstraintSystem::inferKeyPathLiteralCapability(TypeVariableType *keyPathType) {
73847384
auto *typeLocator = keyPathType->getImpl().getLocator();
73857385
assert(typeLocator->isLastElement<LocatorPathElt::KeyPathType>());
@@ -7390,6 +7390,19 @@ ConstraintSystem::inferKeyPathLiteralCapability(TypeVariableType *keyPathType) {
73907390

73917391
bool didOptionalChain = false;
73927392

7393+
auto fail = []() -> std::pair<bool, llvm::Optional<KeyPathCapability>> {
7394+
return std::make_pair(false, llvm::None);
7395+
};
7396+
7397+
auto delay = []() -> std::pair<bool, llvm::Optional<KeyPathCapability>> {
7398+
return std::make_pair(true, llvm::None);
7399+
};
7400+
7401+
auto success = [](KeyPathCapability capability)
7402+
-> std::pair<bool, llvm::Optional<KeyPathCapability>> {
7403+
return std::make_pair(true, capability);
7404+
};
7405+
73937406
for (unsigned i : indices(keyPath->getComponents())) {
73947407
auto &component = keyPath->getComponents()[i];
73957408

@@ -7411,7 +7424,7 @@ ConstraintSystem::inferKeyPathLiteralCapability(TypeVariableType *keyPathType) {
74117424
auto *calleeLoc = getCalleeLocator(componentLoc);
74127425
auto overload = findSelectedOverloadFor(calleeLoc);
74137426
if (!overload)
7414-
return llvm::None;
7427+
return delay();
74157428

74167429
// tuple elements do not change the capability of the key path
74177430
auto choice = overload->choice;
@@ -7421,23 +7434,16 @@ ConstraintSystem::inferKeyPathLiteralCapability(TypeVariableType *keyPathType) {
74217434

74227435
// Discarded unsupported non-decl member lookups.
74237436
if (!choice.isDecl())
7424-
return llvm::None;
7437+
return fail();
74257438

74267439
auto storage = dyn_cast<AbstractStorageDecl>(choice.getDecl());
74277440

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-
}
7441+
if (hasFixFor(calleeLoc, FixKind::AllowInvalidRefInKeyPath) ||
7442+
hasFixFor(calleeLoc, FixKind::UnwrapOptionalBase))
7443+
return fail();
74387444

74397445
if (!storage)
7440-
return llvm::None;
7446+
return fail();
74417447

74427448
if (isReadOnlyKeyPathComponent(storage, component.getLoc())) {
74437449
capability = KeyPathCapability::ReadOnly;
@@ -7481,7 +7487,7 @@ ConstraintSystem::inferKeyPathLiteralCapability(TypeVariableType *keyPathType) {
74817487
if (didOptionalChain)
74827488
capability = KeyPathCapability::ReadOnly;
74837489

7484-
return capability;
7490+
return success(capability);
74857491
}
74867492

74877493
TypeVarBindingProducer::TypeVarBindingProducer(BindingSet &bindings)

0 commit comments

Comments
 (0)