File: invalid_escaping_captures.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (276 lines) | stat: -rw-r--r-- 10,155 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
// RUN: %target-swift-frontend -emit-sil %s -verify

func takesEscaping(_: @escaping () -> ()) {}

func takesNonEscaping(_ fn: () -> ()) { fn() }

func badClosureCaptureInOut1(x: inout Int) { // expected-note {{parameter 'x' is declared 'inout'}}
  takesEscaping { // expected-error {{escaping closure captures 'inout' parameter 'x'}}
    x += 1 // expected-note {{captured here}}
  }
}

func badClosureCaptureInOut2(x: inout Int, b: Bool) { // expected-note 2{{parameter 'x' is declared 'inout'}}
  takesEscaping(b ? { // expected-error {{escaping closure captures 'inout' parameter 'x'}}
                  x += 1 // expected-note {{captured here}}
                } : { // expected-error {{escaping closure captures 'inout' parameter 'x'}}
                  x -= 1 // expected-note {{captured here}}
                })
}

func badClosureCaptureNoEscape1(y: () -> ()) { // expected-note {{parameter 'y' is implicitly non-escaping}}
  takesEscaping { // expected-error {{escaping closure captures non-escaping parameter 'y'}}
    y() // expected-note {{captured here}}
  }
}

func badClosureCaptureNoEscape2(y: () -> (), b: Bool) { // expected-note 2{{parameter 'y' is implicitly non-escaping}}
  takesEscaping(b ? { // expected-error {{escaping closure captures non-escaping parameter 'y'}}
                  y() // expected-note {{captured here}}
                } : { // expected-error {{escaping closure captures non-escaping parameter 'y'}}
                  y() // expected-note {{captured here}}
                })
}

func badClosureCaptureNoEscape3(y: () -> ()) {
  let yy = (y, y)

  takesEscaping { // expected-error {{escaping closure captures non-escaping value}}
    yy.0() // expected-note {{captured here}}
  }
}

func badClosureCaptureNoEscape4(y: () -> (), z: () -> (), b: Bool) {
  let x = b ? y : z

  takesEscaping { // expected-error {{escaping closure captures non-escaping value}}
    x() // expected-note {{captured here}}
  }
}

func badLocalFunctionCaptureInOut1(x: inout Int) { // expected-note {{parameter 'x' is declared 'inout'}}
  func local() {
    x += 1 // expected-note {{captured here}}
  }

  takesEscaping(local) // expected-error {{escaping local function captures 'inout' parameter 'x'}}
}

func badLocalFunctionCaptureInOut2(x: inout Int) { // expected-note {{parameter 'x' is declared 'inout'}}
  func local() {
    x += 1 // expected-note {{captured here}}
  }

  takesEscaping { // expected-error {{escaping closure captures 'inout' parameter 'x'}}
    local() // expected-note {{captured indirectly by this call}}
  }
}

func badLocalFunctionCaptureInOut3(x: inout Int) { // expected-note {{parameter 'x' is declared 'inout'}}
  func local1() {
    x += 1 // expected-note {{captured here}}
  }

  func local2() {
    local1() // expected-note {{captured indirectly by this call}}
  }

  takesEscaping(local2) // expected-error {{escaping local function captures 'inout' parameter 'x'}}
}

func badLocalFunctionCaptureNoEscape1(y: () -> ()) { // expected-note {{parameter 'y' is implicitly non-escaping}}
  func local() {
    y() // expected-note {{captured here}}
  }

  takesEscaping(local) // expected-error {{escaping local function captures non-escaping parameter 'y'}}
}

func badLocalFunctionCaptureNoEscape2(y: () -> ()) { // expected-note {{parameter 'y' is implicitly non-escaping}}
  func local() {
    y() // expected-note {{captured here}}
  }

  takesEscaping { // expected-error {{escaping closure captures non-escaping parameter 'y'}}
    local() // expected-note {{captured indirectly by this call}}
  }
}

func badLocalFunctionCaptureNoEscape3(y: () -> ()) { // expected-note {{parameter 'y' is implicitly non-escaping}}
  func local1() {
    y() // expected-note {{captured here}}
  }

  func local2() {
    local1() // expected-note {{captured indirectly by this call}}
  }

  takesEscaping(local2) // expected-error {{escaping local function captures non-escaping parameter 'y'}}
}

func badLocalFunctionCaptureNoEscape4(y: () -> ()) { // expected-note {{parameter 'y' is implicitly non-escaping}}
  func local1() {
    takesNonEscaping(y) // expected-note {{captured here}}
  }

  func local2() {
    local1() // expected-note {{captured indirectly by this call}}
  }

  takesEscaping(local2) // expected-error {{escaping local function captures non-escaping parameter 'y'}}
}

// Capturing 'self' produces a different diagnostic.
struct SelfCapture {
  var a: Int
  mutating func badLocalFunctionCaptureInOut() {
    // FIXME: The 'captured here' location chosen here is not ideal, because
    // the original closure is formed in a closure that is nested inside the
    // local function. That's a funny edge case that trips up the heuristics.
    func _foo() {
      a += 1
      takesEscaping { // expected-error {{escaping closure captures mutating 'self' parameter}}
        _foo() // expected-note {{captured here}}
      }
    }
  }
}

// Make sure reabstraction thunks don't cause problems.
func takesEscapingGeneric<T>(_: @escaping () -> T) {}

func testGenericClosureReabstraction(x: inout Int) { // expected-note {{parameter 'x' is declared 'inout'}}
  takesEscapingGeneric { () -> Int in // expected-error {{escaping closure captures 'inout' parameter 'x'}}
    x += 1 // expected-note {{captured here}}
    return 0
  }
}

