Skip to content

Add IgnoreContextualType fix on Keypath -> Function type mismatch. #31563

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 1 commit into from
May 7, 2020
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
2 changes: 2 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,8 @@ ERROR(expr_keypath_root_type_mismatch, none,
(Type, Type))
ERROR(expr_swift_keypath_anyobject_root,none,
"the root type of a Swift key path cannot be 'AnyObject'", ())
ERROR(expr_keypath_multiparam_func_conversion, none,
"cannot convert key path into a multi-argument function type %0", (Type))
WARNING(expr_deprecated_writable_keypath,none,
"forming a writable keypath to property %0 that is read-only in this context "
"is deprecated and will be removed in a future release",(DeclName))
Expand Down
7 changes: 7 additions & 0 deletions lib/Sema/CSDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6263,3 +6263,10 @@ bool KeyPathRootTypeMismatchFailure::diagnoseAsError() {
rootType, baseType);
return true;
}

bool MultiArgFuncKeyPathFailure::diagnoseAsError() {
// Diagnose use a keypath where a function with multiple arguments is expected
emitDiagnostic(diag::expr_keypath_multiparam_func_conversion,
resolveType(functionType));
return true;
}
16 changes: 16 additions & 0 deletions lib/Sema/CSDiagnostics.h
Original file line number Diff line number Diff line change
Expand Up @@ -2042,6 +2042,22 @@ class KeyPathRootTypeMismatchFailure final : public ContextualFailure {
bool diagnoseAsError() override;
};

/// Diagnose an attempt to use a KeyPath where a multi-argument function is expected
///
/// ```swift
/// [Item].sorted(\Item.name)
/// ```
class MultiArgFuncKeyPathFailure final : public FailureDiagnostic {
Type functionType;
public:
MultiArgFuncKeyPathFailure(const Solution &solution, Type functionType,
ConstraintLocator *locator)
: FailureDiagnostic(solution, locator),
functionType(functionType) {}

bool diagnoseAsError() override;
};

} // end namespace constraints
} // end namespace swift

Expand Down
13 changes: 13 additions & 0 deletions lib/Sema/CSFix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -723,6 +723,19 @@ AllowAnyObjectKeyPathRoot::create(ConstraintSystem &cs,
return new (cs.getAllocator()) AllowAnyObjectKeyPathRoot(cs, locator);
}

bool AllowMultiArgFuncKeyPathMismatch::diagnose(const Solution &solution,
bool asNote) const {
MultiArgFuncKeyPathFailure failure(solution, functionType, getLocator());
return failure.diagnose(asNote);
}

AllowMultiArgFuncKeyPathMismatch *
AllowMultiArgFuncKeyPathMismatch::create(ConstraintSystem &cs, Type fnType,
ConstraintLocator *locator) {
return new (cs.getAllocator())
AllowMultiArgFuncKeyPathMismatch(cs, fnType, locator);
}

