Skip to content

Commit e462626

Browse files
committed
AST: More consistent definition of a 'local capture'
This commit adds a new ValueDecl::isLocalCapture() predicate and uses it in the right places. The predicate is true if the declaration is in local context, *or* if its at the top level of the main source file and follows a 'guard' statement. Fixes <rdar://problem/23051362> / <https://bugs.swift.org/browse/SR-3528>.
1 parent 8e6dc39 commit e462626

File tree

5 files changed

+31
-3
lines changed

5 files changed

+31
-3
lines changed

include/swift/AST/Decl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2713,6 +2713,10 @@ class ValueDecl : public Decl {
27132713
AccessSemantics getAccessSemanticsFromContext(const DeclContext *DC,
27142714
bool isAccessOnSelf) const;
27152715

2716+
/// Determines if a reference to this declaration from a nested function
2717+
/// should be treated like a capture of a local value.
2718+
bool isLocalCapture() const;
2719+
27162720
/// Print a reference to the given declaration.
27172721
std::string printRef() const;
27182722

lib/AST/CaptureInfo.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ CaptureInfo CaptureInfo::empty() {
5757

5858
bool CaptureInfo::hasLocalCaptures() const {
5959
for (auto capture : getCaptures())
60-
if (capture.getDecl()->getDeclContext()->isLocalContext())
60+
if (capture.getDecl()->isLocalCapture())
6161
return true;
6262
return false;
6363
}
@@ -71,7 +71,7 @@ getLocalCaptures(SmallVectorImpl<CapturedValue> &Result) const {
7171

7272
// Filter out global variables.
7373
for (auto capture : getCaptures()) {
74-
if (!capture.getDecl()->getDeclContext()->isLocalContext())
74+
if (!capture.getDecl()->isLocalCapture())
7575
continue;
7676

7777
Result.push_back(capture);

lib/AST/Decl.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2866,6 +2866,16 @@ bool ValueDecl::isImplicitlyUnwrappedOptional() const {
28662866
false);
28672867
}
28682868

2869+
bool ValueDecl::isLocalCapture() const {
2870+
auto *dc = getDeclContext();
2871+
2872+
if (auto *fd = dyn_cast<FuncDecl>(this))
2873+
if (isa<SourceFile>(dc))
2874+
return fd->hasTopLevelLocalContextCaptures();
2875+
2876+
return dc->isLocalContext();
2877+
}
2878+
28692879
ArrayRef<ValueDecl *>
28702880
ValueDecl::getSatisfiedProtocolRequirements(bool Sorted) const {
28712881
// Dig out the nominal type.

lib/Sema/TypeCheckCaptures.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ class FindCapturedVars : public ASTWalker {
312312

313313
// Only capture var decls at global scope. Other things can be captured
314314
// if they are local.
315-
if (!isa<VarDecl>(D) && !DC->isLocalContext())
315+
if (!isa<VarDecl>(D) && !D->isLocalCapture())
316316
return { false, DRE };
317317

318318
// We're going to capture this, compute flags for the capture.

test/SILGen/top_level_captures.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// RUN: %target-swift-frontend -emit-silgen %s | %FileCheck %s
2+
3+
guard let x: Int = nil else { while true { } }
4+
5+
// CHECK-LABEL: sil hidden [ossa] @$s18top_level_captures0C1XyyF : $@convention(thin) (Int) -> () {
6+
func capturesX() {
7+
_ = x
8+
}
9+
10+
// CHECK-LABEL: sil hidden [ossa] @$s18top_level_captures17transitiveCaptureyyF : $@convention(thin) (Int) -> () {
11+
// CHECK: [[FUNC:%.*]] = function_ref @$s18top_level_captures0C1XyyF : $@convention(thin) (Int) -> ()
12+
func transitiveCapture() {
13+
capturesX()
14+
}

0 commit comments

Comments
 (0)