Skip to content

Commit e97df4a

Browse files
committed
Sema: Implicit conversion for single-expression closures of Never type
This fixes a usability regression with the removal of @NoReturn in Swift 3. Previously, it was legal to write this: let callback: () -> Int = { fatalError() } Now that the special @NoReturn attribute has been replaced with a Never type, the above fails to typecheck, because the expression now has type 'Never', and we expect a value of type 'Int'. Getting around this behavior requires ugly workarounds to force the parser to treat the body as a statement rather than an expression; for example, let callback: () -> Int = { _ = (); fatalError() } This patch generalized single-expression closures to allow the 'Never to T' conversion. Note that this is rather narrow in scope -- it only applies to closure *literals*, single-expression ones at that, not arbitrary function *values*. In fact, it is not really a conversion at all, but more of a desugaring rule for single-expression closures. They can now be summarized as follows: - If the closure literal has contextual return type T and the expression has Never type, the closure desugars as { _ = <expr> }, with no ReturnStmt. - If the closure literal has contextual return type T for some non-void type T, the closure desugars as { return <expr> }; the expression type must be convertible to T. - If the closure literal has contextual return type Void, and the expression has some non-Void type T, the closure desugars as { _ = <expr>; return () }. Fixes <rdar://problem/28269358> and <https://bugs.swift.org/browse/SR-2661>.
1 parent 37c9fb4 commit e97df4a

File tree

7 files changed

+131
-100
lines changed

7 files changed

+131
-100
lines changed

include/swift/AST/Expr.h

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -324,12 +324,8 @@ class alignas(8) Expr {
324324
/// True if closure parameters were synthesized from anonymous closure
325325
/// variables.
326326
unsigned HasAnonymousClosureVars : 1;
327-
328-
/// True if this is a closure created as a result of a void contextual
329-
/// conversion.
330-
unsigned IsVoidConversionClosure : 1;
331327
};
332-
enum { NumClosureExprBits = NumAbstractClosureExprBits + 2 };
328+
enum { NumClosureExprBits = NumAbstractClosureExprBits + 1 };
333329
static_assert(NumClosureExprBits <= 32, "fits in an unsigned");
334330

