Skip to content

[Sema][SR-14096] Change conditions in which key path component mismatch fix are diagnosed #36531

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions lib/Sema/CSDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5973,6 +5973,9 @@ bool ArgumentMismatchFailure::diagnoseAsError() {
if (diagnoseTrailingClosureMismatch())
return true;

if (diagnoseKeyPathAsFunctionResultMismatch())
return true;

auto argType = getFromType();
auto paramType = getToType();

Expand Down Expand Up @@ -6227,6 +6230,31 @@ bool ArgumentMismatchFailure::diagnoseTrailingClosureMismatch() const {
return true;
}

bool ArgumentMismatchFailure::diagnoseKeyPathAsFunctionResultMismatch() const {
auto argExpr = getArgExpr();
if (!isExpr<KeyPathExpr>(argExpr))
return false;

auto argType = getFromType();
auto paramType = getToType();

if (!isKnownKeyPathType(argType))
return false;

auto kpType = argType->castTo<BoundGenericType>();
auto kpRootType = kpType->getGenericArgs()[0];
auto kpValueType = kpType->getGenericArgs()[1];

auto paramFnType = paramType->getAs<FunctionType>();
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
Expand Down
6 changes: 6 additions & 0 deletions lib/Sema/CSDiagnostics.h
Original file line number Diff line number Diff line change
Expand Up @@ -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(); }
Expand Down
11 changes: 7 additions & 4 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<R, V>
// type associated with key path expression, which we need to
// fix-up here.
if (isExpr<KeyPathExpr>(anchor)) {
auto *fnType = lhs->getAs<FunctionType>();
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<KeyPathExpr>(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();
Expand Down
17 changes: 17 additions & 0 deletions test/expr/unary/keypath/keypath.swift
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,7 @@ func test_keypath_with_method_refs() {
}

let _: KeyPath<S, Int> = \.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<S, Int> = \.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()'}}

Expand Down Expand Up @@ -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'}}

}