Skip to content

Commit 190c395

Browse files
authored
Merge pull request swiftlang#10099 from xedin/closure-diagnostics
[Diagnostics] Improvements for closure argument/result diagnostics
2 parents 3fc134f + 676a48f commit 190c395

13 files changed

+416
-33
lines changed

lib/Sema/CSDiag.cpp

Lines changed: 287 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2173,6 +2173,11 @@ class FailureDiagnosis :public ASTVisitor<FailureDiagnosis, /*exprresult*/bool>{
21732173
Optional<std::function<bool(ArrayRef<OverloadChoice>)>> callback = None,
21742174
bool includeInaccessibleMembers = true);
21752175

2176+
bool diagnoseTrailingClosureErrors(ApplyExpr *expr);
2177+
2178+
bool diagnoseClosureExpr(ClosureExpr *closureExpr, Type contextualType,
2179+
std::function<bool(Type, Type)> resultTypeProcessor);
2180+
21762181
bool visitExpr(Expr *E);
21772182
bool visitIdentityExpr(IdentityExpr *E);
21782183
bool visitTryExpr(TryExpr *E);
@@ -5525,7 +5530,7 @@ bool FailureDiagnosis::diagnoseParameterErrors(CalleeCandidateInfo &CCI,
55255530
// It could be that the argument doesn't conform to an archetype.
55265531
if (CCI.diagnoseGenericParameterErrors(badArgExpr))
55275532
return true;
5528-
5533+
55295534
// Re-type-check the argument with the expected type of the candidate set.
55305535
// This should produce a specific and tailored diagnostic saying that the
55315536
// type mismatches with expectations.
@@ -5989,10 +5994,223 @@ static bool isCastToTypedPointer(ConstraintSystem *CS, const Expr *Fn,
59895994
return true;
59905995
}
59915996

5997+
static bool diagnoseClosureExplicitParameterMismatch(
5998+
ConstraintSystem *const CS, SourceLoc loc,
5999+
ArrayRef<AnyFunctionType::Param> params,
6000+
ArrayRef<AnyFunctionType::Param> args) {
6001+
// We are not trying to diagnose structural problems with top-level
6002+
// arguments here.
6003+
if (params.size() != args.size())
6004+
return false;
6005+
6006+
for (unsigned i = 0, n = params.size(); i != n; ++i) {
6007+
auto paramType = params[i].getType();
6008+
auto argType = args[i].getType();
6009+
6010+
if (auto paramFnType = paramType->getAs<AnyFunctionType>()) {
6011+
if (auto argFnType = argType->getAs<AnyFunctionType>())
6012+
return diagnoseClosureExplicitParameterMismatch(
6013+
CS, loc, paramFnType->getParams(), argFnType->getParams());
6014+
}
6015+
6016+
if (!paramType || !argType || isUnresolvedOrTypeVarType(paramType) ||
6017+
isUnresolvedOrTypeVarType(argType))
6018+
continue;
6019+
6020+
if (!CS->TC.isConvertibleTo(argType, paramType, CS->DC)) {
6021+
CS->TC.diagnose(loc, diag::types_not_convertible, false, paramType,
6022+
argType);
6023+
return true;
6024+
}
6025+
}
6026+
6027+
return false;
6028+
}
6029+
6030+
bool FailureDiagnosis::diagnoseTrailingClosureErrors(ApplyExpr *callExpr) {
6031+
if (!callExpr->hasTrailingClosure())
6032+
return false;
6033+
6034+
auto *fnExpr = callExpr->getFn();
6035+
auto *argExpr = callExpr->getArg();
6036+
6037+
ClosureExpr *closureExpr = nullptr;
6038+
if (auto *PE = dyn_cast<ParenExpr>(argExpr)) {
6039+
closureExpr = dyn_cast<ClosureExpr>(PE->getSubExpr());
6040+
} else {
6041+
return false;
6042+
}
6043+
6044+
if (!closureExpr)
6045+
return false;
6046+
6047+
class CallResultListener : public ExprTypeCheckListener {
6048+
Type expectedResultType;
6049+
6050+
public:
6051+
explicit CallResultListener(Type resultType)
6052+
: expectedResultType(resultType) {}
6053+
6054+
bool builtConstraints(ConstraintSystem &cs, Expr *expr) override {
6055+
if (!expectedResultType)
6056+
return false;
6057+
6058+
auto resultType = cs.getType(expr);
6059+
auto *locator = cs.getConstraintLocator(expr);
6060+
6061+
// Since we know that this is trailing closure, format of the
6062+
// type could be like this - ((Input) -> Result) -> ClosureResult
6063+
// which we can leverage to create specific conversion for
6064+
// result type of the call itself, this might help us gain
6065+
// some valuable contextual information.
6066+
if (auto *fnType = resultType->getAs<AnyFunctionType>()) {
6067+
cs.addConstraint(ConstraintKind::Conversion, fnType->getResult(),
6068+
expectedResultType, locator);
6069+
} else if (auto *typeVar = resultType->getAs<TypeVariableType>()) {
6070+
auto tv =
6071+
cs.createTypeVariable(cs.getConstraintLocator(expr),
6072+
TVO_CanBindToLValue | TVO_CanBindToInOut |
6073+
TVO_PrefersSubtypeBinding);
6074+
6075+
auto extInfo = FunctionType::ExtInfo().withThrows();
6076+
auto fTy = FunctionType::get(ParenType::get(cs.getASTContext(), tv),
6077+
expectedResultType, extInfo);
6078+
6079+
// Add a conversion constraint between the types.
6080+
cs.addConstraint(ConstraintKind::Conversion, typeVar, fTy, locator,
6081+
/*isFavored*/ true);
6082+
}
6083+
6084+
return false;
6085+
}
6086+
};
6087+
6088+
SmallVector<Type, 4> possibleTypes;
6089+
auto currentType = CS->getType(fnExpr);
6090+
6091+
// If current type has type variables or unresolved types
6092+
// let's try to re-typecheck it to see if we can get some
6093+
// more information about what is going on.
6094+
if (currentType->hasTypeVariable() || currentType->hasUnresolvedType()) {
6095+
auto contextualType = CS->getContextualType();
6096+
CallResultListener listener(contextualType);
6097+
CS->TC.getPossibleTypesOfExpressionWithoutApplying(
6098+
fnExpr, CS->DC, possibleTypes, FreeTypeVariableBinding::UnresolvedType,
6099+
&listener);
6100+
6101+
// Looks like there is there a contextual mismatch
6102+
// related to function type, let's try to diagnose it.
6103+
if (possibleTypes.empty() && contextualType &&
6104+
!contextualType->hasUnresolvedType())
6105+
return diagnoseContextualConversionError();
6106+
} else {
6107+
possibleTypes.push_back(currentType);
6108+
}
6109+
6110+
for (auto type : possibleTypes) {
6111+
auto *fnType = type->getAs<AnyFunctionType>();
6112+
if (!fnType)
6113+
continue;
6114+
6115+
auto paramType = fnType->getInput();
6116+
switch (paramType->getKind()) {
6117+
case TypeKind::Tuple: {
6118+
auto tuple = paramType->getAs<TupleType>();
6119+
if (tuple->getNumElements() != 1)
6120+
continue;
6121+
6122+
paramType = tuple->getElement(0).getType();
6123+
break;
6124+
}
6125+
6126+
case TypeKind::Paren:
6127+
paramType = paramType->getWithoutParens();
6128+
break;
6129+
6130+
default:
6131+
return false;
6132+
}
6133+
6134+
if (auto paramFnType = paramType->getAs<AnyFunctionType>()) {
6135+
auto closureType = CS->getType(closureExpr);
6136+
if (auto *argFnType = closureType->getAs<AnyFunctionType>()) {
6137+
auto *params = closureExpr->getParameters();
6138+
auto loc = params ? params->getStartLoc() : closureExpr->getStartLoc();
6139+
if (diagnoseClosureExplicitParameterMismatch(
6140+
CS, loc, argFnType->getParams(), paramFnType->getParams()))
6141+
return true;
6142+
}
6143+
}
6144+
6145+
auto processor = [&](Type resultType, Type expectedResultType) -> bool {
6146+
if (resultType && expectedResultType) {
6147+
if (!resultType->isEqual(expectedResultType)) {
6148+
CS->TC.diagnose(closureExpr->getEndLoc(),
6149+
diag::cannot_convert_closure_result, resultType,
6150+
expectedResultType);
6151+
return true;
6152+
}
6153+
6154+
// Looks like both actual and expected result types match,
6155+
// there is nothing we can diagnose in this case.
6156+
return false;
6157+
}
6158+
6159+
// If we got a result type, let's re-typecheck the function using it,
6160+
// maybe we can find a problem where contextually we expect one type
6161+
// but trailing closure produces completely different one.
6162+
auto fnType = paramType->getAs<AnyFunctionType>();
6163+
if (!fnType)
6164+
return false;
6165+
6166+
auto expectedArgType = FunctionType::get(fnType->getInput(), resultType,
6167+
fnType->getExtInfo());
6168+
6169+
auto expectedType =
6170+
FunctionType::get(expectedArgType, CS->getContextualType());
6171+
6172+
class ClosureCalleeListener : public ExprTypeCheckListener {
6173+
Type contextualType;
6174+
6175+
public:
6176+
explicit ClosureCalleeListener(Type contextualType)
6177+
: contextualType(contextualType) {}
6178+
6179+
bool builtConstraints(ConstraintSystem &cs, Expr *expr) override {
6180+
cs.addConstraint(ConstraintKind::Conversion, cs.getType(expr),
6181+
contextualType, cs.getConstraintLocator(expr),
6182+
/*isFavored*/ true);
6183+
return false;
6184+
}
6185+
};
6186+
6187+
ClosureCalleeListener listener(expectedType);
6188+
return !typeCheckChildIndependently(callExpr->getFn(), Type(),
6189+
CTP_CalleeResult, TCC_ForceRecheck,
6190+
&listener);
6191+
};
6192+
6193+
// Let's see if there are any structural problems with closure itself.
6194+
if (diagnoseClosureExpr(closureExpr, paramType, processor))
6195+
return true;
6196+
}
6197+
6198+
return false;
6199+
}
6200+
59926201
bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) {
5993-
// Type check the function subexpression to resolve a type for it if possible.
6202+
// If this call involves trailing closure as an argument,
6203+
// let's treat it specially, because re-typecheck of the
6204+
// either function or arguments might results in diagnosing
6205+
// of the unrelated problems due to luck of context.
6206+
if (diagnoseTrailingClosureErrors(callExpr))
6207+
return true;
6208+
6209+
// Type check the function subexpression to resolve a type for it if
6210+
// possible.
59946211
auto fnExpr = typeCheckChildIndependently(callExpr->getFn());
5995-
if (!fnExpr) return true;
6212+
if (!fnExpr)
6213+
return true;
59966214

59976215
SWIFT_DEFER {
59986216
if (!fnExpr) return;
@@ -6151,6 +6369,23 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) {
61516369
}
61526370
}
61536371

