Skip to content

Commit f329eec

Browse files
authored
Merge pull request #31776 from artemcm/5.3-UnlabeledKeypathFix
[5.3][Type-checker] Improve diagnostic for keypath used without 'keyPath:' label
2 parents a44ddac + ecea9aa commit f329eec

File tree

2 files changed

+67
-6
lines changed

2 files changed

+67
-6
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5682,6 +5682,19 @@ static bool isForKeyPathSubscript(ConstraintSystem &cs,
56825682
return false;
56835683
}
56845684

5685+
static bool isForKeyPathSubscriptWithoutLabel(ConstraintSystem &cs,
5686+
ConstraintLocator *locator) {
5687+
if (!locator || !locator->getAnchor())
5688+
return false;
5689+
5690+
if (auto *SE = dyn_cast<SubscriptExpr>(locator->getAnchor())) {
5691+
auto *indexExpr = SE->getIndex();
5692+
return isa<ParenExpr>(indexExpr) &&
5693+
isa<KeyPathExpr>(indexExpr->getSemanticsProvidingExpr());
5694+
}
5695+
return false;
5696+
}
5697+
56855698
/// Determine whether all of the given candidate overloads
56865699
/// found through conditional conformances of a given base type.
56875700
/// This is useful to figure out whether it makes sense to
@@ -5809,7 +5822,13 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
58095822
MemberLookupResult result;
58105823
result.OverallResult = MemberLookupResult::HasResults;
58115824

5812-
if (isForKeyPathSubscript(*this, memberLocator)) {
5825+
// Add key path result.
5826+
// If we are including inaccessible members, check for the use of a keypath
5827+
// subscript without a `keyPath:` label. Add it to the result so that it
5828+
// can be caught by the missing argument label checking later.
5829+
if (isForKeyPathSubscript(*this, memberLocator) ||
5830+
(isForKeyPathSubscriptWithoutLabel(*this, memberLocator)
5831+
&& includeInaccessibleMembers)) {
58135832
if (baseTy->isAnyObject()) {
58145833
result.addUnviable(
58155834
OverloadChoice(baseTy, OverloadChoiceKind::KeyPathApplication),
@@ -9694,14 +9713,21 @@ ConstraintSystem::addKeyPathApplicationRootConstraint(Type root, ConstraintLocat
96949713
path[0].getKind() == ConstraintLocator::SubscriptMember) ||
96959714
(path.size() == 2 &&
96969715
path[1].getKind() == ConstraintLocator::KeyPathDynamicMember));
9716+
96979717
auto indexTuple = dyn_cast<TupleExpr>(subscript->getIndex());
9698-
if (!indexTuple || indexTuple->getNumElements() != 1)
9699-
return;
9700-
9701-
auto keyPathExpr = dyn_cast<KeyPathExpr>(indexTuple->getElement(0));
9718+
auto indexParen = dyn_cast<ParenExpr>(subscript->getIndex());
9719+
// If a keypath subscript is used without the expected `keyPath:` label,
9720+
// continue with type-checking when attempting fixes so that it gets caught
9721+
// by the argument label checking. In such cases, the KeyPathExpr is contained
9722+
// in a ParenExpr, instead of a TupleExpr.
9723+
assert(((indexTuple && indexTuple->getNumElements() == 1) || indexParen) &&
9724+
"Expected KeyPathExpr to be in either TupleExpr or ParenExpr");
9725+
9726+
auto keyPathExpr = dyn_cast<KeyPathExpr>(
9727+
indexTuple ? indexTuple->getElement(0) : indexParen->getSubExpr());
97029728
if (!keyPathExpr)
97039729
return;
9704-
9730+
97059731
auto typeVar = getType(keyPathExpr)->getAs<TypeVariableType>();
97069732
if (!typeVar)
97079733
return;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// RUN: %target-swift-frontend -typecheck -verify -primary-file %s
2+
// [SR-12745]
3+
// rdar://problem/62957095
4+
struct S1 {
5+
var x : Int = 0
6+
}
7+
var s1 = S1()
8+
s1[\.x] = 10 // expected-error {{missing argument label 'keyPath:' in subscript}} {{4-4=keyPath: }}
9+
10+
struct S2 {
11+
var x : Int = 0
12+
subscript(_ v: Int) -> Int { 0 }
13+
}
14+
var s2 = S2()
15+
s2[\.x] = 10 // expected-error {{missing argument label 'keyPath:' in subscript}} {{4-4=keyPath: }}
16+
17+
struct S3 {
18+
var x : Int = 0
19+
subscript(v v: KeyPath<S3, Int>) -> Int { get { 0 } set(newValue) {} }
20+
}
21+
var s3 = S3()
22+
// TODO(diagnostics): This should actually be a diagnostic that correctly identifies that in the presence
23+
// of a missing label, there are two options for resolution: 'keyPath' and 'v:' and to offer the user
24+
// a choice.
25+
// Today, the ExprTypeChecker identifies the disjunction with two of these possibilities, but
26+
// filters out some of the terms based on label mismatch (but not implicit keypath terms, for example).
27+
// It should probably not do that.
28+
s3[\.x] = 10 // expected-error {{missing argument label 'keyPath:' in subscript}} {{4-4=keyPath: }}
29+
30+
struct S4 {
31+
var x : Int = 0
32+
subscript(v: KeyPath<S4, String>) -> Int { get { 0 } set(newValue) {} }
33+
}
34+
var s4 = S4()
35+
s4[\.x] = 10 // expected-error {{key path value type 'Int' cannot be converted to contextual type 'String'}}

0 commit comments

Comments
 (0)