Skip to content

Commit 9b055ae

Browse files
committed
Add unit tests for lifetime dependence semantics.
1 parent f41dd11 commit 9b055ae

File tree

1 file changed

+155
-0
lines changed

1 file changed

+155
-0
lines changed
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
// RUN: %target-swift-frontend %s -emit-sil \
2+
// RUN: -o /dev/null \
3+
// RUN: -verify \
4+
// RUN: -sil-verify-all \
5+
// RUN: -module-name test \
6+
// RUN: -enable-experimental-feature NonescapableTypes
7+
8+
// REQUIRES: asserts
9+
// REQUIRES: swift_in_compiler
10+
11+
// Lifetime dependence semantics by example.
12+
13+
struct Span<T>: ~Escapable {
14+
private var base: UnsafePointer<T>
15+
private var count: Int
16+
17+
init(base: UnsafePointer<T>, count: Int) -> dependsOn(base) Self {
18+
self.base = base
19+
self.count = count
20+
}
21+
22+
init<S>(base: UnsafePointer<T>, count: Int, generic: borrowing S) -> dependsOn(generic) Self {
23+
self.base = base
24+
self.count = count
25+
}
26+
}
27+
28+
extension Span {
29+
consuming func dropFirst() -> Span<T> {
30+
let local = Span(base: self.base + 1, count: self.count - 1)
31+
return unsafeLifetime(dependent: local, dependsOn: self)
32+
}
33+
}
34+
35+
// TODO: Remove @_unsafeNonescapableResult. Instead, the unsafe dependence should be expressed by a builtin that is
36+
// hidden within the function body.
37+
@_unsafeNonescapableResult
38+
func unsafeLifetime<T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable>(
39+
dependent: consuming T, dependsOn source: borrowing U)
40+
-> dependsOn(source) T {
41+
dependent
42+
}
43+
44+
// TODO: Remove @_unsafeNonescapableResult. Instead, the unsafe dependence should be expressed by a builtin that is
45+
// hidden within the function body.
46+
@_unsafeNonescapableResult
47+
func unsafeLifetime<T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable>(
48+
dependent: consuming T, scope source: borrowing U)
49+
-> dependsOn(scoped source) T {
50+
dependent
51+
}
52+
53+
extension Span {
54+
mutating func droppingPrefix(length: Int) -> /* dependsOn(self) */ Span<T> {
55+
let oldBase = base
56+
let result = Span(base: oldBase, count: length)
57+
self.base += length
58+
self.count -= length
59+
return unsafeLifetime(dependent: result, dependsOn: self)
60+
}
61+
}
62+
63+
extension Array {
64+
// TODO: comment out dependsOn(scoped)
65+
borrowing func span() -> /* dependsOn(scoped self) */ Span<Element> {
66+
/* not the real implementation */
67+
let p = self.withUnsafeBufferPointer { $0.baseAddress! }
68+
return Span(base: p, count: 1)
69+
}
70+
}
71+
72+
func parse(_ span: Span<Int>) {}
73+
74+
// =============================================================================
75+
// Scoped dependence on values
76+
// =============================================================================
77+
78+
// The duration of a scoped dependence is the lexical scope of the variable.
79+
func testScopedLet(_ arg: [Int] ) {
80+
let a: Array<Int> = arg
81+
let span: Span<Int> // expected-error {{lifetime-dependent variable 'span' escapes its scope}}
82+
do {
83+
let a2 = a // expected-note {{it depends on the lifetime of variable 'a2'}}
84+
span = a2.span()
85+
}
86+
parse(span) // expected-note {{this use of the lifetime-dependent value is out of scope}}
87+
}
88+
89+
func testScopedCopy(_ arg: [Int] ) {
90+
let a: Array<Int> = arg
91+
let span: Span<Int> // expected-error {{lifetime-dependent variable 'span' escapes its scope}}
92+
do {
93+
let a2 = a // expected-note {{it depends on the lifetime of variable 'a2'}}
94+
span = a2.span()
95+
}
96+
parse(span) // expected-note {{this use of the lifetime-dependent value is out of scope}}
97+
}
98+
99+
// =============================================================================
100+
// Inherited dependence on values
101+
// =============================================================================
102+
103+
func copySpan<T>(_ arg: Span<T>) -> /* dependsOn(arg) */ Span<T> { arg }
104+
105+
func testInheritedCopy(_ arg: [Int] ) {
106+
let a: Array<Int> = arg
107+
let result: Span<Int>
108+
do {
109+
let span = a.span()
110+
let temp = span
111+
result = copySpan(temp)
112+
}
113+
parse(result) // ✅ Safe: within lifetime of 'a'
114+
}
115+
116+
func testInheritedCopyVar(_ arg: [Int] ) {
117+
let a1: Array<Int> = arg
118+
let a2: Array<Int> = arg
119+
var span = a1.span()
120+
var result: Span<Int>
121+
do {
122+
var temp = span
123+
result = copySpan(temp)
124+
span = a2.span()
125+
temp = a2.span()
126+
// 'result' still depends on 'a1', not 'a2'
127+
}
128+
parse(result) // ✅ Safe: within lifetime of 'a'
129+
}
130+
131+
// =============================================================================
132+
// Scoped dependence on inherited dependence
133+
// =============================================================================
134+
135+
func reborrowSpan<T>(_ arg: Span<T>) -> dependsOn(scoped arg) Span<T> { arg }
136+
137+
func testScopedOfInheritedWithCall(_ arg: [Int] ) {
138+
let a: Array<Int> = arg
139+
let span = a.span()
140+
// TODO: should be // ✅ Safe: 'copySpan' result should be borrowed over `parse`
141+
// rdar://128821299 ([nonescaping] extend borrowed arguments that are the source of a scoped dependence)
142+
parse(reborrowSpan(copySpan(span))) // expected-error {{lifetime-dependent value escapes its scope}}
143+
// expected-note @-1{{this use of the lifetime-dependent value is out of scope}}
144+
// expected-note @-2{{it depends on the lifetime of this parent value}}
145+
}
146+
147+
func testScopedOfInheritedWithLet(_ arg: [Int] ) {
148+
let a: Array<Int> = arg
149+
let span = a.span()
150+
// TODO: should be // ✅ Safe: 'copySpan' result should be borrowed over `result`
151+
// rdar://128821299 ([nonescaping] extend borrowed arguments that are the source of a scoped dependence)
152+
let result = reborrowSpan(copySpan(span)) // expected-error {{lifetime-dependent variable 'result' escapes its scope}}
153+
// expected-note @-1{{it depends on the lifetime of this parent value}}
154+
_ = result
155+
} // expected-note {{this use of the lifetime-dependent value is out of scope}}

0 commit comments

Comments
 (0)