Files
swift-mirror/test/SIL/implicit_lifetime_dependence.swift
Andrew Trick b5b0c75ccd 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.
2024-05-22 17:10:56 -07:00

172 lines
6.6 KiB
Swift

// RUN: %target-swift-frontend %s \
// RUN: -emit-sil -disable-availability-checking \
// RUN: -enable-experimental-feature NonescapableTypes | %FileCheck %s
// REQUIRES: asserts
struct BufferView : ~Escapable {
let ptr: UnsafeRawBufferPointer
let c: Int
init(_ ptr: UnsafeRawBufferPointer, _ c: Int) -> dependsOn(ptr) Self {
self.ptr = ptr
self.c = c
}
@_unsafeNonescapableResult
init(independent ptr: UnsafeRawBufferPointer, _ c: Int) {
self.ptr = ptr
self.c = c
}
// CHECK: sil hidden @$s28implicit_lifetime_dependence10BufferViewVyA2ChYlicfC : $@convention(method) (@guaranteed BufferView, @thin BufferView.Type) -> _inherit(0) @owned BufferView {
init(_ otherBV: borrowing BufferView) {
self.ptr = otherBV.ptr
self.c = otherBV.c
}
// CHECK: sil hidden @$s28implicit_lifetime_dependence10BufferViewVyA2CYlicfC : $@convention(method) (@owned BufferView, @thin BufferView.Type) -> _inherit(0) @owned BufferView {
init(_ otherBV: consuming BufferView) {
self.ptr = otherBV.ptr
self.c = otherBV.c
}
// CHECK: sil hidden @$s28implicit_lifetime_dependence10BufferViewVyACSW_SaySiGhYlstcfC : $@convention(method) (UnsafeRawBufferPointer, @guaranteed Array<Int>, @thin BufferView.Type) -> _scope(1) @owned BufferView {
init(_ ptr: UnsafeRawBufferPointer, _ a: borrowing Array<Int>) {
self.ptr = ptr
self.c = a.count
}
}
struct MutableBufferView : ~Escapable, ~Copyable {
let ptr: UnsafeMutableRawBufferPointer
let c: Int
init(_ ptr: UnsafeMutableRawBufferPointer, _ c: Int) -> dependsOn(ptr) Self {
self.ptr = ptr
self.c = c
}
}
func testBasic() {
let a = [Int](repeating: 0, count: 4)
a.withUnsafeBytes {
let view = BufferView($0, a.count)
let derivedView = derive(view)
let newView = consumeAndCreate(derivedView)
use(newView)
}
}
// CHECK: sil hidden @$s28implicit_lifetime_dependence6deriveyAA10BufferViewVADYliF : $@convention(thin) (@guaranteed BufferView) -> _inherit(0) @owned BufferView {
func derive(_ x: borrowing BufferView) -> BufferView {
return BufferView(x.ptr, x.c)
}
func derive(_ unused: Int, _ x: borrowing BufferView) -> BufferView {
return BufferView(independent: x.ptr, x.c)
}
// CHECK: sil hidden @$s28implicit_lifetime_dependence16consumeAndCreateyAA10BufferViewVADnYliF : $@convention(thin) (@owned BufferView) -> _inherit(0) @owned BufferView {
func consumeAndCreate(_ x: consuming BufferView) -> BufferView {
return BufferView(independent: x.ptr, x.c)
}
func use(_ x: borrowing BufferView) {}
struct Wrapper : ~Escapable {
var _view: BufferView
var view: BufferView {
// CHECK: sil hidden @$s28implicit_lifetime_dependence7WrapperV4viewAA10BufferViewVvr : $@yield_once @convention(method) (@guaranteed Wrapper) -> _inherit(0) @yields @guaranteed BufferView {
_read {
yield _view
}
// CHECK: sil hidden @$s28implicit_lifetime_dependence7WrapperV4viewAA10BufferViewVvM : $@yield_once @convention(method) (@inout Wrapper) -> _inherit(0) @yields @inout BufferView {
_modify {
yield &_view
}
}
// CHECK: sil hidden @$s28implicit_lifetime_dependence7WrapperVyAcA10BufferViewVYlicfC : $@convention(method) (@owned BufferView, @thin Wrapper.Type) -> _inherit(0) @owned Wrapper {
init(_ view: consuming BufferView) {
self._view = view
}
// TODO: Investigate why it was mangled as Yli and not YLi before
// CHECK: sil hidden @$s28implicit_lifetime_dependence7WrapperV8getView1AA10BufferViewVyKYLiF : $@convention(method) (@guaranteed Wrapper) -> _inherit(0) (@owned BufferView, @error any Error) {
borrowing func getView1() throws -> BufferView {
return _view
}
// CHECK: sil hidden @$s28implicit_lifetime_dependence7WrapperV8getView2AA10BufferViewVyYaKYLiF : $@convention(method) @async (@owned Wrapper) -> _inherit(0) (@owned BufferView, @error any Error) {
consuming func getView2() async throws -> BufferView {
return _view
}
}
struct Container1 : ~Copyable {
let ptr: UnsafeRawBufferPointer
let c: Int
// CHECK: sil hidden @$s28implicit_lifetime_dependence10Container1V4viewAA10BufferViewVvg : $@convention(method) (@guaranteed Container1) -> _scope(0) @owned BufferView {
var view: BufferView {
get {
return BufferView(ptr, c)
}
}
}
struct Container2 : ~Copyable {
let ptr: UnsafeMutableRawBufferPointer
let c: Int
// CHECK: sil hidden @$s28implicit_lifetime_dependence10Container2V11mutableViewAA013MutableBufferF0Vvr : $@yield_once @convention(method) (@guaranteed Container2) -> _scope(0) @yields @guaranteed MutableBufferView {
var mutableView: MutableBufferView {
_read {
yield MutableBufferView(ptr, c)
}
}
}
struct FakeRange<Bound> {
public let lowerBound: Bound
public let upperBound: Bound
}
struct GenericBufferView<Element> : ~Escapable {
typealias Index = Int
typealias Pointer = UnsafeRawPointer
let baseAddress: Pointer
let count: Int
// CHECK: sil hidden @$s28implicit_lifetime_dependence17GenericBufferViewV11baseAddress5count9dependsOnACyxGSV_Siqd__hYlstclufC : $@convention(method) <Element><Storage> (UnsafeRawPointer, Int, @in_guaranteed Storage, @thin GenericBufferView<Element>.Type) -> _scope(2) @owned GenericBufferView<Element> {
init<Storage>(baseAddress: Pointer,
count: Int,
dependsOn: borrowing Storage) {
self = GenericBufferView<Element>(baseAddress: baseAddress,
count: count)
}
// unsafe private API
init(baseAddress: Pointer, count: Int) -> dependsOn(baseAddress) Self {
precondition(count >= 0, "Count must not be negative")
self.baseAddress = baseAddress
self.count = count
}
subscript(position: Pointer) -> Element {
get {
if _isPOD(Element.self) {
return position.loadUnaligned(as: Element.self)
}
else {
return position.load(as: Element.self)
}
}
}
// CHECK: sil hidden @$s28implicit_lifetime_dependence17GenericBufferViewVyACyxGAA9FakeRangeVySVGcig : $@convention(method) <Element> (FakeRange<UnsafeRawPointer>, @guaranteed GenericBufferView<Element>) -> _inherit(1) @owned GenericBufferView<Element> {
subscript(bounds: FakeRange<Pointer>) -> Self {
get {
GenericBufferView(
baseAddress: UnsafeRawPointer(bounds.lowerBound),
count: bounds.upperBound.distance(to:bounds.lowerBound) / MemoryLayout<Element>.stride
)
}
}
}
// CHECK: sil hidden @$s28implicit_lifetime_dependence23tupleLifetimeDependenceyAA10BufferViewV_ADtADYliF : $@convention(thin) (@guaranteed BufferView) -> _inherit(0) (@owned BufferView, @owned BufferView) {
func tupleLifetimeDependence(_ x: borrowing BufferView) -> (BufferView, BufferView) {
return (BufferView(x.ptr, x.c), BufferView(x.ptr, x.c))
}