@@ -238,37 +238,53 @@ private struct DiagnoseDependence {
238
238
239
239
// Identify the escaping variable.
240
240
let escapingVar = LifetimeVariable ( dependent: operand. value, context)
241
- let varName = escapingVar. name
242
- if let varName {
243
- diagnose ( escapingVar. sourceLoc, . lifetime_variable_outside_scope,
244
- varName)
241
+ if let varDecl = escapingVar. varDecl {
242
+ // Use the variable location, not the access location.
243
+ diagnose ( varDecl. nameLoc, . lifetime_variable_outside_scope, escapingVar. name ?? " " )
244
+ } else if let sourceLoc = escapingVar. sourceLoc {
245
+ diagnose ( sourceLoc, . lifetime_value_outside_scope)
245
246
} else {
246
- diagnose ( escapingVar. sourceLoc, . lifetime_value_outside_scope)
247
+ // Always raise an error even if we can't find a source location.
248
+ let sourceLoc = function. location. sourceLoc
249
+ if let accessorKind = escapingVar. accessorKind {
250
+ diagnose ( sourceLoc, . lifetime_value_outside_accessor, accessorKind)
251
+ } else {
252
+ // Thunks do not have a source location, but we try to use the function location anyway.
253
+ let thunkSelect = dependence. function. thunkKind == . noThunk ? 0 : 1
254
+ diagnose ( sourceLoc, . lifetime_value_outside_thunk, thunkSelect, function. name)
255
+ }
247
256
}
248
257
reportScope ( )
249
258
// Identify the use point.
250
- let userSourceLoc = operand. instruction. location. sourceLoc
251
- diagnose ( userSourceLoc, diagID)
259
+ if let userSourceLoc = operand. instruction. location. sourceLoc {
260
+ diagnose ( userSourceLoc, diagID)
261
+ }
252
262
}
253
263
254
- // Identify the dependence scope.
264
+ // Identify the dependence scope. If no source location is found, bypass this diagnostic.
255
265
func reportScope( ) {
256
- if case let . access( beginAccess) = dependence. scope {
257
- let parentVar = LifetimeVariable ( dependent: beginAccess, context)
258
- if let sourceLoc = beginAccess. location. sourceLoc ?? parentVar. sourceLoc {
259
- diagnose ( sourceLoc, . lifetime_outside_scope_access,
260
- parentVar. name ?? " " )
261
- }
266
+ let parentVar = LifetimeVariable ( dependent: dependence. parentValue, context)
267
+ // First check if the dependency is limited to an access scope. If the access has no source location then
268
+ // fall-through to report possible dependence on an argument.
269
+ if parentVar. isAccessScope, let accessLoc = parentVar. sourceLoc {
270
+ diagnose ( accessLoc, . lifetime_outside_scope_access, parentVar. name ?? " " )
262
271
return
263
272
}
264
- if let arg = dependence. parentValue as? Argument ,
265
- let varDecl = arg. varDecl,
266
- let sourceLoc = arg. sourceLoc {
267
- diagnose ( sourceLoc, . lifetime_outside_scope_argument,
268
- varDecl. userFacingName)
273
+ // If the argument does not have a source location (e.g. a synthesized accessor), report the function location. The
274
+ // function's source location is sufficient for argument diagnostics, but if the function has no location, don't
275
+ // report any scope.
276
+ if parentVar. isArgument, let argLoc = parentVar. sourceLoc ?? function. location. sourceLoc {
277
+ if parentVar. isClosureCapture {
278
+ diagnose ( argLoc, . lifetime_outside_scope_capture)
279
+ } else if let parentName = parentVar. name {
280
+ diagnose ( argLoc, . lifetime_outside_scope_argument, parentName)
281
+ } else {
282
+ diagnose ( argLoc, . lifetime_outside_scope_synthesized_argument, parentVar. accessorKind ?? function. name)
283
+ }
269
284
return
270
285
}
271
- let parentVar = LifetimeVariable ( dependent: dependence. parentValue, context)
286
+ // Now diagnose dependencies on regular variable and value scopes.
287
+ // Thunks do not have a function location, so any scopes inside the thunk will be ignored.
272
288
if let parentLoc = parentVar. sourceLoc {
273
289
if let parentName = parentVar. name {
274
290
diagnose ( parentLoc, . lifetime_outside_scope_variable, parentName)
@@ -282,24 +298,35 @@ private struct DiagnoseDependence {
282
298
// Identify a best-effort variable declaration based on a defining SIL
283
299
// value or any lifetime dependent use of that SIL value.
284
300
private struct LifetimeVariable {
285
- var varDecl : VarDecl ?
286
- var sourceLoc : SourceLoc ?
301
+ var varDecl : VarDecl ? = nil
302
+ var sourceLoc : SourceLoc ? = nil
303
+ var isAccessScope : Bool = false
304
+ var isArgument : Bool = false
305
+ var isClosureCapture : Bool = false
306
+ var accessorKind : String ?
307
+ var thunkKind : Function . ThunkKind = . noThunk
287
308
288
309
var name : StringRef ? {
289
310
return varDecl? . userFacingName
290
311
}
291
312
292
313
init ( dependent value: Value , _ context: some Context ) {
293
- if value. type. isAddress {
294
- self = Self ( accessBase: value. accessBase, context)
314
+ guard let introducer = getFirstVariableIntroducer ( of: value, context) else {
295
315
return
296
316
}
297
- if let firstIntroducer = getFirstVariableIntroducer ( of: value, context) {
298
- self = Self ( introducer: firstIntroducer)
317
+ if introducer. type. isAddress {
318
+ if let beginAccess = introducer as? BeginAccessInst {
319
+ // Recurse through beginAccess to find the variable introducer rather than the variable access.
320
+ self = . init( dependent: beginAccess. address, context)
321
+ self . isAccessScope = true
322
+ // However, remember source location of the innermost access.
323
+ self . sourceLoc = beginAccess. location. sourceLoc ?? self . sourceLoc
324
+ return
325
+ }
326
+ self = . init( accessBase: introducer. accessBase, context)
299
327
return
300
328
}
301
- self . varDecl = nil
302
- self . sourceLoc = nil
329
+ self = Self ( introducer: introducer, context)
303
330
}
304
331
305
332
private func getFirstVariableIntroducer( of value: Value , _ context: some Context ) -> Value ? {
@@ -313,15 +340,22 @@ private struct LifetimeVariable {
313
340
return introducer
314
341
}
315
342
316
- private init ( introducer: Value ) {
317
- if let arg = introducer as? Argument {
343
+ private init ( introducer: Value , _ context : some Context ) {
344
+ if let arg = introducer as? FunctionArgument {
318
345
self . varDecl = arg. varDecl
319
- } else {
320
- self . sourceLoc = introducer. definingInstruction? . location. sourceLoc
321
- self . varDecl = introducer. definingInstruction? . findVarDecl ( )
346
+ self . sourceLoc = arg. sourceLoc
347
+ self . isArgument = true
348
+ self . isClosureCapture = arg. isClosureCapture
349
+ return
322
350
}
323
- if let varDecl {
324
- sourceLoc = varDecl. nameLoc
351
+ if let varDecl = introducer. definingInstruction? . findVarDecl ( ) {
352
+ self . varDecl = varDecl
353
+ self . sourceLoc = varDecl. nameLoc
354
+ } else if let sourceLoc = introducer. definingInstruction? . location. sourceLoc {
355
+ self . sourceLoc = sourceLoc
356
+ } else {
357
+ self . accessorKind = introducer. parentFunction. accessorKindName
358
+ self . thunkKind = introducer. parentFunction. thunkKind
325
359
}
326
360
}
327
361
@@ -335,32 +369,27 @@ private struct LifetimeVariable {
335
369
// never be produced by one of these, except when it is redundant with the `alloc_box` VarDecl. It does not seem
336
370
// possible for a box to be moved/borrowed directly into another variable's box. Reassignment always loads/stores
337
371
// the value.
338
- self = Self ( introducer: projectBox. box. referenceRoot)
372
+ self = . init ( introducer: projectBox. box. referenceRoot, context )
339
373
case . stack( let allocStack) :
340
- self = Self ( introducer: allocStack)
374
+ self = . init ( introducer: allocStack, context )
341
375
case . global( let globalVar) :
342
376
self . varDecl = globalVar. varDecl
343
377
self . sourceLoc = varDecl? . nameLoc
344
378
case . class( let refAddr) :
345
- self . varDecl = refAddr. varDecl
346
- self . sourceLoc = refAddr. location. sourceLoc
379
+ self = . init( introducer: refAddr, context)
347
380
case . tail( let refTail) :
348
- self = Self ( introducer: refTail. instance)
381
+ self = . init ( introducer: refTail. instance, context )
349
382
case . argument( let arg) :
350
- self . varDecl = arg. varDecl
351
- self . sourceLoc = arg. sourceLoc
383
+ self = . init( introducer: arg, context)
352
384
case . yield( let result) :
353
385
// TODO: bridge VarDecl for FunctionConvention.Yields
354
- self . varDecl = nil
355
- self . sourceLoc = result. parentInstruction. location. sourceLoc
386
+ self = . init( introducer: result, context)
356
387
case . storeBorrow( let sb) :
357
388
self = . init( dependent: sb. source, context)
358
389
case . pointer( let ptrToAddr) :
359
- self . varDecl = nil
360
- self . sourceLoc = ptrToAddr. location. sourceLoc
390
+ self = . init( introducer: ptrToAddr, context)
361
391
case . index, . unidentified:
362
- self . varDecl = nil
363
- self . sourceLoc = nil
392
+ break
364
393
}
365
394
}
366
395
}
0 commit comments