Skip to content

Commit 3b137b2

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 eb68737 commit 3b137b2

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
@@ -329,12 +329,8 @@ class alignas(8) Expr {
329329
/// True if closure parameters were synthesized from anonymous closure
330330
/// variables.
331331
unsigned HasAnonymousClosureVars : 1;
332-
333-
/// True if this is a closure created as a result of a void contextual
334-
/// conversion.
335-
unsigned IsVoidConversionClosure : 1;
336332
};
337-
enum { NumClosureExprBits = NumAbstractClosureExprBits + 2 };
333+
enum { NumClosureExprBits = NumAbstractClosureExprBits + 1 };
338334
static_assert(NumClosureExprBits <= 32, "fits in an unsigned");
339335

340336
class BindOptionalExprBitfields {
@@ -3393,7 +3389,6 @@ class ClosureExpr : public AbstractClosureExpr {
33933389
Body(nullptr) {
33943390
setParameterList(params);
33953391
ClosureExprBits.HasAnonymousClosureVars = false;
3396-
ClosureExprBits.IsVoidConversionClosure = false;
33973392
}
33983393

33993394
SourceRange getSourceRange() const;
@@ -3419,18 +3414,6 @@ class ClosureExpr : public AbstractClosureExpr {
34193414
ClosureExprBits.HasAnonymousClosureVars = true;
34203415
}
34213416

3422-
/// \brief Determine if this closure was created to satisfy a contextual
3423-
/// conversion to a void function type.
3424-
bool isVoidConversionClosure() const {
3425-
return ClosureExprBits.IsVoidConversionClosure;
3426-
}
3427-
3428-
/// \brief Indicate that this closure was created to satisfy a contextual
3429-
/// conversion to a void function type.
3430-
void setIsVoidConversionClosure() {
3431-
ClosureExprBits.IsVoidConversionClosure = true;
3432-
}
3433-
34343417
/// \brief Determine whether this closure expression has an
34353418
/// explicitly-specified result type.
34363419
bool hasExplicitResultType() const { return ArrowLoc.isValid(); }

lib/AST/ASTDumper.cpp

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

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

lib/AST/Expr.cpp

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

18281828
Expr *ClosureExpr::getSingleExpressionBody() const {
18291829
assert(hasSingleExpressionBody() && "Not a single-expression body");
1830-
if (!isVoidConversionClosure()) {
1831-
return cast<ReturnStmt>(getBody()->getElement(0).get<Stmt *>())
1832-
->getResult();
1833-
} else {
1834-
return getBody()->getElement(0).get<Expr *>();
1835-
}
1830+
auto body = getBody()->getElement(0);
1831+
if (body.is<Stmt *>())
1832+
return cast<ReturnStmt>(body.get<Stmt *>())->getResult();
1833+
return body.get<Expr *>();
18361834
}
18371835

18381836
void ClosureExpr::setSingleExpressionBody(Expr *NewBody) {
18391837
assert(hasSingleExpressionBody() && "Not a single-expression body");
1840-
if (!isVoidConversionClosure()) {
1841-
cast<ReturnStmt>(getBody()->getElement(0).get<Stmt *>())
1842-
->setResult(NewBody);
1843-
} else {
1844-
return getBody()->setElement(0, NewBody);
1838+
auto body = getBody()->getElement(0);
1839+
if (body.is<Stmt *>()) {
1840+
cast<ReturnStmt>(body.get<Stmt *>())->setResult(NewBody);
1841+
return;
18451842
}
1843+
getBody()->setElement(0, NewBody);
18461844
}
18471845

18481846
FORWARD_SOURCE_LOCS_TO(AutoClosureExpr, Body)

lib/Sema/CSApply.cpp

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1198,10 +1198,10 @@ namespace {
11981198
}
11991199

12001200
public:
1201-
1202-
1203-
/// \brief Coerce a closure expression with a non-void return type to a
1204-
/// contextual function type with a void return type.
1201+
1202+
1203+
/// \brief Coerce a closure expression with a non-Void return type to a
1204+
/// contextual function type with a Void return type.
12051205
///
12061206
/// This operation cannot fail.
12071207
///
@@ -1210,6 +1210,17 @@ namespace {
12101210
/// \returns The coerced closure expression.
12111211
///
12121212
ClosureExpr *coerceClosureExprToVoid(ClosureExpr *expr);
1213+
1214+
/// \brief Coerce a closure expression with a Never return type to a
1215+
/// contextual function type with some other return type.
1216+
///
1217+
/// This operation cannot fail.
1218+
///
1219+
/// \param expr The closure expression to coerce.
1220+
///
1221+
/// \returns The coerced closure expression.
1222+
///
1223+
ClosureExpr *coerceClosureExprFromNever(ClosureExpr *expr);
12131224

12141225
/// \brief Coerce the given expression to the given type.
12151226
///
@@ -5017,14 +5028,12 @@ ClosureExpr *ExprRewriter::coerceClosureExprToVoid(ClosureExpr *closureExpr) {
50175028

50185029
// Re-write the single-expression closure to return '()'
50195030
assert(closureExpr->hasSingleExpressionBody());
5020-
5021-
// Transform the ClosureExpr representation into the "expr + return ()" rep
5022-
// if it isn't already.
5023-
if (!closureExpr->isVoidConversionClosure()) {
5024-
5025-
auto member = closureExpr->getBody()->getElement(0);
5026-
5027-
// A single-expression body contains a single return statement.
5031+
5032+
// A single-expression body contains a single return statement
5033+
// prior to this transformation.
5034+
auto member = closureExpr->getBody()->getElement(0);
5035+
5036+
if (member.is<Stmt *>()) {
50285037
auto returnStmt = cast<ReturnStmt>(member.get<Stmt *>());
50295038
auto singleExpr = returnStmt->getResult();
50305039
auto voidExpr = TupleExpr::createEmpty(tc.Context,
@@ -5051,7 +5060,6 @@ ClosureExpr *ExprRewriter::coerceClosureExprToVoid(ClosureExpr *closureExpr) {
50515060
/*implicit*/true);
50525061

50535062
closureExpr->setImplicit();
5054-
closureExpr->setIsVoidConversionClosure();
50555063
closureExpr->setBody(braceStmt, /*isSingleExpression*/true);
50565064
}
50575065

@@ -5065,6 +5073,38 @@ ClosureExpr *ExprRewriter::coerceClosureExprToVoid(ClosureExpr *closureExpr) {
50655073
return closureExpr;
50665074
}
50675075

5076+
ClosureExpr *ExprRewriter::coerceClosureExprFromNever(ClosureExpr *closureExpr) {
5077+
auto &tc = cs.getTypeChecker();
5078+
5079+
// Re-write the single-expression closure to drop the 'return'.
5080+
assert(closureExpr->hasSingleExpressionBody());
5081+
5082+
// A single-expression body contains a single return statement
5083+
// prior to this transformation.
5084+
auto member = closureExpr->getBody()->getElement(0);
5085+
5086+
if (member.is<Stmt *>()) {
5087+
auto returnStmt = cast<ReturnStmt>(member.get<Stmt *>());
5088+
auto singleExpr = returnStmt->getResult();
5089+
5090+
tc.checkIgnoredExpr(singleExpr);
5091+
5092+
SmallVector<ASTNode, 1> elements;
5093+
elements.push_back(singleExpr);
5094+
5095+
auto braceStmt = BraceStmt::create(tc.Context,
5096+
closureExpr->getStartLoc(),
5097+
elements,
5098+
closureExpr->getEndLoc(),
5099+
/*implicit*/true);
5100+
5101+
closureExpr->setImplicit();
5102+
closureExpr->setBody(braceStmt, /*isSingleExpression*/true);
5103+
}
5104+
5105+
return closureExpr;
5106+
}
5107+
50685108
static void
50695109
maybeDiagnoseUnsupportedFunctionConversion(TypeChecker &tc, Expr *expr,
50705110
AnyFunctionType *toType) {
@@ -6471,9 +6511,15 @@ namespace {
64716511
closure->setSingleExpressionBody(body);
64726512

64736513
if (body) {
6474-
6514+
// A single-expression closure with a non-Void expression type
6515+
// coerces to a Void-returning function type.
64756516
if (fnType->getResult()->isVoid() && !body->getType()->isVoid()) {
64766517
closure = Rewriter.coerceClosureExprToVoid(closure);
6518+
// A single-expression closure with a Never expression type
6519+
// coerces to any other function type.
6520+
} else if (!fnType->getResult()->isUninhabited() &&
6521+
body->getType()->isUninhabited()) {
6522+
closure = Rewriter.coerceClosureExprFromNever(closure);
64776523
} else {
64786524

64796525
body = Rewriter.coerceToType(body,

lib/Sema/CSSimplify.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1959,10 +1959,11 @@ ConstraintSystem::matchTypes(Type type1, Type type2, TypeMatchKind kind,
19591959
}
19601960
}
19611961

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

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)