Skip to content

Commit 8aceb03

Browse files
authored
Merge pull request #31716 from artemcm/UnlabeledKeypathFix
[Type-checker] Improve diagnostic for keypath used without 'keyPath:' label
2 parents 30b5fd5 + 97e32ab commit 8aceb03

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
@@ -5674,6 +5674,19 @@ static bool isForKeyPathSubscript(ConstraintSystem &cs,
56745674
return false;
56755675
}
56765676

5677+
static bool isForKeyPathSubscriptWithoutLabel(ConstraintSystem &cs,
5678+
ConstraintLocator *locator) {
5679+
if (!locator || !locator->getAnchor())
5680+
return false;
5681+
5682+
if (auto *SE = getAsExpr<SubscriptExpr>(locator->getAnchor())) {
5683+
auto *indexExpr = SE->getIndex();
5684+
return isa<ParenExpr>(indexExpr) &&
5685+
isa<KeyPathExpr>(indexExpr->getSemanticsProvidingExpr());
5686+
}
5687+
return false;
5688+
}
5689+
56775690
/// Determine whether all of the given candidate overloads
56785691
/// found through conditional conformances of a given base type.
56795692
/// This is useful to figure out whether it makes sense to
@@ -5801,7 +5814,13 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
58015814
MemberLookupResult result;
58025815
result.OverallResult = MemberLookupResult::HasResults;
58035816

5804-
if (isForKeyPathSubscript(*this, memberLocator)) {
5817+
// Add key path result.
5818+
// If we are including inaccessible members, check for the use of a keypath
5819+
// subscript without a `keyPath:` label. Add it to the result so that it
5820+
// can be caught by the missing argument label checking later.
5821+
if (isForKeyPathSubscript(*this, memberLocator) ||
5822+
(isForKeyPathSubscriptWithoutLabel(*this, memberLocator)
5823+
&& includeInaccessibleMembers)) {
58055824
if (baseTy->isAnyObject()) {
58065825
result.addUnviable(
58075826
OverloadChoice(baseTy, OverloadChoiceKind::KeyPathApplication),
@@ -9700,14 +9719,21 @@ ConstraintSystem::addKeyPathApplicationRootConstraint(Type root, ConstraintLocat
97009719
path[0].getKind() == ConstraintLocator::SubscriptMember) ||
97019720
(path.size() == 2 &&
97029721
path[1].getKind() == ConstraintLocator::KeyPathDynamicMember));
9722+
97039723
auto indexTuple = dyn_cast<TupleExpr>(subscript->getIndex());
9704-
if (!indexTuple || indexTuple->getNumElements() != 1)
9705-
return;
9706-
9707-
auto keyPathExpr = dyn_cast<KeyPathExpr>(indexTuple->getElement(0));
9724+
auto indexParen = dyn_cast<ParenExpr>(subscript->getIndex());
9725+
// If a keypath subscript is used without the expected `keyPath:` label,
9726+
// continue with type-checking when attempting fixes so that it gets caught
9727+
// by the argument label checking. In such cases, the KeyPathExpr is contained
9728+
// in a ParenExpr, instead of a TupleExpr.
9729+
assert(((indexTuple && indexTuple->getNumElements() == 1) || indexParen) &&
9730+
"Expected KeyPathExpr to be in either TupleExpr or ParenExpr");
9731+
9732+
auto keyPathExpr = dyn_cast<KeyPathExpr>(
9733+
indexTuple ? indexTuple->getElement(0) : indexParen->getSubExpr());
97089734
if (!keyPathExpr)
97099735
return;
9710-
9736+
97119737
auto typeVar = getType(keyPathExpr)->getAs<TypeVariableType>();
97129738
if (!typeVar)
97139739
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)