Skip to content

Commit 2340350

Browse files
authored
Merge pull request #24097 from xedin/diag-mutating-getter-in-keypath
[ConstraintSystem] Detect and diagnoses use of members with mutating getters in key path
2 parents 86e4467 + eaf1dc0 commit 2340350

File tree

7 files changed

+164
-45
lines changed

7 files changed

+164
-45
lines changed

lib/Sema/CSApply.cpp

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4566,11 +4566,7 @@ namespace {
45664566
} else {
45674567
// Key paths don't work with mutating-get properties.
45684568
auto varDecl = cast<VarDecl>(property);
4569-
if (varDecl->isGetterMutating()) {
4570-
cs.TC.diagnose(componentLoc, diag::expr_keypath_mutating_getter,
4571-
property->getFullName());
4572-
}
4573-
4569+
assert(!varDecl->isGetterMutating());
45744570
// Key paths don't currently support static members.
45754571
// There is a fix which diagnoses such situation already.
45764572
assert(!varDecl->isStatic());
@@ -4605,10 +4601,7 @@ namespace {
46054601
SelectedOverload &overload, SourceLoc componentLoc, Expr *indexExpr,
46064602
ArrayRef<Identifier> labels, ConstraintLocator *locator) {
46074603
auto subscript = cast<SubscriptDecl>(overload.choice.getDecl());
4608-
if (subscript->isGetterMutating()) {
4609-
cs.TC.diagnose(componentLoc, diag::expr_keypath_mutating_getter,
4610-
subscript->getFullName());
4611-
}
4604+
assert(!subscript->isGetterMutating());
46124605

46134606
cs.TC.requestMemberLayout(subscript);
46144607

lib/Sema/CSDiagnostics.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2453,9 +2453,8 @@ bool KeyPathSubscriptIndexHashableFailure::diagnoseAsError() {
24532453
return true;
24542454
}
24552455

2456-
bool InvalidStaticMemberRefInKeyPath::diagnoseAsError() {
2456+
SourceLoc InvalidMemberRefInKeyPath::getLoc() const {
24572457
auto *anchor = getRawAnchor();
2458-
auto loc = anchor->getLoc();
24592458

24602459
if (auto *KPE = dyn_cast<KeyPathExpr>(anchor)) {
24612460
auto *locator = getLocator();
@@ -2465,9 +2464,18 @@ bool InvalidStaticMemberRefInKeyPath::diagnoseAsError() {
24652464
});
24662465

24672466
assert(component != locator->getPath().end());
2468-
loc = KPE->getComponents()[component->getValue()].getLoc();
2467+
return KPE->getComponents()[component->getValue()].getLoc();
24692468
}
24702469

2471-
emitDiagnostic(loc, diag::expr_keypath_static_member, Member->getBaseName());
2470+
return anchor->getLoc();
2471+
}
2472+
2473+
bool InvalidStaticMemberRefInKeyPath::diagnoseAsError() {
2474+
emitDiagnostic(getLoc(), diag::expr_keypath_static_member, getName());
2475+
return true;
2476+
}
2477+
2478+
bool InvalidMemberWithMutatingGetterInKeyPath::diagnoseAsError() {
2479+
emitDiagnostic(getLoc(), diag::expr_keypath_mutating_getter, getName());
24722480
return true;
24732481
}

lib/Sema/CSDiagnostics.h

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,6 +1031,26 @@ class KeyPathSubscriptIndexHashableFailure final : public FailureDiagnostic {
10311031
bool diagnoseAsError() override;
10321032
};
10331033

1034+
class InvalidMemberRefInKeyPath : public FailureDiagnostic {
1035+
ValueDecl *Member;
1036+
1037+
public:
1038+
InvalidMemberRefInKeyPath(Expr *root, ConstraintSystem &cs, ValueDecl *member,
1039+
ConstraintLocator *locator)
1040+
: FailureDiagnostic(root, cs, locator), Member(member) {
1041+
assert(member->hasName());
1042+
assert(locator->isForKeyPathComponent());
1043+
}
1044+
1045+
DeclName getName() const { return Member->getFullName(); }
1046+
1047+
bool diagnoseAsError() override = 0;
1048+
1049+
protected:
1050+
/// Compute location of the failure for diagnostic.
1051+
SourceLoc getLoc() const;
1052+
};
1053+
10341054
/// Diagnose an attempt to reference a static member as a key path component
10351055
/// e.g.
10361056
///
@@ -1041,16 +1061,39 @@ class KeyPathSubscriptIndexHashableFailure final : public FailureDiagnostic {
10411061
///
10421062
/// _ = \S.Type.foo
10431063
/// ```
1044-
class InvalidStaticMemberRefInKeyPath final : public FailureDiagnostic {
1045-
ValueDecl *Member;
1046-
1064+
class InvalidStaticMemberRefInKeyPath final : public InvalidMemberRefInKeyPath {
10471065
public:
10481066
InvalidStaticMemberRefInKeyPath(Expr *root, ConstraintSystem &cs,
10491067
ValueDecl *member, ConstraintLocator *locator)
1050-
: FailureDiagnostic(root, cs, locator), Member(member) {
1051-
assert(member->hasName());
1052-
assert(locator->isForKeyPathComponent());
1053-
}
1068+
: InvalidMemberRefInKeyPath(root, cs, member, locator) {}
1069+
1070+
bool diagnoseAsError() override;
1071+
};
1072+
1073+
/// Diagnose an attempt to reference a member which has a mutating getter as a
1074+
/// key path component e.g.
1075+
///
1076+
/// ```swift
1077+
/// struct S {
1078+
/// var foo: Int {
1079+
/// mutating get { return 42 }
1080+
/// }
1081+
///
1082+
/// subscript(_: Int) -> Bool {
1083+
/// mutating get { return false }
1084+
/// }
1085+
/// }
1086+
///
1087+
/// _ = \S.foo
1088+
/// _ = \S.[42]
1089+
/// ```
1090+
class InvalidMemberWithMutatingGetterInKeyPath final
1091+
: public InvalidMemberRefInKeyPath {
1092+
public:
1093+
InvalidMemberWithMutatingGetterInKeyPath(Expr *root, ConstraintSystem &cs,
1094+
ValueDecl *member,
1095+
ConstraintLocator *locator)
1096+
: InvalidMemberRefInKeyPath(root, cs, member, locator) {}
10541097

10551098
bool diagnoseAsError() override;
10561099
};

lib/Sema/CSFix.cpp

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -423,15 +423,26 @@ TreatKeyPathSubscriptIndexAsHashable::create(ConstraintSystem &cs, Type type,
423423
TreatKeyPathSubscriptIndexAsHashable(cs, type, locator);
424424
}
425425

426-
bool AllowStaticMemberRefInKeyPath::diagnose(Expr *root, bool asNote) const {
427-
InvalidStaticMemberRefInKeyPath failure(root, getConstraintSystem(), Member,
428-
getLocator());
429-
return failure.diagnose(asNote);
426+
bool AllowInvalidRefInKeyPath::diagnose(Expr *root, bool asNote) const {
427+
switch (Kind) {
428+
case RefKind::StaticMember: {
429+
InvalidStaticMemberRefInKeyPath failure(root, getConstraintSystem(), Member,
430+
getLocator());
431+
return failure.diagnose(asNote);
432+
}
433+
434+
case RefKind::MutatingGetter: {
435+
InvalidMemberWithMutatingGetterInKeyPath failure(
436+
root, getConstraintSystem(), Member, getLocator());
437+
return failure.diagnose(asNote);
438+
}
439+
}
430440
}
431441

432-
AllowStaticMemberRefInKeyPath *
433-
AllowStaticMemberRefInKeyPath::create(ConstraintSystem &cs, ValueDecl *member,
434-
ConstraintLocator *locator) {
442+
AllowInvalidRefInKeyPath *
443+
AllowInvalidRefInKeyPath::create(ConstraintSystem &cs, RefKind kind,
444+
ValueDecl *member,
445+
ConstraintLocator *locator) {
435446
return new (cs.getAllocator())
436-
AllowStaticMemberRefInKeyPath(cs, member, locator);
447+
AllowInvalidRefInKeyPath(cs, kind, member, locator);
437448
}

lib/Sema/CSFix.h

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,9 @@ enum class FixKind : uint8_t {
144144
/// of the index arguments to be Hashable.
145145
TreatKeyPathSubscriptIndexAsHashable,
146146

147-
/// Allow a reference to a static member as a key path component.
148-
AllowStaticMemberRefInKeyPath,
147+
/// Allow an invalid reference to a member declaration as part
148+
/// of a key path component.
149+
AllowInvalidRefInKeyPath,
149150
};
150151

151152
class ConstraintFix {
@@ -783,23 +784,51 @@ class TreatKeyPathSubscriptIndexAsHashable final : public ConstraintFix {
783784
create(ConstraintSystem &cs, Type type, ConstraintLocator *locator);
784785
};
785786

786-
class AllowStaticMemberRefInKeyPath final : public ConstraintFix {
787+
class AllowInvalidRefInKeyPath final : public ConstraintFix {
788+
enum RefKind {
789+
// Allow a reference to a static member as a key path component.
790+
StaticMember,
791+
// Allow a reference to a declaration with mutating getter as
792+
// a key path component.
793+
MutatingGetter,
794+
} Kind;
795+
787796
ValueDecl *Member;
788797

789-
AllowStaticMemberRefInKeyPath(ConstraintSystem &cs, ValueDecl *member,
790-
ConstraintLocator *locator)
791-
: ConstraintFix(cs, FixKind::AllowStaticMemberRefInKeyPath, locator),
792-
Member(member) {}
798+
AllowInvalidRefInKeyPath(ConstraintSystem &cs, RefKind kind,
799+
ValueDecl *member, ConstraintLocator *locator)
800+
: ConstraintFix(cs, FixKind::AllowInvalidRefInKeyPath, locator),
801+
Kind(kind), Member(member) {}
793802

794803
public:
795804
std::string getName() const override {
796-
return "allow reference to a static member as a key path component";
805+
switch (Kind) {
806+
case RefKind::StaticMember:
807+
return "allow reference to a static member as a key path component";
808+
case RefKind::MutatingGetter:
809+
return "allow reference to a member with mutating getter as a key "
810+
"path component";
811+
}
797812
}
798813

799814
bool diagnose(Expr *root, bool asNote = false) const override;
800815

801-
static AllowStaticMemberRefInKeyPath *
802-
create(ConstraintSystem &cs, ValueDecl *member, ConstraintLocator *locator);
816+
static AllowInvalidRefInKeyPath *forStaticMember(ConstraintSystem &cs,
817+
ValueDecl *member,
818+
ConstraintLocator *locator) {
819+
return create(cs, RefKind::StaticMember, member, locator);
820+
}
821+
822+
static AllowInvalidRefInKeyPath *
823+
forMutatingGetter(ConstraintSystem &cs, ValueDecl *member,
824+
ConstraintLocator *locator) {
825+
return create(cs, RefKind::MutatingGetter, member, locator);
826+
}
827+
828+
private:
829+
static AllowInvalidRefInKeyPath *create(ConstraintSystem &cs, RefKind kind,
830+
ValueDecl *member,
831+
ConstraintLocator *locator);
803832
};
804833

805834
} // end namespace constraints

lib/Sema/CSSimplify.cpp

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5023,14 +5023,21 @@ ConstraintSystem::simplifyKeyPathConstraint(Type keyPathTy,
50235023
}
50245024

50255025
// Referencing static members in key path is not currently allowed.
5026-
if (storage->isStatic()) {
5026+
if (storage->isStatic() || storage->isGetterMutating()) {
50275027
if (!shouldAttemptFixes())
50285028
return SolutionKind::Error;
50295029

5030-
auto componentLoc =
5031-
locator.withPathElement(LocatorPathElt::getKeyPathComponent(i));
5032-
auto *fix = AllowStaticMemberRefInKeyPath::create(
5033-
*this, choices[i].getDecl(), getConstraintLocator(componentLoc));
5030+
auto *componentLoc = getConstraintLocator(
5031+
locator.withPathElement(LocatorPathElt::getKeyPathComponent(i)));
5032+
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);
5040+
}
50345041

50355042
if (recordFix(fix))
50365043
return SolutionKind::Error;
@@ -6334,7 +6341,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
63346341
case FixKind::AllowInaccessibleMember:
63356342
case FixKind::AllowAnyObjectKeyPathRoot:
63366343
case FixKind::TreatKeyPathSubscriptIndexAsHashable:
6337-
case FixKind::AllowStaticMemberRefInKeyPath:
6344+
case FixKind::AllowInvalidRefInKeyPath:
63386345
llvm_unreachable("handled elsewhere");
63396346
}
63406347

test/expr/unary/keypath/keypath.swift

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -732,7 +732,7 @@ func test_keypath_with_static_members(_ p: P_With_Static_Members) {
732732
static var baz: Int = 42
733733
}
734734

735-
func foo(_ s: S) {
735+
func foo(_ s: S) {
736736
let _ = \S.Type.foo
737737
// expected-error@-1 {{key path cannot refer to static member 'foo'}}
738738
let _ = s[keyPath: \.foo]
@@ -748,6 +748,34 @@ func foo(_ s: S) {
748748
}
749749
}
750750

751+
func test_keypath_with_mutating_getter() {
752+
struct S {
753+
var foo: Int {
754+
mutating get { return 42 }
755+
}
756+
757+
subscript(_: Int) -> [Int] {
758+
mutating get { return [] }
759+
}
760+
}
761+
762+
_ = \S.foo
763+
// expected-error@-1 {{key path cannot refer to 'foo', which has a mutating getter}}
764+
let _: KeyPath<S, Int> = \.foo
765+
// expected-error@-1 {{key path cannot refer to 'foo', which has a mutating getter}}
766+
_ = \S.[0]
767+
// expected-error@-1 {{key path cannot refer to 'subscript(_:)', which has a mutating getter}}
768+
_ = \S.[0].count
769+
// expected-error@-1 {{key path cannot refer to 'subscript(_:)', which has a mutating getter}}
770+
771+
func test_via_subscript(_ s: S) {
772+
_ = s[keyPath: \.foo]
773+
// expected-error@-1 {{key path cannot refer to 'foo', which has a mutating getter}}
774+
_ = s[keyPath: \.[0].count]
775+
// expected-error@-1 {{key path cannot refer to 'subscript(_:)', which has a mutating getter}}
776+
}
777+
}
778+
751779
func testSyntaxErrors() { // expected-note{{}}
752780
_ = \. ; // expected-error{{expected member name following '.'}}
753781
_ = \.a ;

0 commit comments

Comments
 (0)