Skip to content

Commit d8997c6

Browse files
authored
Merge pull request #28573 from xedin/remove-closure-handling-from-csdiag
[Diagnostics] Port last remaining closure expression diagnostics
2 parents 5386296 + 35c437b commit d8997c6

File tree

7 files changed

+39
-279
lines changed

7 files changed

+39
-279
lines changed

docs/TypeChecker.rst

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -988,8 +988,6 @@ The things in the queue yet to be ported are:
988988
Most of the associated diagnostics have been ported and fixes are
989989
located in ``ConstraintSystem::simplifyMemberConstraint``.
990990

991-
- Closure expression diagnostics: ``diagnoseClosureExpr``.
992-
993991
- Diagnostics related to ``if`` statement - "conditional" type mismatch
994992
and, in case of ternary operator, type mismatches between branches.
995993

lib/Sema/CSDiag.cpp

Lines changed: 0 additions & 249 deletions
Original file line numberDiff line numberDiff line change
@@ -250,10 +250,6 @@ class FailureDiagnosis :public ASTVisitor<FailureDiagnosis, /*exprresult*/bool>{
250250

251251
bool diagnoseTrailingClosureErrors(ApplyExpr *expr);
252252

253-
bool
254-
diagnoseClosureExpr(ClosureExpr *closureExpr, Type contextualType,
255-
llvm::function_ref<bool(Type, Type)> resultTypeProcessor);
256-
257253
bool diagnoseSubscriptErrors(SubscriptExpr *SE, bool performingSet);
258254

259255
bool visitExpr(Expr *E);
@@ -272,8 +268,6 @@ class FailureDiagnosis :public ASTVisitor<FailureDiagnosis, /*exprresult*/bool>{
272268
bool visitCoerceExpr(CoerceExpr *CE);
273269
bool visitIfExpr(IfExpr *IE);
274270
bool visitRebindSelfInConstructorExpr(RebindSelfInConstructorExpr *E);
275-
bool visitCaptureListExpr(CaptureListExpr *CLE);
276-
bool visitClosureExpr(ClosureExpr *CE);
277271
};
278272
} // end anonymous namespace
279273

@@ -1720,7 +1714,6 @@ bool FailureDiagnosis::diagnoseTrailingClosureErrors(ApplyExpr *callExpr) {
17201714
if (!callExpr->hasTrailingClosure())
17211715
return false;
17221716

1723-
auto *DC = CS.DC;
17241717
auto *fnExpr = callExpr->getFn();
17251718
auto *argExpr = callExpr->getArg();
17261719

@@ -1819,63 +1812,6 @@ bool FailureDiagnosis::diagnoseTrailingClosureErrors(ApplyExpr *callExpr) {
18191812
return true;
18201813
}
18211814
}
1822-
1823-
auto processor = [&](Type resultType, Type expectedResultType) -> bool {
1824-
if (resultType && expectedResultType) {
1825-
if (!resultType->isEqual(expectedResultType)) {
1826-
auto &DE = CS.getASTContext().Diags;
1827-
DE.diagnose(closureExpr->getEndLoc(),
1828-
diag::cannot_convert_closure_result, resultType,
1829-
expectedResultType);
1830-
return true;
1831-
}
1832-
1833-
// Looks like both actual and expected result types match,
1834-
// there is nothing we can diagnose in this case.
1835-
return false;
1836-
}
1837-
1838-
// If we got a result type, let's re-typecheck the function using it,
1839-
// maybe we can find a problem where contextually we expect one type
1840-
// but trailing closure produces completely different one.
1841-
auto fnType = paramType->getAs<AnyFunctionType>();
1842-
if (!fnType)
1843-
return false;
1844-
1845-
class ClosureCalleeListener : public ExprTypeCheckListener {
1846-
FunctionType *InputType;
1847-
Type ResultType;
1848-
1849-
public:
1850-
explicit ClosureCalleeListener(FunctionType *inputType, Type resultType)
1851-
: InputType(inputType), ResultType(resultType) {}
1852-
1853-
bool builtConstraints(ConstraintSystem &cs, Expr *expr) override {
1854-
if (!ResultType)
1855-
return false;
1856-
1857-
AnyFunctionType::Param Input(InputType);
1858-
auto expectedType = FunctionType::get({Input}, ResultType);
1859-
cs.addConstraint(ConstraintKind::Conversion, cs.getType(expr),
1860-
expectedType, cs.getConstraintLocator(expr),
1861-
/*isFavored*/ true);
1862-
return false;
1863-
}
1864-
};
1865-
1866-
auto expectedArgType = FunctionType::get(fnType->getParams(), resultType,
1867-
fnType->getExtInfo());
1868-
1869-
llvm::SaveAndRestore<DeclContext *> SavedDC(CS.DC, DC);
1870-
ClosureCalleeListener listener(expectedArgType, CS.getContextualType());
1871-
return !typeCheckChildIndependently(callExpr->getFn(), Type(),
1872-
CTP_CalleeResult, TCC_ForceRecheck,
1873-
&listener);
1874-
};
1875-
1876-
// Let's see if there are any structural problems with closure itself.
1877-
if (diagnoseClosureExpr(closureExpr, paramType, processor))
1878-
return true;
18791815
}
18801816

