Skip to content

Commit f739db1

Browse files
committed
[5.3] Add fix on Keypath -> Function with multiple arguments type mismatch.
Cherry-pick #31563, originally reviewed by @xedin. When simplifying a keypath constraint with a function type binding, single-parameter functions have the parameter type and the return type matched against the keypath root and value; whereas multiple-parameter functions cause an ambiguous failure (in `simplifyKeyPathConstraint`). Resolves rdar://problem/57930643
1 parent cfdb65f commit f739db1

9 files changed

+109
-5
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,8 @@ ERROR(expr_keypath_root_type_mismatch, none,
585585
(Type, Type))
586586
ERROR(expr_swift_keypath_anyobject_root,none,
587587
"the root type of a Swift key path cannot be 'AnyObject'", ())
588+
ERROR(expr_keypath_multiparam_func_conversion, none,
589+
"cannot convert key path into a multi-argument function type %0", (Type))
588590
WARNING(expr_deprecated_writable_keypath,none,
589591
"forming a writable keypath to property %0 that is read-only in this context "
590592
"is deprecated and will be removed in a future release",(DeclName))

lib/Sema/CSDiagnostics.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6266,3 +6266,10 @@ bool KeyPathRootTypeMismatchFailure::diagnoseAsError() {
62666266
rootType, baseType);
62676267
return true;
62686268
}
6269+
6270+
bool MultiArgFuncKeyPathFailure::diagnoseAsError() {
6271+
// Diagnose use a keypath where a function with multiple arguments is expected
6272+
emitDiagnostic(diag::expr_keypath_multiparam_func_conversion,
6273+
resolveType(functionType));
6274+
return true;
6275+
}

lib/Sema/CSDiagnostics.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2044,6 +2044,22 @@ class KeyPathRootTypeMismatchFailure final : public ContextualFailure {
20442044
bool diagnoseAsError() override;
20452045
};
20462046

2047+
/// Diagnose an attempt to use a KeyPath where a multi-argument function is expected
2048+
///
2049+
/// ```swift
2050+
/// [Item].sorted(\Item.name)
2051+
/// ```
2052+
class MultiArgFuncKeyPathFailure final : public FailureDiagnostic {
2053+
Type functionType;
2054+
public:
2055+
MultiArgFuncKeyPathFailure(const Solution &solution, Type functionType,
2056+
ConstraintLocator *locator)
2057+
: FailureDiagnostic(solution, locator),
2058+
functionType(functionType) {}
2059+
2060+
bool diagnoseAsError() override;
2061+
};
2062+
20472063
} // end namespace constraints
20482064
} // end namespace swift
20492065

lib/Sema/CSFix.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,19 @@ AllowAnyObjectKeyPathRoot::create(ConstraintSystem &cs,
722722
return new (cs.getAllocator()) AllowAnyObjectKeyPathRoot(cs, locator);
723723
}
724724

