Skip to content

Commit 8bcc192

Browse files
committed
[Diagnostics] Diagnose inability to infer (complex) closure return type
1 parent 899cc20 commit 8bcc192

File tree

16 files changed

+108
-23
lines changed

16 files changed

+108
-23
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -302,11 +302,11 @@ ERROR(cannot_invoke_closure_type,none,
302302
ERROR(cannot_infer_closure_type,none,
303303
"unable to infer closure type in the current context", ())
304304
ERROR(cannot_infer_closure_result_type,none,
305-
"unable to infer complex closure return type; "
306-
"add explicit type to disambiguate", ())
307-
FIXIT(insert_closure_return_type,
308-
"%select{| () }1-> %0 %select{|in }1",
309-
(Type, bool))
305+
"unable to infer%select{ complex|}0 closure return type; "
306+
"add explicit type to disambiguate", (bool))
307+
FIXIT(insert_closure_return_type_placeholder,
308+
"%select{| () }0-> <#Result#> %select{|in }0",
309+
(bool))
310310

311311
ERROR(incorrect_explicit_closure_result,none,
312312
"declared closure result %0 is incompatible with contextual type %1",

lib/Sema/CSBindings.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -999,6 +999,11 @@ bool TypeVariableBinding::attempt(ConstraintSystem &cs) const {
999999
cs.getConstraintLocator(dstLocator->getAnchor(), path.drop_back()));
10001000
if (cs.recordFix(fix))
10011001
return true;
1002+
} else if (TypeVar->getImpl().isClosureResultType()) {
1003+
auto *fix = SpecifyClosureReturnType::create(
1004+
cs, TypeVar->getImpl().getLocator());
1005+
if (cs.recordFix(fix))
1006+
return true;
10021007
}
10031008
}
10041009
}

lib/Sema/CSDiagnostics.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5915,3 +5915,31 @@ bool MissingContextualBaseInMemberRefFailure::diagnoseAsError() {
59155915
.highlight(anchor->getSourceRange());
59165916
return true;
59175917
}
5918+
5919+
bool UnableToInferClosureReturnType::diagnoseAsError() {
5920+
auto *closure = cast<ClosureExpr>(getRawAnchor());
5921+
5922+
auto diagnostic =
5923+
emitDiagnostic(closure->getLoc(),
5924+
diag::cannot_infer_closure_result_type,
5925+
closure->hasSingleExpressionBody());
5926+
5927+
// If there is a location for an 'in' token, then the argument list was
5928+
// specified somehow but no return type was. Insert a "-> ReturnType "
5929+
// before the in token.
5930+
if (closure->getInLoc().isValid()) {
5931+
diagnostic.fixItInsert(closure->getInLoc(),
5932+
diag::insert_closure_return_type_placeholder,
5933+
/*argListSpecified=*/false);
5934+
} else if (closure->getParameters()->size() == 0) {
5935+
// Otherwise, the closure must take zero arguments.
5936+
//
5937+
// As such, we insert " () -> ReturnType in " right after the '{' that
5938+
// starts the closure body.
5939+
diagnostic.fixItInsertAfter(closure->getBody()->getLBraceLoc(),
5940+
diag::insert_closure_return_type_placeholder,
5941+
/*argListSpecified=*/true);
5942+
}
5943+
5944+
return true;
5945+
}

lib/Sema/CSDiagnostics.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1912,6 +1912,15 @@ class MissingContextualBaseInMemberRefFailure final : public FailureDiagnostic {
19121912
bool diagnoseAsError();
19131913
};
19141914

1915+
class UnableToInferClosureReturnType final : public FailureDiagnostic {
1916+
public:
1917+
UnableToInferClosureReturnType(ConstraintSystem &cs,
1918+
ConstraintLocator *locator)
1919+
: FailureDiagnostic(cs, locator) {}
1920+
1921+
bool diagnoseAsError();
1922+
};
1923+
19151924
} // end namespace constraints
19161925
} // end namespace swift
19171926

lib/Sema/CSFix.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,3 +1155,15 @@ SpecifyBaseTypeForContextualMember *SpecifyBaseTypeForContextualMember::create(
11551155
return new (cs.getAllocator())
11561156
SpecifyBaseTypeForContextualMember(cs, member, locator);
11571157
}
1158+
1159+
bool SpecifyClosureReturnType::diagnose(bool asNote) const {
1160+
auto &cs = getConstraintSystem();
1161+
UnableToInferClosureReturnType failure(cs, getLocator());
1162+
return failure.diagnose(asNote);
1163+
}
1164+
1165+
SpecifyClosureReturnType *
1166+
SpecifyClosureReturnType::create(ConstraintSystem &cs,
1167+
ConstraintLocator *locator) {
1168+
return new (cs.getAllocator()) SpecifyClosureReturnType(cs, locator);
1169+
}