bool TreatKeyPathSubscriptIndexAsHashable::diagnose(const Solution &solution,
bool asNote) const {
KeyPathSubscriptIndexHashableFailure failure(solution, NonConformingType,
Expand Down
23 changes: 23 additions & 0 deletions lib/Sema/CSFix.h
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,9 @@ enum class FixKind : uint8_t {
/// Allow key path root type mismatch when applying a key path that has a
/// root type not convertible to the type of the base instance.
AllowKeyPathRootTypeMismatch,

/// Allow key path to be bound to a function type with more than 1 argument
AllowMultiArgFuncKeyPathMismatch
};

class ConstraintFix {
Expand Down Expand Up @@ -1266,6 +1269,26 @@ class AllowAnyObjectKeyPathRoot final : public ConstraintFix {
ConstraintLocator *locator);
};

class AllowMultiArgFuncKeyPathMismatch final : public ConstraintFix {
Type functionType;

AllowMultiArgFuncKeyPathMismatch(ConstraintSystem &cs, Type fnType,
ConstraintLocator *locator)
: ConstraintFix(cs, FixKind::AllowMultiArgFuncKeyPathMismatch, locator),
functionType(fnType) {}

public:
std::string getName() const override {
return "allow conversion of a keypath type to a multi-argument function";
}

bool diagnose(const Solution &solution, bool asNote = false) const override;

static AllowMultiArgFuncKeyPathMismatch *create(ConstraintSystem &cs,
Type fnType,
ConstraintLocator *locator);
};

class TreatKeyPathSubscriptIndexAsHashable final : public ConstraintFix {
Type NonConformingType;

Expand Down
24 changes: 19 additions & 5 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
//===----------------------------------------------------------------------===//

#include "CSFix.h"
#include "CSDiagnostics.h"
#include "ConstraintSystem.h"
#include "swift/AST/ExistentialLayout.h"
#include "swift/AST/GenericEnvironment.h"
Expand Down Expand Up @@ -7538,10 +7539,10 @@ ConstraintSystem::simplifyKeyPathConstraint(
auto subflags = getDefaultDecompositionOptions(flags);
// The constraint ought to have been anchored on a KeyPathExpr.
auto keyPath = castToExpr<KeyPathExpr>(locator.getBaseLocator()->getAnchor());

keyPathTy = getFixedTypeRecursive(keyPathTy, /*want rvalue*/ true);
bool definitelyFunctionType = false;
bool definitelyKeyPathType = false;
bool resolveAsMultiArgFuncFix = false;

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

if (auto fnTy = type->getAs<FunctionType>()) {
definitelyFunctionType = true;
if (fnTy->getParams().size() != 1) {
if (!shouldAttemptFixes())
return false;

if (fnTy->getParams().size() != 1)
return false;
resolveAsMultiArgFuncFix = true;
auto *fix = AllowMultiArgFuncKeyPathMismatch::create(
*this, fnTy, locator.getBaseLocator());
// Pretend the keypath type got resolved and move on.
return !recordFix(fix);
}

definitelyFunctionType = true;

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

// If we fix this keypath as `AllowMultiArgFuncKeyPathMismatch`, just proceed
if (resolveAsMultiArgFuncFix)
return SolutionKind::Solved;

// See if we resolved overloads for all the components involved.
enum {
ReadOnly,
Expand Down Expand Up @@ -7768,7 +7781,7 @@ ConstraintSystem::simplifyKeyPathConstraint(
} else if (!anyComponentsUnresolved ||
(definitelyKeyPathType && capability == ReadOnly)) {
auto resolvedKPTy =
BoundGenericType::get(kpDecl, nullptr, {rootTy, valueTy});
BoundGenericType::get(kpDecl, nullptr, {rootTy, valueTy});
return matchTypes(keyPathTy, resolvedKPTy, ConstraintKind::Bind, subflags,
loc);
} else {
Expand Down Expand Up @@ -9497,6 +9510,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
case FixKind::AllowClosureParameterDestructuring:
case FixKind::AllowInaccessibleMember:
case FixKind::AllowAnyObjectKeyPathRoot:
case FixKind::AllowMultiArgFuncKeyPathMismatch:
case FixKind::TreatKeyPathSubscriptIndexAsHashable:
case FixKind::AllowInvalidRefInKeyPath:
case FixKind::DefaultGenericArgument:
Expand Down
10 changes: 10 additions & 0 deletions test/Constraints/keypath_closure_conv_mismatch.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// RUN: %target-swift-frontend -typecheck -verify -primary-file %s

// rdar://problem/57930643
struct School {
var name: String
}
func testKeyPathClosureLiteralError() -> [School] {
let slist = [School(name:"AHS"), School(name:"BHS")]
return slist.sorted(by: \School.name) // expected-error {{cannot convert key path into a multi-argument function type '(School, School) throws -> Bool'}}
}
9 changes: 9 additions & 0 deletions test/Constraints/keypath_closure_conv_mismatch_generic.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// RUN: %target-swift-frontend -typecheck -verify -primary-file %s

// rdar://problem/57930643
struct School {
var name: String
}
func test<A, B>(_: (A, B) -> Bool) {} // expected-note {{in call to function 'test'}}
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'}}

10 changes: 10 additions & 0 deletions test/Constraints/keypath_closure_conv_mismatch_norettype.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// RUN: %target-swift-frontend -typecheck -verify -primary-file %s

// rdar://problem/57930643
struct School {
var name: String
}
func testKeyPathClosureLiteralError() {
let slist = [School(name:"AHS"), School(name:"BHS")]
_ = slist.sorted(by: \School.name) // expected-error {{cannot convert key path into a multi-argument function type '(School, School) throws -> Bool'}}
}