Skip to content

Commit 7aed042

Browse files
committed
Add fix on Keypath -> Function with multiple arguments type mismatch.
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 bab23d8 commit 7aed042

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
@@ -6263,3 +6263,10 @@ bool KeyPathRootTypeMismatchFailure::diagnoseAsError() {
62636263
rootType, baseType);
62646264
return true;
62656265
}
6266+
6267+
bool MultiArgFuncKeyPathFailure::diagnoseAsError() {
6268+
// Diagnose use a keypath where a function with multiple arguments is expected
6269+
emitDiagnostic(diag::expr_keypath_multiparam_func_conversion,
6270+
resolveType(functionType));
6271+
return true;
6272+
}

lib/Sema/CSDiagnostics.h

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

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

lib/Sema/CSFix.cpp

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

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

lib/Sema/CSFix.h

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

262265
class ConstraintFix {
@@ -1266,6 +1269,26 @@ class AllowAnyObjectKeyPathRoot final : public ConstraintFix {
12661269
ConstraintLocator *locator);
12671270
};
12681271

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

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"
@@ -7538,10 +7539,10 @@ ConstraintSystem::simplifyKeyPathConstraint(
75387539
auto subflags = getDefaultDecompositionOptions(flags);
75397540
// The constraint ought to have been anchored on a KeyPathExpr.
75407541
auto keyPath = castToExpr<KeyPathExpr>(locator.getBaseLocator()->getAnchor());
7541-
75427542
keyPathTy = getFixedTypeRecursive(keyPathTy, /*want rvalue*/ true);
75437543
bool definitelyFunctionType = false;
75447544
bool definitelyKeyPathType = false;
7545+
bool resolveAsMultiArgFuncFix = false;
75457546

75467547
auto tryMatchRootAndValueFromType = [&](Type type,
75477548
bool allowPartial = true) -> bool {
@@ -7566,10 +7567,18 @@ ConstraintSystem::simplifyKeyPathConstraint(
75667567
}
75677568

75687569
if (auto fnTy = type->getAs<FunctionType>()) {
7569-
definitelyFunctionType = true;
7570+
if (fnTy->getParams().size() != 1) {
7571+
if (!shouldAttemptFixes())
7572+
return false;
75707573

7571-
if (fnTy->getParams().size() != 1)
7572-
return false;
7574+
resolveAsMultiArgFuncFix = true;
7575+
auto *fix = AllowMultiArgFuncKeyPathMismatch::create(
7576+
*this, fnTy, locator.getBaseLocator());
7577+
// Pretend the keypath type got resolved and move on.
7578+
return !recordFix(fix);
7579+
}
7580+
7581+
definitelyFunctionType = true;
75737582

75747583
// Match up the root and value types to the function's param and return
75757584
// types. Note that we're using the type of the parameter as referenced
@@ -7604,6 +7613,10 @@ ConstraintSystem::simplifyKeyPathConstraint(
76047613
return SolutionKind::Error;
76057614
}
76067615

7616+
// If we fix this keypath as `AllowMultiArgFuncKeyPathMismatch`, just proceed
7617+
if (resolveAsMultiArgFuncFix)
7618+
return SolutionKind::Solved;
7619+
76077620
// See if we resolved overloads for all the components involved.
76087621
enum {
76097622
ReadOnly,
@@ -7768,7 +7781,7 @@ ConstraintSystem::simplifyKeyPathConstraint(
77687781
} else if (!anyComponentsUnresolved ||
77697782
(definitelyKeyPathType && capability == ReadOnly)) {
77707783
auto resolvedKPTy =
7771-
BoundGenericType::get(kpDecl, nullptr, {rootTy, valueTy});
7784+
BoundGenericType::get(kpDecl, nullptr, {rootTy, valueTy});
77727785
return matchTypes(keyPathTy, resolvedKPTy, ConstraintKind::Bind, subflags,
77737786
loc);
77747787
} else {
@@ -9497,6 +9510,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
94979510
case FixKind::AllowClosureParameterDestructuring:
94989511
case FixKind::AllowInaccessibleMember:
94999512
case FixKind::AllowAnyObjectKeyPathRoot:
9513+
case FixKind::AllowMultiArgFuncKeyPathMismatch:
95009514
case FixKind::TreatKeyPathSubscriptIndexAsHashable:
95019515
case FixKind::AllowInvalidRefInKeyPath:
95029516
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)