func testGenericLocalFunctionReabstraction(x: inout Int) { // expected-note {{parameter 'x' is declared 'inout'}}
  func local() -> Int {
    x += 1 // expected-note {{captured here}}
    return 0
  }
  takesEscapingGeneric(local) // expected-error {{escaping local function captures 'inout' parameter 'x'}}
}

// Make sure that withoutActuallyEscaping counts as a safe use.
func goodUseOfNoEscapeClosure(fn: () -> (), fn2: () -> ()) {
  withoutActuallyEscaping(fn) { _fn in
    takesEscaping(_fn)
  }
}

// Some random regression tests
infix operator ~>
protocol Target {}

func ~> <Target, Arg0, Result>(x: inout Target, f: @escaping (_: inout Target, _: Arg0) -> Result) -> (Arg0) -> Result {
  // expected-note@-1 {{parameter 'x' is declared 'inout'}}
  return { f(&x, $0) } // expected-note {{captured here}}
  // expected-error@-1 {{escaping closure captures 'inout' parameter 'x'}}
}

func ~> (x: inout Int, f: @escaping (_: inout Int, _: Target) -> Target) -> (Target) -> Target {
  // expected-note@-1 {{parameter 'x' is declared 'inout'}}
  return { f(&x, $0) } // expected-note {{captured here}}
  // expected-error@-1 {{escaping closure captures 'inout' parameter 'x'}}
}

func addHandler(_: @escaping () -> ()) {}

public struct SelfEscapeFromInit {
  public init() {
    addHandler { self.handler() }
    // expected-error@-1 {{escaping closure captures mutating 'self' parameter}}
    // expected-note@-2 {{captured here}}
  }

  public mutating func handler() {}
}

func autoclosureTakesEscaping(_ x: @escaping @autoclosure () ->Int) {}

// Test that captures of escaping autoclosure are diagnosed correctly.
func badCaptureInAutoclosure(x: inout Int) {
    // expected-note@-1 {{parameter 'x' is declared 'inout'}}
    // expected-note@-2 {{parameter 'x' is declared 'inout'}}

  autoclosureTakesEscaping(x)
    // expected-error@-1 {{escaping autoclosure captures 'inout' parameter 'x'}}
    // expected-note@-2 {{pass a copy of 'x'}}

  autoclosureTakesEscaping((x + 1) - 100)
    // expected-error@-1 {{escaping autoclosure captures 'inout' parameter 'x'}}
    // expected-note@-2 {{pass a copy of 'x'}}
}

// Test that transitive captures in autoclosures are diagnosed correctly.
func badTransitiveCaptureInClosures(x: inout Int) -> ((Int) -> Void) {
    // expected-note@-1 {{parameter 'x' is declared 'inout'}}
    // expected-note@-2 {{parameter 'x' is declared 'inout'}}
    // expected-note@-3 {{parameter 'x' is declared 'inout'}}

  // Test capture of x by an autoclosure within a non-escaping closure.
  let _ = { (y: Int) in
    autoclosureTakesEscaping(x + y)
      // expected-error@-1 {{escaping autoclosure captures 'inout' parameter 'x'}}
      // expected-note@-2 {{pass a copy of 'x'}}
  }

  // Test capture of x by an autoclosure within an escaping closure.
  let escapingClosure = { (y: Int) in
      // expected-error@-1 {{escaping closure captures 'inout' parameter 'x'}}

    autoclosureTakesEscaping(x + y)
      // expected-note@-1 {{captured indirectly by this call}}
      // expected-note@-2 {{captured here}}

      // expected-error@-4 {{escaping autoclosure captures 'inout' parameter 'x'}}
      // expected-note@-5 {{pass a copy of 'x'}}
  }
  return escapingClosure
}

// Test that captures of mutating 'self' in escaping autoclosures are diagnosed correctly.
struct S {
  var i = 0
  init() {
    autoclosureTakesEscaping(i)
      // expected-error@-1 {{escaping autoclosure captures mutating 'self' parameter}}
      // expected-note@-2 {{pass a copy of 'self'}}
  }
  mutating func method() {
    autoclosureTakesEscaping(i)
      // expected-error@-1 {{escaping autoclosure captures mutating 'self' parameter}}
      // expected-note@-2 {{pass a copy of 'self'}}
  }
}

// Test that we look through the SILBoxType used for a 'var' binding
func badNoEscapeCaptureThroughVar(_ fn: () -> ()) {
  var myFunc = fn // expected-warning {{never mutated}} // expected-note {{captured here}}

  takesEscaping { // expected-error {{escaping closure captures non-escaping value}}
    myFunc()
  }
}

@inline(never)
func takeNoEscapeReturnGetter(f: ()->()->Int64) -> ()->Int64 { return f() }

// Test that invalid escaping capture diagnostics are run on nested
// closures before exclusivity diagnostics are run. Exclusivity
// diagnostics need to inspect all referenced closures that capture
// inouts. Also ensure that exclusivity diagnostics does not crash
// when verifying that a closure that captures inout does not escape
// as long as a previous diagnostic error is present.
struct TestInoutEscapeInClosure {
  var someValue: Int64 = 0
  mutating func testInoutEscapeInClosure() -> () -> Int64 {
    return takeNoEscapeReturnGetter {
      return { return someValue } // expected-error {{escaping closure captures mutating 'self' parameter}}
      // expected-note@-1 {{captured here}}
    }
  }
}