Skip to content

Commit 05e1284

Browse files
authored
Merge pull request #37285 from atrick/fix-self-capture
Fix an assert in exclusivity diagnostics when inouts escape.
2 parents 25ba22c + b0dc18d commit 05e1284

File tree

3 files changed

+59
-14
lines changed

3 files changed

+59
-14
lines changed

lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,22 @@ void AccessSummaryAnalysis::processArgument(FunctionInfo *info,
143143
/// used by directly calling it or passing it as argument, but not using it as a
144144
/// partial_apply callee.
145145
///
146-
/// FIXME: This needs to be checked in the SILVerifier.
146+
/// An error found in DiagnoseInvalidEscapingCaptures can indicate invalid SIL
147+
/// that is detected here but not in normal SIL verification. When the
148+
/// source-level closure captures an inout argument, it appears in SIL to be a
149+
/// non-escaping closure. The following verification then fails because the
150+
/// "nonescaping" closure actually escapes.
151+
///
152+
/// FIXME: This should be checked in the SILVerifier, with consideration for the
153+
/// caveat above where an inout has been captured be an escaping closure.
147154
static bool hasExpectedUsesOfNoEscapePartialApply(Operand *partialApplyUse) {
148155
SILInstruction *user = partialApplyUse->getUser();
149156

157+
// Bypass this verification when a diagnostic error is present. See comments
158+
// on DiagnoseInvalidEscapingCaptures above.
159+
if (user->getModule().getASTContext().hadError())
160+
return true;
161+
150162
// It is fine to call the partial apply
151163
switch (user->getKind()) {
152164
case SILInstructionKind::ApplyInst:

lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,25 +1054,39 @@ static void checkAccessedAddress(Operand *memOper, StorageMap &Accesses) {
10541054

10551055
namespace {
10561056

1057-
class DiagnoseStaticExclusivity : public SILFunctionTransform {
1057+
/// TODO: This is currently a module transform to ensure that source-level
1058+
/// diagnostics, like DiagnoseInvalidCaptures run on closures (in addition to
1059+
/// other callees) before this pass processes their parent functions. Otherwise,
1060+
/// AccessSummaryAnalysis may crash on invalid SIL. Fix the pass manager to
1061+
/// ensure that closures are always diagnosed before their parent. Then add an
1062+
/// SCC transform to ensure that the previous diagnostic pass runs on all
1063+
/// functions in the SCC before the next diagnostic pass. This will handle
1064+
/// closures that call back into their parent. Then this can be converted to an
1065+
/// SCC transform.
1066+
class DiagnoseStaticExclusivity : public SILModuleTransform {
10581067
public:
10591068
DiagnoseStaticExclusivity() {}
10601069

10611070
private:
10621071
void run() override {
1063-
// Don't rerun diagnostics on deserialized functions.
1064-
if (getFunction()->wasDeserializedCanonical())
1065-
return;
1072+
for (auto &function : *getModule()) {
1073+
if (!function.isDefinition())
1074+
continue;
10661075

1067-
SILFunction *Fn = getFunction();
1068-
// This is a staging flag. Eventually the ability to turn off static
1069-
// enforcement will be removed.
1070-
if (!Fn->getModule().getOptions().EnforceExclusivityStatic)
1071-
return;
1076+
// Don't rerun diagnostics on deserialized functions.
1077+
if (function.wasDeserializedCanonical())
1078+
continue;
10721079

1073-
PostOrderFunctionInfo *PO = getAnalysis<PostOrderAnalysis>()->get(Fn);
1074-
auto *ASA = getAnalysis<AccessSummaryAnalysis>();
1075-
checkStaticExclusivity(*Fn, PO, ASA);
1080+
// This is a staging flag. Eventually the ability to turn off static
1081+
// enforcement will be removed.
1082+
if (!function.getModule().getOptions().EnforceExclusivityStatic)
1083+
continue;
1084+
1085+
PostOrderFunctionInfo *PO =
1086+
getAnalysis<PostOrderAnalysis>()->get(&function);
1087+
auto *ASA = getAnalysis<AccessSummaryAnalysis>();
1088+
checkStaticExclusivity(function, PO, ASA);
1089+
}
10761090
}
10771091
};
10781092

test/SILOptimizer/invalid_escaping_captures.swift

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,4 +255,23 @@ func badNoEscapeCaptureThroughVar(_ fn: () -> ()) {
255255
takesEscaping { // expected-error {{escaping closure captures non-escaping value}}
256256
myFunc()
257257
}
258-
}
258+
}
259+
260+
@inline(never)
261+
func takeNoEscapeReturnGetter(f: ()->()->Int64) -> ()->Int64 { return f() }
262+
263+
// Test that invalid escaping capture diagnostics are run on nested
264+
// closures before exclusivity diagnostics are run. Exclusivity
265+
// diagnostics need to inspect all referenced closures that capture
266+
// inouts. Also ensure that exclusivity diagnostics does not crash
267+
// when verifying that a closure that captures inout does not escape
268+
// as long as a previous diagnostic error is present.
269+
struct TestInoutEscapeInClosure {
270+
var someValue: Int64 = 0
271+
mutating func testInoutEscapeInClosure() -> () -> Int64 {
272+
return takeNoEscapeReturnGetter {
273+
return { return someValue } // expected-error {{escaping closure captures mutating 'self' parameter}}
274+
// expected-note@-1 {{captured here}}
275+
}
276+
}
277+
}

0 commit comments

Comments
 (0)