Skip to content

Commit 83399f9

Browse files
authored
Merge pull request #83111 from meg-gupta/lifediag
Diagnose @_lifetime on targets that are Escapable
2 parents c7b51ed + b705b8f commit 83399f9

File tree

13 files changed

+84
-100
lines changed

13 files changed

+84
-100
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8334,6 +8334,8 @@ ERROR(lifetime_dependence_duplicate_target, none,
83348334
"invalid duplicate target lifetime dependencies on function", ())
83358335
ERROR(lifetime_parameter_requires_inout, none,
83368336
"lifetime-dependent parameter '%0' must be 'inout'", (StringRef))
8337+
ERROR(lifetime_target_requires_nonescapable, none,
8338+
"invalid lifetime dependence on an Escapable %0", (StringRef))
83378339

83388340
//------------------------------------------------------------------------------
83398341
// MARK: Lifetime Dependence Requirements

lib/AST/LifetimeDependence.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,13 @@ static bool isDiagnosedNonEscapable(Type type) {
227227
return !type->isEscapable();
228228
}
229229

230+
static bool isDiagnosedEscapable(Type type) {
231+
if (type->hasError()) {
232+
return false;
233+
}
234+
return type->isEscapable();
235+
}
236+
230237
void LifetimeDependenceInfo::getConcatenatedData(
231238
SmallVectorImpl<bool> &concatenatedData) const {
232239
auto pushData = [&](IndexSubset *paramIndices) {
@@ -893,8 +900,16 @@ class LifetimeDependenceChecker {
893900
diag::lifetime_parameter_requires_inout,
894901
targetDescriptor->getString());
895902
}
903+
if (isDiagnosedEscapable(targetDeclAndIndex->first->getTypeInContext())) {
904+
diagnose(targetDescriptor->getLoc(),
905+
diag::lifetime_target_requires_nonescapable, "target");
906+
}
896907
targetIndex = targetDeclAndIndex->second;
897908
} else {
909+
if (isDiagnosedEscapable(getResultOrYield())) {
910+
diagnose(entry->getLoc(), diag::lifetime_target_requires_nonescapable,
911+
"result");
912+
}
898913
targetIndex = afd->hasImplicitSelfDecl()
899914
? afd->getParameters()->size() + 1
900915
: afd->getParameters()->size();

stdlib/private/StdlibUnittest/StdlibUnittest.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -754,7 +754,6 @@ public func expectNil<T>(
754754
}
755755
}
756756

