Skip to content

[ConstraintSystem] Diagnose missing explicit calls in assignment #24407

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
May 1, 2019
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
11 changes: 11 additions & 0 deletions lib/Sema/CSDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1589,6 +1589,17 @@ bool MissingCallFailure::diagnoseAsError() {
}
}

if (auto *AE = dyn_cast<AssignExpr>(baseExpr)) {
auto *srcExpr = AE->getSrc();
if (auto *fnType = getType(srcExpr)->getAs<FunctionType>()) {
emitDiagnostic(srcExpr->getLoc(), diag::missing_nullary_call,
fnType->getResult())
.highlight(srcExpr->getSourceRange())
.fixItInsertAfter(srcExpr->getEndLoc(), "()");
return true;
}
}

emitDiagnostic(baseExpr->getLoc(), diag::did_not_call_function_value)
.fixItInsertAfter(insertLoc, "()");
return true;
Expand Down
64 changes: 42 additions & 22 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1978,24 +1978,44 @@ static ConstraintFix *fixRequirementFailure(ConstraintSystem &cs, Type type1,
/// Attempt to repair typing failures and record fixes if needed.
/// \return true if at least some of the failures has been repaired
/// successfully, which allows type matcher to continue.
static bool
repairFailures(ConstraintSystem &cs, Type lhs, Type rhs,
SmallVectorImpl<RestrictionOrFix> &conversionsOrFixes,
ConstraintLocatorBuilder locator) {
bool ConstraintSystem::repairFailures(
Type lhs, Type rhs, SmallVectorImpl<RestrictionOrFix> &conversionsOrFixes,
ConstraintLocatorBuilder locator) {
SmallVector<LocatorPathElt, 4> path;
auto *anchor = locator.getLocatorParts(path);

if (path.empty()) {
if (!anchor)
return false;

// If method reference forms a value type of the key path,
// 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 (anchor && isa<KeyPathExpr>(anchor)) {
if (isa<KeyPathExpr>(anchor)) {
auto *fnType = lhs->getAs<FunctionType>();
if (fnType && fnType->getResult()->isEqual(rhs))
return true;
}

if (isa<AssignExpr>(anchor)) {
if (auto *fnType = lhs->getAs<FunctionType>()) {
// If left-hand side is a function type but right-hand
// side isn't, let's check it would be possible to fix
// this by forming an explicit call.
if (!rhs->is<FunctionType>() && !rhs->isVoid() &&
fnType->getNumParams() == 0 &&
matchTypes(fnType->getResult(), rhs, ConstraintKind::Conversion,
TypeMatchFlags::TMF_ApplyingFix, locator)
.isSuccess()) {
conversionsOrFixes.push_back(
InsertExplicitCall::create(*this, getConstraintLocator(locator)));
return true;
}
}
}

return false;
}

Expand All @@ -2005,14 +2025,14 @@ repairFailures(ConstraintSystem &cs, Type lhs, Type rhs,
case ConstraintLocator::ApplyArgToParam: {
if (lhs->getOptionalObjectType() && !rhs->getOptionalObjectType()) {
conversionsOrFixes.push_back(
ForceOptional::create(cs, lhs, lhs->getOptionalObjectType(),
cs.getConstraintLocator(locator)));
ForceOptional::create(*this, lhs, lhs->getOptionalObjectType(),
getConstraintLocator(locator)));
}
break;
}

case ConstraintLocator::FunctionArgument: {
auto *argLoc = cs.getConstraintLocator(
auto *argLoc = getConstraintLocator(
locator.withPathElement(LocatorPathElt::getSynthesizedArgument(0)));

// Let's drop the last element which points to a single argument
Expand All @@ -2023,7 +2043,7 @@ repairFailures(ConstraintSystem &cs, Type lhs, Type rhs,
path.back().getKind() == ConstraintLocator::ContextualType))
return false;

auto arg = llvm::find_if(cs.getTypeVariables(),
auto arg = llvm::find_if(getTypeVariables(),
[&argLoc](const TypeVariableType *typeVar) {
return typeVar->getImpl().getLocator() == argLoc;
});
Expand All @@ -2040,27 +2060,27 @@ repairFailures(ConstraintSystem &cs, Type lhs, Type rhs,
//
// But if `T.Element` didn't get resolved to `Void` we'd like
// to diagnose this as a missing argument which can't be ignored.
if (arg != cs.getTypeVariables().end()) {
if (arg != getTypeVariables().end()) {
auto fnType = FunctionType::get({FunctionType::Param(lhs)},
cs.getASTContext().TheEmptyTupleType);
getASTContext().TheEmptyTupleType);
conversionsOrFixes.push_back(AddMissingArguments::create(
cs, fnType, {FunctionType::Param(*arg)},
cs.getConstraintLocator(anchor, path,
/*summaryFlags=*/0)));
*this, fnType, {FunctionType::Param(*arg)},
getConstraintLocator(anchor, path,
/*summaryFlags=*/0)));
}
break;
}

case ConstraintLocator::TypeParameterRequirement:
case ConstraintLocator::ConditionalRequirement: {
if (auto *fix = fixRequirementFailure(cs, lhs, rhs, anchor, path))
if (auto *fix = fixRequirementFailure(*this, lhs, rhs, anchor, path))
conversionsOrFixes.push_back(fix);
break;
}

case ConstraintLocator::ClosureResult: {
auto *fix = ContextualMismatch::create(cs, lhs, rhs,
cs.getConstraintLocator(locator));
auto *fix = ContextualMismatch::create(*this, lhs, rhs,
getConstraintLocator(locator));
conversionsOrFixes.push_back(fix);
break;
}
Expand All @@ -2070,14 +2090,14 @@ repairFailures(ConstraintSystem &cs, Type lhs, Type rhs,
// between them are mutability and/or root, value type mismatch.
if (isKnownKeyPathType(lhs) && isKnownKeyPathType(rhs)) {
auto *fix = KeyPathContextualMismatch::create(
cs, lhs, rhs, cs.getConstraintLocator(locator));
*this, lhs, rhs, getConstraintLocator(locator));
conversionsOrFixes.push_back(fix);
}

if (lhs->is<FunctionType>() && !rhs->is<AnyFunctionType>() &&
isa<ClosureExpr>(anchor)) {
auto *fix = ContextualMismatch::create(cs, lhs, rhs,
cs.getConstraintLocator(locator));
auto *fix = ContextualMismatch::create(*this, lhs, rhs,
getConstraintLocator(locator));
conversionsOrFixes.push_back(fix);
}
break;
Expand Down Expand Up @@ -2899,7 +2919,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,

// Attempt to repair any failures identifiable at this point.
if (attemptFixes) {
if (repairFailures(*this, type1, type2, conversionsOrFixes, locator)) {
if (repairFailures(type1, type2, conversionsOrFixes, locator)) {
if (conversionsOrFixes.empty())
return getTypeMatchSuccess();
}
Expand Down Expand Up @@ -6448,6 +6468,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
return result;
}

case FixKind::InsertCall:
case FixKind::SkipSameTypeRequirement:
case FixKind::SkipSuperclassRequirement:
case FixKind::ContextualMismatch:
Expand All @@ -6456,7 +6477,6 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
}

case FixKind::UseSubscriptOperator:
case FixKind::InsertCall:
case FixKind::ExplicitlyEscaping:
case FixKind::CoerceToCheckedCast:
case FixKind::RelabelArguments:
Expand Down
7 changes: 7 additions & 0 deletions lib/Sema/ConstraintSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -2624,6 +2624,13 @@ class ConstraintSystem {
TypeMatchResult(SolutionKind result) : Kind(result) {}
};

/// Attempt to repair typing failures and record fixes if needed.
/// \return true if at least some of the failures has been repaired
/// successfully, which allows type matcher to continue.
bool repairFailures(Type lhs, Type rhs,
SmallVectorImpl<RestrictionOrFix> &conversionsOrFixes,
ConstraintLocatorBuilder locator);

/// Subroutine of \c matchTypes(), which matches up two tuple types.
///
/// \returns the result of performing the tuple-to-tuple conversion.
Expand Down
6 changes: 3 additions & 3 deletions test/Constraints/fixes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ func f4() -> B { }
func f5(_ a: A) { }
func f6(_ a: A, _: Int) { }

func createB() -> B { } // expected-note {{found this candidate}}
func createB(_ i: Int) -> B { } // expected-note {{found this candidate}}
func createB() -> B { }
func createB(_ i: Int) -> B { }

func f7(_ a: A, _: @escaping () -> Int) -> B { }
func f7(_ a: A, _: Int) -> Int { }
Expand All @@ -37,7 +37,7 @@ func forgotCall() {
f6(f4, f2) // expected-error{{function produces expected type 'B'; did you mean to call it with '()'?}}{{8-8=()}}

// With overloading: only one succeeds.
a = createB // expected-error{{ambiguous reference to member 'createB()'}}
a = createB // expected-error{{function produces expected type 'B'; did you mean to call it with '()'?}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!


// With overloading, pick the fewest number of fixes.
var b = f7(f4, f1) // expected-error{{function produces expected type 'B'; did you mean to call it with '()'?}}
Expand Down