lib/Sema/CSFix.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,10 @@ enum class FixKind : uint8_t {
231231
/// Base type in reference to the contextual member e.g. `.foo` couldn't be
232232
/// inferred and has to be specified explicitly.
233233
SpecifyBaseTypeForContextualMember,
234+
235+
/// Closure return type has to be explicitly specified because it can't be
236+
/// inferred in current context e.g. because it's a multi-statement closure.
237+
SpecifyClosureReturnType,
234238
};
235239

236240
class ConstraintFix {
@@ -1605,6 +1609,21 @@ class SpecifyBaseTypeForContextualMember final : public ConstraintFix {
16051609
create(ConstraintSystem &cs, DeclNameRef member, ConstraintLocator *locator);
16061610
};
16071611

1612+
class SpecifyClosureReturnType final : public ConstraintFix {
1613+
SpecifyClosureReturnType(ConstraintSystem &cs, ConstraintLocator *locator)
1614+
: ConstraintFix(cs, FixKind::SpecifyClosureReturnType, locator) {}
1615+
1616+
public:
1617+
std::string getName() const {
1618+
return "specify closure return type";
1619+
}
1620+
1621+
bool diagnose(bool asNote = false) const;
1622+
1623+
static SpecifyClosureReturnType *create(ConstraintSystem &cs,
1624+
ConstraintLocator *locator);
1625+
};
1626+
16081627
} // end namespace constraints
16091628
} // end namespace swift
16101629

lib/Sema/CSGen.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2430,7 +2430,8 @@ namespace {
24302430
CS.getConstraintLocator(expr, ConstraintLocator::ClosureResult);
24312431

24322432
if (expr->hasEmptyBody()) {
2433-
resultTy = CS.createTypeVariable(locator, 0);
2433+
resultTy = CS.createTypeVariable(
2434+
locator, expr->hasSingleExpressionBody() ? 0 : TVO_CanBindToHole);
24342435

24352436
// Closures with empty bodies should be inferred to return
24362437
// ().
@@ -2442,7 +2443,8 @@ namespace {
24422443
} else {
24432444
// If no return type was specified, create a fresh type
24442445
// variable for it.
2445-
resultTy = CS.createTypeVariable(locator, 0);
2446+
resultTy = CS.createTypeVariable(
2447+
locator, expr->hasSingleExpressionBody() ? 0 : TVO_CanBindToHole);
24462448

24472449
if (closureHasNoResult(expr)) {
24482450
// Allow it to default to () if there are no return statements.

lib/Sema/CSSimplify.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8596,6 +8596,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
85968596
case FixKind::AllowMutatingMemberOnRValueBase:
85978597
case FixKind::AllowTupleSplatForSingleParameter:
85988598
case FixKind::AllowInvalidUseOfTrailingClosure:
8599+
case FixKind::SpecifyClosureReturnType:
85998600
llvm_unreachable("handled elsewhere");
86008601
}
86018602

lib/Sema/ConstraintSystem.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,9 @@ class TypeVariableType::Implementation {
297297
/// Retrieve the generic parameter opened by this type variable.
298298
GenericTypeParamType *getGenericParameter() const;
299299

300+
/// Determine whether this type variable represents a closure result type.
301+
bool isClosureResultType() const;
302+
300303
/// Retrieve the representative of the equivalence class to which this
301304
/// type variable belongs.
302305
///

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,14 @@ TypeVariableType::Implementation::getGenericParameter() const {
8383
return locator ? locator->getGenericParameter() : nullptr;
8484
}
8585

86+
bool TypeVariableType::Implementation::isClosureResultType() const {
87+
if (!(locator && locator->getAnchor()))
88+
return false;
89+
90+
return isa<ClosureExpr>(locator->getAnchor()) &&
91+
locator->isLastElement<LocatorPathElt::ClosureResult>();
92+
}
93+
8694
void *operator new(size_t bytes, ConstraintSystem& cs,
8795
size_t alignment) {
8896
return cs.getAllocator().Allocate(bytes, alignment);

0 commit comments

Comments
 (0)