Skip to content

Commit 77d23ef

Browse files
authored
Merge pull request #24220 from xedin/diagnose-method-ref-in-keypath-5.1
[5.1][ConstraintSystem] Detect and fix use of method references in key path
2 parents 1f51364 + 2efdca1 commit 77d23ef

File tree

7 files changed

+148
-51
lines changed

7 files changed

+148
-51
lines changed

lib/Sema/CSApply.cpp

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4579,20 +4579,13 @@ namespace {
45794579
ConstraintLocator *locator) {
45804580
if (overload.choice.isDecl()) {
45814581
auto property = overload.choice.getDecl();
4582-
45834582
// Key paths can only refer to properties currently.
4584-
if (!isa<VarDecl>(property)) {
4585-
cs.TC.diagnose(componentLoc, diag::expr_keypath_not_property,
4586-
property->getDescriptiveKind(),
4587-
property->getFullName());
4588-
} else {
4589-
// Key paths don't work with mutating-get properties.
4590-
auto varDecl = cast<VarDecl>(property);
4591-
assert(!varDecl->isGetterMutating());
4592-
// Key paths don't currently support static members.
4593-
// There is a fix which diagnoses such situation already.
4594-
assert(!varDecl->isStatic());
4595-
}
4583+
auto varDecl = cast<VarDecl>(property);
4584+
// Key paths don't work with mutating-get properties.
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());
45964589

45974590
cs.TC.requestMemberLayout(property);
45984591

lib/Sema/CSDiagnostics.cpp

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

1515+
// Calls are not yet supported by key path, but it
1516+
// is useful to record this fix to diagnose chaining
1517+
// where one of the key path components is a method
1518+
// reference.
1519+
if (isa<KeyPathExpr>(baseExpr))
1520+
return false;
1521+
15151522
if (auto *DRE = dyn_cast<DeclRefExpr>(baseExpr)) {
15161523
emitDiagnostic(baseExpr->getLoc(), diag::did_not_call_function,
15171524
DRE->getDecl()->getBaseName().getIdentifier())
@@ -2475,3 +2482,9 @@ bool InvalidMemberWithMutatingGetterInKeyPath::diagnoseAsError() {
24752482
emitDiagnostic(getLoc(), diag::expr_keypath_mutating_getter, getName());
24762483
return true;
24772484
}
2485+
2486+
bool InvalidMethodRefInKeyPath::diagnoseAsError() {
2487+
emitDiagnostic(getLoc(), diag::expr_keypath_not_property, getKind(),
2488+
getName());
2489+
return true;
2490+
}

lib/Sema/CSDiagnostics.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,6 +1042,8 @@ class InvalidMemberRefInKeyPath : public FailureDiagnostic {
10421042
assert(locator->isForKeyPathComponent());
10431043
}
10441044

1045+
DescriptiveDeclKind getKind() const { return Member->getDescriptiveKind(); }
1046+
10451047
DeclName getName() const { return Member->getFullName(); }
10461048

10471049
bool diagnoseAsError() override = 0;
@@ -1098,6 +1100,29 @@ class InvalidMemberWithMutatingGetterInKeyPath final
10981100
bool diagnoseAsError() override;
10991101
};
11001102

1103+
/// Diagnose an attempt to reference a method as a key path component
1104+
/// e.g.
1105+
///
1106+
/// ```swift
1107+
/// struct S {
1108+
/// func foo() -> Int { return 42 }
1109+
/// static func bar() -> Int { return 0 }
1110+
/// }
1111+
///
1112+
/// _ = \S.foo
1113+
/// _ = \S.Type.bar
1114+
/// ```
1115+
class InvalidMethodRefInKeyPath final : public InvalidMemberRefInKeyPath {
1116+
public:
1117+
InvalidMethodRefInKeyPath(Expr *root, ConstraintSystem &cs, ValueDecl *method,
1118+
ConstraintLocator *locator)
1119+
: InvalidMemberRefInKeyPath(root, cs, method, locator) {
1120+
assert(isa<FuncDecl>(method));
1121+
}
1122+
1123+
bool diagnoseAsError() override;
1124+
};
1125+
11011126
} // end namespace constraints
11021127
} // end namespace swift
11031128

