Skip to content

Commit 21216d8

Browse files
committed
[ConstraintSystem] Detect and fix invalid refs in dynamic key path member lookup
KeyPath dynamic member lookup is limited to what key path itself could do, so let's detect and diagnose invalid references just like we do for regular key path expressions. Resolves: rdar://problem/50376224
1 parent 3acd050 commit 21216d8

File tree

8 files changed

+60
-18
lines changed

8 files changed

+60
-18
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -473,13 +473,15 @@ ERROR(expr_keypath_type_of_property,none,
473473
ERROR(expr_keypath_generic_type,none,
474474
"key path cannot refer to generic type %0", (DeclName))
475475
ERROR(expr_keypath_not_property,none,
476-
"key path cannot refer to %0 %1", (DescriptiveDeclKind, DeclName))
476+
"%select{key path|dynamic key path member lookup}2 cannot refer to %0 %1",
477+
(DescriptiveDeclKind, DeclName, bool))
477478
ERROR(expr_keypath_mutating_getter,none,
478-
"key path cannot refer to %0, which has a mutating getter",
479-
(DeclName))
479+
"%select{key path|dynamic key path member lookup}1 cannot refer to %0, "
480+
"which has a mutating getter",
481+
(DeclName, bool))
480482
ERROR(expr_keypath_static_member,none,
481-
"key path cannot refer to static member %0",
482-
(DeclName))
483+
"%select{key path|dynamic key path member lookup}1 cannot refer to static member %0",
484+
(DeclName, bool))
483485
ERROR(expr_keypath_empty,none,
484486
"empty key path does not refer to a property", ())
485487
ERROR(expr_unsupported_objc_key_path_component,none,

lib/Sema/CSDiagnostics.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2532,17 +2532,19 @@ SourceLoc InvalidMemberRefInKeyPath::getLoc() const {
25322532
}
25332533

25342534
bool InvalidStaticMemberRefInKeyPath::diagnoseAsError() {
2535-
emitDiagnostic(getLoc(), diag::expr_keypath_static_member, getName());
2535+
emitDiagnostic(getLoc(), diag::expr_keypath_static_member, getName(),
2536+
isForKeyPathDynamicMemberLookup());
25362537
return true;
25372538
}
25382539

25392540
bool InvalidMemberWithMutatingGetterInKeyPath::diagnoseAsError() {
2540-
emitDiagnostic(getLoc(), diag::expr_keypath_mutating_getter, getName());
2541+
emitDiagnostic(getLoc(), diag::expr_keypath_mutating_getter, getName(),
2542+
isForKeyPathDynamicMemberLookup());
25412543
return true;
25422544
}
25432545

25442546
bool InvalidMethodRefInKeyPath::diagnoseAsError() {
25452547
emitDiagnostic(getLoc(), diag::expr_keypath_not_property, getKind(),
2546-
getName());
2548+
getName(), isForKeyPathDynamicMemberLookup());
25472549
return true;
25482550
}

lib/Sema/CSDiagnostics.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1039,7 +1039,8 @@ class InvalidMemberRefInKeyPath : public FailureDiagnostic {
10391039
ConstraintLocator *locator)
10401040
: FailureDiagnostic(root, cs, locator), Member(member) {
10411041
assert(member->hasName());
1042-
assert(locator->isForKeyPathComponent());
1042+
assert(locator->isForKeyPathComponent() ||
1043+
locator->isForKeyPathDynamicMemberLookup());
10431044
}
10441045

10451046
DescriptiveDeclKind getKind() const { return Member->getDescriptiveKind(); }
@@ -1051,6 +1052,10 @@ class InvalidMemberRefInKeyPath : public FailureDiagnostic {
10511052
protected:
10521053
/// Compute location of the failure for diagnostic.
10531054
SourceLoc getLoc() const;
1055+
1056+
bool isForKeyPathDynamicMemberLookup() const {
1057+
return getLocator()->isForKeyPathDynamicMemberLookup();
1058+
}
10541059
};
10551060

10561061
/// Diagnose an attempt to reference a static member as a key path component

lib/Sema/CSSimplify.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3710,11 +3710,6 @@ bool swift::hasDynamicMemberLookupAttribute(Type type) {
37103710
return ::hasDynamicMemberLookupAttribute(type, DynamicMemberLookupCache);
37113711
}
37123712