725+
bool AllowMultiArgFuncKeyPathMismatch::diagnose(const Solution &solution,
726+
bool asNote) const {
727+
MultiArgFuncKeyPathFailure failure(solution, functionType, getLocator());
728+
return failure.diagnose(asNote);
729+
}
730+
731+
AllowMultiArgFuncKeyPathMismatch *
732+
AllowMultiArgFuncKeyPathMismatch::create(ConstraintSystem &cs, Type fnType,
733+
ConstraintLocator *locator) {
734+
return new (cs.getAllocator())
735+
AllowMultiArgFuncKeyPathMismatch(cs, fnType, locator);
736+
}
737+
725738
bool TreatKeyPathSubscriptIndexAsHashable::diagnose(const Solution &solution,
726739
bool asNote) const {
727740
KeyPathSubscriptIndexHashableFailure failure(solution, NonConformingType,

lib/Sema/CSFix.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,9 @@ enum class FixKind : uint8_t {
255255
/// Allow key path root type mismatch when applying a key path that has a
256256
/// root type not convertible to the type of the base instance.
257257
AllowKeyPathRootTypeMismatch,
258+
259+
/// Allow key path to be bound to a function type with more than 1 argument
260+
AllowMultiArgFuncKeyPathMismatch
258261
};
259262

260263
class ConstraintFix {
@@ -1264,6 +1267,26 @@ class AllowAnyObjectKeyPathRoot final : public ConstraintFix {
12641267
ConstraintLocator *locator);
12651268
};
12661269

1270+
class AllowMultiArgFuncKeyPathMismatch final : public ConstraintFix {
1271+
Type functionType;
1272+
1273+
AllowMultiArgFuncKeyPathMismatch(ConstraintSystem &cs, Type fnType,
1274+
ConstraintLocator *locator)
1275+
: ConstraintFix(cs, FixKind::AllowMultiArgFuncKeyPathMismatch, locator),
1276+
functionType(fnType) {}
1277+
1278+
public:
1279+
std::string getName() const override {
1280+
return "allow conversion of a keypath type to a multi-argument function";
1281+
}
1282+
1283+
bool diagnose(const Solution &solution, bool asNote = false) const override;
1284+
1285+
static AllowMultiArgFuncKeyPathMismatch *create(ConstraintSystem &cs,
1286+
Type fnType,
1287+
ConstraintLocator *locator);
1288+
};
1289+
12671290
class TreatKeyPathSubscriptIndexAsHashable final : public ConstraintFix {
12681291
Type NonConformingType;
12691292

lib/Sema/CSSimplify.cpp

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
//===----------------------------------------------------------------------===//
1717

1818
#include "CSFix.h"
19+
#include "CSDiagnostics.h"
1920
#include "ConstraintSystem.h"
2021
#include "swift/AST/ExistentialLayout.h"
2122
#include "swift/AST/GenericEnvironment.h"
@@ -7615,10 +7616,10 @@ ConstraintSystem::simplifyKeyPathConstraint(
76157616
auto subflags = getDefaultDecompositionOptions(flags);
76167617
// The constraint ought to have been anchored on a KeyPathExpr.
76177618
auto keyPath = cast<KeyPathExpr>(locator.getBaseLocator()->getAnchor());
7618-
76197619
keyPathTy = getFixedTypeRecursive(keyPathTy, /*want rvalue*/ true);
76207620
bool definitelyFunctionType = false;
76217621
bool definitelyKeyPathType = false;
7622+
bool resolveAsMultiArgFuncFix = false;
76227623

76237624
auto tryMatchRootAndValueFromType = [&](Type type,
76247625
bool allowPartial = true) -> bool {
@@ -7643,10 +7644,18 @@ ConstraintSystem::simplifyKeyPathConstraint(
76437644
}
76447645

76457646
if (auto fnTy = type->getAs<FunctionType>()) {
7646-
definitelyFunctionType = true;
7647+
if (fnTy->getParams().size() != 1) {
7648+
if (!shouldAttemptFixes())
7649+
return false;
76477650

7648-
if (fnTy->getParams().size() != 1)
7649-
return false;
7651+
resolveAsMultiArgFuncFix = true;
7652+
auto *fix = AllowMultiArgFuncKeyPathMismatch::create(
7653+
*this, fnTy, locator.getBaseLocator());
7654+
// Pretend the keypath type got resolved and move on.
7655+
return !recordFix(fix);
7656+
}
7657+
7658+
definitelyFunctionType = true;
76507659

76517660
// Match up the root and value types to the function's param and return
76527661
// types. Note that we're using the type of the parameter as referenced
@@ -7681,6 +7690,10 @@ ConstraintSystem::simplifyKeyPathConstraint(
76817690
return SolutionKind::Error;
76827691
}
76837692

7693+
// If we fix this keypath as `AllowMultiArgFuncKeyPathMismatch`, just proceed
7694+
if (resolveAsMultiArgFuncFix)
7695+
return SolutionKind::Solved;
7696+
76847697
// See if we resolved overloads for all the components involved.
76857698
enum {
76867699
ReadOnly,
@@ -7845,7 +7858,7 @@ ConstraintSystem::simplifyKeyPathConstraint(
78457858
} else if (!anyComponentsUnresolved ||
78467859
(definitelyKeyPathType && capability == ReadOnly)) {
78477860
auto resolvedKPTy =
7848-
BoundGenericType::get(kpDecl, nullptr, {rootTy, valueTy});
7861+
BoundGenericType::get(kpDecl, nullptr, {rootTy, valueTy});
78497862
return matchTypes(keyPathTy, resolvedKPTy, ConstraintKind::Bind, subflags,
78507863
loc);
78517864
} else {
@@ -9562,6 +9575,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
95629575
case FixKind::AllowClosureParameterDestructuring:
95639576
case FixKind::AllowInaccessibleMember:
95649577
case FixKind::AllowAnyObjectKeyPathRoot:
9578+
case FixKind::AllowMultiArgFuncKeyPathMismatch:
95659579
case FixKind::TreatKeyPathSubscriptIndexAsHashable:
95669580
case FixKind::AllowInvalidRefInKeyPath:
95679581
case FixKind::DefaultGenericArgument:
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// RUN: %target-swift-frontend -typecheck -verify -primary-file %s
2+
3+
// rdar://problem/57930643
4+
struct School {
5+
var name: String
6+
}
7+
func testKeyPathClosureLiteralError() -> [School] {
8+
let slist = [School(name:"AHS"), School(name:"BHS")]
9+
return slist.sorted(by: \School.name) // expected-error {{cannot convert key path into a multi-argument function type '(School, School) throws -> Bool'}}
10+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// RUN: %target-swift-frontend -typecheck -verify -primary-file %s
2+
3+
// rdar://problem/57930643
4+
struct School {
5+
var name: String
6+
}
7+
func test<A, B>(_: (A, B) -> Bool) {} // expected-note {{in call to function 'test'}}
8+
test(\School.name) // expected-error {{generic parameter 'A' could not be inferred}} // expected-error {{generic parameter 'B' could not be inferred}} // expected-error {{cannot convert key path into a multi-argument function type '(A, B) -> Bool'}}
9+
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// RUN: %target-swift-frontend -typecheck -verify -primary-file %s
2+
3+
// rdar://problem/57930643
4+
struct School {
5+
var name: String
6+
}
7+
func testKeyPathClosureLiteralError() {
8+
let slist = [School(name:"AHS"), School(name:"BHS")]
9+
_ = slist.sorted(by: \School.name) // expected-error {{cannot convert key path into a multi-argument function type '(School, School) throws -> Bool'}}
10+
}

0 commit comments

Comments
 (0)