Skip to content

Commit a8fede4

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 b0c3dd2 commit a8fede4

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
@@ -12193,17 +12193,15 @@ ConstraintSystem::simplifyKeyPathConstraint(
1219312193
ConstraintLocatorBuilder locator) {
1219412194
auto subflags = getDefaultDecompositionOptions(flags);
1219512195
// The constraint ought to have been anchored on a KeyPathExpr.
12196-
auto keyPath = castToExpr<KeyPathExpr>(locator.getBaseLocator()->getAnchor());
12196+
auto keyPath = castToExpr<KeyPathExpr>(locator.getAnchor());
1219712197
keyPathTy = getFixedTypeRecursive(keyPathTy, /*want rvalue*/ true);
12198-
bool definitelyFunctionType = false;
12199-
bool definitelyKeyPathType = false;
1220012198
bool resolveAsMultiArgFuncFix = false;
1220112199

1220212200
auto formUnsolved = [&]() -> SolutionKind {
1220312201
if (flags.contains(TMF_GenerateConstraints)) {
1220412202
addUnsolvedConstraint(Constraint::create(
1220512203
*this, ConstraintKind::KeyPath, keyPathTy, rootTy, valueTy,
12206-
locator.getBaseLocator(), componentTypeVars));
12204+
getConstraintLocator(locator), componentTypeVars));
1220712205
return SolutionKind::Solved;
1220812206
}
1220912207
return SolutionKind::Unsolved;
@@ -12212,25 +12210,18 @@ ConstraintSystem::simplifyKeyPathConstraint(
1221212210
if (keyPathTy->isTypeVariableOrMember())
1221312211
return formUnsolved();
1221412212

12215-
auto tryMatchRootAndValueFromType = [&](Type type,
12216-
bool allowPartial = true) -> bool {
12213+
auto tryMatchRootAndValueFromType = [&](Type type) -> bool {
1221712214
Type boundRoot = Type(), boundValue = Type();
1221812215

1221912216
if (auto bgt = type->getAs<BoundGenericType>()) {
12220-
definitelyKeyPathType = true;
12221-
1222212217
// We can get root and value from a concrete key path type.
1222312218
if (bgt->isKeyPath() ||
1222412219
bgt->isWritableKeyPath() ||
1222512220
bgt->isReferenceWritableKeyPath()) {
1222612221
boundRoot = bgt->getGenericArgs()[0];
1222712222
boundValue = bgt->getGenericArgs()[1];
12228-
} else if (bgt->isPartialKeyPath()) {
12229-
if (!allowPartial)
12230-
return false;
12231-
12232-
// We can still get the root from a PartialKeyPath.
12233-
boundRoot = bgt->getGenericArgs()[0];
12223+
} else {
12224+
return false;
1223412225
}
1223512226
}
1223612227

@@ -12241,13 +12232,11 @@ ConstraintSystem::simplifyKeyPathConstraint(
1224112232

1224212233
resolveAsMultiArgFuncFix = true;
1224312234
auto *fix = AllowMultiArgFuncKeyPathMismatch::create(
12244-
*this, fnTy, locator.getBaseLocator());
12235+
*this, fnTy, getConstraintLocator(locator));
1224512236
// Pretend the keypath type got resolved and move on.
1224612237
return !recordFix(fix);
1224712238
}
1224812239

12249-
definitelyFunctionType = true;
12250-
1225112240
// Match up the root and value types to the function's param and return
1225212241
// types. Note that we're using the type of the parameter as referenced
1225312242
// from inside the function body as we'll be transforming the code into:
@@ -12284,192 +12273,66 @@ ConstraintSystem::simplifyKeyPathConstraint(
1228412273
// If the root type has been bound to a hole, we cannot infer it.
1228512274
if (getFixedTypeRecursive(rootTy, /*wantRValue*/ true)->isPlaceholder())
1228612275
return SolutionKind::Solved;
12287-
12288-
// If we have e.g a missing member somewhere, a component type variable
12289-
// would either be marked as a potential hole or would have a fix.
12290-
if (llvm::any_of(componentTypeVars, [&](TypeVariableType *tv) {
12291-
auto *locator = tv->getImpl().getLocator();
12292-
12293-
// Result type of a component could be bound to a contextual
12294-
// (concrete) type if it's the last component in the chain,
12295-
// so the only way to detect errors is to check for fixes.
12296-
if (locator->isForKeyPathComponentResult()) {
12297-
auto path = locator->getPath();
12298-
auto *componentLoc =
12299-
getConstraintLocator(locator->getAnchor(), path.drop_back());
12300-
12301-
if (hasFixFor(componentLoc, FixKind::DefineMemberBasedOnUse) ||
12302-
hasFixFor(componentLoc, FixKind::UnwrapOptionalBase) ||
12303-
hasFixFor(componentLoc,
12304-
FixKind::UnwrapOptionalBaseWithOptionalResult))
12305-
return true;
12306-
}
12307-
12308-
// If something inside of a component is marked as a hole,
12309-
// let's consider while component to be invalid.
12310-
return locator->isInKeyPathComponent() &&
12311-
tv->getImpl().canBindToHole();
12312-
})) {
12313-
(void)tryMatchRootAndValueFromType(keyPathTy);
12314-
return SolutionKind::Solved;
12315-
}
1231612276
}
1231712277

1231812278
// If we're fixed to a bound generic type, trying harvesting context from it.
1231912279
// However, we don't want a solution that fixes the expression type to
1232012280
// PartialKeyPath; we'd rather that be represented using an upcast conversion.
12321-
if (!tryMatchRootAndValueFromType(keyPathTy, /*allowPartial=*/false))
12281+
if (!tryMatchRootAndValueFromType(keyPathTy))
1232212282
return SolutionKind::Error;
1232312283

1232412284
// If we fix this keypath as `AllowMultiArgFuncKeyPathMismatch`, just proceed
1232512285
if (resolveAsMultiArgFuncFix)
1232612286
return SolutionKind::Solved;
1232712287

12328-
// See if we resolved overloads for all the components involved.
12329-
enum {
12330-
ReadOnly,
12331-
Writable,
12332-
ReferenceWritable
12333-
} capability = Writable;
12288+
bool isValid;
12289+
llvm::Optional<KeyPathCapability> capability;
1233412290

12335-
bool anyComponentsUnresolved = false;
12336-
bool didOptionalChain = false;
12291+
std::tie(isValid, capability) = inferKeyPathLiteralCapability(keyPath);
1233712292

12338-
for (unsigned i : indices(keyPath->getComponents())) {
12339-
auto &component = keyPath->getComponents()[i];
12340-
12341-
switch (component.getKind()) {
12342-
case KeyPathExpr::Component::Kind::Invalid:
12343-
case KeyPathExpr::Component::Kind::Identity:
12344-
break;
12293+
// If key path is invalid, let's not don't attempt match capabilities.
12294+
if (!isValid)
12295+
return shouldAttemptFixes() ? SolutionKind::Solved : SolutionKind::Error;
1234512296

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

1243512302
// Resolve the type.
1243612303
NominalTypeDecl *kpDecl;
12437-
switch (capability) {
12438-
case ReadOnly:
12304+
switch (*capability) {
12305+
case KeyPathCapability::ReadOnly:
1243912306
kpDecl = getASTContext().getKeyPathDecl();
1244012307
break;
1244112308

12442-
case Writable:
12309+
case KeyPathCapability::Writable:
1244312310
kpDecl = getASTContext().getWritableKeyPathDecl();
1244412311
break;
1244512312

12446-
case ReferenceWritable:
12313+
case KeyPathCapability::ReferenceWritable:
1244712314
kpDecl = getASTContext().getReferenceWritableKeyPathDecl();
1244812315
break;
1244912316
}
12450-
12317+
1245112318
// FIXME: Allow the type to be upcast if the type system has a concrete
1245212319
// KeyPath type assigned to the expression already.
1245312320
if (auto keyPathBGT = keyPathTy->getAs<BoundGenericType>()) {
1245412321
if (keyPathBGT->isKeyPath())
1245512322
kpDecl = getASTContext().getKeyPathDecl();
12456-
else if (keyPathBGT->isWritableKeyPath() && capability >= Writable)
12323+
else if (keyPathBGT->isWritableKeyPath() &&
12324+
*capability >= KeyPathCapability::Writable)
1245712325
kpDecl = getASTContext().getWritableKeyPathDecl();
1245812326
}
1245912327

12460-
auto loc = locator.getBaseLocator();
12461-
if (definitelyFunctionType) {
12328+
if (keyPathTy->is<FunctionType>()) {
1246212329
increaseScore(SK_FunctionConversion, locator);
1246312330
return SolutionKind::Solved;
12464-
} else if (!anyComponentsUnresolved ||
12465-
(definitelyKeyPathType && capability == ReadOnly)) {
12466-
auto resolvedKPTy =
12467-
BoundGenericType::get(kpDecl, nullptr, {rootTy, valueTy});
12468-
return matchTypes(resolvedKPTy, keyPathTy, ConstraintKind::Bind, subflags,
12469-
loc);
1247012331
}
1247112332

12472-
return formUnsolved();
12333+
auto resolvedKPTy = BoundGenericType::get(kpDecl, nullptr, {rootTy, valueTy});
12334+
return matchTypes(resolvedKPTy, keyPathTy, ConstraintKind::Bind, subflags,
12335+
locator);
1247312336
}
1247412337

1247512338
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)