@@ -238,37 +238,51 @@ 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 let parentName = parentVar. name {
278
+ diagnose ( argLoc, . lifetime_outside_scope_argument, parentName)
279
+ } else {
280
+ diagnose ( argLoc, . lifetime_outside_scope_synthesized_argument, parentVar. accessorKind ?? function. name)
281
+ }
269
282
return
270
283
}
271
- let parentVar = LifetimeVariable ( dependent: dependence. parentValue, context)
284
+ // Now diagnose dependencies on regular variable and value scopes.
285
+ // Thunks do not have a function location, so any scopes inside the thunk will be ignored.
272
286
if let parentLoc = parentVar. sourceLoc {
273
287
if let parentName = parentVar. name {
274
288
diagnose ( parentLoc, . lifetime_outside_scope_variable, parentName)
@@ -282,24 +296,34 @@ private struct DiagnoseDependence {
282
296
// Identify a best-effort variable declaration based on a defining SIL
283
297
// value or any lifetime dependent use of that SIL value.
284
298
private struct LifetimeVariable {
285
- var varDecl : VarDecl ?
286
- var sourceLoc : SourceLoc ?
299
+ var varDecl : VarDecl ? = nil
300
+ var sourceLoc : SourceLoc ? = nil
301
+ var isArgument : Bool = false
302
+ var isAccessScope : Bool = false
303
+ var accessorKind : String ?
304
+ var thunkKind : Function . ThunkKind = . noThunk
287
305
288
306
var name : StringRef ? {
289
307
return varDecl? . userFacingName
290
308
}
291
309
292
310
init ( dependent value: Value , _ context: some Context ) {
293
- if value. type. isAddress {
294
- self = Self ( accessBase: value. accessBase, context)
311
+ guard let introducer = getFirstVariableIntroducer ( of: value, context) else {
295
312
return
296
313
}
297
- if let firstIntroducer = getFirstVariableIntroducer ( of: value, context) {
298
- self = Self ( introducer: firstIntroducer)
314
+ if introducer. type. isAddress {
315
+ if let beginAccess = introducer as? BeginAccessInst {
316
+ // Recurse through beginAccess to find the variable introducer rather than the variable access.
317
+ self = . init( dependent: beginAccess. address, context)
318
+ self . isAccessScope = true
319
+ // However, remember source location of the innermost access.
320
+ self . sourceLoc = beginAccess. location. sourceLoc ?? self . sourceLoc
321
+ return
322
+ }
323
+ self = . init( accessBase: introducer. accessBase, context)
299
324
return
300
325
}
301
- self . varDecl = nil
302
- self . sourceLoc = nil
326
+ self = Self ( introducer: introducer, context)
303
327
}
304
328
305
329
private func getFirstVariableIntroducer( of value: Value , _ context: some Context ) -> Value ? {
@@ -313,15 +337,21 @@ private struct LifetimeVariable {
313
337
return introducer
314
338
}
315
339
316
- private init ( introducer: Value ) {
340
+ private init ( introducer: Value , _ context : some Context ) {
317
341
if let arg = introducer as? Argument {
318
342
self . varDecl = arg. varDecl
319
- } else {
320
- self . sourceLoc = introducer . definingInstruction ? . location . sourceLoc
321
- self . varDecl = introducer . definingInstruction ? . findVarDecl ( )
343
+ self . sourceLoc = arg . sourceLoc
344
+ self . isArgument = true
345
+ return
322
346
}
323
- if let varDecl {
324
- sourceLoc = varDecl. nameLoc
347
+ if let varDecl = introducer. definingInstruction? . findVarDecl ( ) {
348
+ self . varDecl = varDecl
349
+ self . sourceLoc = varDecl. nameLoc
350
+ } else if let sourceLoc = introducer. definingInstruction? . location. sourceLoc {
351
+ self . sourceLoc = sourceLoc
352
+ } else {
353
+ self . accessorKind = introducer. parentFunction. accessorKindName
354
+ self . thunkKind = introducer. parentFunction. thunkKind
325
355
}
326
356
}
327
357
@@ -335,32 +365,27 @@ private struct LifetimeVariable {
335
365
// never be produced by one of these, except when it is redundant with the `alloc_box` VarDecl. It does not seem
336
366
// possible for a box to be moved/borrowed directly into another variable's box. Reassignment always loads/stores
337
367
// the value.
338
- self = Self ( introducer: projectBox. box. referenceRoot)
368
+ self = . init ( introducer: projectBox. box. referenceRoot, context )
339
369
case . stack( let allocStack) :
340
- self = Self ( introducer: allocStack)
370
+ self = . init ( introducer: allocStack, context )
341
371
case . global( let globalVar) :
342
372
self . varDecl = globalVar. varDecl
343
373
self . sourceLoc = varDecl? . nameLoc
344
374
case . class( let refAddr) :
345
- self . varDecl = refAddr. varDecl
346
- self . sourceLoc = refAddr. location. sourceLoc
375
+ self = . init( introducer: refAddr, context)
347
376
case . tail( let refTail) :
348
- self = Self ( introducer: refTail. instance)
377
+ self = . init ( introducer: refTail. instance, context )
349
378
case . argument( let arg) :
350
- self . varDecl = arg. varDecl
351
- self . sourceLoc = arg. sourceLoc
379
+ self = . init( introducer: arg, context)
352
380
case . yield( let result) :
353
381
// TODO: bridge VarDecl for FunctionConvention.Yields
354
- self . varDecl = nil
355
- self . sourceLoc = result. parentInstruction. location. sourceLoc
382
+ self = . init( introducer: result, context)
356
383
case . storeBorrow( let sb) :
357
384
self = . init( dependent: sb. source, context)
358
385
case . pointer( let ptrToAddr) :
359
- self . varDecl = nil
360
- self . sourceLoc = ptrToAddr. location. sourceLoc
386
+ self = . init( introducer: ptrToAddr, context)
361
387
case . index, . unidentified:
362
- self . varDecl = nil
363
- self . sourceLoc = nil
388
+ break
364
389
}
365
390
}
366
391
}
0 commit comments