Skip to content

Commit 80a61dd

Browse files
committed
ManualOwnership: variable names in diagnostics
Also tailor the messages it a bit based on uses, such as closure captures.
1 parent bfce117 commit 80a61dd

File tree

3 files changed

+116
-30
lines changed

3 files changed

+116
-30
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,12 @@ NOTE(performance_called_from,none,
430430
"called from here", ())
431431
ERROR(manualownership_copy,none,
432432
"explicit 'copy' required here", ())
433+
ERROR(manualownership_copy_happened,none,
434+
"accessing %0 produces a copy of it; write 'copy' to acknowledge", (Identifier))
435+
ERROR(manualownership_copy_demanded,none,
436+
"ownership of %0 is demanded and cannot not be consumed; write 'copy' to satisfy", (Identifier))
437+
ERROR(manualownership_copy_captured,none,
438+
"ownership of %0 is demanded by a closure; write 'copy' in its capture list to satisfy", (Identifier))
433439

434440
// 'transparent' diagnostics
435441
ERROR(circular_transparent,none,

lib/SILOptimizer/Mandatory/PerformanceDiagnostics.cpp

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h"
2121
#include "swift/SILOptimizer/PassManager/Transforms.h"
2222
#include "swift/SILOptimizer/Utils/BasicBlockOptUtils.h"
23+
#include "swift/SILOptimizer/Utils/VariableNameUtils.h"
2324
#include "llvm/Support/Debug.h"
2425

2526
using namespace swift;
@@ -379,6 +380,7 @@ bool PerformanceDiagnostics::visitInst(SILInstruction *inst,
379380
if (impact & RuntimeEffect::RefCounting) {
380381
bool shouldDiagnose = false;
381382
switch (inst->getKind()) {
383+
case SILInstructionKind::PartialApplyInst:
382384
case SILInstructionKind::DestroyAddrInst:
383385
case SILInstructionKind::DestroyValueInst:
384386
break; // These modify reference counts, but aren't copies.
@@ -421,19 +423,40 @@ bool PerformanceDiagnostics::visitInst(SILInstruction *inst,
421423
break;
422424
}
423425
if (shouldDiagnose) {
426+
LLVM_DEBUG(llvm::dbgs()
427+
<< "function " << inst->getFunction()->getName()
428+
<< "\n has unexpected copying instruction: " << *inst);
429+
430+
// Try to come up with a useful diagnostic.
431+
if (auto svi = dyn_cast<SingleValueInstruction>(inst)) {
432+
if (auto name = VariableNameInferrer::inferName(svi)) {
433+
// Simplistic check for whether this is a closure capture.
434+
for (auto user : svi->getUsers()) {
435+
if (isa<PartialApplyInst>(user)) {
436+
LLVM_DEBUG(llvm::dbgs() << "captured by "<< *user);
437+
diagnose(loc, diag::manualownership_copy_captured, *name);
438+
return false;
439+
}
440+
}
441+
442+
// There's no hope of borrowing access if there's a consuming use.
443+
for (auto op : svi->getUses()) {
444+
if (op->getOperandOwnership() == OperandOwnership::ForwardingConsume) {
445+
LLVM_DEBUG(llvm::dbgs() << "demanded by "<< *(op->getUser()));
446+
diagnose(loc, diag::manualownership_copy_demanded, *name);
447+
return false;
448+
}
449+
}
450+
451+
diagnose(loc, diag::manualownership_copy_happened, *name);
452+
return false;
453+
}
454+
}
455+
456+
// Back-up diagnostic, when all-else fails.
424457
diagnose(loc, diag::manualownership_copy);
425-
llvm::dbgs() << "function " << inst->getFunction()->getName();
426-
llvm::dbgs() << "\n has unexpected copying instruction: ";
427-
inst->dump();
428458
return false; // Don't bail-out early; diagnose more issues in the func.
429459
}
430-
} else if (impact == RuntimeEffect::ExclusivityChecking) {
431-
// TODO: diagnose only the nested exclusivity; perhaps as a warning
432-
// since we don't yet have reference bindings?
433-
434-
// diagnose(loc, diag::performance_arc);
435-
// inst->dump();
436-
// return true;
437460
}
438461
return false;
439462
}

test/SIL/manual_ownership.swift

Lines changed: 77 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public func basic_return1() -> Triangle {
5252

5353
@_manualOwnership
5454
public func basic_return2(t: Triangle) -> Triangle {
55-
return t // expected-error {{explicit 'copy' required here}}
55+
return t // expected-error {{ownership of 't' is demanded}}
5656
}
5757
@_manualOwnership
5858
public func basic_return2_fixed(t: Triangle) -> Triangle {
@@ -76,8 +76,9 @@ func reassign_with_lets() -> Triangle {
7676
func renamed_return(_ cond: Bool, _ a: Triangle) -> Triangle {
7777
let b = a
7878
let c = b
79-
if cond { return b } // expected-error {{explicit 'copy' required here}}
80-
return c // expected-error {{explicit 'copy' required here}}
79+
// FIXME: we say 'c' instead of 'b', because of the propagation.
80+
if cond { return b } // expected-error {{ownership of 'c' is demanded}}
81+
return c // expected-error {{ownership of 'c' is demanded}}
8182
}
8283

8384
@_manualOwnership
@@ -109,7 +110,7 @@ func basic_methods_borrowing(_ t1: Triangle) {
109110
@_manualOwnership
110111
func basic_methods_consuming(_ t1: Triangle) {
111112
let t2 = Triangle()
112-
t1.consuming() // expected-error {{explicit 'copy' required here}}
113+
t1.consuming() // expected-error {{ownership of 't1' is demanded}}
113114
t2.consuming()
114115
}
115116
@_manualOwnership
@@ -120,12 +121,16 @@ func basic_methods_consuming_fixed(_ t1: Triangle) {
120121
(copy t2).consuming() // FIXME: why is this not propagated?
121122
}
122123

123-
func consumingFunc(_ t0: consuming Triangle) {}
124+
@_manualOwnership
125+
@discardableResult
126+
func consumingFunc(_ t0: consuming Triangle) -> Bool { return false }
127+
128+
@_manualOwnership
124129
func plainFunc(_ t0: Triangle) {}
125130

126131
@_manualOwnership
127132
func basic_function_call(_ t1: Triangle) {
128-
consumingFunc(t1) // expected-error {{explicit 'copy' required here}}
133+
consumingFunc(t1) // expected-error {{ownership of 't1' is demanded}}
129134
consumingFunc(copy t1)
130135
plainFunc(t1)
131136
}
@@ -151,7 +156,7 @@ func basic_function_call(_ t1: Triangle) {
151156
@_manualOwnership
152157
public func basic_loop_trivial_values(_ t: Triangle, _ xs: [Triangle]) {
153158
var p: Pair = t.a
154-
for x in xs { // expected-error {{explicit 'copy' required here}}
159+
for x in xs { // expected-error {{ownership of 'xs' is demanded}}
155160
p = p.midpoint(x.a)
156161
}
157162
t.a = p
@@ -174,21 +179,21 @@ public func basic_loop_trivial_values_fixed(_ t: Triangle, _ xs: [Triangle]) {
174179

175180
@_manualOwnership
176181
public func basic_loop_nontrivial_values(_ t: Triangle, _ xs: [Triangle]) {
177-
var p: Pair = t.nontrivial.a // expected-error {{explicit 'copy' required here}}
178-
for x in xs { // expected-error {{explicit 'copy' required here}}
179-
p = p.midpoint(x.nontrivial.a) // expected-error {{explicit 'copy' required here}}
182+
var p: Pair = t.nontrivial.a // expected-error {{accessing 't.nontrivial' produces a copy of it}}
183+
for x in xs { // expected-error {{ownership of 'xs' is demanded}}
184+
p = p.midpoint(x.nontrivial.a) // expected-error {{accessing 'x.nontrivial' produces a copy of it}}
180185
}
181-
t.nontrivial.a = p // expected-error {{explicit 'copy' required here}}
186+
t.nontrivial.a = p // expected-error {{accessing 't.nontrivial' produces a copy of it}}
182187
}
183188

184189
// FIXME: there should be no copies required in the below, other than what's already written.
185190
@_manualOwnership
186191
public func basic_loop_nontrivial_values_fixed(_ t: Triangle, _ xs: [Triangle]) {
187-
var p: Pair = (copy t.nontrivial).a // expected-error {{explicit 'copy' required here}}
192+
var p: Pair = (copy t.nontrivial).a // expected-error {{accessing 't.nontrivial' produces a copy of it}}
188193
for x in copy xs {
189-
p = p.midpoint((copy x.nontrivial).a) // expected-error {{explicit 'copy' required here}}
194+
p = p.midpoint((copy x.nontrivial).a) // expected-error {{accessing 'x.nontrivial' produces a copy of it}}
190195
}
191-
(copy t.nontrivial).a = p // expected-error {{explicit 'copy' required here}}
196+
(copy t.nontrivial).a = p // expected-error {{accessing 't.nontrivial' produces a copy of it}}
192197
}
193198

194199

@@ -199,7 +204,7 @@ let ref_result = [5, 13, 29]
199204
// are present to avoid exclusivity issues. We'd need to start generating read coroutines.
200205
@_manualOwnership
201206
func access_global_1() -> Int {
202-
return ref_result[2] // expected-error {{explicit 'copy' required here}}
207+
return ref_result[2] // expected-error {{accessing 'ref_result' produces a copy of it}}
203208
}
204209
@_manualOwnership
205210
func access_global_1_fixed() -> Int {
@@ -212,15 +217,27 @@ return (copy ref_result)[2]
212217
// We also need a better error message for when this is missed for closure captures.
213218
// (2) Escaping closures need to be recursively checked by the PerformanceDiagnostics.
214219
// We might just need to widen the propagation of [manual_ownership]?
220+
// (3) Autoclosures have no ability to annotate captures. Is that OK?
221+
222+
@_manualOwnership
223+
func closure_basic(_ t: Triangle) -> () -> Triangle {
224+
return { return t } // expected-error {{ownership of 't' is demanded by a closure}}
225+
}
226+
@_manualOwnership
227+
func closure_basic_fixed(_ t: Triangle) -> () -> Triangle {
228+
return { [t = copy t] in return t }
229+
}
230+
215231
@_manualOwnership
216-
func testClosures() -> () -> Triangle {
217-
let t = Triangle()
218-
return { [t = copy t] in return t } // expected-error {{explicit 'copy' required here}}
232+
func closure_copies_in_body(_ t: Triangle) -> () -> Triangle {
233+
return { [t = copy t] in
234+
eat(t) // FIXME: missing required copies
235+
eat(t)
236+
return t }
219237
}
220238

221239
@_manualOwnership
222-
func testClosures_noescape() -> Triangle {
223-
let t = Triangle()
240+
func closure_copies_in_body_noescape(_ t: Triangle) -> Triangle {
224241
let f = { [t = copy t] in
225242
eat(t) // FIXME: missing required copies
226243
eat(t)
@@ -229,8 +246,48 @@ func testClosures_noescape() -> Triangle {
229246
return f()
230247
}
231248

249+
@_manualOwnership
250+
func simple_assert(_ f: @autoclosure () -> Bool) {
251+
guard f() else { fatalError() }
252+
}
253+
@_manualOwnership
254+
func try_to_assert(_ n: Int, _ names: [String]) {
255+
simple_assert(names.count == n)
256+
}
257+
258+
@_manualOwnership
259+
func copy_in_autoclosure(_ t: Triangle) {
260+
simple_assert(consumingFunc(t)) // FIXME: missing required copies
261+
}
262+
232263
/// MARK: generics
233264

265+
@_manualOwnership
266+
func return_generic<T>(_ t: T) -> T {
267+
return t // expected-error {{explicit 'copy' required here}}
268+
}
269+
@_manualOwnership
270+
func return_generic_fixed<T>(_ t: T) -> T {
271+
return copy t
272+
}
273+
274+
@_manualOwnership
275+
func reassign_with_lets<T>(_ t: T) -> T {
276+
let x = t // expected-error {{explicit 'copy' required here}}
277+
let y = x // expected-error {{explicit 'copy' required here}}
278+
let z = y // expected-error {{explicit 'copy' required here}}
279+
return copy z
280+
}
281+
282+
// FIXME: there's copy propagation has no effect on address-only types.
283+
@_manualOwnership
284+
func reassign_with_lets_fixed<T>(_ t: T) -> T {
285+
let x = copy t
286+
let y = copy x
287+
let z = copy y
288+
return copy z
289+
}
290+
234291
@_manualOwnership
235292
func copy_generic<T>(_ t: T) {
236293
consume_generic(t) // expected-error {{explicit 'copy' required here}}

0 commit comments

Comments
 (0)