Skip to content

Commit 19f7b99

Browse files
authored
Merge pull request #72635 from atrick/fix-lifedep-capture
Fix LifetimeDependenceDiagnostics to ignore closure captures
2 parents 93d64e9 + f19d94c commit 19f7b99

20 files changed

+237
-76
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceDiagnostics.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,6 @@ private func log(prefix: Bool = true, _ message: @autoclosure () -> String) {
2929
let lifetimeDependenceDiagnosticsPass = FunctionPass(
3030
name: "lifetime-dependence-diagnostics")
3131
{ (function: Function, context: FunctionPassContext) in
32-
if !context.options.hasFeature(.NonescapableTypes) {
33-
return
34-
}
3532
log(prefix: false, "\n--- Diagnosing lifetime dependence in \(function.name)")
3633
log("\(function)")
3734

@@ -45,7 +42,7 @@ let lifetimeDependenceDiagnosticsPass = FunctionPass(
4542
}
4643
}
4744
for instruction in function.instructions {
48-
if let markDep = instruction as? MarkDependenceInst {
45+
if let markDep = instruction as? MarkDependenceInst, markDep.isUnresolved {
4946
if let lifetimeDep = LifetimeDependence(markDep, context) {
5047
analyze(dependence: lifetimeDep, context)
5148
}

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceInsertion.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,6 @@ private func log(prefix: Bool = true, _ message: @autoclosure () -> String) {
3131
let lifetimeDependenceInsertionPass = FunctionPass(
3232
name: "lifetime-dependence-insertion")
3333
{ (function: Function, context: FunctionPassContext) in
34-
if !context.options.hasFeature(.NonescapableTypes) {
35-
return
36-
}
3734
log(prefix: false, "\n--- Inserting lifetime dependence markers in \(function.name)")
3835

3936
for instruction in function.instructions {

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceScopeFixup.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,6 @@ private func log(prefix: Bool = true, _ message: @autoclosure () -> String) {
3131
let lifetimeDependenceScopeFixupPass = FunctionPass(
3232
name: "lifetime-dependence-scope-fixup")
3333
{ (function: Function, context: FunctionPassContext) in
34-
if !context.options.hasFeature(.NonescapableTypes) {
35-
return
36-
}
3734
log(prefix: false, "\n--- Scope fixup for lifetime dependence in \(function.name)")
3835

3936
let localReachabilityCache = LocalVariableReachabilityCache()

lib/SILOptimizer/Mandatory/ConsumeOperatorCopyableAddressesChecker.cpp

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1660,12 +1660,16 @@ bool DataflowState::process(
16601660

16611661
{
16621662
auto diag = diag::sil_movechecking_consuming_use_here;
1663-
diagnose(astContext, mvi->getLoc().getSourceLoc(), diag);
1663+
if (auto sourceLoc = mvi->getLoc().getSourceLoc()) {
1664+
diagnose(astContext, sourceLoc, diag);
1665+
}
16641666
}
16651667

16661668
{
16671669
auto diag = diag::sil_movechecking_nonconsuming_use_here;
1668-
diagnose(astContext, iter->second->getLoc().getSourceLoc(), diag);
1670+
if (auto sourceLoc = iter->second->getLoc().getSourceLoc()) {
1671+
diagnose(astContext, sourceLoc, diag);
1672+
}
16691673
}
16701674

16711675
emittedSingleDiagnostic = true;
@@ -1690,13 +1694,17 @@ bool DataflowState::process(
16901694

16911695
{
16921696
auto diag = diag::sil_movechecking_consuming_use_here;
1693-
diagnose(astContext, mvi->getLoc().getSourceLoc(), diag);
1697+
if (auto sourceLoc = mvi->getLoc().getSourceLoc()) {
1698+
diagnose(astContext, sourceLoc, diag);
1699+
}
16941700
}
16951701

16961702
{
16971703
auto diag = diag::sil_movechecking_nonconsuming_use_here;
16981704
for (auto *user : iter->second->pairedUseInsts) {
1699-
diagnose(astContext, user->getLoc().getSourceLoc(), diag);
1705+
if (auto sourceLoc = user->getLoc().getSourceLoc()) {
1706+
diagnose(astContext, sourceLoc, diag);
1707+
}
17001708
}
17011709
}
17021710

@@ -2115,12 +2123,16 @@ bool ConsumeOperatorCopyableAddressesChecker::performSingleBasicBlockAnalysis(
21152123
}
21162124

21172125
auto diag = diag::sil_movechecking_consuming_use_here;
2118-
diagnose(astCtx, mvi->getLoc().getSourceLoc(), diag);
2126+
if (auto sourceLoc = mvi->getLoc().getSourceLoc()) {
2127+
diagnose(astCtx, sourceLoc, diag);
2128+
}
21192129

21202130
{
21212131
auto diag = diag::sil_movechecking_nonconsuming_use_here;
21222132
for (auto *user : interestingClosureUsers) {
2123-
diagnose(astCtx, user->getLoc().getSourceLoc(), diag);
2133+
if (auto sourceLoc = user->getLoc().getSourceLoc()) {
2134+
diagnose(astCtx, sourceLoc, diag);
2135+
}
21242136
}
21252137
}
21262138

@@ -2157,12 +2169,16 @@ bool ConsumeOperatorCopyableAddressesChecker::performSingleBasicBlockAnalysis(
21572169

21582170
{
21592171
auto diag = diag::sil_movechecking_consuming_use_here;
2160-
diagnose(astCtx, mvi->getLoc().getSourceLoc(), diag);
2172+
if (auto sourceLoc = mvi->getLoc().getSourceLoc()) {
2173+
diagnose(astCtx, sourceLoc, diag);
2174+
}
21612175
}
21622176

21632177
{
21642178
auto diag = diag::sil_movechecking_nonconsuming_use_here;
2165-
diagnose(astCtx, interestingUser->getLoc().getSourceLoc(), diag);
2179+
if (auto sourceLoc = interestingUser->getLoc().getSourceLoc()) {
2180+
diagnose(astCtx, sourceLoc, diag);
2181+
}
21662182
}
21672183

21682184
// We purposely continue to see if at least in simple cases, we can flag

lib/SILOptimizer/Mandatory/ConsumeOperatorCopyableValuesChecker.cpp

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ bool CheckerLivenessInfo::compute() {
155155
}
156156
}
157157
}
158+
// FIXME: this ignores all other forms of Borrow ownership, such as
159+
// partial_apply [onstack] and mark_dependence [nonescaping].
158160
break;
159161
}
160162
case OperandOwnership::GuaranteedForwarding:
@@ -250,8 +252,10 @@ void ConsumeOperatorCopyableValuesChecker::emitDiagnosticForMove(
250252
diagnose(astContext, getSourceLocFromValue(borrowedValue),
251253
diag::sil_movechecking_value_used_after_consume,
252254
borrowedValueName);
253-
diagnose(astContext, mvi->getLoc().getSourceLoc(),
254-
diag::sil_movechecking_consuming_use_here);
255+
if (auto sourceLoc = mvi->getLoc().getSourceLoc()) {
256+
diagnose(astContext, sourceLoc,
257+
diag::sil_movechecking_consuming_use_here);
258+
}
255259

256260
// Then we do a bit of work to figure out where /all/ of the later uses than
257261
// mvi are so we can emit notes to the user telling them this is a problem
@@ -281,8 +285,10 @@ void ConsumeOperatorCopyableValuesChecker::emitDiagnosticForMove(
281285
case PrunedLiveness::NonLifetimeEndingUse:
282286
case PrunedLiveness::LifetimeEndingUse:
283287
LLVM_DEBUG(llvm::dbgs() << "Emitting note for in block use: " << inst);
284-
diagnose(astContext, inst.getLoc().getSourceLoc(),
285-
diag::sil_movechecking_nonconsuming_use_here);
288+
if (auto sourceLoc = inst.getLoc().getSourceLoc()) {
289+
diagnose(astContext, sourceLoc,
290+
diag::sil_movechecking_nonconsuming_use_here);
291+
}
286292
break;
287293
}
288294
}
@@ -340,8 +346,10 @@ void ConsumeOperatorCopyableValuesChecker::emitDiagnosticForMove(
340346
case PrunedLiveness::LifetimeEndingUse:
341347
LLVM_DEBUG(llvm::dbgs()
342348
<< "(3) Emitting diagnostic for user: " << inst);
343-
diagnose(astContext, inst.getLoc().getSourceLoc(),
344-
diag::sil_movechecking_nonconsuming_use_here);
349+
if (auto sourceLoc = inst.getLoc().getSourceLoc()) {
350+
diagnose(astContext, sourceLoc,
351+
diag::sil_movechecking_nonconsuming_use_here);
352+
}
345353
break;
346354
}
347355
}
@@ -366,8 +374,10 @@ void ConsumeOperatorCopyableValuesChecker::emitDiagnosticForMove(
366374
if (livenessInfo.nonLifetimeEndingUsesInLiveOut.contains(&inst)) {
367375
LLVM_DEBUG(llvm::dbgs()
368376
<< "(1) Emitting diagnostic for user: " << inst);
369-
diagnose(astContext, inst.getLoc().getSourceLoc(),
370-
diag::sil_movechecking_nonconsuming_use_here);
377+
if (auto sourceLoc = inst.getLoc().getSourceLoc()) {
378+
diagnose(astContext, sourceLoc,
379+
diag::sil_movechecking_nonconsuming_use_here);
380+
}
371381
continue;
372382
}
373383

@@ -388,8 +398,10 @@ void ConsumeOperatorCopyableValuesChecker::emitDiagnosticForMove(
388398

389399
LLVM_DEBUG(llvm::dbgs()
390400
<< "(2) Emitting diagnostic for user: " << inst);
391-
diagnose(astContext, inst.getLoc().getSourceLoc(),
392-
diag::sil_movechecking_nonconsuming_use_here);
401+
if (auto sourceLoc = inst.getLoc().getSourceLoc()) {
402+
diagnose(astContext, sourceLoc,
403+
diag::sil_movechecking_nonconsuming_use_here);
404+
}
393405
}
394406
}
395407
}

lib/SILOptimizer/Mandatory/MoveOnlyDiagnostics.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,13 @@ static void diagnose(ASTContext &context, SILInstruction *inst, Diag<T...> diag,
6060
if (SilentlyEmitDiagnostics)
6161
return;
6262

63-
context.Diags.diagnose(loc.getSourceLoc(), diag, std::forward<U>(args)...);
63+
auto sourceLoc = loc.getSourceLoc();
64+
auto diagKind = context.Diags.declaredDiagnosticKindFor(diag.ID);
65+
// Do not emit notes on invalid source locations.
66+
if (!sourceLoc && diagKind == DiagnosticKind::Note) {
67+
return;
68+
}
69+
context.Diags.diagnose(sourceLoc, diag, std::forward<U>(args)...);
6470
}
6571

6672
template <typename... T, typename... U>

lib/SILOptimizer/PassManager/PassPipeline.cpp

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -171,13 +171,6 @@ static void addMandatoryDiagnosticOptPipeline(SILPassPipelinePlan &P) {
171171
// Check noImplicitCopy and move only types for objects and addresses.
172172
P.addMoveOnlyChecker();
173173

174-
// Check ~Escapable.
175-
if (P.getOptions().EnableLifetimeDependenceDiagnostics) {
176-
P.addLifetimeDependenceDiagnostics();
177-
}
178-
if (EnableDeinitDevirtualizer)
179-
P.addDeinitDevirtualizer();
180-
181174
// FIXME: rdar://122701694 (`consuming` keyword causes verification error on
182175
// invalid SIL types)
183176
//
@@ -189,6 +182,19 @@ static void addMandatoryDiagnosticOptPipeline(SILPassPipelinePlan &P) {
189182
// No uses after consume operator of copyable value.
190183
P.addConsumeOperatorCopyableValuesChecker();
191184

185+
// Check ~Escapable.
186+
if (P.getOptions().EnableLifetimeDependenceDiagnostics) {
187+
P.addLifetimeDependenceDiagnostics();
188+
}
189+
190+
// Devirtualize deinits early if requested.
191+
//
192+
// FIXME: why is DeinitDevirtualizer in the middle of the mandatory pipeline,
193+
// and what passes/compilation modes depend on it? This pass is never executed
194+
// or tested without '-Xllvm enable-deinit-devirtualizer'.
195+
if (EnableDeinitDevirtualizer)
196+
P.addDeinitDevirtualizer();
197+
192198
// As a temporary measure, we also eliminate move only for non-trivial types
193199
// until we can audit the later part of the pipeline. Eventually, this should
194200
// occur before IRGen.

test/SILOptimizer/lifetime_dependence_borrow.swift

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// RUN: -verify \
44
// RUN: -sil-verify-all \
55
// RUN: -module-name test \
6+
// RUN: -enable-experimental-feature NoncopyableGenerics \
67
// RUN: -enable-experimental-feature NonescapableTypes
78

89
// REQUIRES: asserts
@@ -15,8 +16,7 @@ struct CN: ~Copyable {
1516
}
1617

1718
// Some Bufferview-ish thing.
18-
@_nonescapable
19-
struct BV {
19+
struct BV : ~Escapable {
2020
let p: UnsafeRawPointer
2121
let i: Int
2222

@@ -35,8 +35,7 @@ struct BV {
3535
}
3636

3737
// Some MutableBufferview-ish thing.
38-
@_nonescapable
39-
struct MBV : ~Copyable {
38+
struct MBV : ~Escapable, ~Copyable {
4039
let p: UnsafeRawPointer
4140
let i: Int
4241

@@ -53,8 +52,7 @@ struct MBV : ~Copyable {
5352
}
5453

5554
// Nonescapable wrapper.
56-
@_nonescapable
57-
struct NEBV {
55+
struct NEBV : ~Escapable {
5856
var bv: BV
5957

6058
init(_ bv: consuming BV) {

test/SILOptimizer/lifetime_dependence_borrow_fail.swift

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
// RUN: -verify \
44
// RUN: -sil-verify-all \
55
// RUN: -module-name test \
6+
// RUN: -enable-experimental-feature NoncopyableGenerics \
67
// RUN: -enable-experimental-feature NonescapableTypes
78

89
// REQUIRES: asserts
910
// REQUIRES: swift_in_compiler
1011

11-
@_nonescapable
12-
struct BV {
12+
struct BV : ~Escapable {
1313
let p: UnsafeRawPointer
1414
let i: Int
1515

@@ -34,8 +34,7 @@ struct NC : ~Copyable {
3434
}
3535
}
3636

37-
@_nonescapable
38-
struct NE {
37+
struct NE : ~Escapable {
3938
let p: UnsafeRawPointer
4039
let i: Int
4140

test/SILOptimizer/lifetime_dependence_closure.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// RUN: -sil-verify-all \
55
// RUN: -module-name test \
66
// RUN: -disable-experimental-parser-round-trip \
7+
// RUN: -enable-experimental-feature NoncopyableGenerics \
78
// RUN: -enable-experimental-feature NonescapableTypes
89

910
// REQUIRES: asserts
@@ -15,8 +16,7 @@ struct NCInt: ~Copyable {
1516
init(_ value: Int) { self.value = value }
1617
}
1718

18-
@_nonescapable
19-
struct NEInt /*: ~Escapable*/ {
19+
struct NEInt : ~Escapable {
2020
let value: Int
2121

2222
init(borrowed: borrowing NCInt) -> dependsOn(borrowed) Self {

test/SILOptimizer/lifetime_dependence_diagnostics.swift

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
// RUN: %target-swift-frontend %s -emit-sil \
22
// RUN: -sil-verify-all \
33
// RUN: -module-name test \
4+
// RUN: -enable-experimental-feature NoncopyableGenerics \
45
// RUN: -enable-experimental-feature NonescapableTypes \
56
// RUN: 2>&1 | %FileCheck %s
67

78
// REQUIRES: asserts
89
// REQUIRES: swift_in_compiler
910

10-
@_nonescapable
11-
struct BV {
11+
struct BV : ~Escapable {
1212
let p: UnsafeRawPointer
1313
let c: Int
1414
@_unsafeNonescapableResult
@@ -22,6 +22,12 @@ func bv_copy(_ bv: borrowing BV) -> dependsOn(bv) BV {
2222
copy bv
2323
}
2424

25+
struct NCInt: ~Copyable {
26+
var value: Int
27+
}
28+
29+
func takeClosure(_: () -> ()) {}
30+
2531
// No mark_dependence is needed for a inherited scope.
2632
//
2733
// CHECK-LABEL: sil hidden @$s4test14bv_borrow_copyyAA2BVVADYlsF : $@convention(thin) (@guaranteed BV) -> _scope(1) @owned BV {
@@ -45,3 +51,10 @@ func bv_borrow_copy(_ bv: borrowing BV) -> dependsOn(scoped bv) BV {
4551
func bv_borrow_borrow(bv: borrowing BV) -> dependsOn(scoped bv) BV {
4652
bv_borrow_copy(bv)
4753
}
54+
55+
// This already has a mark_dependence [nonescaping] before diagnostics. If it triggers diagnostics again, it will fail
56+
// because lifetime dependence does not expect a dependence directly on an 'inout' address without any 'begin_access'
57+
// marker.
58+
func ncint_capture(ncInt: inout NCInt) {
59+
takeClosure { _ = ncInt.value }
60+
}

0 commit comments

Comments
 (0)