6372+
// If there is a failing constraint associated with current constraint
6373+
// system which points to the argument/parameter mismatch, let's use
6374+
// that information while re-typechecking argument expression, this
6375+
// makes it a lot easier to determine contextual mismatch.
6376+
if (CS->failedConstraint && !hasTrailingClosure) {
6377+
auto *constraint = CS->failedConstraint;
6378+
if (constraint->getKind() == ConstraintKind::ArgumentTupleConversion) {
6379+
if (auto *locator = constraint->getLocator()) {
6380+
if (locator->getAnchor() == callExpr) {
6381+
argType = constraint->getSecondType();
6382+
if (auto *typeVar = argType->getAs<TypeVariableType>())
6383+
argType = CS->getFixedType(typeVar);
6384+
}
6385+
}
6386+
}
6387+
}
6388+
61546389
// Get the expression result of type checking the arguments to the call
61556390
// independently, so we have some idea of what we're working with.
61566391
//
@@ -6629,9 +6864,36 @@ visitRebindSelfInConstructorExpr(RebindSelfInConstructorExpr *E) {
66296864
return false;
66306865
}
66316866

6867+
static bool isInvalidClosureResultType(Type resultType) {
6868+
return !resultType || resultType->hasUnresolvedType() ||
6869+
resultType->hasTypeVariable() || resultType->hasArchetype();
6870+
}
66326871

