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
}

0 commit comments

Comments
 (0)