Skip to content

Commit a87028e

Browse files
committed
Reject uses of noescape parameters that would cause them to escape. Namely, we only
allow the parameter to be caller, or captured by another no-escape closure. Swift SVN r24121
1 parent b8f7cfd commit a87028e

File tree

4 files changed

+121
-26
lines changed

4 files changed

+121
-26
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1432,6 +1432,15 @@ NOTE(add_self_to_type,tce_sema,none,
14321432

14331433
ERROR(type_of_metatype,tce_sema,none,
14341434
"'.dynamicType' is not allowed after a type name", ())
1435+
ERROR(invalid_noescape_use,tce_sema,none,
1436+
"@noescape argument %0 may only be called", (Identifier))
1437+
1438+
ERROR(closure_noescape_use,tce_sema,none,
1439+
"closure use of @noescape argument %0 may allow it to escape",
1440+
(Identifier))
1441+
ERROR(decl_closure_noescape_use,tce_sema,none,
1442+
"declaration closing over @noescape argument %0 may allow it to escape",
1443+
(Identifier))
14351444

14361445
//------------------------------------------------------------------------------
14371446
// Type Check Statements

lib/Sema/MiscDiagnostics.cpp

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ static void diagUnreachableCode(TypeChecker &TC, const Stmt *S) {
105105
/// - Module values may only occur as part of qualification.
106106
/// - Metatype names cannot generally be used as values: they need a "T.self"
107107
/// qualification unless used in narrow case (e.g. T() for construction).
108+
/// - NoEscape parameters are only allowed to be called, not copied around.
108109
///
109110
static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E) {
110111
class DiagnoseWalker : public ASTWalker {
@@ -122,20 +123,31 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E) {
122123
// Diagnose metatype values that don't appear as part of a property,
123124
// method, or constructor reference.
124125

125-
// See through implicit conversions. We want to treat things like:
126-
/// (apply_expr (metatype_cvt (typeexpr metatype)) (4)) as an apply.
126+
// See through implicit conversions of the expression. We want to be able
127+
// to associate the parent of this expression with the ultimate callee.
127128
auto Base = E;
128129
while (auto Conv = dyn_cast<ImplicitConversionExpr>(Base))
129130
Base = Conv->getSubExpr();
130-
if (auto *DRE = dyn_cast<DeclRefExpr>(Base))
131+
132+
if (auto *DRE = dyn_cast<DeclRefExpr>(Base)) {
133+
// Verify metatype uses.
131134
if (isa<TypeDecl>(DRE->getDecl()))
132135
checkUseOfMetaTypeName(Base);
136+
137+
// Verify noescape parameter uses.
138+
if (auto *PD = dyn_cast<ParamDecl>(DRE->getDecl()))
139+
if (PD->getAttrs().hasAttribute<NoEscapeAttr>())
140+
checkNoEscapeParameterUse(DRE);
141+
}
133142
if (auto *MRE = dyn_cast<MemberRefExpr>(Base))
134143
if (isa<TypeDecl>(MRE->getMember().getDecl()))
135144
checkUseOfMetaTypeName(Base);
136145
if (isa<TypeExpr>(Base))
137146
checkUseOfMetaTypeName(Base);
138147

148+
if (auto *CE = dyn_cast<ClosureExpr>(E))
149+
checkNoEscapeClosureCaptures(CE);
150+
139151
return { true, E };
140152
}
141153

@@ -152,6 +164,37 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E) {
152164
TC.diagnose(E->getStartLoc(), diag::value_of_module_type);
153165
}
154166

167+
/// The DRE argument is a reference to a noescape parameter. Verify that
168+
/// its uses are ok.
169+
void checkNoEscapeParameterUse(DeclRefExpr *DRE) {
170+
// The only valid use of the noescape parameter is an immediate call.
171+
// TODO: this could be generalized in various ways, for example to allow
172+
// passing the decl off as an argument to a noescape call. Start simple.
173+
if (auto *ParentExpr = Parent.getAsExpr())
174+
if (isa<CallExpr>(ParentExpr)) // param()
175+
return;
176+
TC.diagnose(DRE->getStartLoc(), diag::invalid_noescape_use,
177+
DRE->getDecl()->getName());
178+
}
179+
180+
/// Check the specified closure to make sure it doesn't capture a noescape
181+
/// value, or that it is itself noescape if so.
182+
void checkNoEscapeClosureCaptures(ClosureExpr *CE) {
183+
if (!CE->getType() || CE->getType()->is<ErrorType>())
184+
return; // Ignore errorneous code.
185+
auto Ty = CE->getType()->castTo<FunctionType>();
186+
187+
// If this closure is used in a noescape context, it can do anything.
188+
if (Ty->isNoEscape()) return;
189+
190+
// Otherwise, check the capture list to make sure it isn't escaping
191+
// something.
192+
for (auto CapVD : CE->getCaptureInfo().getCaptures())
193+
if (CapVD->getAttrs().hasAttribute<NoEscapeAttr>())
194+
TC.diagnose(CE->getStartLoc(), diag::closure_noescape_use,
195+
CapVD->getName());
196+
}
197+
155198
void checkUseOfMetaTypeName(Expr *E) {
156199
// If we've already checked this at a higher level, we're done.
157200
if (!AlreadyDiagnosedMetatypes.insert(E).second)

lib/Sema/TypeCheckExpr.cpp

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -614,15 +614,14 @@ namespace {
614614
class FindCapturedVars : public ASTWalker {
615615
TypeChecker &TC;
616616
llvm::SetVector<ValueDecl*> &captures;
617-
DeclContext *CurExprAsDC;
617+
DeclContext *CurDC;
618618
SourceLoc CaptureLoc;
619619
llvm::SmallPtrSet<ValueDecl *, 2> Diagnosed;
620620

621621
public:
622-
FindCapturedVars(TypeChecker &tc,
623-
llvm::SetVector<ValueDecl*> &captures,
622+
FindCapturedVars(TypeChecker &tc, llvm::SetVector<ValueDecl*> &captures,
624623
AnyFunctionRef AFR)
625-
: TC(tc), captures(captures), CurExprAsDC(AFR.getAsDeclContext()) {
624+
: TC(tc), captures(captures), CurDC(AFR.getAsDeclContext()) {
626625
if (auto AFD = AFR.getAbstractFunctionDecl())
627626
CaptureLoc = AFD->getLoc();
628627
else {
@@ -642,28 +641,34 @@ namespace {
642641
S->walk(*this);
643642
}
644643

645-
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
646-
if (auto *DRE = dyn_cast<DeclRefExpr>(E))
647-
return walkToDeclRefExpr(DRE);
644+
/// Add the specified capture to the closure's capture list, diagnosing it
645+
/// if invalid.
646+
void addCapture(ValueDecl *VD, SourceLoc Loc) {
647+
captures.insert(VD);
648648

649-
// Don't recur into child closures. They should already have a capture
650-
// list computed; we just propagate it, filtering out stuff that they
651-
// capture from us.
652-
if (auto *SubCE = dyn_cast<AbstractClosureExpr>(E)) {
653-
for (auto D : SubCE->getCaptureInfo().getCaptures())
654-
if (D->getDeclContext() != CurExprAsDC)
655-
captures.insert(D);
656-
return { false, E };
657-
}
658-
return { true, E };
649+
// If VD is a noescape decl, then the closure we're computing this for
650+
// must also be noescape.
651+
if (!VD->getAttrs().hasAttribute<NoEscapeAttr>())
652+
return;
653+
654+
// Closure expressions are processed as part of expression checking.
655+
if (isa<AbstractClosureExpr>(CurDC))
656+
return;
657+
658+
// Don't repeatedly diagnose the same thing.
659+
if (!Diagnosed.insert(VD).second)
660+
return;
661+
662+
// Otherwise, diagnose this as an invalid capture.
663+
TC.diagnose(Loc, diag::decl_closure_noescape_use, VD->getName());
659664
}
660665

661666
std::pair<bool, Expr *> walkToDeclRefExpr(DeclRefExpr *DRE) {
662667
auto *D = DRE->getDecl();
663668

664669
// Decl references that are within the Capture are local references, ones
665670
// from parent context are captures.
666-
if (!CurExprAsDC->isChildContextOf(D->getDeclContext()))
671+
if (!CurDC->isChildContextOf(D->getDeclContext()))
667672
return { false, DRE };
668673

669674
// Only capture var decls at global scope. Other things can be captured
@@ -688,7 +693,7 @@ namespace {
688693
if (auto FD = dyn_cast<FuncDecl>(D)) {
689694
// TODO: Local functions cannot be recursive, because SILGen
690695
// cannot handle it yet.
691-
if (CurExprAsDC == FD) {
696+
if (CurDC == FD) {
692697
TC.diagnose(DRE->getLoc(),
693698
diag::unsupported_recursive_local_function);
694699
return { false, DRE };
@@ -704,10 +709,25 @@ namespace {
704709
TC.LocalFunctionCaptures.push_back({FD, DRE->getLoc()});
705710
}
706711

707-
captures.insert(D);
712+
addCapture(D, DRE->getStartLoc());
708713
return { false, DRE };
709714
}
710715

716+
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
717+
if (auto *DRE = dyn_cast<DeclRefExpr>(E))
718+
return walkToDeclRefExpr(DRE);
719+
720+
// Don't recurse into child closures. They should already have a capture
721+
// list computed; we just propagate it, filtering out stuff that they
722+
// capture from us.
723+
if (auto *SubCE = dyn_cast<AbstractClosureExpr>(E)) {
724+
for (auto D : SubCE->getCaptureInfo().getCaptures())
725+
if (D->getDeclContext() != CurDC)
726+
addCapture(D, E->getStartLoc());
727+
return { false, E };
728+
}
729+
return { true, E };
730+
}
711731
};
712732
}
713733

test/attr/attr_noescape.swift

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,40 @@
22

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

5-
func takesClosure(@__noescape fn : () -> Int) {
6-
takesClosure { 4 } // ok
5+
func doesEscape(fn : () -> Int) {}
6+
7+
func takesNoEscapeClosure(@__noescape fn : () -> Int) {
8+
takesNoEscapeClosure { 4 } // ok
9+
10+
fn() // ok
11+
12+
var x = fn // expected-error {{@noescape argument 'fn' may only be called}}
13+
14+
// This is ok, because the closure itself is noescape.
15+
takesNoEscapeClosure { fn() }
16+
17+
// This is not ok, because it escapes the 'fn' closure.
18+
doesEscape { fn() } // expected-error {{closure use of @noescape argument 'fn' may allow it to escape}}
19+
20+
// This is not ok, because it escapes the 'fn' closure.
21+
func nested_function() {
22+
fn() // expected-error {{declaration closing over @noescape argument 'fn' may allow it to escape}}
23+
}
24+
725
}
826

927
class SomeClass {
1028
final var x = 42
1129

1230
func test() {
31+
// This should require "self."
32+
doesEscape { x } // expected-error {{reference to property 'x' in closure requires explicit 'self.' to make capture semantics explicit}}
33+
1334
// Since 'takesClosure' doesn't escape its closure, it doesn't require
1435
// "self." qualification of member references.
15-
takesClosure { x }
36+
takesNoEscapeClosure { x }
37+
38+
1639
}
1740

1841

0 commit comments

Comments
 (0)