Skip to content

Commit 6b55e87

Browse files
committed
SILGen: Emit mark_function_escape when capturing variable boxes.
There's no longer a direct use of the variable's address when we invoke the closure, but we have a handy mark_function_escape instruction to mark the use without requiring merging analysis of the box and its contents. This also gives us a slightly more accurate error message when a variable is prematurely captured, noting the variable was captured by a closure instead of just generically used.
1 parent 172aee7 commit 6b55e87

File tree

6 files changed

+32
-12
lines changed

6 files changed

+32
-12
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ ERROR(variable_used_before_initialized,sil_analysis,none,
9797
ERROR(variable_inout_before_initialized,sil_analysis,none,
9898
"%select{variable|constant}1 '%0' passed by reference before being"
9999
" initialized", (StringRef, bool))
100-
ERROR(variable_escape_before_initialized,sil_analysis,none,
100+
ERROR(variable_closure_use_uninit,sil_analysis,none,
101101
"%select{variable|constant}1 '%0' captured by a closure before being"
102102
" initialized", (StringRef, bool))
103103

@@ -145,7 +145,7 @@ ERROR(return_from_init_without_initing_stored_properties,sil_analysis,none,
145145
"return from initializer without initializing all"
146146
" stored properties", ())
147147

148-
ERROR(global_variable_function_use_uninit,sil_analysis,none,
148+
ERROR(variable_function_use_uninit,sil_analysis,none,
149149
"%select{variable|constant}1 '%0' used by function definition before"
150150
" being initialized",
151151
(StringRef, bool))

lib/SILGen/SILGenFunction.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,10 @@ void SILGenFunction::emitCaptures(SILLocation loc,
223223
AnyFunctionRef TheClosure,
224224
SmallVectorImpl<ManagedValue> &capturedArgs) {
225225
auto captureInfo = SGM.Types.getLoweredLocalCaptures(TheClosure);
226+
// For boxed captures, we need to mark the contained variables as having
227+
// escaped for DI diagnostics.
228+
SmallVector<SILValue, 2> escapesToMark;
229+
226230
for (auto capture : captureInfo.getCaptures()) {
227231
auto *vd = capture.getDecl();
228232

@@ -285,6 +289,7 @@ void SILGenFunction::emitCaptures(SILLocation loc,
285289
if (vl.box) {
286290
B.createStrongRetain(loc, vl.box);
287291
capturedArgs.push_back(emitManagedRValueWithCleanup(vl.box));
292+
escapesToMark.push_back(vl.value);
288293
} else {
289294
// Address only 'let' values are passed by box. This isn't great, in
290295
// that a variable captured by multiple closures will be boxed for each
@@ -302,6 +307,12 @@ void SILGenFunction::emitCaptures(SILLocation loc,
302307
}
303308
}
304309
}
310+
311+
// Mark box addresses as captured for DI purposes. The values must have
312+
// been fully initialized before we close over them.
313+
if (!escapesToMark.empty()) {
314+
B.createMarkFunctionEscape(loc, escapesToMark);
315+
}
305316
}
306317

307318
ManagedValue

lib/SILPasses/EarlySIL/DefiniteInitialization.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,10 +1089,14 @@ void LifetimeChecker::handleEscapeUse(const DIMemoryUse &Use) {
10891089
}
10901090

10911091
Diag<StringRef, bool> DiagMessage;
1092-
if (isa<MarkFunctionEscapeInst>(Inst))
1093-
DiagMessage = diag::global_variable_function_use_uninit;
1094-
else
1095-
DiagMessage = diag::variable_escape_before_initialized;
1092+
if (isa<MarkFunctionEscapeInst>(Inst)) {
1093+
if (Inst->getLoc().isASTNode<AbstractClosureExpr>())
1094+
DiagMessage = diag::variable_closure_use_uninit;
1095+
else
1096+
DiagMessage = diag::variable_function_use_uninit;
1097+
} else {
1098+
DiagMessage = diag::variable_closure_use_uninit;
1099+
}
10961100

10971101
diagnoseInitError(Use, DiagMessage);
10981102
}

lib/SILPasses/IPO/CapturePromotion.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -650,7 +650,9 @@ isNonmutatingCapture(SILArgument *BoxArg) {
650650
return false;
651651
continue;
652652
}
653-
if (!isa<LoadInst>(O->getUser()) && !isa<DebugValueAddrInst>(O->getUser()))
653+
if (!isa<LoadInst>(O->getUser())
654+
&& !isa<DebugValueAddrInst>(O->getUser())
655+
&& !isa<MarkFunctionEscapeInst>(O->getUser()))
654656
return false;
655657
}
656658
}
@@ -665,6 +667,10 @@ isNonmutatingCapture(SILArgument *BoxArg) {
665667
static bool
666668
isNonescapingUse(Operand *O, SmallVectorImpl<SILInstruction*> &Mutations) {
667669
auto *U = O->getUser();
670+
// Marking the boxed value as escaping is OK. It's just a DI annotation.
671+
if (isa<MarkFunctionEscapeInst>(U))
672+
return true;
673+
668674
// A store or assign is ok if the alloc_box is the destination.
669675
if (isa<StoreInst>(U) || isa<AssignInst>(U)) {
670676
if (O->getOperandNumber() != 1)

test/SILGen/properties.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -675,9 +675,10 @@ func propertyWithDidSetTakingOldValue() {
675675
// CHECK-NEXT: debug_value %5 : $Int
676676
// CHECK-NEXT: assign %0 to %3 : $*Int
677677
// CHECK-NEXT: strong_retain %1 : $@box Int
678+
// CHECK-NEXT: mark_function_escape %3
678679
// CHECK-NEXT: // function_ref
679-
// CHECK-NEXT: %9 = function_ref @_TFF10properties32propertyWithDidSetTakingOldValueFT_T_WL_1pSi : $@convention(thin) (Int, @owned @box Int, @inout Int) -> ()
680-
// CHECK-NEXT: %10 = apply %9(%5, %1, %3) : $@convention(thin) (Int, @owned @box Int, @inout Int) -> ()
680+
// CHECK-NEXT: %9 = function_ref @_TFF10properties32propertyWithDidSetTakingOldValueFT_T_WL_1pSi : $@convention(thin) (Int, @owned @box Int) -> ()
681+
// CHECK-NEXT: %10 = apply %9(%5, %1) : $@convention(thin) (Int, @owned @box Int) -> ()
681682
// CHECK-NEXT: strong_release %1 : $@box Int
682683
// CHECK-NEXT: %12 = tuple ()
683684
// CHECK-NEXT: return %12 : $()

test/SILPasses/definite_init_diagnostics.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
// FIXME: rdar://problem/19648117 Needs splitting objc parts out
44
// XFAIL: linux
55

6-
// XFAIL: *
7-
86
import Swift
97
import gizmo
108

@@ -59,7 +57,7 @@ func test2() {
5957

6058
// expected-warning @+1 {{variable 'b1' was never mutated}} {{3-6=let}}
6159
var b1 : Int // expected-note {{variable defined here}}
62-
takes_closure { // expected-error {{variable 'b1' used before being initialized}}
60+
takes_closure { // expected-error {{variable 'b1' captured by a closure before being initialized}}
6361
markUsed(b1)
6462
}
6563

0 commit comments

Comments
 (0)