lib/Sema/CSFix.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,38 @@ 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+
InvalidMethodRefInKeyPath failure(root, getConstraintSystem(), Member,
442+
getLocator());
443+
return failure.diagnose(asNote);
444+
}
445+
}
446+
}
447+
448+
AllowInvalidRefInKeyPath *
449+
AllowInvalidRefInKeyPath::forRef(ConstraintSystem &cs, ValueDecl *member,
450+
ConstraintLocator *locator) {
451+
// Referencing (instance or static) methods in key path is
452+
// not currently allowed.
453+
if (isa<FuncDecl>(member))
454+
return AllowInvalidRefInKeyPath::create(cs, RefKind::Method, member,
455+
locator);
456+
457+
// Referencing static members in key path is not currently allowed.
458+
if (member->isStatic())
459+
return AllowInvalidRefInKeyPath::create(cs, RefKind::StaticMember, member,
460+
locator);
461+
462+
if (auto *storage = dyn_cast<AbstractStorageDecl>(member)) {
463+
// Referencing members with mutating getters in key path is not
464+
// currently allowed.
465+
if (storage->isGetterMutating())
466+
return AllowInvalidRefInKeyPath::create(cs, RefKind::MutatingGetter,
467+
member, locator);
439468
}
469+
470+
return nullptr;
440471
}
441472

442473
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
@@ -1932,15 +1932,29 @@ static ConstraintFix *fixRequirementFailure(ConstraintSystem &cs, Type type1,
19321932
}
19331933
}
19341934

1935-
static void
1935+
/// Attempt to repair typing failures and record fixes if needed.
1936+
/// \return true if at least some of the failures has been repaired
1937+
/// successfully, which allows type matcher to continue.
1938+
static bool
19361939
repairFailures(ConstraintSystem &cs, Type lhs, Type rhs,
19371940
SmallVectorImpl<RestrictionOrFix> &conversionsOrFixes,
19381941
ConstraintLocatorBuilder locator) {
19391942
SmallVector<LocatorPathElt, 4> path;
19401943
auto *anchor = locator.getLocatorParts(path);
19411944

1942-
if (path.empty())
1943-
return;
1945+
if (path.empty()) {
1946+
// If method reference forms a value type of the key path,
1947+
// there is going to be a constraint to match result of the
1948+
// member lookup to the generic parameter `V` of *KeyPath<R, V>
1949+
// type associated with key path expression, which we need to
1950+
// fix-up here.
1951+
if (anchor && isa<KeyPathExpr>(anchor)) {
1952+
auto *fnType = lhs->getAs<FunctionType>();
1953+
if (fnType && fnType->getResult()->isEqual(rhs))
1954+
return true;
1955+
}
1956+
return false;
1957+
}
19441958

19451959
auto &elt = path.back();
19461960
switch (elt.getKind()) {
@@ -1964,7 +1978,7 @@ repairFailures(ConstraintSystem &cs, Type lhs, Type rhs,
19641978
if (path.empty() ||
19651979
!(path.back().getKind() == ConstraintLocator::ApplyArgToParam ||
19661980
path.back().getKind() == ConstraintLocator::ContextualType))
1967-
return;
1981+
return false;
19681982

19691983
auto arg = llvm::find_if(cs.getTypeVariables(),
19701984
[&argLoc](const TypeVariableType *typeVar) {
@@ -2015,13 +2029,14 @@ repairFailures(ConstraintSystem &cs, Type lhs, Type rhs,
20152029
cs.getConstraintLocator(locator));
20162030
conversionsOrFixes.push_back(fix);
20172031
}
2018-
20192032
break;
20202033
}
20212034

20222035
default:
2023-
return;
2036+
break;
20242037
}
2038+
2039+
return !conversionsOrFixes.empty();
20252040
}
20262041

20272042
ConstraintSystem::TypeMatchResult
@@ -2806,8 +2821,12 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
28062821
}
28072822

28082823
// Attempt to repair any failures identifiable at this point.
2809-
if (attemptFixes)
2810-
repairFailures(*this, type1, type2, conversionsOrFixes, locator);
2824+
if (attemptFixes) {
2825+
if (repairFailures(*this, type1, type2, conversionsOrFixes, locator)) {
2826+
if (conversionsOrFixes.empty())
2827+
return getTypeMatchSuccess();
2828+
}
2829+
}
28112830

