Skip to content

Commit 352e4a2

Browse files
committed
[ConstraintSystem] Infer empty closures as returning () more eagerly.
We previously allowed these closures to default to (), but be inferred as other types as well, which means that we will find some expressions to be ambiguous because we end up finding multiple viable solutions where there is really only one reasonable solution. Fixes: rdar://problem/42337247
1 parent 9cda374 commit 352e4a2

File tree

7 files changed

+78
-13
lines changed

7 files changed

+78
-13
lines changed

include/swift/AST/Expr.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3653,6 +3653,9 @@ class ClosureExpr : public AbstractClosureExpr {
36533653
/// its body; it can only update that expression.
36543654
void setSingleExpressionBody(Expr *NewBody);
36553655

3656+
/// \brief Is this a completely empty closure?
3657+
bool hasEmptyBody() const;
3658+
36563659
static bool classof(const Expr *E) {
36573660
return E->getKind() == ExprKind::Closure;
36583661
}

lib/AST/Expr.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1855,6 +1855,10 @@ void ClosureExpr::setSingleExpressionBody(Expr *NewBody) {
18551855
getBody()->setElement(0, NewBody);
18561856
}
18571857

1858+
bool ClosureExpr::hasEmptyBody() const {
1859+
return getBody()->getNumElements() == 0;
1860+
}
1861+
18581862
FORWARD_SOURCE_LOCS_TO(AutoClosureExpr, Body)
18591863

18601864
void AutoClosureExpr::setBody(Expr *E) {

lib/Sema/CSGen.cpp

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2400,22 +2400,30 @@ namespace {
24002400
expr->getExplicitResultTypeLoc().getType()) {
24012401
resultTy = expr->getExplicitResultTypeLoc().getType();
24022402
CS.setFavoredType(expr, resultTy.getPointer());
2403-
} else if (!crt.isNull()) {
2404-
resultTy = crt;
2405-
} else{
2403+
} else {
24062404
auto locator =
24072405
CS.getConstraintLocator(expr, ConstraintLocator::ClosureResult);
24082406

2409-
// If no return type was specified, create a fresh type
2410-
// variable for it.
2411-
resultTy = CS.createTypeVariable(locator);
2407+
if (expr->hasEmptyBody()) {
2408+
resultTy = CS.createTypeVariable(locator);
24122409

2413-
// Allow it to default to () if there are no return statements.
2414-
if (closureHasNoResult(expr)) {
2415-
CS.addConstraint(ConstraintKind::Defaultable,
2416-
resultTy,
2417-
TupleType::getEmpty(CS.getASTContext()),
2418-
locator);
2410+
// Closures with empty bodies should be inferred to return
2411+
// ().
2412+
CS.addConstraint(ConstraintKind::Bind, resultTy,
2413+
TupleType::getEmpty(CS.getASTContext()), locator);
2414+
} else if (crt) {
2415+
// Otherwise, use the contextual type if present.
2416+
resultTy = crt;
2417+
} else {
2418+
// If no return type was specified, create a fresh type
2419+
// variable for it.
2420+
resultTy = CS.createTypeVariable(locator);
2421+
2422+
if (closureHasNoResult(expr)) {
2423+
// Allow it to default to () if there are no return statements.
2424+
CS.addConstraint(ConstraintKind::Defaultable, resultTy,
2425+
TupleType::getEmpty(CS.getASTContext()), locator);
2426+
}
24192427
}
24202428
}
24212429

test/Constraints/closures.swift

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -749,3 +749,51 @@ func f20371273() {
749749
let y: UInt = 4
750750
_ = x.filter { ($0 + y) > 42 } // expected-error {{'+' is unavailable}}
751751
}
752+
753+
// rdar://problem/42337247
754+
755+
func overloaded(_ handler: () -> Int) {} // expected-note {{found this candidate}}
756+
func overloaded(_ handler: () -> Void) {} // expected-note {{found this candidate}}
757+
758+
overloaded { } // empty body => inferred as returning ()
759+
760+
overloaded { print("hi") } // single-expression closure => typechecked with body
761+
762+
overloaded { print("hi"); print("bye") } // multiple expression closure without explicit returns; can default to any return type
763+
// expected-error@-1 {{ambiguous use of 'overloaded'}}
764+
765+
func not_overloaded(_ handler: () -> Int) {}
766+
767+
not_overloaded { } // empty body
768+
// expected-error@-1 {{cannot convert value of type '() -> ()' to expected argument type '() -> Int'}}
769+
770+
not_overloaded { print("hi") } // single-expression closure
771+
// expected-error@-1 {{cannot convert value of type '()' to closure result type 'Int'}}
772+
773+
// no error in -typecheck, but dataflow diagnostics will complain about missing return
774+
not_overloaded { print("hi"); print("bye") } // multiple expression closure
775+
776+
func apply(_ fn: (Int) throws -> Int) rethrows -> Int {
777+
return try fn(0)
778+
}
779+
780+
enum E : Error {
781+
case E
782+
}
783+
784+
func test() -> Int? {
785+
return try? apply({ _ in throw E.E })
786+
}
787+
788+
var fn: () -> [Int] = {}
789+
// expected-error@-1 {{cannot convert value of type '() -> ()' to specified type '() -> [Int]'}}
790+
791+
fn = {}
792+
// expected-error@-1 {{cannot assign value of type '() -> ()' to type '() -> [Int]'}}
793+
794+
func test<Instances : Collection>(
795+
_ instances: Instances,
796+
_ fn: (Instances.Index, Instances.Index) -> Bool
797+
) { fatalError() }
798+
799+
test([1]) { _, _ in fatalError(); () }

test/SILGen/objc_blocks_bridging.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ class Test: NSObject {
154154
// CHECK: return [[RESULT]]
155155

156156
func clearDraggingItemImageComponentsProvider(_ x: NSDraggingItem) {
157-
x.imageComponentsProvider = {}
157+
x.imageComponentsProvider = { [] }
158158
}
159159
// CHECK-LABEL: sil shared [transparent] [serializable] [reabstraction_thunk] @$SSayypGIego_So7NSArrayCSgIeyBa_TR
160160
// CHECK: [[CONVERT:%.*]] = function_ref @$SSa10FoundationE19_bridgeToObjectiveCSo7NSArrayCyF

test/decl/func/default-values-swift4.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,5 @@ public func evilCode(
109109
versionedFunction()
110110
// expected-error@-1 {{global function 'versionedFunction()' is internal and cannot be referenced from a default argument value}}
111111
}
112+
return 0
112113
}()) {}

test/expr/closure/closures.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ func rdar19179412() -> (Int) -> Int {
252252
class A {
253253
let d : Int = 0
254254
}
255+
return 0
255256
}
256257
}
257258

0 commit comments

Comments
 (0)