Skip to content

Commit 89933c6

Browse files
committed
[ConstraintSystem] Don't allow explicit closure result to be implicitly converted to Void
It's allowed to convert a single statement closure from `(...) -> T` to `(...) -> Void` _only_ if there is no explicit `return` in the body. Resolves: [SR-12277](https://bugs.swift.org/browse/SR-12277) Resolves: rdar://problem/52204414
1 parent 0ebdba0 commit 89933c6

File tree

5 files changed

+37
-9
lines changed

5 files changed

+37
-9
lines changed

lib/Sema/CSDiagnostics.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,7 @@ bool GenericArgumentsMismatchFailure::diagnoseAsError() {
659659
break;
660660
}
661661

662+
case ConstraintLocator::ClosureBody:
662663
case ConstraintLocator::ClosureResult: {
663664
diagnostic = diag::cannot_convert_closure_result;
664665
break;
@@ -1902,6 +1903,7 @@ bool ContextualFailure::diagnoseAsError() {
19021903

19031904
Diag<Type, Type> diagnostic;
19041905
switch (path.back().getKind()) {
1906+
case ConstraintLocator::ClosureBody:
19051907
case ConstraintLocator::ClosureResult: {
19061908
auto *closure = cast<ClosureExpr>(getRawAnchor());
19071909
if (closure->hasExplicitResultType() &&
@@ -3786,7 +3788,8 @@ bool MissingArgumentsFailure::diagnoseAsError() {
37863788
if (!(locator->isLastElement<LocatorPathElt::ApplyArgToParam>() ||
37873789
locator->isLastElement<LocatorPathElt::ContextualType>() ||
37883790
locator->isLastElement<LocatorPathElt::ApplyArgument>() ||
3789-
locator->isLastElement<LocatorPathElt::ClosureResult>()))
3791+
locator->isLastElement<LocatorPathElt::ClosureResult>() ||
3792+
locator->isLastElement<LocatorPathElt::ClosureBody>()))
37903793
return false;
37913794

37923795
// If this is a misplaced `missng argument` situation, it would be
@@ -4045,7 +4048,8 @@ bool MissingArgumentsFailure::diagnoseClosure(ClosureExpr *closure) {
40454048
if (auto objectType = paramType->getOptionalObjectType())
40464049
paramType = objectType;
40474050
funcType = paramType->getAs<FunctionType>();
4048-
} else if (locator->isLastElement<LocatorPathElt::ClosureResult>()) {
4051+
} else if (locator->isLastElement<LocatorPathElt::ClosureResult>() ||
4052+
locator->isLastElement<LocatorPathElt::ClosureBody>()) {
40494053
// Based on the locator we know this this is something like this:
40504054
// `let _: () -> ((Int) -> Void) = { return {} }`.
40514055
funcType = getType(getRawAnchor())

lib/Sema/CSSimplify.cpp

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3663,6 +3663,7 @@ bool ConstraintSystem::repairFailures(
36633663
break;
36643664
}
36653665

3666+
case ConstraintLocator::ClosureBody:
36663667
case ConstraintLocator::ClosureResult: {
36673668
if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes,
36683669
locator))
@@ -4792,9 +4793,21 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
47924793
// literals and expressions representing an implicit return type of the single
47934794
// expression functions.
47944795
if (auto elt = locator.last()) {
4795-
if (elt->isClosureResult() || elt->isResultOfSingleExprFunction()) {
4796-
if (kind >= ConstraintKind::Subtype &&
4797-
(type1->isUninhabited() || type2->isVoid())) {
4796+
if (kind >= ConstraintKind::Subtype &&
4797+
(type1->isUninhabited() || type2->isVoid())) {
4798+
// A conversion from closure body type to its signature result type.
4799+
if (auto resultElt = elt->getAs<LocatorPathElt::ClosureBody>()) {
4800+
// If a single statement closure has explicit `return` let's
4801+
// forbid conversion to `Void` and report an error instead to
4802+
// honor user's intent.
4803+
if (type1->isUninhabited() || !resultElt->hasExplicitReturn()) {
4804+
increaseScore(SK_FunctionConversion);
4805+
return getTypeMatchSuccess();
4806+
}
4807+
}
4808+
4809+
// Single expression function with implicit `return`.
4810+
if (elt->isResultOfSingleExprFunction()) {
47984811
increaseScore(SK_FunctionConversion);
47994812
return getTypeMatchSuccess();
48004813
}
@@ -6993,20 +7006,20 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar,
69937006
}
69947007
}
69957008

7009+
bool hasReturn = hasExplicitResult(closure);
7010+
69967011
// If this is a multi-statement closure its body doesn't participate
69977012
// in type-checking.
69987013
if (closure->hasSingleExpressionBody()) {
69997014
auto *closureBody = generateConstraints(closure);
70007015
if (!closureBody)
70017016
return false;
70027017

7003-
// Since result of the closure type has to be r-value type, we have
7004-
// to use equality here.
70057018
addConstraint(
70067019
ConstraintKind::Conversion, getType(closureBody),
70077020
closureType->getResult(),
7008-
getConstraintLocator(closure, ConstraintLocator::ClosureResult));
7009-
} else if (!hasExplicitResult(closure)) {
7021+
getConstraintLocator(closure, LocatorPathElt::ClosureBody(hasReturn)));
7022+
} else if (!hasReturn) {
70107023
// If multi-statement closure doesn't have an explicit result
70117024
// (no `return` statements) let's default it to `Void`.
70127025
auto &ctx = getASTContext();

lib/Sema/ConstraintSystem.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3434,6 +3434,7 @@ void constraints::simplifyLocator(Expr *&anchor,
34343434
}
34353435
break;
34363436

3437+
case ConstraintLocator::ClosureBody:
34373438
case ConstraintLocator::ClosureResult:
34383439
if (auto CE = dyn_cast<ClosureExpr>(anchor)) {
34393440
if (CE->hasSingleExpressionBody()) {

lib/Sema/TypeCheckExpr.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -823,6 +823,9 @@ ClosureHasExplicitResultRequest::evaluate(Evaluator &evaluator,
823823
std::pair<bool, Stmt *> walkToStmtPre(Stmt *stmt) override {
824824
// Record return statements.
825825
if (auto ret = dyn_cast<ReturnStmt>(stmt)) {
826+
if (ret->isImplicit())
827+
return {true, stmt};
828+
826829
// If it has a result, remember that we saw one, but keep
827830
// traversing in case there's a no-result return somewhere.
828831
if (ret->hasResult()) {

test/Constraints/closures.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,3 +1005,10 @@ func r60074136() {
10051005
takesClosure { ((Int) -> Void) -> Void in // expected-warning {{unnamed parameters must be written with the empty name '_'}}
10061006
}
10071007
}
1008+
1009+
func rdar52204414() {
1010+
let _: () -> Void = { return 42 }
1011+
// expected-error@-1 {{cannot convert value of type 'Int' to closure result type 'Void'}}
1012+
let _ = { () -> Void in return 42 }
1013+
// expected-error@-1 {{declared closure result 'Int' is incompatible with contextual type 'Void'}}
1014+
}

0 commit comments

Comments
 (0)