66336872
bool FailureDiagnosis::visitClosureExpr(ClosureExpr *CE) {
6634-
auto contextualType = CS->getContextualType();
6873+
return diagnoseClosureExpr(
6874+
CE, CS->getContextualType(),
6875+
[&](Type resultType, Type expectedResultType) -> bool {
6876+
if (isInvalidClosureResultType(expectedResultType))
6877+
return false;
6878+
6879+
// Following situations are possible:
6880+
// * No result type - possible structurable problem in the body;
6881+
// * Function result type - possible use of function without calling it,
6882+
// which is properly diagnosed by actual type-check call.
6883+
if (resultType && !resultType->getRValueType()->is<AnyFunctionType>()) {
6884+
if (!resultType->isEqual(expectedResultType)) {
6885+
diagnose(CE->getEndLoc(), diag::cannot_convert_closure_result,
6886+
resultType, expectedResultType);
6887+
return true;
6888+
}
6889+
}
6890+
return false;
6891+
});
6892+
}
6893+
6894+
bool FailureDiagnosis::diagnoseClosureExpr(
6895+
ClosureExpr *CE, Type contextualType,
6896+
std::function<bool(Type, Type)> resultTypeProcessor) {
66356897
// Look through IUO because it doesn't influence
66366898
// neither parameter nor return type diagnostics itself,
66376899
// but if we have function type inside, that might
@@ -6792,7 +7054,7 @@ bool FailureDiagnosis::visitClosureExpr(ClosureExpr *CE) {
67927054
// - if the there is a result type associated with the closure;
67937055
// - and it's not a void type;
67947056
// - and it hasn't been explicitly written.
6795-
auto resultType = CS->getResultType(CE);
7057+
auto resultType = fnType->getResult();
67967058
auto hasResult = [](Type resultType) -> bool {
67977059
return resultType && !resultType->isVoid();
67987060
};
@@ -6912,6 +7174,9 @@ bool FailureDiagnosis::visitClosureExpr(ClosureExpr *CE) {
69127174
if (!CE->hasSingleExpressionBody())
69137175
return false;
69147176

7177+
if (isInvalidClosureResultType(expectedResultType))
7178+
expectedResultType = Type();
7179+
69157180
// When we're type checking a single-expression closure, we need to reset the
69167181
// DeclContext to this closure for the recursive type checking. Otherwise,
69177182
// if there is a closure in the subexpression, we can violate invariants.
@@ -6937,19 +7202,8 @@ bool FailureDiagnosis::visitClosureExpr(ClosureExpr *CE) {
69377202
auto type = CS->TC.getTypeOfExpressionWithoutApplying(
69387203
closure, CS->DC, decl, FreeTypeVariableBinding::Disallow);
69397204

6940-
Type resultType = type.hasValue() ? *type : Type();
6941-
6942-
// Following situations are possible:
6943-
// * No result type - possible structurable problem in the body;
6944-
// * Function result type - possible use of function without calling it,
6945-
// which is properly diagnosed by actual type-check call.
6946-
if (resultType && !resultType->getRValueType()->is<AnyFunctionType>()) {
6947-
if (!resultType->isEqual(expectedResultType)) {
6948-
diagnose(closure->getEndLoc(), diag::cannot_convert_closure_result,
6949-
resultType, expectedResultType);
6950-
return true;
6951-
}
6952-
}
7205+
if (type && resultTypeProcessor(*type, expectedResultType))
7206+
return true;
69537207
}
69547208

69557209
// If the closure had an expected result type, use it.
@@ -6960,9 +7214,14 @@ bool FailureDiagnosis::visitClosureExpr(ClosureExpr *CE) {
69607214
// let's run proper type-check with expected type and try to verify it.
69617215

69627216
auto CTP = expectedResultType ? CTP_ClosureResult : CTP_Unused;
6963-
if (!typeCheckChildIndependently(CE->getSingleExpressionBody(),
6964-
expectedResultType, CTP, TCCOptions(),
6965-
nullptr, false))
7217+
auto *bodyExpr = typeCheckChildIndependently(CE->getSingleExpressionBody(),
7218+
expectedResultType, CTP,
7219+
TCCOptions(), nullptr, false);
7220+
7221+
if (!bodyExpr)
7222+
return true;
7223+
7224+
if (resultTypeProcessor(bodyExpr->getType(), expectedResultType))
69667225
return true;
69677226
}
69687227

@@ -6974,12 +7233,18 @@ bool FailureDiagnosis::visitClosureExpr(ClosureExpr *CE) {
69747233
if (!fnType || fnType->isEqual(CS->getType(CE)))
69757234
return false;
69767235

7236+
auto contextualResultType = fnType->getResult();
7237+
// If the result type was unknown, it doesn't really make
7238+
// sense to diagnose from expected to unknown here.
7239+
if (isInvalidClosureResultType(contextualResultType))
7240+
return false;
7241+
69777242
// If the closure had an explicitly written return type incompatible with
69787243
// the contextual type, diagnose that.
69797244
if (CE->hasExplicitResultType() &&
69807245
CE->getExplicitResultTypeLoc().getTypeRepr()) {
69817246
auto explicitResultTy = CE->getExplicitResultTypeLoc().getType();
6982-
if (fnType && !explicitResultTy->isEqual(fnType->getResult())) {
7247+
if (fnType && !explicitResultTy->isEqual(contextualResultType)) {
69837248
auto repr = CE->getExplicitResultTypeLoc().getTypeRepr();
69847249
diagnose(repr->getStartLoc(), diag::incorrect_explicit_closure_result,
69857250
explicitResultTy, fnType->getResult())

lib/Sema/CSSolver.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2069,7 +2069,8 @@ bool ConstraintSystem::solve(SmallVectorImpl<Solution> &solutions,
20692069
// If there is more than one viable system, attempt to pick the best
20702070
// solution.
20712071
auto size = solutions.size();
2072-
if (size > 1) {
2072+
if (size > 1 &&
2073+
!Options.contains(ConstraintSystemFlags::ReturnAllDiscoveredSolutions)) {
20732074
if (auto best = findBestSolution(solutions, /*minimize=*/false)) {
20742075
if (*best != 0)
20752076
solutions[0] = std::move(solutions[*best]);

0 commit comments

Comments
 (0)