Skip to content

Commit d6a3a31

Browse files
committed
[ConstraintSystem] Detect and fix use of method references in key path
Referencing (instance or static) methods in the key path is not currently allowed, solver should be responsible for early detection and diagnosis of both standalone e.g. `\.foo` and chained e.g. `\.foo.bar` (where foo is a method) references in key path components. ```swift struct S { func foo() -> Int { return 42 } } let _: KeyPath<S, Int> = \.foo ``` Resolves: rdar://problem/49413561
1 parent bd1ce98 commit d6a3a31

File tree

5 files changed

+90
-51
lines changed

5 files changed

+90
-51
lines changed

lib/Sema/CSApply.cpp

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4573,20 +4573,13 @@ namespace {
45734573
ConstraintLocator *locator) {
45744574
if (overload.choice.isDecl()) {
45754575
auto property = overload.choice.getDecl();
4576-
45774576
// Key paths can only refer to properties currently.
4578-
if (!isa<VarDecl>(property)) {
4579-
cs.TC.diagnose(componentLoc, diag::expr_keypath_not_property,
4580-
property->getDescriptiveKind(),
4581-
property->getFullName());
4582-
} else {
4583-
// Key paths don't work with mutating-get properties.
4584-
auto varDecl = cast<VarDecl>(property);
4585-
assert(!varDecl->isGetterMutating());
4586-
// Key paths don't currently support static members.
4587-
// There is a fix which diagnoses such situation already.
4588-
assert(!varDecl->isStatic());
4589-
}
4577+
auto varDecl = cast<VarDecl>(property);
4578+
// Key paths don't work with mutating-get properties.
4579+
assert(!varDecl->isGetterMutating());
4580+
// Key paths don't currently support static members.
4581+
// There is a fix which diagnoses such situation already.
4582+
assert(!varDecl->isStatic());
45904583

45914584
cs.TC.requestMemberLayout(property);
45924585

lib/Sema/CSDiagnostics.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1516,6 +1516,13 @@ bool MissingCallFailure::diagnoseAsError() {
15161516
if (auto *FVE = dyn_cast<ForceValueExpr>(baseExpr))
15171517
baseExpr = FVE->getSubExpr();
15181518

1519+
// Calls are not yet supported by key path, but it
1520+
// is useful to record this fix to diagnose chaining
1521+
// where one of the key path components is a method
1522+
// reference.
1523+
if (isa<KeyPathExpr>(baseExpr))
1524+
return false;
1525+
15191526
if (auto *DRE = dyn_cast<DeclRefExpr>(baseExpr)) {
15201527
emitDiagnostic(baseExpr->getLoc(), diag::did_not_call_function,
15211528
DRE->getDecl()->getBaseName().getIdentifier())

lib/Sema/CSFix.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,36 @@ bool AllowInvalidRefInKeyPath::diagnose(Expr *root, bool asNote) const {
436436
root, getConstraintSystem(), Member, getLocator());
437437
return failure.diagnose(asNote);
438438
}
439+
440+
case RefKind::Method: {
441+
return false;
442+
}
443+
}
444+
}
445+
446+
AllowInvalidRefInKeyPath *
447+
AllowInvalidRefInKeyPath::forRef(ConstraintSystem &cs, ValueDecl *member,
448+
ConstraintLocator *locator) {
449+
// Referencing (instance or static) methods in key path is
450+
// not currently allowed.
451+
if (isa<FuncDecl>(member))
452+
return AllowInvalidRefInKeyPath::create(cs, RefKind::Method, member,
453+
locator);
454+
455+
// Referencing static members in key path is not currently allowed.
456+
if (member->isStatic())
457+
return AllowInvalidRefInKeyPath::create(cs, RefKind::StaticMember, member,
458+
locator);
459+
460+
if (auto *storage = dyn_cast<AbstractStorageDecl>(member)) {
461+
// Referencing members with mutating getters in key path is not
462+
// currently allowed.
463+
if (storage->isGetterMutating())
464+
return AllowInvalidRefInKeyPath::create(cs, RefKind::MutatingGetter,
465+
member, locator);
439466
}
467+
468+
return nullptr;
440469
}
441470

442471
AllowInvalidRefInKeyPath *

lib/Sema/CSFix.h

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -791,6 +791,9 @@ class AllowInvalidRefInKeyPath final : public ConstraintFix {
791791
// Allow a reference to a declaration with mutating getter as
792792
// a key path component.
793793
MutatingGetter,
794+
// Allow a reference to a method (instance or static) as
795+
// a key path component.
796+
Method,
794797
} Kind;
795798

796799
ValueDecl *Member;
@@ -808,22 +811,16 @@ class AllowInvalidRefInKeyPath final : public ConstraintFix {
808811
case RefKind::MutatingGetter:
809812
return "allow reference to a member with mutating getter as a key "
810813
"path component";
814+
case RefKind::Method:
815+
return "allow reference to a method as a key path component";
811816
}
812817
}
813818

814819
bool diagnose(Expr *root, bool asNote = false) const override;
815820

816-
static AllowInvalidRefInKeyPath *forStaticMember(ConstraintSystem &cs,
817-
ValueDecl *member,
818-
ConstraintLocator *locator) {
819-
return create(cs, RefKind::StaticMember, member, locator);
820-
}
821-
821+
/// Determine whether give reference requires a fix and produce one.
822822
static AllowInvalidRefInKeyPath *
823-
forMutatingGetter(ConstraintSystem &cs, ValueDecl *member,
824-
ConstraintLocator *locator) {
825-
return create(cs, RefKind::MutatingGetter, member, locator);
826-
}
823+
forRef(ConstraintSystem &cs, ValueDecl *member, ConstraintLocator *locator);
827824