335331
class BindOptionalExprBitfields {
@@ -3375,7 +3371,6 @@ class ClosureExpr : public AbstractClosureExpr {
33753371
Body(nullptr) {
33763372
setParameterList(params);
33773373
ClosureExprBits.HasAnonymousClosureVars = false;
3378-
ClosureExprBits.IsVoidConversionClosure = false;
33793374
}
33803375

33813376
SourceRange getSourceRange() const;
@@ -3401,18 +3396,6 @@ class ClosureExpr : public AbstractClosureExpr {
34013396
ClosureExprBits.HasAnonymousClosureVars = true;
34023397
}
34033398

3404-
/// \brief Determine if this closure was created to satisfy a contextual
3405-
/// conversion to a void function type.
3406-
bool isVoidConversionClosure() const {
3407-
return ClosureExprBits.IsVoidConversionClosure;
3408-
}
3409-
3410-
/// \brief Indicate that this closure was created to satisfy a contextual
3411-
/// conversion to a void function type.
3412-
void setIsVoidConversionClosure() {
3413-
ClosureExprBits.IsVoidConversionClosure = true;
3414-
}
3415-
34163399
/// \brief Determine whether this closure expression has an
34173400
/// explicitly-specified result type.
34183401
bool hasExplicitResultType() const { return ArrowLoc.isValid(); }

lib/AST/ASTDumper.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2081,8 +2081,6 @@ class PrintExpr : public ExprVisitor<PrintExpr> {
20812081
printClosure(E, "closure_expr");
20822082
if (E->hasSingleExpressionBody())
20832083
OS << " single-expression";
2084-
if (E->isVoidConversionClosure())
2085-
OS << " void-conversion";
20862084

20872085
if (E->getParameters()) {
20882086
OS << '\n';

lib/AST/Expr.cpp

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1811,22 +1811,20 @@ FORWARD_SOURCE_LOCS_TO(ClosureExpr, Body.getPointer())
18111811

18121812
Expr *ClosureExpr::getSingleExpressionBody() const {
18131813
assert(hasSingleExpressionBody() && "Not a single-expression body");
1814-
if (!isVoidConversionClosure()) {
1815-
return cast<ReturnStmt>(getBody()->getElement(0).get<Stmt *>())
1816-
->getResult();
1817-
} else {
1818-
return getBody()->getElement(0).get<Expr *>();
1819-
}
1814+
auto body = getBody()->getElement(0);
1815+
if (body.is<Stmt *>())
1816+
return cast<ReturnStmt>(body.get<Stmt *>())->getResult();
1817+
return body.get<Expr *>();
18201818
}
18211819

18221820
void ClosureExpr::setSingleExpressionBody(Expr *NewBody) {
18231821
assert(hasSingleExpressionBody() && "Not a single-expression body");
1824-
if (!isVoidConversionClosure()) {
1825-
cast<ReturnStmt>(getBody()->getElement(0).get<Stmt *>())
1826-
->setResult(NewBody);
1827-
} else {
1828-
return getBody()->setElement(0, NewBody);
1822+
auto body = getBody()->getElement(0);
1823+
if (body.is<Stmt *>()) {
1824+
cast<ReturnStmt>(body.get<Stmt *>())->setResult(NewBody);
1825+
return;
18291826
}
1827+
getBody()->setElement(0, NewBody);
18301828
}
18311829

18321830
FORWARD_SOURCE_LOCS_TO(AutoClosureExpr, Body)

lib/Sema/CSApply.cpp

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1144,10 +1144,10 @@ namespace {
11441144
}
11451145

11461146
public:
1147-
1148-
1149-
/// \brief Coerce a closure expression with a non-void return type to a
1150-
/// contextual function type with a void return type.
1147+
1148+
1149+
/// \brief Coerce a closure expression with a non-Void return type to a
1150+
/// contextual function type with a Void return type.
11511151
///
11521152
/// This operation cannot fail.
11531153
///
@@ -1156,6 +1156,17 @@ namespace {
11561156
/// \returns The coerced closure expression.
11571157
///
11581158
ClosureExpr *coerceClosureExprToVoid(ClosureExpr *expr);
1159+
1160+
/// \brief Coerce a closure expression with a Never return type to a
1161+
/// contextual function type with some other return type.
1162+
///
1163+
/// This operation cannot fail.
1164+
///
1165+
/// \param expr The closure expression to coerce.
1166+
///
1167+
/// \returns The coerced closure expression.
1168+
///
1169+
ClosureExpr *coerceClosureExprFromNever(ClosureExpr *expr);
11591170

11601171
/// \brief Coerce the given expression to the given type.
11611172
///
@@ -4954,14 +4965,12 @@ ClosureExpr *ExprRewriter::coerceClosureExprToVoid(ClosureExpr *closureExpr) {
49544965

49554966
// Re-write the single-expression closure to return '()'
49564967
assert(closureExpr->hasSingleExpressionBody());
4957-
4958-
// Transform the ClosureExpr representation into the "expr + return ()" rep
4959-
// if it isn't already.
4960-
if (!closureExpr->isVoidConversionClosure()) {
4961-
4962-
auto member = closureExpr->getBody()->getElement(0);
4963-
4964-
// A single-expression body contains a single return statement.
4968+
4969+
// A single-expression body contains a single return statement
4970+
// prior to this transformation.
4971+
auto member = closureExpr->getBody()->getElement(0);
4972+
4973+
if (member.is<Stmt *>()) {
49654974
auto returnStmt = cast<ReturnStmt>(member.get<Stmt *>());
49664975
auto singleExpr = returnStmt->getResult();
49674976
auto voidExpr = TupleExpr::createEmpty(tc.Context,
@@ -4988,7 +4997,6 @@ ClosureExpr *ExprRewriter::coerceClosureExprToVoid(ClosureExpr *closureExpr) {
49884997
/*implicit*/true);
49894998

49904999
closureExpr->setImplicit();
4991-
closureExpr->setIsVoidConversionClosure();
49925000
closureExpr->setBody(braceStmt, /*isSingleExpression*/true);
49935001
}
49945002

@@ -5002,6 +5010,38 @@ ClosureExpr *ExprRewriter::coerceClosureExprToVoid(ClosureExpr *closureExpr) {
50025010
return closureExpr;
50035011
}
50045012

5013+
ClosureExpr *ExprRewriter::coerceClosureExprFromNever(ClosureExpr *closureExpr) {
5014+
auto &tc = cs.getTypeChecker();
5015+
5016+
// Re-write the single-expression closure to drop the 'return'.
5017+
assert(closureExpr->hasSingleExpressionBody());
5018+
5019+
// A single-expression body contains a single return statement
5020+
// prior to this transformation.
5021+
auto member = closureExpr->getBody()->getElement(0);
5022+
5023+
if (member.is<Stmt *>()) {
5024+
auto returnStmt = cast<ReturnStmt>(member.get<Stmt *>());
5025+
auto singleExpr = returnStmt->getResult();
5026+
5027+
tc.checkIgnoredExpr(singleExpr);
5028+
5029+
SmallVector<ASTNode, 1> elements;
5030+
elements.push_back(singleExpr);
5031+
5032+
auto braceStmt = BraceStmt::create(tc.Context,
5033+
closureExpr->getStartLoc(),
5034+
elements,
5035+
closureExpr->getEndLoc(),
5036+
/*implicit*/true);
5037+
5038+
closureExpr->setImplicit();
5039+
closureExpr->setBody(braceStmt, /*isSingleExpression*/true);
5040+
}
5041+
5042+
return closureExpr;
5043+
}
5044+
50055045
static void
50065046
maybeDiagnoseUnsupportedFunctionConversion(TypeChecker &tc, Expr *expr,
50075047
AnyFunctionType *toType) {
@@ -6397,9 +6437,15 @@ namespace {
63976437
closure->setSingleExpressionBody(body);
63986438

63996439
if (body) {
6400-
6440+
// A single-expression closure with a non-Void expression type
6441+
// coerces to a Void-returning function type.
64016442
if (fnType->getResult()->isVoid() && !body->getType()->isVoid()) {
64026443
closure = Rewriter.coerceClosureExprToVoid(closure);
6444+
// A single-expression closure with a Never expression type
6445+
// coerces to any other function type.
6446+
} else if (!fnType->getResult()->isUninhabited() &&
6447+
body->getType()->isUninhabited()) {
6448+
closure = Rewriter.coerceClosureExprFromNever(closure);
64036449
} else {
64046450

64056451
body = Rewriter.coerceToType(body,

lib/Sema/CSSimplify.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1956,10 +1956,11 @@ ConstraintSystem::matchTypes(Type type1, Type type2, TypeMatchKind kind,
19561956
}
19571957
}
19581958

1959-
// If the types disagree, but we're comparing a non-void, single-expression
1960-
// closure result type to a void function result type, allow the conversion.
1959+
// Allow '() -> T' to '() -> ()' and '() -> Never' to '() -> T' for closure
1960+
// literals.
19611961
{
1962-
if (concrete && kind >= TypeMatchKind::Subtype && type2->isVoid()) {
1962+
if (concrete && kind >= TypeMatchKind::Subtype &&
1963+
(type1->isUninhabited() || type2->isVoid())) {
19631964
SmallVector<LocatorPathElt, 2> parts;
19641965
locator.getLocatorParts(parts);
19651966

test/Misc/single_expr_closure_conversion.swift

Lines changed: 0 additions & 52 deletions
This file was deleted.

test/expr/closure/single_expr.swift

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,60 @@ func expect<T>(_ expression: @autoclosure () -> T) -> Expectation<T> {
4242
}
4343
func describe(_ closure: () -> ()) {}
4444
func f() { describe { _ = expect("what") } }
45+
46+
struct Blob {}
47+
48+
func withBlob(block: (Blob) -> ()) {}
49+
50+
protocol Binding {}
51+
extension Int: Binding {}
52+
extension Double: Binding {}
53+
extension String: Binding {}
54+
extension Blob: Binding {}
55+
56+
struct Stmt {
57+
@discardableResult
58+
func bind(_ values: Binding?...) -> Stmt {
59+
return self
60+
}
61+
62+
@discardableResult
63+
func bind(_ values: [Binding?]) -> Stmt {
64+
return self
65+
}
66+
67+
@discardableResult
68+
func bind(_ values: [String: Binding?]) -> Stmt {
69+
return self
70+
}
71+
}
72+
73+
let stmt = Stmt()
74+
withBlob { stmt.bind(1, 2.0, "3", $0) }
75+
withBlob { stmt.bind([1, 2.0, "3", $0]) }
76+
withBlob { stmt.bind(["1": 1, "2": 2.0, "3": "3", "4": $0]) }
77+
78+
// <rdar://problem/19840785>
79+
// We shouldn't crash on the call to 'a.dispatch' below.
80+
class A {
81+
func dispatch(_ f : () -> Void) {
82+
f()
83+
}
84+
}
85+
86+
class C {
87+
var prop = 0
88+
var a = A()
89+
90+
func act() {
91+
a.dispatch({() -> Void in
92+
self.prop // expected-warning {{expression of type 'Int' is unused}}
93+
})
94+
}
95+
}
96+
97+
// Never-returning expressions
98+
func haltAndCatchFire() -> Never { while true { } }
99+
let backupPlan: () -> Int = { haltAndCatchFire() }
100+
func missionCritical(storage: () -> String) {}
101+
missionCritical(storage: { haltAndCatchFire() })

0 commit comments

Comments
 (0)