Skip to content

Commit d74c472

Browse files
committed
[ConstraintSystem] De-duplicate key path constraint simplification
Move some of the checks from the constraint simplification into `inferKeyPathLiteralCapability` and start using it for both inference and constraint simplification.
1 parent d066881 commit d74c472

File tree

3 files changed

+53
-170
lines changed

3 files changed

+53
-170
lines changed

include/swift/Sema/ConstraintSystem.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5522,6 +5522,15 @@ class ConstraintSystem {
55225522
/// Attempts to infer a capability of a key path (i.e. whether it
55235523
/// is read-only, writable, etc.) based on the referenced members.
55245524
///
5525+
/// \param keyPath The key path literal expression.
5526+
///
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>>
5530+
inferKeyPathLiteralCapability(KeyPathExpr *keyPath);
5531+
5532+
/// A convenience overload of \c inferKeyPathLiteralCapability.
5533+
///
55255534
/// \param keyPathType The type variable that represents the key path literal.
55265535
///
55275536
/// \returns `bool` to indicate whether key path is valid or not,

lib/Sema/CSSimplify.cpp

Lines changed: 28 additions & 165 deletions
Original file line numberDiff line numberDiff line change
@@ -12199,17 +12199,15 @@ ConstraintSystem::simplifyKeyPathConstraint(
1219912199
ConstraintLocatorBuilder locator) {
1220012200
auto subflags = getDefaultDecompositionOptions(flags);
1220112201
// The constraint ought to have been anchored on a KeyPathExpr.
12202-
auto keyPath = castToExpr<KeyPathExpr>(locator.getBaseLocator()->getAnchor());
12202+
auto keyPath = castToExpr<KeyPathExpr>(locator.getAnchor());
1220312203
keyPathTy = getFixedTypeRecursive(keyPathTy, /*want rvalue*/ true);
12204-
bool definitelyFunctionType = false;
12205-
bool definitelyKeyPathType = false;
1220612204
bool resolveAsMultiArgFuncFix = false;
1220712205

1220812206
auto formUnsolved = [&]() -> SolutionKind {
1220912207
if (flags.contains(TMF_GenerateConstraints)) {
1221012208
addUnsolvedConstraint(Constraint::create(
1221112209
*this, ConstraintKind::KeyPath, keyPathTy, rootTy, valueTy,
12212-
locator.getBaseLocator(), componentTypeVars));
12210+
getConstraintLocator(locator), componentTypeVars));
1221312211
return SolutionKind::Solved;
1221412212
}
1221512213
return SolutionKind::Unsolved;
@@ -12218,25 +12216,18 @@ ConstraintSystem::simplifyKeyPathConstraint(
1221812216
if (keyPathTy->isTypeVariableOrMember())
1221912217
return formUnsolved();
1222012218

12221-
auto tryMatchRootAndValueFromType = [&](Type type,
12222-
bool allowPartial = true) -> bool {
12219+
auto tryMatchRootAndValueFromType = [&](Type type) -> bool {
1222312220
Type boundRoot = Type(), boundValue = Type();
1222412221

1222512222
if (auto bgt = type->getAs<BoundGenericType>()) {
12226-
definitelyKeyPathType = true;
12227-
1222812223
// We can get root and value from a concrete key path type.
1222912224
if (bgt->isKeyPath() ||
1223012225
bgt->isWritableKeyPath() ||
1223112226
bgt->isReferenceWritableKeyPath()) {
1223212227
boundRoot = bgt->getGenericArgs()[0];
1223312228
boundValue = bgt->getGenericArgs()[1];
12234-
} else if (bgt->isPartialKeyPath()) {
12235-
if (!allowPartial)
12236-
return false;
12237-
12238-
// We can still get the root from a PartialKeyPath.
12239-
boundRoot = bgt->getGenericArgs()[0];
12229+
} else {
12230+
return false;
1224012231
}
1224112232
}
1224212233

@@ -12247,13 +12238,11 @@ ConstraintSystem::simplifyKeyPathConstraint(
1224712238

1224812239
resolveAsMultiArgFuncFix = true;
1224912240
auto *fix = AllowMultiArgFuncKeyPathMismatch::create(
12250-
*this, fnTy, locator.getBaseLocator());
12241+
*this, fnTy, getConstraintLocator(locator));
1225112242
// Pretend the keypath type got resolved and move on.
1225212243
return !recordFix(fix);
1225312244
}
1225412245

12255-
definitelyFunctionType = true;
12256-
1225712246
// Match up the root and value types to the function's param and return
1225812247
// types. Note that we're using the type of the parameter as referenced
1225912248
// from inside the function body as we'll be transforming the code into:
@@ -12300,192 +12289,66 @@ ConstraintSystem::simplifyKeyPathConstraint(
1230012289
// If the root type has been bound to a hole, we cannot infer it.
1230112290
if (getFixedTypeRecursive(rootTy, /*wantRValue*/ true)->isPlaceholder())
1230212291
return SolutionKind::Solved;
12303-
12304-
// If we have e.g a missing member somewhere, a component type variable
12305-
// would either be marked as a potential hole or would have a fix.
12306-
if (llvm::any_of(componentTypeVars, [&](TypeVariableType *tv) {
12307-
auto *locator = tv->getImpl().getLocator();
12308-
12309-
// Result type of a component could be bound to a contextual
12310-
// (concrete) type if it's the last component in the chain,
12311-
// so the only way to detect errors is to check for fixes.
12312-
if (locator->isForKeyPathComponentResult()) {
12313-
auto path = locator->getPath();
12314-
auto *componentLoc =
12315-
getConstraintLocator(locator->getAnchor(), path.drop_back());
12316-
12317-
if (hasFixFor(componentLoc, FixKind::DefineMemberBasedOnUse) ||
12318-
hasFixFor(componentLoc, FixKind::UnwrapOptionalBase) ||
12319-
hasFixFor(componentLoc,
12320-
FixKind::UnwrapOptionalBaseWithOptionalResult))
12321-
return true;
12322-
}
12323-
12324-
// If something inside of a component is marked as a hole,
12325-
// let's consider while component to be invalid.
12326-
return locator->isInKeyPathComponent() &&
12327-
tv->getImpl().canBindToHole();
12328-
})) {
12329-
(void)tryMatchRootAndValueFromType(keyPathTy);
12330-
return SolutionKind::Solved;
12331-
}
1233212292
}
1233312293

1233412294
// If we're fixed to a bound generic type, trying harvesting context from it.
1233512295
// However, we don't want a solution that fixes the expression type to
1233612296
// PartialKeyPath; we'd rather that be represented using an upcast conversion.
12337-
if (!tryMatchRootAndValueFromType(keyPathTy, /*allowPartial=*/false))
12297+
if (!tryMatchRootAndValueFromType(keyPathTy))
1233812298
return SolutionKind::Error;
1233912299

1234012300
// If we fix this keypath as `AllowMultiArgFuncKeyPathMismatch`, just proceed
1234112301
if (resolveAsMultiArgFuncFix)
1234212302
return SolutionKind::Solved;
1234312303

12344-
// See if we resolved overloads for all the components involved.
12345-
enum {
12346-
ReadOnly,
12347-
Writable,
12348-
ReferenceWritable
12349-
} capability = Writable;
12304+
bool isValid;
12305+
llvm::Optional<KeyPathCapability> capability;
1235012306

12351-
bool anyComponentsUnresolved = false;
12352-
bool didOptionalChain = false;
12307+
std::tie(isValid, capability) = inferKeyPathLiteralCapability(keyPath);
1235312308

12354-
for (unsigned i : indices(keyPath->getComponents())) {
12355-
auto &component = keyPath->getComponents()[i];
12356-
12357-
switch (component.getKind()) {
12358-
case KeyPathExpr::Component::Kind::Invalid:
12359-
case KeyPathExpr::Component::Kind::Identity:
12360-
break;
12309+
// If key path is invalid, let's not don't attempt match capabilities.
12310+
if (!isValid)
12311+
return shouldAttemptFixes() ? SolutionKind::Solved : SolutionKind::Error;
1236112312

12362-
case KeyPathExpr::Component::Kind::CodeCompletion: {
12363-
anyComponentsUnresolved = true;
12364-
capability = ReadOnly;
12365-
break;
12366-
}
12367-
case KeyPathExpr::Component::Kind::Property:
12368-
case KeyPathExpr::Component::Kind::Subscript:
12369-
case KeyPathExpr::Component::Kind::UnresolvedProperty:
12370-
case KeyPathExpr::Component::Kind::UnresolvedSubscript: {
12371-
auto *componentLoc = getConstraintLocator(
12372-
locator.withPathElement(LocatorPathElt::KeyPathComponent(i)));
12373-
auto *calleeLoc = getCalleeLocator(componentLoc);
12374-
auto overload = findSelectedOverloadFor(calleeLoc);
12375-
12376-
// If no choice was made, leave the constraint unsolved. But when
12377-
// generating constraints, we may already have enough information
12378-
// to determine whether the result will be a function type vs BGT KeyPath
12379-
// type, so continue through components to create new constraint at the
12380-
// end.
12381-
if (!overload) {
12382-
if (flags.contains(TMF_GenerateConstraints)) {
12383-
anyComponentsUnresolved = true;
12384-
continue;
12385-
}
12386-
return SolutionKind::Unsolved;
12387-
}
12388-
12389-
// tuple elements do not change the capability of the key path
12390-
auto choice = overload->choice;
12391-
if (choice.getKind() == OverloadChoiceKind::TupleIndex) {
12392-
continue;
12393-
}
12394-
12395-
// Discarded unsupported non-decl member lookups.
12396-
if (!choice.isDecl()) {
12397-
return SolutionKind::Error;
12398-
}
12399-
12400-
auto storage = dyn_cast<AbstractStorageDecl>(choice.getDecl());
12401-
12402-
if (hasFixFor(calleeLoc, FixKind::AllowInvalidRefInKeyPath)) {
12403-
return shouldAttemptFixes() ? SolutionKind::Solved
12404-
: SolutionKind::Error;
12405-
}
12406-
12407-
if (!storage)
12408-
return SolutionKind::Error;
12409-
12410-
if (isReadOnlyKeyPathComponent(storage, component.getLoc())) {
12411-
capability = ReadOnly;
12412-
continue;
12413-
}
12414-
12415-
// A nonmutating setter indicates a reference-writable base.
12416-
if (!storage->isSetterMutating()) {
12417-
capability = ReferenceWritable;
12418-
continue;
12419-
}
12420-
12421-
// Otherwise, the key path maintains its current capability.
12422-
break;
12423-
}
12424-
12425-
case KeyPathExpr::Component::Kind::OptionalChain:
12426-
didOptionalChain = true;
12427-
break;
12428-
12429-
case KeyPathExpr::Component::Kind::OptionalForce:
12430-
// Forcing an optional preserves its lvalue-ness.
12431-
break;
12432-
12433-
case KeyPathExpr::Component::Kind::OptionalWrap:
12434-
// An optional chain should already have been recorded.
12435-
assert(didOptionalChain);
12436-
break;
12437-
12438-
case KeyPathExpr::Component::Kind::TupleElement:
12439-
llvm_unreachable("not implemented");
12440-
break;
12441-
case KeyPathExpr::Component::Kind::DictionaryKey:
12442-
llvm_unreachable("DictionaryKey only valid in #keyPath");
12443-
break;
12444-
}
12445-
}
12446-
12447-
// Optional chains force the entire key path to be read-only.
12448-
if (didOptionalChain)
12449-
capability = ReadOnly;
12313+
// If key path is valid but not yet sufficiently resolved, let's delay
12314+
// capability checking.
12315+
if (!capability)
12316+
return formUnsolved();
1245012317

1245112318
// Resolve the type.
1245212319
NominalTypeDecl *kpDecl;
12453-
switch (capability) {
12454-
case ReadOnly:
12320+
switch (*capability) {
12321+
case KeyPathCapability::ReadOnly:
1245512322
kpDecl = getASTContext().getKeyPathDecl();
1245612323
break;
1245712324

12458-
case Writable:
12325+
case KeyPathCapability::Writable:
1245912326
kpDecl = getASTContext().getWritableKeyPathDecl();
1246012327
break;
1246112328

12462-
case ReferenceWritable:
12329+
case KeyPathCapability::ReferenceWritable:
1246312330
kpDecl = getASTContext().getReferenceWritableKeyPathDecl();
1246412331
break;
1246512332
}
12466-
12333+
1246712334
// FIXME: Allow the type to be upcast if the type system has a concrete
1246812335
// KeyPath type assigned to the expression already.
1246912336
if (auto keyPathBGT = keyPathTy->getAs<BoundGenericType>()) {
1247012337
if (keyPathBGT->isKeyPath())
1247112338
kpDecl = getASTContext().getKeyPathDecl();
12472-
else if (keyPathBGT->isWritableKeyPath() && capability >= Writable)
12339+
else if (keyPathBGT->isWritableKeyPath() &&
12340+
*capability >= KeyPathCapability::Writable)
1247312341
kpDecl = getASTContext().getWritableKeyPathDecl();
1247412342
}
1247512343

12476-
auto loc = locator.getBaseLocator();
12477-
if (definitelyFunctionType) {
12344+
if (keyPathTy->is<FunctionType>()) {
1247812345
increaseScore(SK_FunctionConversion, locator);
1247912346
return SolutionKind::Solved;
12480-
} else if (!anyComponentsUnresolved ||
12481-
(definitelyKeyPathType && capability == ReadOnly)) {
12482-
auto resolvedKPTy =
12483-
BoundGenericType::get(kpDecl, nullptr, {rootTy, valueTy});
12484-
return matchTypes(resolvedKPTy, keyPathTy, ConstraintKind::Bind, subflags,
12485-
loc);
1248612347
}
1248712348

12488-
return formUnsolved();
12349+
auto resolvedKPTy = BoundGenericType::get(kpDecl, nullptr, {rootTy, valueTy});
12350+
return matchTypes(resolvedKPTy, keyPathTy, ConstraintKind::Bind, subflags,
12351+
locator);
1248912352
}
1249012353

1249112354
ConstraintSystem::SolutionKind

lib/Sema/ConstraintSystem.cpp

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7385,9 +7385,11 @@ ConstraintSystem::inferKeyPathLiteralCapability(TypeVariableType *keyPathType) {
73857385
assert(typeLocator->isLastElement<LocatorPathElt::KeyPathType>());
73867386

73877387
auto *keyPath = castToExpr<KeyPathExpr>(typeLocator->getAnchor());
7388+
return inferKeyPathLiteralCapability(keyPath);
7389+
}
73887390

7389-
auto capability = KeyPathCapability::Writable;
7390-
7391+
std::pair<bool, llvm::Optional<KeyPathCapability>>
7392+
ConstraintSystem::inferKeyPathLiteralCapability(KeyPathExpr *keyPath) {
73917393
bool didOptionalChain = false;
73927394

73937395
auto fail = []() -> std::pair<bool, llvm::Optional<KeyPathCapability>> {
@@ -7403,6 +7405,7 @@ ConstraintSystem::inferKeyPathLiteralCapability(TypeVariableType *keyPathType) {
74037405
return std::make_pair(true, capability);
74047406
};
74057407

7408+
auto capability = KeyPathCapability::Writable;
74067409
for (unsigned i : indices(keyPath->getComponents())) {
74077410
auto &component = keyPath->getComponents()[i];
74087411

@@ -7423,8 +7426,14 @@ ConstraintSystem::inferKeyPathLiteralCapability(TypeVariableType *keyPathType) {
74237426
getConstraintLocator(keyPath, LocatorPathElt::KeyPathComponent(i));
74247427
auto *calleeLoc = getCalleeLocator(componentLoc);
74257428
auto overload = findSelectedOverloadFor(calleeLoc);
7426-
if (!overload)
7429+
if (!overload) {
7430+
// If overload cannot be found because member is missing,
7431+
// that's a failure.
7432+
if (hasFixFor(componentLoc, FixKind::DefineMemberBasedOnUse))
7433+
return fail();
7434+
74277435
return delay();
7436+
}
74287437

74297438
// tuple elements do not change the capability of the key path
74307439
auto choice = overload->choice;
@@ -7438,8 +7447,10 @@ ConstraintSystem::inferKeyPathLiteralCapability(TypeVariableType *keyPathType) {
74387447

74397448
auto storage = dyn_cast<AbstractStorageDecl>(choice.getDecl());
74407449

7441-
if (hasFixFor(calleeLoc, FixKind::AllowInvalidRefInKeyPath) ||
7442-
hasFixFor(calleeLoc, FixKind::UnwrapOptionalBase))
7450+
if (hasFixFor(componentLoc, FixKind::AllowInvalidRefInKeyPath) ||
7451+
hasFixFor(componentLoc, FixKind::UnwrapOptionalBase) ||
7452+
hasFixFor(componentLoc,
7453+
FixKind::UnwrapOptionalBaseWithOptionalResult))
74437454
return fail();
74447455

74457456
if (!storage)

0 commit comments

Comments
 (0)