Skip to content

Commit 1486238

Browse files
committed
Reapply:
propagate the no-escape bit from a parameter into a closure-expr argument. When a ClosureExpr is thus inferred to be non-escape, disable the "self." requirement. .. with fixes. Thanks again to Dmitri for reverting + adding testcase. Swift SVN r24115
1 parent 6fc8179 commit 1486238

File tree

4 files changed

+62
-10
lines changed

4 files changed

+62
-10
lines changed

lib/AST/Expr.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@ SourceLoc Expr::getLoc() const {
8484
}
8585

8686
Expr *Expr::getSemanticsProvidingExpr() {
87-
if (IdentityExpr *PE = dyn_cast<IdentityExpr>(this))
88-
return PE->getSubExpr()->getSemanticsProvidingExpr();
87+
if (IdentityExpr *IE = dyn_cast<IdentityExpr>(this))
88+
return IE->getSubExpr()->getSemanticsProvidingExpr();
8989

9090
if (DefaultValueExpr *DE = dyn_cast<DefaultValueExpr>(this))
9191
return DE->getSubExpr()->getSemanticsProvidingExpr();

lib/Sema/CSApply.cpp

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4013,6 +4013,27 @@ Expr *ExprRewriter::coerceCallArguments(Expr *arg, Type paramType,
40134013
return shuffle;
40144014
}
40154015

4016+
/// If the expression is an explicit closure expression (potentially wrapped in
4017+
/// IdentityExprs), change the type of the closure and identities to the
4018+
/// specified type and return true. Otherwise, return false with no effect.
4019+
static bool applyTypeToClosureExpr(Expr *expr, Type toType) {
4020+
// Look through identity expressions, like parens.
4021+
if (auto IE = dyn_cast<IdentityExpr>(expr)) {
4022+
if (!applyTypeToClosureExpr(IE->getSubExpr(), toType)) return false;
4023+
IE->setType(toType);
4024+
return true;
4025+
}
4026+
4027+
// If we found an explicit ClosureExpr, update its type.
4028+
if (auto CE = dyn_cast<ClosureExpr>(expr)) {
4029+
CE->setType(toType);
4030+
return true;
4031+
}
4032+
// Otherwise fail.
4033+
return false;
4034+
}
4035+
4036+
40164037
Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
40174038
ConstraintLocatorBuilder locator) {
40184039
auto &tc = cs.getTypeChecker();
@@ -4370,8 +4391,24 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
43704391
}
43714392

43724393
// Coercion from one function type to another.
4373-
auto fromFunc = fromType->getAs<FunctionType>();
4374-
if (fromFunc) {
4394+
if (auto fromFunc = fromType->getAs<FunctionType>()) {
4395+
// If the input and output types match, then the difference is a change
4396+
// in the ExtInfo bits.
4397+
if (fromFunc->getInput()->isEqual(toFunc->getInput()) &&
4398+
fromFunc->getResult()->isEqual(toFunc->getResult())) {
4399+
// If the only difference is in the noreturn or noescape bits, try to
4400+
// propagate them into the expression.
4401+
auto fromEI = fromFunc->getExtInfo(), toEI = toFunc->getExtInfo();
4402+
if (fromEI.withIsNoReturn(false).withNoEscape(false) ==
4403+
toEI.withIsNoReturn(false).withNoEscape(false) &&
4404+
// Adding - not stripping off - the bits.
4405+
(!fromEI.isNoReturn() || toEI.isNoReturn()) &&
4406+
(!fromEI.isNoEscape() || toEI.isNoEscape())) {
4407+
if (applyTypeToClosureExpr(expr, toType))
4408+
return expr;
4409+
}
4410+
}
4411+
43754412
return new (tc.Context) FunctionConversionExpr(expr, toType);
43764413
}
43774414
}

lib/Sema/MiscDiagnostics.cpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,20 @@ static void diagnoseImplicitSelfUseInClosure(TypeChecker &TC, const Expr *E) {
313313
DRE->getDecl()->getName().str() == "self";
314314
}
315315

316+
/// Return true if this is a closure expression that will require "self."
317+
/// qualification of member references.
318+
static bool isClosureRequiringSelfQualification(const Expr *E) {
319+
if (!isa<ClosureExpr>(E)) return false;
320+
321+
// If the closure's type was inferred to be noescape, then it doesn't
322+
// need qualification.
323+
if (E->getType())
324+
if (auto *FT = E->getType()->getAs<FunctionType>())
325+
return !FT->isNoEscape();
326+
return true;
327+
}
328+
329+
316330
// Don't walk into nested decls.
317331
bool walkToDeclPre(Decl *D) override {
318332
return false;
@@ -322,7 +336,7 @@ static void diagnoseImplicitSelfUseInClosure(TypeChecker &TC, const Expr *E) {
322336

323337
// If this is an explicit closure expression - not an autoclosure - then
324338
// we keep track of the fact that recursive walks are within the closure.
325-
if (isa<ClosureExpr>(E))
339+
if (isClosureRequiringSelfQualification(E))
326340
++InClosure;
327341

328342
// If we aren't in a closure, no diagnostics will be produced.
@@ -362,7 +376,7 @@ static void diagnoseImplicitSelfUseInClosure(TypeChecker &TC, const Expr *E) {
362376
}
363377

364378
Expr *walkToExprPost(Expr *E) {
365-
if (isa<ClosureExpr>(E)) {
379+
if (isClosureRequiringSelfQualification(E)) {
366380
assert(InClosure);
367381
--InClosure;
368382
}

test/attr/attr_noescape.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@
22

33
@__noescape var fn : () -> Int = { 4 } // expected-error {{'__noescape' may only be used on 'parameter' declarations}}
44

5-
func f(@__noescape fn : () -> Int) {
6-
f { 4 } // ok
5+
func takesClosure(@__noescape fn : () -> Int) {
6+
takesClosure { 4 } // ok
77
}
88

99
class SomeClass {
1010
final var x = 42
1111

12-
// TODO: We should be able to eliminate this.
1312
func test() {
14-
f { x } // expected-error {{reference to property 'x' in closure requires explicit 'self.' to make capture semantics explicit}}
13+
// Since 'takesClosure' doesn't escape its closure, it doesn't require
14+
// "self." qualification of member references.
15+
takesClosure { x }
1516
}
1617

1718

0 commit comments

Comments
 (0)