828825
private:
829826
static AllowInvalidRefInKeyPath *create(ConstraintSystem &cs, RefKind kind,

lib/Sema/CSSimplify.cpp

Lines changed: 41 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1975,15 +1975,29 @@ static ConstraintFix *fixRequirementFailure(ConstraintSystem &cs, Type type1,
19751975
}
19761976
}
19771977

1978-
static void
1978+
/// Attempt to repair typing failures and record fixes if needed.
1979+
/// \return true if at least some of the failures has been repaired
1980+
/// successfully, which allows type matcher to continue.
1981+
static bool
19791982
repairFailures(ConstraintSystem &cs, Type lhs, Type rhs,
19801983
SmallVectorImpl<RestrictionOrFix> &conversionsOrFixes,
19811984
ConstraintLocatorBuilder locator) {
19821985
SmallVector<LocatorPathElt, 4> path;
19831986
auto *anchor = locator.getLocatorParts(path);
19841987

1985-
if (path.empty())
1986-
return;
1988+
if (path.empty()) {
1989+
// If method reference forms a value type of the key path,
1990+
// there is going to be a constraint to match result of the
1991+
// member lookup to the generic parameter `V` of *KeyPath<R, V>
1992+
// type associated with key path expression, which we need to
1993+
// fix-up here.
1994+
if (anchor && isa<KeyPathExpr>(anchor)) {
1995+
auto *fnType = lhs->getAs<FunctionType>();
1996+
if (fnType && fnType->getResult()->isEqual(rhs))
1997+
return true;
1998+
}
1999+
return false;
2000+
}
19872001

19882002
auto &elt = path.back();
19892003
switch (elt.getKind()) {
@@ -2007,7 +2021,7 @@ repairFailures(ConstraintSystem &cs, Type lhs, Type rhs,
20072021
if (path.empty() ||
20082022
!(path.back().getKind() == ConstraintLocator::ApplyArgToParam ||
20092023
path.back().getKind() == ConstraintLocator::ContextualType))
2010-
return;
2024+
return false;
20112025

20122026
auto arg = llvm::find_if(cs.getTypeVariables(),
20132027
[&argLoc](const TypeVariableType *typeVar) {
@@ -2058,13 +2072,14 @@ repairFailures(ConstraintSystem &cs, Type lhs, Type rhs,
20582072
cs.getConstraintLocator(locator));
20592073
conversionsOrFixes.push_back(fix);
20602074
}
2061-
20622075
break;
20632076
}
20642077

20652078
default:
2066-
return;
2079+
break;
20672080
}
2081+
2082+
return !conversionsOrFixes.empty();
20682083
}
20692084

20702085
ConstraintSystem::TypeMatchResult
@@ -2873,8 +2888,12 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
28732888
}
28742889

28752890
// Attempt to repair any failures identifiable at this point.
2876-
if (attemptFixes)
2877-
repairFailures(*this, type1, type2, conversionsOrFixes, locator);
2891+
if (attemptFixes) {
2892+
if (repairFailures(*this, type1, type2, conversionsOrFixes, locator)) {
2893+
if (conversionsOrFixes.empty())
2894+
return getTypeMatchSuccess();
2895+
}
2896+
}
28782897

28792898
if (conversionsOrFixes.empty())
28802899
return getTypeMatchFailure(locator);
@@ -4368,7 +4387,7 @@ fixMemberRef(ConstraintSystem &cs, Type baseTy,
43684387
break;
43694388
case MemberLookupResult::UR_KeyPathWithAnyObjectRootType:
43704389
return AllowAnyObjectKeyPathRoot::create(cs, locator);
4371-
}
4390+
}
43724391
}
43734392

43744393
return nullptr;
@@ -5124,31 +5143,25 @@ ConstraintSystem::simplifyKeyPathConstraint(Type keyPathTy,
51245143
}
51255144

51265145
auto storage = dyn_cast<AbstractStorageDecl>(choices[i].getDecl());
5127-
if (!storage) {
5128-
return SolutionKind::Error;
5129-
}
51305146

5131-
// Referencing static members in key path is not currently allowed.
5132-
if (storage->isStatic() || storage->isGetterMutating()) {
5133-
if (!shouldAttemptFixes())
5134-
return SolutionKind::Error;
5147+
auto *componentLoc = getConstraintLocator(
5148+
locator.withPathElement(LocatorPathElt::getKeyPathComponent(i)));
51355149

5136-
auto *componentLoc = getConstraintLocator(
5137-
locator.withPathElement(LocatorPathElt::getKeyPathComponent(i)));
5150+
if (auto *fix = AllowInvalidRefInKeyPath::forRef(
5151+
*this, choices[i].getDecl(), componentLoc)) {
5152+
if (!shouldAttemptFixes() || recordFix(fix))
5153+
return SolutionKind::Error;
51385154

5139-
ConstraintFix *fix = nullptr;
5140-
if (storage->isStatic()) {
5141-
fix = AllowInvalidRefInKeyPath::forStaticMember(
5142-
*this, choices[i].getDecl(), componentLoc);
5143-
} else {
5144-
fix = AllowInvalidRefInKeyPath::forMutatingGetter(
5145-
*this, choices[i].getDecl(), componentLoc);
5155+
// If this was a method reference let's mark it as read-only.
5156+
if (!storage) {
5157+
capability = ReadOnly;
5158+
continue;
51465159
}
5147-
5148-
if (recordFix(fix))
5149-
return SolutionKind::Error;
51505160
}
51515161

5162+
if (!storage)
5163+
return SolutionKind::Error;
5164+
51525165
if (isReadOnlyKeyPathComponent(storage)) {
51535166
capability = ReadOnly;
51545167
continue;

0 commit comments

Comments
 (0)