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>.
This commit is contained in:
Slava Pestov
2016-09-22 22:12:10 -07:00
parent 37c9fb4ceb
commit e97df4a285
7 changed files with 131 additions and 100 deletions

View File

@@ -1956,10 +1956,11 @@ ConstraintSystem::matchTypes(Type type1, Type type2, TypeMatchKind kind,
}
}
// If the types disagree, but we're comparing a non-void, single-expression
// closure result type to a void function result type, allow the conversion.
// Allow '() -> T' to '() -> ()' and '() -> Never' to '() -> T' for closure
// literals.
{
if (concrete && kind >= TypeMatchKind::Subtype && type2->isVoid()) {
if (concrete && kind >= TypeMatchKind::Subtype &&
(type1->isUninhabited() || type2->isVoid())) {
SmallVector<LocatorPathElt, 2> parts;
locator.getLocatorParts(parts);