Skip to content

Commit 3abb0bd

Browse files
authored
Merge pull request #31563 from artemcm/TypeCheckKeypathAsClosure
Add IgnoreContextualType fix on Keypath -> Function type mismatch.
2 parents 9a8b91e + 7aed042 commit 3abb0bd

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"
@@ -7601,10 +7602,10 @@ ConstraintSystem::simplifyKeyPathConstraint(
76017602
auto subflags = getDefaultDecompositionOptions(flags);
76027603
// The constraint ought to have been anchored on a KeyPathExpr.
76037604
auto keyPath = castToExpr<KeyPathExpr>(locator.getBaseLocator()->getAnchor());
7604-
76057605
keyPathTy = getFixedTypeRecursive(keyPathTy, /*want rvalue*/ true);
76067606
bool definitelyFunctionType = false;
76077607
bool definitelyKeyPathType = false;
7608+
bool resolveAsMultiArgFuncFix = false;
76087609

76097610
auto tryMatchRootAndValueFromType = [&](Type type,
76107611
bool allowPartial = true) -> bool {
@@ -7629,10 +7630,18 @@ ConstraintSystem::simplifyKeyPathConstraint(
76297630
}
76307631

76317632
if (auto fnTy = type->getAs<FunctionType>()) {
7632-
definitelyFunctionType = true;
7633+
if (fnTy->getParams().size() != 1) {
7634+
if (!shouldAttemptFixes())
7635+
return false;
76337636

7634-
if (fnTy->getParams().size() != 1)
7635-
return false;
7637+
resolveAsMultiArgFuncFix = true;
7638+
auto *fix = AllowMultiArgFuncKeyPathMismatch::create(
7639+
*this, fnTy, locator.getBaseLocator());
7640+
// Pretend the keypath type got resolved and move on.
7641+
return !recordFix(fix);
7642+
}
7643+
7644+
definitelyFunctionType = true;
76367645

76377646
// Match up the root and value types to the function's param and return
76387647
// types. Note that we're using the type of the parameter as referenced
@@ -7667,6 +7676,10 @@ ConstraintSystem::simplifyKeyPathConstraint(
76677676
return SolutionKind::Error;
76687677
}
76697678

7679+
// If we fix this keypath as `AllowMultiArgFuncKeyPathMismatch`, just proceed
7680+
if (resolveAsMultiArgFuncFix)
7681+
return SolutionKind::Solved;
7682+
76707683
// See if we resolved overloads for all the components involved.
76717684
enum {
76727685
ReadOnly,
@@ -7831,7 +7844,7 @@ ConstraintSystem::simplifyKeyPathConstraint(
78317844
} else if (!anyComponentsUnresolved ||
78327845
(definitelyKeyPathType && capability == ReadOnly)) {
78337846
auto resolvedKPTy =
7834-
BoundGenericType::get(kpDecl, nullptr, {rootTy, valueTy});
7847+
BoundGenericType::get(kpDecl, nullptr, {rootTy, valueTy});
78357848
return matchTypes(keyPathTy, resolvedKPTy, ConstraintKind::Bind, subflags,
78367849
loc);
78377850
} else {
@@ -9561,6 +9574,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
95619574
case FixKind::AllowClosureParameterDestructuring:
95629575
case FixKind::AllowInaccessibleMember:
95639576
case FixKind::AllowAnyObjectKeyPathRoot:
9577+
case FixKind::AllowMultiArgFuncKeyPathMismatch:
95649578
case FixKind::TreatKeyPathSubscriptIndexAsHashable:
95659579
case FixKind::AllowInvalidRefInKeyPath:
95669580
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)