Skip to content

Commit 1b2fc8c

Browse files
committed
Fix a move-checker diagnostic message (textual change)
The move-checker was assuming that any non-Copyable variable in a box must be captured by a closure. The underlying problem is that the move-checker relies on the best-effort AllocBoxToStack optimization to be perfect. But when non-Escapable values depend on the variable, it remains boxed. That's good for lifetime diagnostics but caused an incorrect move-checker diagnostic. Fixes rdar://154519148 (Returning non-copyable type after accessing borrowed field emits incorrect error about escaped closure capturing the noncopyable)
1 parent 5528cf1 commit 1b2fc8c

File tree

2 files changed

+31
-1
lines changed

2 files changed

+31
-1
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -857,7 +857,8 @@ ERROR(sil_movechecking_borrowed_parameter_captured_by_closure, none,
857857
"parameter",
858858
(StringRef))
859859
ERROR(sil_movechecking_capture_consumed, none,
860-
"noncopyable '%0' cannot be consumed when captured by an escaping closure", (StringRef))
860+
"noncopyable '%0' cannot be consumed when captured by an escaping closure or borrowed by a non-Escapable type",
861+
(StringRef))
861862
ERROR(sil_movechecking_not_reinitialized_before_end_of_function, none,
862863
"missing reinitialization of %select{inout parameter|closure capture}1 '%0' "
863864
"after consume", (StringRef, bool))

test/SILOptimizer/lifetime_dependence/verify_diagnostics.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,23 @@ struct TestDeinitCallsAddressor: ~Copyable, ~Escapable {
121121
}
122122
}
123123

124+
struct NCBuffer: ~Copyable {
125+
fileprivate let buffer: UnsafeMutableRawBufferPointer
126+
127+
public init() {
128+
let ptr = UnsafeMutableRawPointer.init(bitPattern: 0)
129+
self.buffer = UnsafeMutableRawBufferPointer(start: ptr, count: 0)
130+
}
131+
132+
public var bytes: Span<UInt8> {
133+
@_lifetime(borrow self)
134+
borrowing get {
135+
let span: Span<UInt8> = Span(_bytes: self.buffer.bytes)
136+
return span
137+
}
138+
}
139+
}
140+
124141
// Test a borrowed dependency on an address
125142
@_lifetime(immortal)
126143
public func testGenericDep<T: ~Escapable>(type: T.Type) -> T {
@@ -287,3 +304,15 @@ func testSpanMayThrow(buffer: inout [Int]) {
287304
let bufferSpan = buffer.mutableSpan
288305
try! mutableSpanMayThrow(bufferSpan)
289306
}
307+
308+
// =============================================================================
309+
// Dependence on non-Copyable values
310+
// =============================================================================
311+
312+
@_lifetime(immortal)
313+
func dependOnNonCopyable() -> NCBuffer {
314+
let buffer = NCBuffer()
315+
let count = buffer.bytes.count
316+
_ = count
317+
return buffer // expected-error {{noncopyable 'buffer' cannot be consumed when captured by an escaping closure or borrowed by a non-Escapable type}}
318+
}

0 commit comments

Comments
 (0)