757-
@_lifetime(copy value)
758757
public func expectNil<T: ~Copyable & ~Escapable>(
759758
_ value: borrowing T?,
760759
_ message: @autoclosure () -> String = "",

test/Interop/C/swiftify-import/Inputs/counted-by-noescape.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ void lenName(int lenName, int size, int * __counted_by(lenName * size) _Nullable
4444
void func(int len, int * __counted_by(len) _Nullable func __noescape);
4545

4646
void *funcRenameKeyword(int len, int * __counted_by(len) _Nullable func __noescape,
47-
int extension __lifetimebound,
47+
int extension,
4848
int init,
4949
int open,
5050
int var,
@@ -55,7 +55,7 @@ void *funcRenameKeyword(int len, int * __counted_by(len) _Nullable func __noesca
5555
int where) __attribute__((swift_name("funcRenamed(len:func:extension:init:open:var:is:as:in:guard:where:)")));
5656

5757
void *funcRenameKeywordAnonymous(int len, int * __counted_by(len) _Nullable __noescape,
58-
int __lifetimebound,
58+
int,
5959
int,
6060
int,
6161
int,

test/Interop/C/swiftify-import/counted-by-noescape.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,11 @@ import CountedByNoEscapeClang
3838

3939
// CHECK-NEXT: /// This is an auto-generated wrapper for safer interop
4040
// CHECK-NEXT: @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
41-
// CHECK-NEXT: @_lifetime(borrow extension)
4241
// CHECK-NEXT: @_lifetime(func: copy func)
4342
// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func funcRenamed(func: inout MutableSpan<Int32>?, extension: Int32, init: Int32, open: Int32, var: Int32, is: Int32, as: Int32, in: Int32, guard: Int32, where: Int32) -> UnsafeMutableRawPointer!
4443

4544
// CHECK-NEXT: /// This is an auto-generated wrapper for safer interop
4645
// CHECK-NEXT: @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
47-
// CHECK-NEXT: @_lifetime(borrow _funcRenamedAnon_param2)
4846
// CHECK-NEXT: @_lifetime(_funcRenamedAnon_param1: copy _funcRenamedAnon_param1)
4947
// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func funcRenamedAnon(func _funcRenamedAnon_param1: inout MutableSpan<Int32>?, extension _funcRenamedAnon_param2: Int32, init _funcRenamedAnon_param3: Int32, open _funcRenamedAnon_param4: Int32, var _funcRenamedAnon_param5: Int32, is _funcRenamedAnon_param6: Int32, as _funcRenamedAnon_param7: Int32, in _funcRenamedAnon_param8: Int32, guard _funcRenamedAnon_param9: Int32, where _funcRenamedAnon_param10: Int32) -> UnsafeMutableRawPointer!
5048

test/Macros/SwiftifyImport/CountedBy/ConstantCount.swift

Lines changed: 16 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,16 @@ func mutReturn() -> UnsafeMutablePointer<CInt> {}
4141
func mutOptReturn() -> UnsafeMutablePointer<CInt>? {}
4242

4343

44-
@_SwiftifyImport(.countedBy(pointer: .param(1), count: "37"), .lifetimeDependence(dependsOn: .param(1), pointer: .return, type: .copy))
44+
@_SwiftifyImport(.countedBy(pointer: .param(1), count: "37"))
4545
func noescape(_ ptr: UnsafePointer<CInt>) -> UnsafePointer<CInt> {}
4646

47-
@_SwiftifyImport(.countedBy(pointer: .param(1), count: "37"), .lifetimeDependence(dependsOn: .param(1), pointer: .return, type: .copy))
47+
@_SwiftifyImport(.countedBy(pointer: .param(1), count: "37"))
4848
func noescapeOpt(_ ptr: UnsafePointer<CInt>?) -> UnsafePointer<CInt>? {}
4949

50-
@_SwiftifyImport(.countedBy(pointer: .param(1), count: "37"), .lifetimeDependence(dependsOn: .param(1), pointer: .return, type: .copy))
50+
@_SwiftifyImport(.countedBy(pointer: .param(1), count: "37"))
5151
func noescapeMut(_ ptr: UnsafeMutablePointer<CInt>) -> UnsafeMutablePointer<CInt> {}
5252

53-
@_SwiftifyImport(.countedBy(pointer: .param(1), count: "37"), .lifetimeDependence(dependsOn: .param(1), pointer: .return, type: .copy))
53+
@_SwiftifyImport(.countedBy(pointer: .param(1), count: "37"))
5454
func noescapeMutOpt(_ ptr: UnsafeMutablePointer<CInt>?) -> UnsafeMutablePointer<CInt>? {}
5555

5656
// CHECK: @_alwaysEmitIntoClient @_disfavoredOverload
@@ -100,15 +100,13 @@ func noescapeMutOpt(_ ptr: UnsafeMutablePointer<CInt>?) -> UnsafeMutablePointer<
100100
// CHECK-NEXT: }
101101
// CHECK-NEXT: }
102102

103-
// CHECK: @_alwaysEmitIntoClient @_lifetime(copy ptr) @_disfavoredOverload
104-
// CHECK-NEXT: func noescape(_ ptr: Span<CInt>) -> UnsafePointer<CInt> {
105-
// CHECK-NEXT: let _ptrCount = ptr.count
103+
// CHECK: @_alwaysEmitIntoClient @_disfavoredOverload
104+
// CHECK-NEXT: func noescape(_ ptr: UnsafeBufferPointer<CInt>) -> UnsafePointer<CInt> {
105+
// CHECK-NEXT: let _ptrCount = unsafe ptr.count
106106
// CHECK-NEXT: if _ptrCount != 37 {
107107
// CHECK-NEXT: fatalError("bounds check failure in noescape: expected \(37) but got \(_ptrCount)")
108108
// CHECK-NEXT: }
109-
// CHECK-NEXT: return unsafe ptr.withUnsafeBufferPointer { _ptrPtr in
110-
// CHECK-NEXT: return unsafe noescape(_ptrPtr.baseAddress!)
111-
// CHECK-NEXT: }
109+
// CHECK-NEXT: return unsafe noescape(ptr.baseAddress!)
112110
// CHECK-NEXT: }
113111

114112
// CHECK: @_alwaysEmitIntoClient @_disfavoredOverload
@@ -128,23 +126,6 @@ func noescapeMutOpt(_ ptr: UnsafeMutablePointer<CInt>?) -> UnsafeMutablePointer<
128126
// CHECK-NEXT: }()
129127
// CHECK-NEXT: }
130128

131-
// CHECK: @_alwaysEmitIntoClient @_lifetime(copy ptr) @_disfavoredOverload
132-
// CHECK-NEXT: func noescapeOpt(_ ptr: Span<CInt>?) -> UnsafePointer<CInt>? {
133-
// CHECK-NEXT: let _ptrCount = ptr?.count ?? 0
134-
// CHECK-NEXT: if _ptrCount != 37 {
135-
// CHECK-NEXT: fatalError("bounds check failure in noescapeOpt: expected \(37) but got \(_ptrCount)")
136-
// CHECK-NEXT: }
137-
// CHECK-NEXT: return { () in
138-
// CHECK-NEXT: return if ptr == nil {
139-
// CHECK-NEXT: unsafe noescapeOpt(nil)
140-
// CHECK-NEXT: } else {
141-
// CHECK-NEXT: unsafe ptr!.withUnsafeBufferPointer { _ptrPtr in
142-
// CHECK-NEXT: return unsafe noescapeOpt(_ptrPtr.baseAddress)
143-
// CHECK-NEXT: }
144-
// CHECK-NEXT: }
145-
// CHECK-NEXT: }()
146-
// CHECK-NEXT: }
147-
148129
// CHECK: @_alwaysEmitIntoClient @_lifetime(ptr: copy ptr) @_disfavoredOverload
149130
// CHECK-NEXT: func noescapeMut(_ ptr: inout MutableSpan<CInt>) {
150131
// CHECK-NEXT: let _ptrCount = ptr.count
@@ -156,49 +137,22 @@ func noescapeMutOpt(_ ptr: UnsafeMutablePointer<CInt>?) -> UnsafeMutablePointer<
156137
// CHECK-NEXT: }
157138
// CHECK-NEXT: }
158139

159-
// CHECK: @_alwaysEmitIntoClient @_lifetime(copy ptr) @_lifetime(ptr: copy ptr) @_disfavoredOverload
160-
// CHECK-NEXT: func noescapeMut(_ ptr: inout MutableSpan<CInt>) -> UnsafeMutablePointer<CInt> {
161-
// CHECK-NEXT: let _ptrCount = ptr.count
140+
// CHECK: @_alwaysEmitIntoClient @_disfavoredOverload
141+
// CHECK-NEXT: func noescapeMut(_ ptr: UnsafeMutableBufferPointer<CInt>) -> UnsafeMutablePointer<CInt> {
142+
// CHECK-NEXT: let _ptrCount = unsafe ptr.count
162143
// CHECK-NEXT: if _ptrCount != 37 {
163144
// CHECK-NEXT: fatalError("bounds check failure in noescapeMut: expected \(37) but got \(_ptrCount)")
164145
// CHECK-NEXT: }
165-
// CHECK-NEXT: return unsafe ptr.withUnsafeMutableBufferPointer { _ptrPtr in
166-
// CHECK-NEXT: return unsafe noescapeMut(_ptrPtr.baseAddress!)
167-
// CHECK-NEXT: }
168-
// CHECK-NEXT: }
169-
170-
// CHECK: @_alwaysEmitIntoClient @_lifetime(ptr: copy ptr) @_disfavoredOverload
171-
// CHECK-NEXT: func noescapeMutOpt(_ ptr: inout MutableSpan<CInt>?) {
172-
// CHECK-NEXT: let _ptrCount = ptr?.count ?? 0
173-
// CHECK-NEXT: if _ptrCount != 37 {
174-
// CHECK-NEXT: fatalError("bounds check failure in noescapeMutOpt: expected \(37) but got \(_ptrCount)")
175-
// CHECK-NEXT: }
176-
// CHECK-NEXT: return { () in
177-
// CHECK-NEXT: return if ptr == nil {
178-
// CHECK-NEXT: unsafe noescapeMutOpt(nil)
179-
// CHECK-NEXT: } else {
180-
// CHECK-NEXT: unsafe ptr!.withUnsafeMutableBufferPointer { _ptrPtr in
181-
// CHECK-NEXT: return unsafe noescapeMutOpt(_ptrPtr.baseAddress)
182-
// CHECK-NEXT: }
183-
// CHECK-NEXT: }
184-
// CHECK-NEXT: }()
146+
// CHECK-NEXT: return unsafe noescapeMut(ptr.baseAddress!)
185147
// CHECK-NEXT: }
186148

187-
// CHECK: @_alwaysEmitIntoClient @_lifetime(copy ptr) @_lifetime(ptr: copy ptr) @_disfavoredOverload
188-
// CHECK-NEXT: func noescapeMutOpt(_ ptr: inout MutableSpan<CInt>?) -> UnsafeMutablePointer<CInt>? {
189-
// CHECK-NEXT: let _ptrCount = ptr?.count ?? 0
149+
// CHECK: @_alwaysEmitIntoClient @_disfavoredOverload
150+
// CHECK-NEXT: func noescapeMutOpt(_ ptr: UnsafeMutableBufferPointer<CInt>?) -> UnsafeMutablePointer<CInt>? {
151+
// CHECK-NEXT: let _ptrCount = unsafe ptr?.count ?? 0
190152
// CHECK-NEXT: if _ptrCount != 37 {
191153
// CHECK-NEXT: fatalError("bounds check failure in noescapeMutOpt: expected \(37) but got \(_ptrCount)")
192154
// CHECK-NEXT: }
193-
// CHECK-NEXT: return { () in
194-
// CHECK-NEXT: return if ptr == nil {
195-
// CHECK-NEXT: unsafe noescapeMutOpt(nil)
196-
// CHECK-NEXT: } else {
197-
// CHECK-NEXT: unsafe ptr!.withUnsafeMutableBufferPointer { _ptrPtr in
198-
// CHECK-NEXT: return unsafe noescapeMutOpt(_ptrPtr.baseAddress)
199-
// CHECK-NEXT: }
200-
// CHECK-NEXT: }
201-
// CHECK-NEXT: }()
155+
// CHECK-NEXT: return unsafe noescapeMutOpt(ptr?.baseAddress)
202156
// CHECK-NEXT: }
203157

204158
// CHECK: @_alwaysEmitIntoClient @_disfavoredOverload

test/SILOptimizer/Inputs/SpanExtras.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,12 +240,11 @@ extension MutableSpan where Element: ~Copyable {
240240
/// - Complexity: O(1)
241241
@_alwaysEmitIntoClient
242242
public subscript(_ position: Index) -> Element {
243-
@_lifetime(borrow self)
244243
_read {
245244
precondition(indices.contains(position), "index out of bounds")
246245
yield self[unchecked: position]
247246
}
248-
@_lifetime(&self)
247+
@_lifetime(self: copy self)
249248
_modify {
250249
precondition(indices.contains(position), "index out of bounds")
251250
yield &self[unchecked: position]

test/SILOptimizer/lifetime_dependence/semantics.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -627,7 +627,6 @@ func test(arg: inout AddressableInt) -> Span<Int> {
627627
}
628628

629629
// unsafeAddress generates an addressable value with a local scope.
630-
@_lifetime(borrow arg)
631630
func testBorrowedAddressableInt(arg: Holder) -> Int {
632631
let span = arg.addressableInt.span()
633632
return span[0]

test/SILOptimizer/lifetime_dependence/verify_diagnostics.swift

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ public struct NoncopyableImplicitAccessors : ~Copyable & ~Escapable {
169169
@_lifetime(borrow self)
170170
get { ne }
171171

172-
@_lifetime(&self)
172+
@_lifetime(self: copy newValue)
173173
set {
174174
ne = newValue
175175
}
@@ -315,14 +315,3 @@ func inoutToImmortal(_ s: inout RawSpan) {
315315
s = _overrideLifetime(tmp, borrowing: ())
316316
}
317317

318-
// =============================================================================
319-
// Dependence on non-Copyable values
320-
// =============================================================================
321-
322-
@_lifetime(immortal)
323-
func dependOnNonCopyable() -> NCBuffer {
324-
let buffer = NCBuffer()
325-
let count = buffer.bytes.count
326-
_ = count
327-
return buffer // expected-error {{noncopyable 'buffer' cannot be consumed when captured by an escaping closure or borrowed by a non-Escapable type}}
328-
}

test/SILOptimizer/lifetime_dependence/verify_library_diagnostics.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public struct NoncopyableImplicitAccessors : ~Copyable & ~Escapable {
5050
@_lifetime(borrow self)
5151
get { ne }
5252

53-
@_lifetime(&self)
53+
@_lifetime(self: copy newValue)
5454
set {
5555
ne = newValue
5656
}

0 commit comments

Comments
 (0)