Skip to content

Commit 5d8bffd

Browse files
authored
Merge pull request #24260 from xedin/sr-10467-5.1
[5.1][ConstraintSystem] Type of key path expression should be a known `KeyPath` type
2 parents 409581c + 7f9347d commit 5d8bffd

File tree

7 files changed

+73
-9
lines changed

7 files changed

+73
-9
lines changed

lib/Sema/CSBindings.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,15 @@ ConstraintSystem::getPotentialBindingForRelationalConstraint(
276276
if (type->hasError())
277277
return None;
278278

279+
if (auto *locator = typeVar->getImpl().getLocator()) {
280+
if (locator->isKeyPathType()) {
281+
auto *BGT =
282+
type->lookThroughAllOptionalTypes()->getAs<BoundGenericType>();
283+
if (!BGT || !isKnownKeyPathDecl(getASTContext(), BGT->getDecl()))
284+
return None;
285+
}
286+
}
287+
279288
// If the source of the binding is 'OptionalObject' constraint
280289
// and type variable is on the left-hand side, that means
281290
// that it _has_ to be of optional type, since the right-hand

lib/Sema/CSGen.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3025,7 +3025,9 @@ namespace {
30253025
} else {
30263026
// The type of key path depends on the overloads chosen for the key
30273027
// path components.
3028-
kpTy = CS.createTypeVariable(CS.getConstraintLocator(E));
3028+
auto typeLoc =
3029+
CS.getConstraintLocator(E, ConstraintLocator::KeyPathType);
3030+
kpTy = CS.createTypeVariable(typeLoc);
30293031
CS.addKeyPathConstraint(kpTy, root, rvalueBase,
30303032
CS.getConstraintLocator(E));
30313033
}

lib/Sema/ConstraintLocator.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ void ConstraintLocator::Profile(llvm::FoldingSetNodeID &id, Expr *anchor,
7979
case DynamicLookupResult:
8080
case ContextualType:
8181
case SynthesizedArgument:
82+
case KeyPathType:
8283
case KeyPathRoot:
8384
case KeyPathValue:
8485
if (unsigned numValues = numNumericValuesInPathElement(elt.getKind())) {
@@ -103,6 +104,15 @@ bool ConstraintLocator::isSubscriptMemberRef() const {
103104
return path.back().getKind() == ConstraintLocator::SubscriptMember;
104105
}
105106

107+
bool ConstraintLocator::isKeyPathType() const {
108+
auto *anchor = getAnchor();
109+
auto path = getPath();
110+
// The format of locator should be `<keypath expr> -> key path type`
111+
if (!anchor || !isa<KeyPathExpr>(anchor) || path.size() != 1)
112+
return false;
113+
return path.back().getKind() == ConstraintLocator::KeyPathType;
114+
}
115+
106116
bool ConstraintLocator::isKeyPathRoot() const {
107117
auto *anchor = getAnchor();
108118
auto path = getPath();
@@ -339,6 +349,10 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) {
339349
out << " keypath dynamic member lookup";
340350
break;
341351

352+
case KeyPathType:
353+
out << "key path type";
354+
break;
355+
342356
case KeyPathRoot:
343357
out << " keypath root";
344358
break;

lib/Sema/ConstraintLocator.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,11 @@ class ConstraintLocator : public llvm::FoldingSetNode {
129129
SynthesizedArgument,
130130
/// The member looked up via keypath based dynamic lookup.
131131
KeyPathDynamicMember,
132-
/// The root of a keypath
132+
/// The type of the key path expression
133+
KeyPathType,
134+
/// The root of a key path
133135
KeyPathRoot,
134-
/// The value of a keypath
136+
/// The value of a key path
135137
KeyPathValue,
136138
};
137139

@@ -163,6 +165,7 @@ class ConstraintLocator : public llvm::FoldingSetNode {
163165
case ImplicitlyUnwrappedDisjunctionChoice:
164166
case DynamicLookupResult:
165167
case ContextualType:
168+
case KeyPathType:
166169
case KeyPathRoot:
167170
case KeyPathValue:
168171
return 0;
@@ -231,6 +234,7 @@ class ConstraintLocator : public llvm::FoldingSetNode {
231234
case ContextualType:
232235
case SynthesizedArgument:
233236
case KeyPathDynamicMember:
237+
case KeyPathType:
234238
case KeyPathRoot:
235239
case KeyPathValue:
236240
return 0;
@@ -530,6 +534,10 @@ class ConstraintLocator : public llvm::FoldingSetNode {
530534
/// e.g. `foo[0]` or `\Foo.[0]`
531535
bool isSubscriptMemberRef() const;
532536

537+
/// Determine whether give locator points to the type of the
538+
/// key path expression.
539+
bool isKeyPathType() const;
540+
533541
/// Determine whether given locator points to the keypath root
534542
bool isKeyPathRoot() const;
535543

lib/Sema/ConstraintSystem.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1916,12 +1916,8 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
19161916
refType = fnType->getResult();
19171917

19181918
auto *keyPathDecl = keyPathTy->getAnyNominal();
1919-
assert(
1920-
keyPathDecl &&
1921-
(keyPathDecl == getASTContext().getKeyPathDecl() ||
1922-
keyPathDecl == getASTContext().getWritableKeyPathDecl() ||
1923-
keyPathDecl == getASTContext().getReferenceWritableKeyPathDecl()) &&
1924-
"parameter is supposed to be a keypath");
1919+
assert(isKnownKeyPathDecl(getASTContext(), keyPathDecl) &&
1920+
"parameter is supposed to be a keypath");
19251921

19261922
auto *keyPathLoc = getConstraintLocator(
19271923
locator, LocatorPathElt::getKeyPathDynamicMember(keyPathDecl));
@@ -2710,3 +2706,9 @@ void ConstraintSystem::generateConstraints(
27102706
recordChoice(constraints, index, choices[index]);
27112707
}
27122708
}
2709+
2710+
bool constraints::isKnownKeyPathDecl(ASTContext &ctx, ValueDecl *decl) {
2711+
return decl == ctx.getKeyPathDecl() || decl == ctx.getWritableKeyPathDecl() ||
2712+
decl == ctx.getReferenceWritableKeyPathDecl() ||
2713+
decl == ctx.getPartialKeyPathDecl() || decl == ctx.getAnyKeyPathDecl();
2714+
}

lib/Sema/ConstraintSystem.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3937,6 +3937,10 @@ class DisjunctionChoiceProducer : public BindingProducer<DisjunctionChoice> {
39373937
IsExplicitConversion, isBeginningOfPartition);
39383938
}
39393939
};
3940+
3941+
/// Determine whether given declaration is one for a key path
3942+
/// `{Writable, ReferenceWritable}KeyPath`.
3943+
bool isKnownKeyPathDecl(ASTContext &ctx, ValueDecl *decl);
39403944
} // end namespace constraints
39413945

39423946
template<typename ...Args>

test/expr/unary/keypath/keypath.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -801,6 +801,31 @@ func test_keypath_with_method_refs() {
801801
let _ = \A.Type.faz.bar // expected-error {{key path cannot refer to static method 'faz()'}}
802802
}
803803

804+
// SR-10467 - Argument type 'KeyPath<String, Int>' does not conform to expected type 'Any'
805+
func test_keypath_in_any_context() {
806+
func foo(_: Any) {}
807+
_ = foo(\String.count) // Ok
808+
}
809+
810+
protocol PWithTypeAlias {
811+
typealias Key = WritableKeyPath<Self, Int?>
812+
static var fooKey: Key? { get }
813+
static var barKey: Key! { get }
814+
static var fazKey: Key?? { get }
815+
static var bazKey: Key?! { get }
816+
}
817+
818+
func test_keypath_inference_with_optionals() {
819+
final class S : PWithTypeAlias {
820+
static var fooKey: Key? { return \.foo }
821+
static var barKey: Key! { return \.foo }
822+
static var fazKey: Key?? { return \.foo }
823+
static var bazKey: Key?! { return \.foo }
824+
825+
var foo: Int? = nil
826+
}
827+
}
828+
804829
func testSyntaxErrors() { // expected-note{{}}
805830
_ = \. ; // expected-error{{expected member name following '.'}}
806831
_ = \.a ;

0 commit comments

Comments
 (0)