Skip to content

Commit cdb9fb4

Browse files
authored
Merge pull request #24234 from xedin/sr-10467
[ConstraintSystem] Type of key path expression should be a known `KeyPath` type
2 parents f448788 + bb13bd9 commit cdb9fb4

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
@@ -3084,7 +3084,9 @@ namespace {
30843084
} else {
30853085
// The type of key path depends on the overloads chosen for the key
30863086
// path components.
3087-
kpTy = CS.createTypeVariable(locator, TVO_CanBindToNoEscape);
3087+
auto typeLoc =
3088+
CS.getConstraintLocator(locator, ConstraintLocator::KeyPathType);
3089+
kpTy = CS.createTypeVariable(typeLoc, TVO_CanBindToNoEscape);
30883090
CS.addKeyPathConstraint(kpTy, root, rvalueBase, locator);
30893091
}
30903092
return kpTy;

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
case KeyPathComponentResult:
@@ -104,6 +105,15 @@ bool ConstraintLocator::isSubscriptMemberRef() const {
104105
return path.back().getKind() == ConstraintLocator::SubscriptMember;
105106
}
106107

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

353+
case KeyPathType:
354+
out << "key path type";
355+
break;
356+
343357
case KeyPathRoot:
344358
out << "key path root";
345359
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
/// The result type of a key path component. Not used for subscripts.
137139
KeyPathComponentResult,
@@ -165,6 +167,7 @@ class ConstraintLocator : public llvm::FoldingSetNode {
165167
case ImplicitlyUnwrappedDisjunctionChoice:
166168
case DynamicLookupResult:
167169
case ContextualType:
170+
case KeyPathType:
168171
case KeyPathRoot:
169172
case KeyPathValue:
170173
case KeyPathComponentResult:
@@ -234,6 +237,7 @@ class ConstraintLocator : public llvm::FoldingSetNode {
234237
case ContextualType:
235238
case SynthesizedArgument:
236239
case KeyPathDynamicMember:
240+
case KeyPathType:
237241
case KeyPathRoot:
238242
case KeyPathValue:
239243
case KeyPathComponentResult:
@@ -534,6 +538,10 @@ class ConstraintLocator : public llvm::FoldingSetNode {
534538
/// e.g. `foo[0]` or `\Foo.[0]`
535539
bool isSubscriptMemberRef() const;
536540

541+
/// Determine whether give locator points to the type of the
542+
/// key path expression.
543+
bool isKeyPathType() const;
544+
537545
/// Determine whether given locator points to the keypath root
538546
bool isKeyPathRoot() const;
539547

lib/Sema/ConstraintSystem.cpp

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

19151915
auto *keyPathDecl = keyPathTy->getAnyNominal();
1916-
assert(
1917-
keyPathDecl &&
1918-
(keyPathDecl == getASTContext().getKeyPathDecl() ||
1919-
keyPathDecl == getASTContext().getWritableKeyPathDecl() ||
1920-
keyPathDecl == getASTContext().getReferenceWritableKeyPathDecl()) &&
1921-
"parameter is supposed to be a keypath");
1916+
assert(isKnownKeyPathDecl(getASTContext(), keyPathDecl) &&
1917+
"parameter is supposed to be a keypath");
19221918

19231919
auto *keyPathLoc = getConstraintLocator(
19241920
locator, LocatorPathElt::getKeyPathDynamicMember(keyPathDecl));
@@ -2712,3 +2708,9 @@ void ConstraintSystem::generateConstraints(
27122708
recordChoice(constraints, index, choices[index]);
27132709
}
27142710
}
2711+
2712+
bool constraints::isKnownKeyPathDecl(ASTContext &ctx, ValueDecl *decl) {
2713+
return decl == ctx.getKeyPathDecl() || decl == ctx.getWritableKeyPathDecl() ||
2714+
decl == ctx.getReferenceWritableKeyPathDecl() ||
2715+
decl == ctx.getPartialKeyPathDecl() || decl == ctx.getAnyKeyPathDecl();
2716+
}

lib/Sema/ConstraintSystem.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4010,6 +4010,10 @@ class DisjunctionChoiceProducer : public BindingProducer<DisjunctionChoice> {
40104010
IsExplicitConversion, isBeginningOfPartition);
40114011
}
40124012
};
4013+
4014+
/// Determine whether given declaration is one for a key path
4015+
/// `{Writable, ReferenceWritable}KeyPath`.
4016+
bool isKnownKeyPathDecl(ASTContext &ctx, ValueDecl *decl);
40134017
} // end namespace constraints
40144018

40154019
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)