28122831
if (conversionsOrFixes.empty())
28132832
return getTypeMatchFailure(locator);
@@ -4298,7 +4317,7 @@ fixMemberRef(ConstraintSystem &cs, Type baseTy,
42984317
break;
42994318
case MemberLookupResult::UR_KeyPathWithAnyObjectRootType:
43004319
return AllowAnyObjectKeyPathRoot::create(cs, locator);
4301-
}
4320+
}
43024321
}
43034322

43044323
return nullptr;
@@ -5018,31 +5037,25 @@ ConstraintSystem::simplifyKeyPathConstraint(Type keyPathTy,
50185037
}
50195038

50205039
auto storage = dyn_cast<AbstractStorageDecl>(choices[i].getDecl());
5021-
if (!storage) {
5022-
return SolutionKind::Error;
5023-
}
50245040

5025-
// Referencing static members in key path is not currently allowed.
5026-
if (storage->isStatic() || storage->isGetterMutating()) {
5027-
if (!shouldAttemptFixes())
5028-
return SolutionKind::Error;
5041+
auto *componentLoc = getConstraintLocator(
5042+
locator.withPathElement(LocatorPathElt::getKeyPathComponent(i)));
50295043

5030-
auto *componentLoc = getConstraintLocator(
5031-
locator.withPathElement(LocatorPathElt::getKeyPathComponent(i)));
5044+
if (auto *fix = AllowInvalidRefInKeyPath::forRef(
5045+
*this, choices[i].getDecl(), componentLoc)) {
5046+
if (!shouldAttemptFixes() || recordFix(fix))
5047+
return SolutionKind::Error;
50325048

5033-
ConstraintFix *fix = nullptr;
5034-
if (storage->isStatic()) {
5035-
fix = AllowInvalidRefInKeyPath::forStaticMember(
5036-
*this, choices[i].getDecl(), componentLoc);
5037-
} else {
5038-
fix = AllowInvalidRefInKeyPath::forMutatingGetter(
5039-
*this, choices[i].getDecl(), componentLoc);
5049+
// If this was a method reference let's mark it as read-only.
5050+
if (!storage) {
5051+
capability = ReadOnly;
5052+
continue;
50405053
}
5041-
5042-
if (recordFix(fix))
5043-
return SolutionKind::Error;
50445054
}
50455055

5056+
if (!storage)
5057+
return SolutionKind::Error;
5058+
50465059
if (isReadOnlyKeyPathComponent(storage)) {
50475060
capability = ReadOnly;
50485061
continue;

test/expr/unary/keypath/keypath.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,31 @@ func test_keypath_with_mutating_getter() {
776776
}
777777
}
778778

779+
func test_keypath_with_method_refs() {
780+
struct S {
781+
func foo() -> Int { return 42 }
782+
static func bar() -> Int { return 0 }
783+
}
784+
785+
let _: KeyPath<S, Int> = \.foo // expected-error {{key path cannot refer to instance method 'foo()'}}
786+
let _: KeyPath<S, Int> = \.bar // expected-error {{key path cannot refer to static member 'bar()'}}
787+
let _ = \S.Type.bar // expected-error {{key path cannot refer to static method 'bar()'}}
788+
789+
struct A {
790+
func foo() -> B { return B() }
791+
static func faz() -> B { return B() }
792+
}
793+
794+
struct B {
795+
var bar: Int = 42
796+
}
797+
798+
let _: KeyPath<A, Int> = \.foo.bar // expected-error {{key path cannot refer to instance method 'foo()'}}
799+
let _: KeyPath<A, Int> = \.faz.bar // expected-error {{key path cannot refer to static member 'faz()'}}
800+
let _ = \A.foo.bar // expected-error {{key path cannot refer to instance method 'foo()'}}
801+
let _ = \A.Type.faz.bar // expected-error {{key path cannot refer to static method 'faz()'}}
802+
}
803+
779804
func testSyntaxErrors() { // expected-note{{}}
780805
_ = \. ; // expected-error{{expected member name following '.'}}
781806
_ = \.a ;

0 commit comments

Comments
 (0)