diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 3f5760975f2f1..295e54891cd9b 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -5973,6 +5973,9 @@ bool ArgumentMismatchFailure::diagnoseAsError() { if (diagnoseTrailingClosureMismatch()) return true; + if (diagnoseKeyPathAsFunctionResultMismatch()) + return true; + auto argType = getFromType(); auto paramType = getToType(); @@ -6227,6 +6230,31 @@ bool ArgumentMismatchFailure::diagnoseTrailingClosureMismatch() const { return true; } +bool ArgumentMismatchFailure::diagnoseKeyPathAsFunctionResultMismatch() const { + auto argExpr = getArgExpr(); + if (!isExpr(argExpr)) + return false; + + auto argType = getFromType(); + auto paramType = getToType(); + + if (!isKnownKeyPathType(argType)) + return false; + + auto kpType = argType->castTo(); + auto kpRootType = kpType->getGenericArgs()[0]; + auto kpValueType = kpType->getGenericArgs()[1]; + + auto paramFnType = paramType->getAs(); + if (!(paramFnType && paramFnType->getNumParams() == 1 && + paramFnType->getParams().front().getPlainType()->isEqual(kpRootType))) + return false; + + emitDiagnostic(diag::expr_smart_keypath_value_covert_to_contextual_type, + kpValueType, paramFnType->getResult()); + return true; +} + void ExpandArrayIntoVarargsFailure::tryDropArrayBracketsFixIt( const Expr *anchor) const { // If this is an array literal, offer to remove the brackets and pass the diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index 329998d592d29..97285c499c9d0 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -1909,6 +1909,12 @@ class ArgumentMismatchFailure : public ContextualFailure { /// closures being passed to non-closure parameters. bool diagnoseTrailingClosureMismatch() const; + /// Tailored key path as function diagnostics for argument mismatches where + /// argument is a keypath expression that has a root type that matches a + /// function parameter, but keypath value don't match the function parameter + /// result value. + bool diagnoseKeyPathAsFunctionResultMismatch() const; + protected: /// \returns The position of the argument being diagnosed, starting at 1. unsigned getArgPosition() const { return Info.getArgPosition(); } diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 38f71d0fb481b..654be8eac9684 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -3795,10 +3795,13 @@ bool ConstraintSystem::repairFailures( // there is going to be a constraint to match result of the // member lookup to the generic parameter `V` of *KeyPath // type associated with key path expression, which we need to - // fix-up here. - if (isExpr(anchor)) { - auto *fnType = lhs->getAs(); - if (fnType && fnType->getResult()->isEqual(rhs)) + // fix-up here unless last component has already a invalid type or + // instance fix recorded. + if (auto *kpExpr = getAsExpr(anchor)) { + auto i = kpExpr->getComponents().size() - 1; + auto lastCompLoc = getConstraintLocator( + locator.withPathElement(LocatorPathElt::KeyPathComponent(i))); + if (hasFixFor(lastCompLoc, FixKind::AllowTypeOrInstanceMember)) return true; auto lastComponentType = lhs->lookThroughAllOptionalTypes(); diff --git a/test/expr/unary/keypath/keypath.swift b/test/expr/unary/keypath/keypath.swift index 50bdded7d12bc..923bcd05f5abe 100644 --- a/test/expr/unary/keypath/keypath.swift +++ b/test/expr/unary/keypath/keypath.swift @@ -795,6 +795,7 @@ func test_keypath_with_method_refs() { } let _: KeyPath = \.foo // expected-error {{key path cannot refer to instance method 'foo()'}} + // expected-error@-1 {{key path value type '() -> Int' cannot be converted to contextual type 'Int'}} let _: KeyPath = \.bar // expected-error {{key path cannot refer to static member 'bar()'}} let _ = \S.Type.bar // expected-error {{key path cannot refer to static method 'bar()'}} @@ -1094,3 +1095,19 @@ func rdar74711236() { }() } } + +extension String { + var filterOut : (Self) throws -> Bool { + { $0.contains("a") } + } +} + +func test_kp_as_function_mismatch() { + let a : [String] = [ "asd", "bcd", "def" ] + + let _ : (String) -> Bool = \.filterOut // expected-error{{key path value type '(String) throws -> Bool' cannot be converted to contextual type 'Bool'}} + _ = a.filter(\.filterOut) // expected-error{{key path value type '(String) throws -> Bool' cannot be converted to contextual type 'Bool'}} + let _ : (String) -> Bool = \String.filterOut // expected-error{{key path value type '(String) throws -> Bool' cannot be converted to contextual type 'Bool'}} + _ = a.filter(\String.filterOut) // expected-error{{key path value type '(String) throws -> Bool' cannot be converted to contextual type 'Bool'}} + +}