Skip to content

Commit 764d0fc

Browse files
committed
improve the diagnostics for when a multi-statement closure has no inferred result type.
Previously: error: generic parameter 'T' could not be inferred now: error: unable to infer closure return type in current context There is still more to do, but this fixes: <rdar://problem/23570873> QoI: Poor error calling map without being able to infer "U" (closure result inference)
1 parent e7f01c6 commit 764d0fc

File tree

4 files changed

+54
-12
lines changed

4 files changed

+54
-12
lines changed

lib/Sema/CSDiag.cpp

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6116,6 +6116,24 @@ static void noteArchetypeSource(const TypeLoc &loc, ArchetypeType *archetype,
61166116
}
61176117

61186118

6119+
/// Check the specified closure to see if it is a multi-statement closure with
6120+
/// an uninferred type. If so, diagnose the problem with an error and return
6121+
/// true.
6122+
static bool checkMultistatementClosureForAmbiguity(ClosureExpr *closure,
6123+
TypeChecker &tc) {
6124+
if (closure->hasSingleExpressionBody() ||
6125+
closure->hasExplicitResultType())
6126+
return false;
6127+
6128+
auto closureType = closure->getType()->getAs<AnyFunctionType>();
6129+
if (!closureType || !isUnresolvedOrTypeVarType(closureType->getResult()))
6130+
return false;
6131+
6132+
tc.diagnose(closure->getLoc(), diag::cannot_infer_closure_result_type);
6133+
return true;
6134+
}
6135+
6136+
61196137
/// Emit an error message about an unbound generic parameter existing, and
61206138
/// emit notes referring to the target of a diagnostic, e.g., the function
61216139
/// or parameter being used.
@@ -6140,6 +6158,21 @@ static void diagnoseUnboundArchetype(Expr *overallExpr,
61406158
ND->getDeclaredType());
61416159
return;
61426160
}
6161+
6162+
// A very common cause of this diagnostic is a situation where a closure expr
6163+
// has no inferred type, due to being a multiline closure. Check to see if
6164+
// this is the case and (if so), speculatively diagnose that as the problem.
6165+
bool didDiagnose = false;
6166+
overallExpr->forEachChildExpr([&](Expr *subExpr) -> Expr*{
6167+
auto closure = dyn_cast<ClosureExpr>(subExpr);
6168+
if (!didDiagnose && closure)
6169+
didDiagnose = checkMultistatementClosureForAmbiguity(closure, tc);
6170+
6171+
return subExpr;
6172+
});
6173+
6174+
if (didDiagnose) return;
6175+
61436176

61446177
// Otherwise, emit an error message on the expr we have, and emit a note
61456178
// about where the archetype came from.
@@ -6225,16 +6258,11 @@ void FailureDiagnosis::diagnoseAmbiguity(Expr *E) {
62256258
// Unresolved/Anonymous ClosureExprs are common enough that we should give
62266259
// them tailored diagnostics.
62276260
if (auto CE = dyn_cast<ClosureExpr>(E->getValueProvidingExpr())) {
6228-
auto CFTy = CE->getType()->getAs<AnyFunctionType>();
6229-
62306261
// If this is a multi-statement closure with no explicit result type, emit
62316262
// a note to clue the developer in.
6232-
if (!CE->hasExplicitResultType() && CFTy &&
6233-
isUnresolvedOrTypeVarType(CFTy->getResult())) {
6234-
diagnose(CE->getLoc(), diag::cannot_infer_closure_result_type);
6263+
if (checkMultistatementClosureForAmbiguity(CE, CS->getTypeChecker()))
62356264
return;
6236-
}
6237-
6265+
62386266
diagnose(E->getLoc(), diag::cannot_infer_closure_type)
62396267
.highlight(E->getSourceRange());
62406268
return;

test/Constraints/closures.swift

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,10 @@ var _: (Int,Int) -> Int = {$0+$1+$2} // expected-error {{contextual closure typ
165165
// Crash when re-typechecking bodies of non-single expression closures
166166

167167
struct CC {}
168-
// expected-note @+1 {{in call to function 'callCC'}}
169168
func callCC<U>(_ f: (CC) -> U) -> () {}
170169

171170
func typeCheckMultiStmtClosureCrash() {
172-
callCC { // expected-error {{generic parameter 'U' could not be inferred}}
171+
callCC { // expected-error {{unable to infer closure return type in current context}}
173172
_ = $0
174173
return 1
175174
}
@@ -206,3 +205,18 @@ func testAcceptNothingToInt(ac1: @autoclosure () -> Int) {
206205
// expected-error@-1{{cannot convert value of type '(_) -> Int' to expected argument type '() -> Int'}}
207206
// FIXME: expected-error@-2{{closure use of non-escaping parameter 'ac1' may allow it to escape}}
208207
}
208+
209+
// <rdar://problem/23570873> QoI: Poor error calling map without being able to infer "U" (closure result inference)
210+
struct Thing {
211+
init?() {}
212+
}
213+
// This throws a compiler error
214+
let things = Thing().map { thing in // expected-error {{unable to infer closure return type in current context}}
215+
// Commenting out this makes it compile
216+
_ = thing
217+
return thing
218+
}
219+
220+
221+
222+

test/Constraints/diagnostics.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ func r18800223(_ i : Int) {
286286
}
287287

288288
// <rdar://problem/21883806> Bogus "'_' can only appear in a pattern or on the left side of an assignment" is back
289-
_ = { $0 } // expected-error {{unable to infer closure return type in current context}}
289+
_ = { $0 } // expected-error {{unable to infer closure type in the current context}}
290290

291291

292292

test/Constraints/patterns.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,14 +222,14 @@ func good(_ a: A<EE>) -> Int {
222222
}
223223

224224
func bad(_ a: A<EE>) {
225-
a.map { // expected-error {{generic parameter 'T' could not be inferred}}
225+
a.map { // expected-error {{unable to infer closure return type in current context}}
226226
let _: EE = $0
227227
return 1
228228
}
229229
}
230230

231231
func ugly(_ a: A<EE>) {
232-
a.map { // expected-error {{generic parameter 'T' could not be inferred}}
232+
a.map { // expected-error {{unable to infer closure return type in current context}}
233233
switch $0 {
234234
case .A:
235235
return 1

0 commit comments

Comments
 (0)