18811817
return false;
@@ -2516,191 +2452,6 @@ visitRebindSelfInConstructorExpr(RebindSelfInConstructorExpr *E) {
25162452
return false;
25172453
}
25182454

2519-
bool FailureDiagnosis::visitCaptureListExpr(CaptureListExpr *CLE) {
2520-
// Always walk into the closure of a capture list expression.
2521-
return visitClosureExpr(CLE->getClosureBody());
2522-
}
2523-
2524-
static bool isInvalidClosureResultType(Type resultType) {
2525-
return !resultType || resultType->hasUnresolvedType() ||
2526-
resultType->hasTypeVariable() || resultType->hasArchetype();
2527-
}
2528-
2529-
bool FailureDiagnosis::visitClosureExpr(ClosureExpr *CE) {
2530-
return diagnoseClosureExpr(
2531-
CE, CS.getContextualType(),
2532-
[&](Type resultType, Type expectedResultType) -> bool {
2533-
if (isInvalidClosureResultType(expectedResultType))
2534-
return false;
2535-
2536-
// Following situations are possible:
2537-
// * No result type - possible structurable problem in the body;
2538-
// * Function result type - possible use of function without calling it,
2539-
// which is properly diagnosed by actual type-check call.
2540-
if (resultType && !resultType->getRValueType()->is<AnyFunctionType>()) {
2541-
if (!resultType->isEqual(expectedResultType)) {
2542-
diagnose(CE->getEndLoc(), diag::cannot_convert_closure_result,
2543-
resultType, expectedResultType);
2544-
return true;
2545-
}
2546-
}
2547-
return false;
2548-
});
2549-
}
2550-
2551-
bool FailureDiagnosis::diagnoseClosureExpr(
2552-
ClosureExpr *CE, Type contextualType,
2553-
llvm::function_ref<bool(Type, Type)> resultTypeProcessor) {
2554-
// Look through IUO because it doesn't influence
2555-
// neither parameter nor return type diagnostics itself,
2556-
// but if we have function type inside, that might
2557-
// signficantly improve diagnostic quality.
2558-
// FIXME: We need to rework this with IUOs out of the type system.
2559-
// if (contextualType) {
2560-
// if (auto IUO =
2561-
// CS.lookThroughImplicitlyUnwrappedOptionalType(contextualType))
2562-
// contextualType = IUO;
2563-
// }
2564-
2565-
Type expectedResultType;
2566-
2567-
// If we have a contextual type available for this closure, apply it to the
2568-
// ParamDecls in our parameter list. This ensures that any uses of them get
2569-
// appropriate types.
2570-
if (contextualType && contextualType->is<FunctionType>()) {
2571-
auto fnType = contextualType->getAs<FunctionType>();
2572-
auto *params = CE->getParameters();
2573-
auto inferredArgs = fnType->getParams();
2574-
2575-
// It is very common for a contextual type to disagree with the argument
2576-
// list built into the closure expr. This can be because the closure expr
2577-
// had an explicitly specified pattern, a la:
2578-
// { a,b in ... }
2579-
// or could be because the closure has an implicitly generated one:
2580-
// { $0 + $1 }
2581-
// in either case, we want to produce nice and clear diagnostics.
2582-
unsigned actualArgCount = params->size();
2583-
unsigned inferredArgCount = inferredArgs.size();
2584-
2585-
if (actualArgCount != inferredArgCount) {
2586-
if (inferredArgCount == 1 && actualArgCount > 1) {
2587-
auto *argTupleTy = inferredArgs.front().getOldType()->getAs<TupleType>();
2588-
// Let's see if inferred argument is actually a tuple inside of Paren.
2589-
if (argTupleTy) {
2590-
// Looks like the number of closure parameters matches number
2591-
// of inferred arguments, which means we can we can emit an
2592-
// error about an attempt to make use of tuple splat or tuple
2593-
// destructuring and provide a proper fix-it.
2594-
if (argTupleTy->getNumElements() == actualArgCount) {
2595-
ClosureParamDestructuringFailure failure(
2596-
CS, fnType, CS.getConstraintLocator(CE));
2597-
return failure.diagnoseAsError();
2598-
}
2599-
}
2600-
}
2601-
2602-
// Extraneous arguments.
2603-
if (inferredArgCount < actualArgCount) {
2604-
auto diag = diagnose(
2605-
params->getStartLoc(), diag::closure_argument_list_tuple, fnType,
2606-
inferredArgCount, actualArgCount, (actualArgCount == 1));
2607-
2608-
bool onlyAnonymousParams =
2609-
std::all_of(params->begin(), params->end(),
2610-
[](ParamDecl *param) { return !param->hasName(); });
2611-
2612-
// If closure expects no parameters but N was given,
2613-
// and all of them are anonymous let's suggest removing them.
2614-
if (inferredArgCount == 0 && onlyAnonymousParams) {
2615-
auto inLoc = CE->getInLoc();
2616-
auto &sourceMgr = CS.getASTContext().SourceMgr;
2617-
2618-
if (inLoc.isValid())
2619-
diag.fixItRemoveChars(params->getStartLoc(),
2620-
Lexer::getLocForEndOfToken(sourceMgr, inLoc));
2621-
}
2622-
return true;
2623-
}
2624-
2625-
// Missing arguments are already diagnosed via new diagnostic framework.
2626-
return false;
2627-
}
2628-
2629-
// Coerce parameter types here only if there are no unresolved
2630-
TypeChecker::coerceParameterListToType(params, CE, fnType);
2631-
expectedResultType = fnType->getResult();
2632-
}
2633-
2634-
// Defend against type variables from our constraint system leaking into
2635-
// recursive constraints systems formed when checking the body of the
2636-
// closure. These typevars come into them when the body does name
2637-
// lookups against the parameter decls.
2638-
//
2639-
// Handle this by rewriting the arguments to UnresolvedType().
2640-
for (auto VD : *CE->getParameters()) {
2641-
if (VD->hasInterfaceType() && (VD->getType()->hasTypeVariable() ||
2642-
VD->getType()->hasError())) {
2643-
VD->setInterfaceType(CS.getASTContext().TheUnresolvedType);
2644-
}
2645-
}
2646-
2647-
// If this is a complex leaf closure, there is nothing more we can do.
2648-
if (!CE->hasSingleExpressionBody())
2649-
return false;
2650-
2651-
if (isInvalidClosureResultType(expectedResultType))
2652-
expectedResultType = Type();
2653-
2654-
// When we're type checking a single-expression closure, we need to reset the
2655-
// DeclContext to this closure for the recursive type checking. Otherwise,
2656-
// if there is a closure in the subexpression, we can violate invariants.
2657-
{
2658-
llvm::SaveAndRestore<DeclContext *> SavedDC(CS.DC, CE);
2659-
2660-
// Explicitly disallow to produce solutions with unresolved type variables,
2661-
// because there is no auxiliary logic which would handle that and it's
2662-
// better to allow failure diagnosis to run directly on the closure body.
2663-
// Note that presence of contextual type implicitly forbids such solutions,
2664-
// but it's not always reset.
2665-
2666-
if (expectedResultType && !CE->hasExplicitResultType()) {
2667-
auto closure = CE->getSingleExpressionBody();
2668-
ConcreteDeclRef decl = nullptr;
2669-
// Let's try to compute result type without mutating AST and
2670-
// using expected (contextual) result type, that's going to help
2671-
// diagnose situations where contextual type expected one result
2672-
// type but actual closure produces a different one without explicitly
2673-
// declaring it (e.g. by using anonymous parameters).
2674-
auto type = TypeChecker::getTypeOfExpressionWithoutApplying(
2675-
closure, CS.DC, decl, FreeTypeVariableBinding::Disallow);
2676-
2677-
if (type && resultTypeProcessor(type, expectedResultType))
2678-
return true;
2679-
}
2680-
2681-
// If the closure had an expected result type, use it.
2682-
if (CE->hasExplicitResultType())
2683-
expectedResultType = CE->getExplicitResultTypeLoc().getType();
2684-
2685-
// If we couldn't diagnose anything related to the contextual result type
2686-
// let's run proper type-check with expected type and try to verify it.
2687-
2688-
auto CTP = expectedResultType ? CTP_ClosureResult : CTP_Unused;
2689-
auto *bodyExpr = typeCheckChildIndependently(CE->getSingleExpressionBody(),
2690-
expectedResultType, CTP,
2691-
TCCOptions(), nullptr, false);
2692-
2693-
if (!bodyExpr)
2694-
return true;
2695-
2696-
if (resultTypeProcessor(CS.getType(bodyExpr), expectedResultType))
2697-
return true;
2698-
}
2699-
2700-
// Otherwise, we can't produce a specific diagnostic.
2701-
return false;
2702-
}
2703-
27042455
bool FailureDiagnosis::visitArrayExpr(ArrayExpr *E) {
27052456
// If we had a contextual type, then it either conforms to
27062457
// ExpressibleByArrayLiteral or it is an invalid contextual type.

lib/Sema/CSDiagnostics.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3767,12 +3767,11 @@ bool ImplicitInitOnNonConstMetatypeFailure::diagnoseAsError() {
37673767
bool MissingArgumentsFailure::diagnoseAsError() {
37683768
auto &cs = getConstraintSystem();
37693769
auto *locator = getLocator();
3770-
auto path = locator->getPath();
37713770

3772-
if (path.empty() ||
3773-
!(path.back().getKind() == ConstraintLocator::ApplyArgToParam ||
3774-
path.back().getKind() == ConstraintLocator::ContextualType ||
3775-
path.back().getKind() == ConstraintLocator::ApplyArgument))
3771+
if (!(locator->isLastElement<LocatorPathElt::ApplyArgToParam>() ||
3772+
locator->isLastElement<LocatorPathElt::ContextualType>() ||
3773+
locator->isLastElement<LocatorPathElt::ApplyArgument>() ||
3774+
locator->isLastElement<LocatorPathElt::ClosureResult>()))
37763775
return false;
37773776

37783777
// If this is a misplaced `missng argument` situation, it would be
@@ -4015,6 +4014,13 @@ bool MissingArgumentsFailure::diagnoseClosure(ClosureExpr *closure) {
40154014
funcType = cs.getContextualType()->getAs<FunctionType>();
40164015
} else if (auto info = getFunctionArgApplyInfo(locator)) {
40174016
funcType = info->getParamType()->getAs<FunctionType>();
4017+
} else if (locator->isLastElement<LocatorPathElt::ClosureResult>()) {
4018+
// Based on the locator we know this this is something like this:
4019+
// `let _: () -> ((Int) -> Void) = { return {} }`.
4020+
funcType = getType(getRawAnchor())
4021+
->castTo<FunctionType>()
4022+
->getResult()
4023+
->castTo<FunctionType>();
40184024
}
40194025

40204026
if (!funcType)

lib/Sema/CSSimplify.cpp

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6060,20 +6060,33 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
60606060

60616061
auto locator = getConstraintLocator(locatorB);
60626062

6063-
// If this is an unresolved member ref e.g. `.foo` and its contextual base
6064-
// type has been determined to be a "hole", let's mark the resulting member
6065-
// type as a potential hole and continue solving.
6066-
if (shouldAttemptFixes() && kind == ConstraintKind::UnresolvedValueMember &&
6067-
baseObjTy->getMetatypeInstanceType()->isHole()) {
6068-
auto *fix =
6069-
SpecifyBaseTypeForContextualMember::create(*this, member, locator);
6070-
if (recordFix(fix))
6071-
return SolutionKind::Error;
6063+
// If the base type of this member lookup is a "hole" there is no
6064+
// reason to perform a lookup because it wouldn't return any results.
6065+
if (shouldAttemptFixes()) {
6066+
auto markMemberTypeAsPotentialHole = [&](Type memberTy) {
6067+
if (auto *typeVar = memberTy->getAs<TypeVariableType>())
6068+
recordPotentialHole(typeVar);
6069+
};
60726070

6073-
if (auto *typeVar = memberTy->getAs<TypeVariableType>())
6074-
recordPotentialHole(typeVar);
6071+
// If this is an unresolved member ref e.g. `.foo` and its contextual base
6072+
// type has been determined to be a "hole", let's mark the resulting member
6073+
// type as a potential hole and continue solving.
6074+
if (kind == ConstraintKind::UnresolvedValueMember &&
6075+
baseObjTy->getMetatypeInstanceType()->isHole()) {
6076+
auto *fix =
6077+
SpecifyBaseTypeForContextualMember::create(*this, member, locator);
6078+
if (recordFix(fix))
6079+
return SolutionKind::Error;
60756080

6076-
return SolutionKind::Solved;
6081+
markMemberTypeAsPotentialHole(memberTy);
6082+
return SolutionKind::Solved;
6083+
} else if (kind == ConstraintKind::ValueMember && baseObjTy->isHole()) {
6084+
// If base type is a "hole" there is no reason to record any
6085+
// more "member not found" fixes for chained member references.
6086+
increaseScore(SK_Fix);
6087+
markMemberTypeAsPotentialHole(memberTy);
6088+
return SolutionKind::Solved;
6089+
}
60776090
}
60786091

60796092
MemberLookupResult result =
@@ -6345,15 +6358,6 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
63456358
// fake its presence based on use, that makes it possible to diagnose
63466359
// problems related to member lookup more precisely.
63476360

6348-
// If base type is a "hole" there is no reason to record any
6349-
// more "member not found" fixes for chained member references.
6350-
if (baseTy->isHole()) {
6351-
increaseScore(SK_Fix);
6352-
if (auto *memberTypeVar = memberTy->getAs<TypeVariableType>())
6353-
recordPotentialHole(memberTypeVar);
6354-
return SolutionKind::Solved;
6355-
}
6356-
63576361
return fixMissingMember(origBaseTy, memberTy, locator);
63586362
}
63596363
return SolutionKind::Error;

test/Constraints/closures.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,7 @@ let _ = { $0 = $0 = 42 } // expected-error {{assigning a variable to itself}}
484484
let mismatchInClosureResultType : (String) -> ((Int) -> Void) = {
485485
(String) -> ((Int) -> Void) in
486486
return { }
487-
// expected-error@-1 {{contextual type for closure argument list expects 1 argument, which cannot be implicitly ignored}}
487+
// expected-error@-1 {{contextual type for closure argument list expects 1 argument, which cannot be implicitly ignored}} {{13-13= _ in}}
488488
}
489489

490490
// SR-3520: Generic function taking closure with inout parameter can result in a variety of compiler errors or EXC_BAD_ACCESS

0 commit comments

Comments
 (0)