@@ -52,8 +52,9 @@ let lifetimeDependenceDiagnosticsPass = FunctionPass(
52
52
if let apply = instruction as? FullApplySite {
53
53
// Handle ~Escapable results that do not have a lifetime
54
54
// dependence (@_unsafeNonescapableResult).
55
- apply. dependentValues. forEach {
56
- if let lifetimeDep = LifetimeDependence ( applyResult: $0, context) {
55
+ apply. resultOrYields. forEach {
56
+ if let lifetimeDep = LifetimeDependence ( unsafeApplyResult: $0,
57
+ context) {
57
58
analyze ( dependence: lifetimeDep, context)
58
59
}
59
60
}
@@ -75,23 +76,35 @@ private func analyze(dependence: LifetimeDependence,
75
76
var range = dependence. computeRange ( context)
76
77
defer { range? . deinitialize ( ) }
77
78
79
+ var error = false
78
80
let diagnostics =
79
- DiagnoseDependence ( dependence: dependence, range: range, context: context)
81
+ DiagnoseDependence ( dependence: dependence, range: range,
82
+ onError: { error = true } , context: context)
80
83
81
84
// Check each lifetime-dependent use via a def-use visitor
82
85
var walker = DiagnoseDependenceWalker ( diagnostics, context)
83
86
defer { walker. deinitialize ( ) }
84
- _ = walker. walkDown ( root: dependence. parentValue)
87
+ _ = walker. walkDown ( root: dependence. dependentValue)
88
+
89
+ if !error {
90
+ dependence. resolve ( context)
91
+ }
85
92
}
86
93
87
94
/// Analyze and diagnose a single LifetimeDependence.
88
95
private struct DiagnoseDependence {
89
96
let dependence : LifetimeDependence
90
97
let range : InstructionRange ?
98
+ let onError : ( ) -> ( )
91
99
let context : FunctionPassContext
92
100
93
101
var function : Function { dependence. function }
94
102
103
+ func diagnose( _ position: SourceLoc ? , _ id: DiagID ,
104
+ _ args: DiagnosticArgument ... ) {
105
+ context. diagnosticEngine. diagnose ( position, id, args)
106
+ }
107
+
95
108
/// Check that this use is inside the dependence scope.
96
109
func checkInScope( operand: Operand ) -> WalkResult {
97
110
if let range, !range. inclusiveRangeContains ( operand. instruction) {
@@ -114,45 +127,73 @@ private struct DiagnoseDependence {
114
127
}
115
128
116
129
func checkFunctionResult( operand: Operand ) -> WalkResult {
117
- // TODO: Get the argument dependence for this result. Check that it is the
118
- // same as the current dependence scope
119
130
120
131
if function. hasUnsafeNonEscapableResult {
121
132
return . continueWalk
122
133
}
123
- // TODO: Take ResultInfo as an argument and provide better
124
- // diagnostics for missing lifetime dependencies.
134
+ // Allow returning an apply result (@_unsafeNonescapableResult) if
135
+ // the calling function has a dependence. This implicitly makes
136
+ // the unsafe nonescapable result dependent on the calling
137
+ // function's lifetime dependence arguments.
138
+ if dependence. isUnsafeApplyResult, function. hasResultDependence {
139
+ return . continueWalk
140
+ }
141
+ // Check that the argument dependence for this result is the same
142
+ // as the current dependence scope.
143
+ if let arg = dependence. scope. parentValue as? FunctionArgument ,
144
+ function. argumentConventions [ resultDependsOn: arg. index] != nil {
145
+ // The returned value depends on a lifetime that is inherited or
146
+ // borrowed in the caller. The lifetime of the argument value
147
+ // itself is irrelevant here.
148
+ return . continueWalk
149
+ }
125
150
reportEscaping ( operand: operand)
126
151
return . abortWalk
127
152
}
128
153
129
154
func reportError( operand: Operand , diagID: DiagID ) {
155
+ onError ( )
156
+
130
157
// Identify the escaping variable.
131
158
let escapingVar = LifetimeVariable ( dependent: operand. value, context)
132
159
let varName = escapingVar. name
133
160
if let varName {
134
- context. diagnosticEngine. diagnose ( escapingVar. sourceLoc,
135
- . lifetime_variable_outside_scope,
136
- varName)
161
+ diagnose ( escapingVar. sourceLoc, . lifetime_variable_outside_scope,
162
+ varName)
137
163
} else {
138
- context. diagnosticEngine. diagnose ( escapingVar. sourceLoc,
139
- . lifetime_value_outside_scope)
140
- }
141
- // Identify the dependence scope.
142
- //
143
- // TODO: add bridging for function argument locations
144
- // [SILArgument.getDecl().getLoc()]
145
- //
146
- // TODO: For clear diagnostics: switch on dependence.scope.
147
- // For an access, report both the accessed variable, and the access.
148
- if let parentSourceLoc =
149
- dependence. parentValue. definingInstruction? . location. sourceLoc {
150
- context. diagnosticEngine. diagnose ( parentSourceLoc,
151
- . lifetime_outside_scope_parent)
164
+ diagnose ( escapingVar. sourceLoc, . lifetime_value_outside_scope)
152
165
}
166
+ reportScope ( )
153
167
// Identify the use point.
154
168
let userSourceLoc = operand. instruction. location. sourceLoc
155
- context. diagnosticEngine. diagnose ( userSourceLoc, diagID)
169
+ diagnose ( userSourceLoc, diagID)
170
+ }
171
+
172
+ // Identify the dependence scope.
173
+ func reportScope( ) {
174
+ if case let . access( beginAccess) = dependence. scope {
175
+ let parentVar = LifetimeVariable ( dependent: beginAccess, context)
176
+ if let sourceLoc = beginAccess. location. sourceLoc ?? parentVar. sourceLoc {
177
+ diagnose ( sourceLoc, . lifetime_outside_scope_access,
178
+ parentVar. name ?? " " )
179
+ }
180
+ return
181
+ }
182
+ if let arg = dependence. parentValue as? Argument ,
183
+ let varDecl = arg. varDecl,
184
+ let sourceLoc = arg. sourceLoc {
185
+ diagnose ( sourceLoc, . lifetime_outside_scope_argument,
186
+ varDecl. userFacingName)
187
+ return
188
+ }
189
+ let parentVar = LifetimeVariable ( dependent: dependence. parentValue, context)
190
+ if let parentLoc = parentVar. sourceLoc {
191
+ if let parentName = parentVar. name {
192
+ diagnose ( parentLoc, . lifetime_outside_scope_variable, parentName)
193
+ } else {
194
+ diagnose ( parentLoc, . lifetime_outside_scope_value)
195
+ }
196
+ }
156
197
}
157
198
}
158
199
@@ -182,18 +223,41 @@ private struct LifetimeVariable {
182
223
return varDecl? . userFacingName
183
224
}
184
225
185
- init ( introducer: Value ) {
186
- if introducer. type. isAddress {
187
- switch introducer. enclosingAccessScope {
188
- case let . scope( beginAccess) :
189
- // TODO: report both the access point and original variable.
190
- self = LifetimeVariable ( introducer: beginAccess. operand. value)
191
- return
192
- case . base( _) :
193
- // TODO: use an address walker to get the allocation point.
194
- break
195
- }
226
+ init ( dependent value: Value , _ context: some Context ) {
227
+ if value. type. isAddress {
228
+ self = Self ( accessBase: value. accessBase, context)
229
+ return
196
230
}
231
+ if let firstIntroducer = getFirstBorrowIntroducer ( of: value, context) {
232
+ self = Self ( introducer: firstIntroducer)
233
+ return
234
+ }
235
+ self . varDecl = nil
236
+ self . sourceLoc = nil
237
+ }
238
+
239
+ // FUTURE: consider diagnosing multiple variable introducers. It's
240
+ // unclear how more than one can happen.
241
+ private func getFirstBorrowIntroducer( of value: Value ,
242
+ _ context: some Context )
243
+ -> Value ? {
244
+ var introducers = Stack < Value > ( context)
245
+ gatherBorrowIntroducers ( for: value, in: & introducers, context)
246
+ return introducers. pop ( )
247
+ }
248
+
249
+ private func getFirstLifetimeIntroducer( of value: Value ,
250
+ _ context: some Context )
251
+ -> Value ? {
252
+ var introducer : Value ?
253
+ _ = visitLifetimeIntroducers ( for: value, context) {
254
+ introducer = $0
255
+ return . abortWalk
256
+ }
257
+ return introducer
258
+ }
259
+
260
+ private init ( introducer: Value ) {
197
261
if let arg = introducer as? Argument {
198
262
self . varDecl = arg. varDecl
199
263
} else {
@@ -205,17 +269,42 @@ private struct LifetimeVariable {
205
269
}
206
270
}
207
271
208
- init ( dependent value: Value , _ context: Context ) {
209
- // TODO: consider diagnosing multiple variable introducers. It's
210
- // unclear how more than one can happen.
211
- var introducers = Stack < Value > ( context)
212
- gatherBorrowIntroducers ( for: value, in: & introducers, context)
213
- if let firstIntroducer = introducers. pop ( ) {
214
- self = LifetimeVariable ( introducer: firstIntroducer)
215
- return
272
+ // Record the source location of the variable decl if possible. The
273
+ // caller will already have a source location for the formal access,
274
+ // which is more relevant for diagnostics.
275
+ private init ( accessBase: AccessBase , _ context: some Context ) {
276
+ switch accessBase {
277
+ case . box( let projectBox) :
278
+ if let box = getFirstLifetimeIntroducer ( of: projectBox. box, context) {
279
+ self = Self ( introducer: box)
280
+ }
281
+ // We should always find an introducer since boxes are nontrivial.
282
+ self . varDecl = nil
283
+ self . sourceLoc = nil
284
+ case . stack( let allocStack) :
285
+ self = Self ( introducer: allocStack)
286
+ case . global( let globalVar) :
287
+ self . varDecl = globalVar. varDecl
288
+ self . sourceLoc = nil
289
+ case . class( let refAddr) :
290
+ self . varDecl = refAddr. varDecl
291
+ self . sourceLoc = refAddr. location. sourceLoc
292
+ case . tail( let refTail) :
293
+ self = Self ( introducer: refTail. instance)
294
+ case . argument( let arg) :
295
+ self . varDecl = arg. varDecl
296
+ self . sourceLoc = arg. sourceLoc
297
+ case . yield( let result) :
298
+ // TODO: bridge VarDecl for FunctionConvention.Yields
299
+ self . varDecl = nil
300
+ self . sourceLoc = result. parentInstruction. location. sourceLoc
301
+ case . pointer( let ptrToAddr) :
302
+ self . varDecl = nil
303
+ self . sourceLoc = ptrToAddr. location. sourceLoc
304
+ case . unidentified:
305
+ self . varDecl = nil
306
+ self . sourceLoc = nil
216
307
}
217
- self . varDecl = nil
218
- self . sourceLoc = nil
219
308
}
220
309
}
221
310
@@ -229,8 +318,8 @@ private struct LifetimeVariable {
229
318
///
230
319
/// TODO: handle stores to singly initialized temporaries like copies using a standard reaching-def analysis.
231
320
private struct DiagnoseDependenceWalker {
232
- let diagnostics : DiagnoseDependence
233
321
let context : Context
322
+ var diagnostics : DiagnoseDependence
234
323
var visitedValues : ValueSet
235
324
236
325
var function : Function { diagnostics. function }
0 commit comments