3713-
static bool isKeyPathDynamicMemberLookup(ConstraintLocator *locator) {
3714-
auto path = locator ? locator->getPath() : None;
3715-
return !path.empty() && path.back().isKeyPathDynamicMember();
3716-
}
3717-
37183713
/// Given a ValueMember, UnresolvedValueMember, or TypeMember constraint,
37193714
/// perform a lookup into the specified base type to find a candidate list.
37203715
/// The list returned includes the viable candidates as well as the unviable
@@ -3753,7 +3748,7 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
37533748
// is a single argument with `keypath:` label or `\.` syntax is used.
37543749
if (memberName.isSimpleName() &&
37553750
memberName.getBaseName().getKind() == DeclBaseName::Kind::Subscript &&
3756-
!isKeyPathDynamicMemberLookup(memberLocator)) {
3751+
!(memberLocator && memberLocator->isForKeyPathDynamicMemberLookup())) {
37573752
if (baseTy->isAnyObject()) {
37583753
result.addUnviable(
37593754
OverloadChoice(baseTy, OverloadChoiceKind::KeyPathApplication),
@@ -4019,7 +4014,7 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
40194014
// based dynamic member lookup. Since it's unknown upfront
40204015
// what kind of declaration lookup is going to find, let's
40214016
// double check here that given keypath is appropriate for it.
4022-
if (isKeyPathDynamicMemberLookup(memberLocator)) {
4017+
if (memberLocator && memberLocator->isForKeyPathDynamicMemberLookup()) {
40234018
auto path = memberLocator->getPath();
40244019
auto *keyPath = path.back().getKeyPath();
40254020
if (auto *storage = dyn_cast<AbstractStorageDecl>(decl)) {
@@ -4416,10 +4411,17 @@ fixMemberRef(ConstraintSystem &cs, Type baseTy,
44164411
// Not all of the choices handled here are going
44174412
// to refer to a declaration.
44184413
if (choice.isDecl()) {
4419-
if (auto *CD = dyn_cast<ConstructorDecl>(choice.getDecl())) {
4414+
auto *decl = choice.getDecl();
4415+
4416+
if (auto *CD = dyn_cast<ConstructorDecl>(decl)) {
44204417
if (auto *fix = validateInitializerRef(cs, CD, locator))
44214418
return fix;
44224419
}
4420+
4421+
if (locator->isForKeyPathDynamicMemberLookup()) {
4422+
if (auto *fix = AllowInvalidRefInKeyPath::forRef(cs, decl, locator))
4423+
return fix;
4424+
}
44234425
}
44244426

44254427
if (reason) {

lib/Sema/ConstraintLocator.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,11 @@ bool ConstraintLocator::isKeyPathSubscriptComponent() const {
159159
});
160160
}
161161

162+
bool ConstraintLocator::isForKeyPathDynamicMemberLookup() const {
163+
auto path = getPath();
164+
return !path.empty() && path.back().isKeyPathDynamicMember();
165+
}
166+
162167
bool ConstraintLocator::isForKeyPathComponent() const {
163168
return llvm::any_of(getPath(), [&](const LocatorPathElt &elt) {
164169
return elt.isKeyPathComponent();

lib/Sema/ConstraintLocator.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,10 @@ class ConstraintLocator : public llvm::FoldingSetNode {
560560
/// of the key path at some index.
561561
bool isKeyPathSubscriptComponent() const;
562562

563+
/// Determine whether this locator points to the member found
564+
/// via key path dynamic member lookup.
565+
bool isForKeyPathDynamicMemberLookup() const;
566+
563567
/// Determine whether this locator points to one of the key path
564568
/// components.
565569
bool isForKeyPathComponent() const;

lib/Sema/TypeCheckExprObjC.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,8 @@ Optional<Type> TypeChecker::checkObjCKeyPathExpr(DeclContext *dc,
395395

396396
// Declarations that cannot be part of a key-path.
397397
diagnose(componentNameLoc, diag::expr_keypath_not_property,
398-
found->getDescriptiveKind(), found->getFullName());
398+
found->getDescriptiveKind(), found->getFullName(),
399+
/*isForDynamicKeyPathMemberLookup=*/false);
399400
isInvalid = true;
400401
break;
401402
}

test/attr/attr_dynamic_member_lookup.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -667,3 +667,24 @@ func test_chain_of_recursive_lookups(_ lens: Lens<Lens<Lens<Point>>>) {
667667
_ = \Lens<Lens<Point>>.x
668668
_ = \Lens<Lens<Point>>.obj.x
669669
}
670+
671+
// KeyPath Dynamic Member Lookup can't refer to methods, mutating setters and static members
672+
// because of the KeyPath limitations
673+
func invalid_refs_through_dynamic_lookup() {
674+
struct S {
675+
static var foo: Int = 42
676+
func bar() -> Q { return Q() }
677+
static func baz(_: String) -> Int { return 0 }
678+
}
679+
680+
struct Q {
681+
var faz: Int = 0
682+
}
683+
684+
func test(_ lens: A<S>) {
685+
_ = lens.foo // expected-error {{dynamic key path member lookup cannot refer to static member 'foo'}}
686+
_ = lens.bar() // expected-error {{dynamic key path member lookup cannot refer to instance method 'bar()'}}
687+
_ = lens.bar().faz + 1 // expected-error {{dynamic key path member lookup cannot refer to instance method 'bar()'}}
688+
_ = lens.baz("hello") // expected-error {{dynamic key path member lookup cannot refer to static method 'baz'}}
689+
}
690+
}

0 commit comments

Comments
 (0)