Skip to content

Commit b5b0c75

Browse files
committed
Remove diagnostic: lifetime_dependence_on_bitwise_copyable
Allow lifetime depenendence on types that are BitwiseCopyable & Escapable. This is unsafe in the sense that the compiler will not diagnose any use of the dependent value outside of the lexcial scope of the source value. But, in practice, dependence on an UnsafePointer is often needed. In that case, the programmer should have already taken responsibility for ensuring the lifetime of the pointer over all dependent uses. Typically, an unsafe pointer is valid for the duration of a closure. Lifetime dependence prevents the dependent value from being returned by the closure, so common usage is safe by default. Typical example: func decode(_ bufferRef: Span<Int>) { /*...*/ } extension UnsafeBufferPointer { // The client must ensure the lifetime of the buffer across the invocation of `body`. // The client must ensure that no code modifies the buffer during the invocation of `body`. func withUnsafeSpan<Result>(_ body: (Span<Element>) throws -> Result) rethrows -> Result { // Construct Span using its internal, unsafe API. try body(Span(unsafePointer: baseAddress!, count: count)) } } func decodeArrayAsUBP(array: [Int]) { array.withUnsafeBufferPointer { buffer in buffer.withUnsafeSpan { decode($0) } } } In the future, we may add SILGen support for tracking the lexical scope of BitwiseCopyable values. That would allow them to have the same dependence behavior as other source values.
1 parent a2283dd commit b5b0c75

29 files changed

+98
-100
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7985,8 +7985,6 @@ ERROR(lifetime_dependence_cannot_infer_ambiguous_candidate, none,
79857985
"expected nil or self as return values in an initializer with "
79867986
"lifetime dependent specifiers",
79877987
())
7988-
ERROR(lifetime_dependence_on_bitwise_copyable, none,
7989-
"invalid lifetime dependence on BitwiseCopyable type", ())
79907988
ERROR(lifetime_dependence_cannot_be_applied_to_tuple_elt, none,
79917989
"lifetime dependence specifiers cannot be applied to tuple elements", ())
79927990
ERROR(lifetime_dependence_method_escapable_bitwisecopyable_self, none,

lib/AST/LifetimeDependence.cpp

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -207,24 +207,16 @@ LifetimeDependenceInfo::fromTypeRepr(AbstractFunctionDecl *afd) {
207207
Type paramType,
208208
ValueOwnership ownership) {
209209
auto loc = specifier.getLoc();
210-
211-
// Diagnose when we have lifetime dependence on a type that is
212-
// BitwiseCopyable & Escapable.
213-
// ~Escapable types are non-trivial in SIL and we should not raise this
214-
// error.
215-
// TODO: Diagnose ~Escapable types are always non-trivial in SIL.
216-
if (paramType->isEscapable()) {
217-
if (isBitwiseCopyable(paramType, mod, ctx)) {
218-
diags.diagnose(loc, diag::lifetime_dependence_on_bitwise_copyable);
219-
return true;
220-
}
221-
}
222-
223210
auto parsedLifetimeKind = specifier.getParsedLifetimeDependenceKind();
224211
auto lifetimeKind =
225212
getLifetimeDependenceKindFromDecl(parsedLifetimeKind, paramType);
226-
bool isCompatible = isLifetimeDependenceCompatibleWithOwnership(
213+
bool isCompatible = true;
214+
// Lifetime dependence always propagates through temporary BitwiseCopyable
215+
// values, even if the dependence is scoped.
216+
if (!isBitwiseCopyable(paramType, mod, ctx)) {
217+
isCompatible = isLifetimeDependenceCompatibleWithOwnership(
227218
lifetimeKind, ownership, afd);
219+
}
228220
if (parsedLifetimeKind == ParsedLifetimeDependenceKind::Scope &&
229221
!isCompatible) {
230222
diags.diagnose(

test/ModuleInterface/Inputs/lifetime_dependence.swift

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
public struct AnotherView : ~Escapable {
22
@usableFromInline let _ptr: UnsafeRawBufferPointer
33
@usableFromInline let _count: Int
4-
@_unsafeNonescapableResult
5-
internal init(_ ptr: UnsafeRawBufferPointer, _ count: Int) {
4+
internal init(_ ptr: UnsafeRawBufferPointer, _ count: Int) -> dependsOn(ptr) Self {
65
self._ptr = ptr
76
self._count = count
87
}
@@ -11,9 +10,8 @@ public struct AnotherView : ~Escapable {
1110
public struct BufferView : ~Escapable {
1211
@usableFromInline let _ptr: UnsafeRawBufferPointer
1312
@usableFromInline let _count: Int
14-
@_unsafeNonescapableResult
1513
@usableFromInline
16-
internal init(_ ptr: UnsafeRawBufferPointer, _ count: Int) {
14+
internal init(_ ptr: UnsafeRawBufferPointer, _ count: Int) -> dependsOn(ptr) Self {
1715
self._ptr = ptr
1816
self._count = count
1917
}

test/Parse/explicit_lifetime_dependence_specifiers.swift

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ import Builtin
55

66
struct BufferView : ~Escapable {
77
let ptr: UnsafeRawBufferPointer
8-
@_unsafeNonescapableResult
9-
init(_ ptr: UnsafeRawBufferPointer) {
8+
init(_ ptr: UnsafeRawBufferPointer) -> dependsOn(ptr) Self {
109
self.ptr = ptr
1110
}
11+
// TODO: -> dependsOn(ptr) Self
1212
@_unsafeNonescapableResult
1313
init?(_ ptr: UnsafeRawBufferPointer, _ i: Int) {
1414
if (i % 2 == 0) {
@@ -45,8 +45,7 @@ struct BufferView : ~Escapable {
4545

4646
struct MutableBufferView : ~Escapable, ~Copyable {
4747
let ptr: UnsafeMutableRawBufferPointer
48-
@_unsafeNonescapableResult
49-
init(_ ptr: UnsafeMutableRawBufferPointer) {
48+
init(_ ptr: UnsafeMutableRawBufferPointer) -> dependsOn(ptr) Self {
5049
self.ptr = ptr
5150
}
5251
}

test/SIL/Parser/lifetime_dependence.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import Swift
88

99
struct BufferView : ~Escapable {
1010
@_hasStorage let ptr: UnsafeRawBufferPointer { get }
11-
@_unsafeNonescapableResult @inlinable init(_ ptr: UnsafeRawBufferPointer)
11+
@inlinable init(_ ptr: UnsafeRawBufferPointer) -> _scope(ptr) Self
1212
init(_ ptr: UnsafeRawBufferPointer, _ a: borrowing Array<Int>) -> _scope(a) Self
1313
}
1414

test/SIL/explicit_lifetime_dependence_specifiers.swift

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,21 @@ import Builtin
88

99
struct BufferView : ~Escapable {
1010
let ptr: UnsafeRawBufferPointer
11-
@_unsafeNonescapableResult
12-
init(_ ptr: UnsafeRawBufferPointer) {
11+
init(_ ptr: UnsafeRawBufferPointer) -> dependsOn(ptr) Self {
1312
self.ptr = ptr
1413
}
14+
// TODO: -> dependsOn(ptr) Self
1515
@_unsafeNonescapableResult
1616
init?(_ ptr: UnsafeRawBufferPointer, _ i: Int) {
1717
if (i % 2 == 0) {
1818
return nil
1919
}
2020
self.ptr = ptr
2121
}
22+
@_unsafeNonescapableResult
23+
init(independent ptr: UnsafeRawBufferPointer) {
24+
self.ptr = ptr
25+
}
2226
// CHECK: sil hidden @$s39explicit_lifetime_dependence_specifiers10BufferViewVyACSW_SaySiGhYlstcfC : $@convention(method) (UnsafeRawBufferPointer, @guaranteed Array<Int>, @thin BufferView.Type) -> _scope(1) @owned BufferView {
2327
init(_ ptr: UnsafeRawBufferPointer, _ a: borrowing Array<Int>) -> dependsOn(a) Self {
2428
self.ptr = ptr
@@ -38,8 +42,7 @@ struct BufferView : ~Escapable {
3842

3943
struct MutableBufferView : ~Escapable, ~Copyable {
4044
let ptr: UnsafeMutableRawBufferPointer
41-
@_unsafeNonescapableResult
42-
init(_ ptr: UnsafeMutableRawBufferPointer) {
45+
init(_ ptr: UnsafeMutableRawBufferPointer) -> dependsOn(ptr) Self {
4346
self.ptr = ptr
4447
}
4548
}
@@ -57,28 +60,28 @@ func testBasic() {
5760

5861
// CHECK: sil hidden @$s39explicit_lifetime_dependence_specifiers6deriveyAA10BufferViewVADYlsF : $@convention(thin) (@guaranteed BufferView) -> _scope(0) @owned BufferView {
5962
func derive(_ x: borrowing BufferView) -> dependsOn(scoped x) BufferView {
60-
return BufferView(x.ptr)
63+
return BufferView(independent: x.ptr)
6164
}
6265

6366
// CHECK: sil hidden @$s39explicit_lifetime_dependence_specifiers16consumeAndCreateyAA10BufferViewVADnYliF : $@convention(thin) (@owned BufferView) -> _inherit(0) @owned BufferView {
6467
func consumeAndCreate(_ x: consuming BufferView) -> dependsOn(x) BufferView {
65-
return BufferView(x.ptr)
68+
return BufferView(independent: x.ptr)
6669
}
6770

6871
// CHECK: sil hidden @$s39explicit_lifetime_dependence_specifiers17deriveThisOrThat1yAA10BufferViewVADYls_ADYlstF : $@convention(thin) (@guaranteed BufferView, @guaranteed BufferView) -> _scope(0, 1) @owned BufferView {
6972
func deriveThisOrThat1(_ this: borrowing BufferView, _ that: borrowing BufferView) -> dependsOn(scoped this, that) BufferView {
7073
if (Int.random(in: 1..<100) == 0) {
71-
return BufferView(this.ptr)
74+
return BufferView(independent: this.ptr)
7275
}
73-
return BufferView(that.ptr)
76+
return BufferView(independent: that.ptr)
7477
}
7578

7679
// CHECK: sil hidden @$s39explicit_lifetime_dependence_specifiers17deriveThisOrThat2yAA10BufferViewVADYls_ADnYlitF : $@convention(thin) (@guaranteed BufferView, @owned BufferView) -> _inherit(1) _scope(0) @owned BufferView {
7780
func deriveThisOrThat2(_ this: borrowing BufferView, _ that: consuming BufferView) -> dependsOn(scoped this) dependsOn(that) BufferView {
7881
if (Int.random(in: 1..<100) == 0) {
79-
return BufferView(this.ptr)
82+
return BufferView(independent: this.ptr)
8083
}
81-
return BufferView(that.ptr)
84+
return BufferView(independent: that.ptr)
8285
}
8386

8487
func use(_ x: borrowing BufferView) {}
@@ -101,19 +104,18 @@ struct Wrapper : ~Escapable {
101104

102105
struct Container : ~Escapable {
103106
let ptr: UnsafeRawBufferPointer
104-
@_unsafeNonescapableResult
105-
init(_ ptr: UnsafeRawBufferPointer) {
107+
init(_ ptr: UnsafeRawBufferPointer) -> dependsOn(ptr) Self {
106108
self.ptr = ptr
107109
}
108110
}
109111

110112
// CHECK-LABEL: sil hidden @$s39explicit_lifetime_dependence_specifiers16getConsumingViewyAA06BufferG0VAA9ContainerVnYliF : $@convention(thin) (@owned Container) -> _inherit(0) @owned BufferView {
111113
func getConsumingView(_ x: consuming Container) -> dependsOn(x) BufferView {
112-
return BufferView(x.ptr)
114+
return BufferView(independent: x.ptr)
113115
}
114116

115117
// CHECK-LABEL: sil hidden @$s39explicit_lifetime_dependence_specifiers16getBorrowingViewyAA06BufferG0VAA9ContainerVYlsF : $@convention(thin) (@guaranteed Container) -> _scope(0) @owned BufferView {
116118
func getBorrowingView(_ x: borrowing Container) -> dependsOn(scoped x) BufferView {
117-
return BufferView(x.ptr)
119+
return BufferView(independent: x.ptr)
118120
}
119121

test/SIL/implicit_lifetime_dependence.swift

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,12 @@
66
struct BufferView : ~Escapable {
77
let ptr: UnsafeRawBufferPointer
88
let c: Int
9+
init(_ ptr: UnsafeRawBufferPointer, _ c: Int) -> dependsOn(ptr) Self {
10+
self.ptr = ptr
11+
self.c = c
12+
}
913
@_unsafeNonescapableResult
10-
init(_ ptr: UnsafeRawBufferPointer, _ c: Int) {
14+
init(independent ptr: UnsafeRawBufferPointer, _ c: Int) {
1115
self.ptr = ptr
1216
self.c = c
1317
}
@@ -31,8 +35,7 @@ struct BufferView : ~Escapable {
3135
struct MutableBufferView : ~Escapable, ~Copyable {
3236
let ptr: UnsafeMutableRawBufferPointer
3337
let c: Int
34-
@_unsafeNonescapableResult
35-
init(_ ptr: UnsafeMutableRawBufferPointer, _ c: Int) {
38+
init(_ ptr: UnsafeMutableRawBufferPointer, _ c: Int) -> dependsOn(ptr) Self {
3639
self.ptr = ptr
3740
self.c = c
3841
}
@@ -54,12 +57,12 @@ func derive(_ x: borrowing BufferView) -> BufferView {
5457
}
5558

5659
func derive(_ unused: Int, _ x: borrowing BufferView) -> BufferView {
57-
return BufferView(x.ptr, x.c)
60+
return BufferView(independent: x.ptr, x.c)
5861
}
5962

6063
// CHECK: sil hidden @$s28implicit_lifetime_dependence16consumeAndCreateyAA10BufferViewVADnYliF : $@convention(thin) (@owned BufferView) -> _inherit(0) @owned BufferView {
6164
func consumeAndCreate(_ x: consuming BufferView) -> BufferView {
62-
return BufferView(x.ptr, x.c)
65+
return BufferView(independent: x.ptr, x.c)
6366
}
6467

6568
func use(_ x: borrowing BufferView) {}
@@ -135,8 +138,7 @@ struct GenericBufferView<Element> : ~Escapable {
135138
count: count)
136139
}
137140
// unsafe private API
138-
@_unsafeNonescapableResult
139-
init(baseAddress: Pointer, count: Int) {
141+
init(baseAddress: Pointer, count: Int) -> dependsOn(baseAddress) Self {
140142
precondition(count >= 0, "Count must not be negative")
141143
self.baseAddress = baseAddress
142144
self.count = count

test/SIL/lifetime_dependence_generics.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ extension P {
1515
}
1616

1717
public struct View: ~Escapable {
18+
// TODO: dependsOn(immortal)
1819
@_unsafeNonescapableResult
1920
init() { }
2021
}

test/SIL/type_lowering_unit.sil

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ extension GSNCInt: Copyable where T: Copyable {}
248248

249249
struct GSNEInt<T: ~Escapable>: ~Escapable {
250250
var x: Int
251+
// TODO: dependsOn(immortal)
251252
@_unsafeNonescapableResult
252253
init() { x = 0 }
253254
}

test/SILOptimizer/lifetime_dependence_borrow.swift

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ struct BV : ~Escapable {
2121

2222
public var isEmpty: Bool { i == 0 }
2323

24-
@_unsafeNonescapableResult
25-
init(_ p: UnsafeRawPointer, _ i: Int) {
24+
init(_ p: UnsafeRawPointer, _ i: Int) -> dependsOn(p) Self {
2625
self.p = p
2726
self.i = i
2827
}
@@ -38,8 +37,7 @@ struct MBV : ~Escapable, ~Copyable {
3837
let p: UnsafeRawPointer
3938
let i: Int
4039

41-
@_unsafeNonescapableResult
42-
init(_ p: UnsafeRawPointer, _ i: Int) {
40+
init(_ p: UnsafeRawPointer, _ i: Int) -> dependsOn(p) Self {
4341
self.p = p
4442
self.i = i
4543
}

test/SILOptimizer/lifetime_dependence_borrow_fail.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ struct BV : ~Escapable {
1313
let i: Int
1414

1515
@_unsafeNonescapableResult
16-
init(_ p: UnsafeRawPointer, _ i: Int) {
16+
init(_ p: UnsafeRawPointer, _ i: Int) -> dependsOn(p) Self {
1717
self.p = p
1818
self.i = i
1919
}
@@ -23,7 +23,6 @@ struct NC : ~Copyable {
2323
let p: UnsafeRawPointer
2424
let i: Int
2525

26-
@_unsafeNonescapableResult
2726
init(_ p: UnsafeRawPointer, _ i: Int) {
2827
self.p = p
2928
self.i = i
@@ -38,7 +37,7 @@ struct NE : ~Escapable {
3837
let i: Int
3938

4039
@_unsafeNonescapableResult
41-
init(_ p: UnsafeRawPointer, _ i: Int) {
40+
init(_ p: UnsafeRawPointer, _ i: Int) -> dependsOn(p) Self {
4241
self.p = p
4342
self.i = i
4443
}

test/SILOptimizer/lifetime_dependence_diagnostics.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@
1010
struct BV : ~Escapable {
1111
let p: UnsafeRawPointer
1212
let c: Int
13-
@_unsafeNonescapableResult
14-
init(_ p: UnsafeRawPointer, _ c: Int) {
13+
init(_ p: UnsafeRawPointer, _ c: Int) -> dependsOn(p) Self {
1514
self.p = p
1615
self.c = c
1716
}

test/SILOptimizer/lifetime_dependence_inherit.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,21 @@ struct BV : ~Escapable {
1212
let p: UnsafeRawPointer
1313
let i: Int
1414

15+
init(_ p: UnsafeRawPointer, _ i: Int) -> dependsOn(p) Self {
16+
self.p = p
17+
self.i = i
18+
}
19+
1520
@_unsafeNonescapableResult
16-
init(_ p: UnsafeRawPointer, _ i: Int) {
21+
init(independent p: UnsafeRawPointer, _ i: Int) {
1722
self.p = p
1823
self.i = i
1924
}
2025

2126
consuming func derive() -> dependsOn(self) BV {
2227
// Technically, this "new" view does not depend on the 'view' argument.
2328
// This unsafely creates a new view with no dependence.
24-
return BV(self.p, self.i)
29+
return BV(independent: self.p, self.i)
2530
}
2631
}
2732

test/SILOptimizer/lifetime_dependence_inherit_fail.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,21 @@ struct BV : ~Escapable {
1212
let p: UnsafeRawPointer
1313
let i: Int
1414

15+
init(_ p: UnsafeRawPointer, _ i: Int) -> dependsOn(p) Self {
16+
self.p = p
17+
self.i = i
18+
}
19+
1520
@_unsafeNonescapableResult
16-
init(_ p: UnsafeRawPointer, _ i: Int) {
21+
init(independent p: UnsafeRawPointer, _ i: Int) {
1722
self.p = p
1823
self.i = i
1924
}
2025

2126
consuming func derive() -> dependsOn(self) BV {
2227
// Technically, this "new" view does not depend on the 'view' argument.
2328
// This unsafely creates a new view with no dependence.
24-
return BV(self.p, self.i)
29+
return BV(independent: self.p, self.i)
2530
}
2631
}
2732

test/SILOptimizer/lifetime_dependence_insertion.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@ struct BV : ~Escapable {
1212
let p: UnsafeRawPointer
1313
let i: Int
1414

15-
@_unsafeNonescapableResult
16-
init(_ p: UnsafeRawPointer, _ i: Int) {
15+
init(_ p: UnsafeRawPointer, _ i: Int) -> dependsOn(p) Self {
1716
self.p = p
1817
self.i = i
1918
}

test/SILOptimizer/lifetime_dependence_mutate.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@ struct MBV : ~Escapable, ~Copyable {
1212
let p: UnsafeMutableRawPointer
1313
let c: Int
1414

15-
@_unsafeNonescapableResult
16-
init(_ p: UnsafeMutableRawPointer, _ c: Int) {
15+
init(_ p: UnsafeMutableRawPointer, _ c: Int) -> dependsOn(p) Self {
1716
self.p = p
1817
self.c = c
1918
}

test/SILOptimizer/lifetime_dependence_optional.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
// Simply test that it is possible for a module to define a pseudo-Optional type without triggering any compiler errors.
1111

1212
public protocol ExpressibleByNilLiteral: ~Copyable & ~Escapable {
13+
// TODO: dependsOn(immortal)
1314
@_unsafeNonescapableResult
1415
init(nilLiteral: ())
1516
}
@@ -29,6 +30,7 @@ extension Nillable: Sendable where Wrapped: ~Copyable & ~Escapable & Sendable {
2930
extension Nillable: BitwiseCopyable where Wrapped: BitwiseCopyable { }
3031

3132
extension Nillable: ExpressibleByNilLiteral where Wrapped: ~Copyable & ~Escapable {
33+
// TODO: dependsOn(immortal)
3234
@_transparent
3335
@_unsafeNonescapableResult
3436
public init(nilLiteral: ()) {

0 commit comments

Comments
 (0)