@@ -238,37 +238,53 @@ private struct DiagnoseDependence {
238238
239239 // Identify the escaping variable.
240240 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)
245246 } 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+ }
247256 }
248257 reportScope ( )
249258 // 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+ }
252262 }
253263
254- // Identify the dependence scope.
264+ // Identify the dependence scope. If no source location is found, bypass this diagnostic.
255265 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 ?? " " )
262271 return
263272 }
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+ }
269284 return
270285 }
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.
272288 if let parentLoc = parentVar. sourceLoc {
273289 if let parentName = parentVar. name {
274290 diagnose ( parentLoc, . lifetime_outside_scope_variable, parentName)
@@ -282,24 +298,35 @@ private struct DiagnoseDependence {
282298// Identify a best-effort variable declaration based on a defining SIL
283299// value or any lifetime dependent use of that SIL value.
284300private 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
287308
288309 var name : StringRef ? {
289310 return varDecl? . userFacingName
290311 }
291312
292313 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 {
295315 return
296316 }
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)
299327 return
300328 }
301- self . varDecl = nil
302- self . sourceLoc = nil
329+ self = Self ( introducer: introducer, context)
303330 }
304331
305332 private func getFirstVariableIntroducer( of value: Value , _ context: some Context ) -> Value ? {
@@ -313,15 +340,22 @@ private struct LifetimeVariable {
313340 return introducer
314341 }
315342
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 {
318345 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
322350 }
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
325359 }
326360 }
327361
@@ -335,32 +369,27 @@ private struct LifetimeVariable {
335369 // never be produced by one of these, except when it is redundant with the `alloc_box` VarDecl. It does not seem
336370 // possible for a box to be moved/borrowed directly into another variable's box. Reassignment always loads/stores
337371 // the value.
338- self = Self ( introducer: projectBox. box. referenceRoot)
372+ self = . init ( introducer: projectBox. box. referenceRoot, context )
339373 case . stack( let allocStack) :
340- self = Self ( introducer: allocStack)
374+ self = . init ( introducer: allocStack, context )
341375 case . global( let globalVar) :
342376 self . varDecl = globalVar. varDecl
343377 self . sourceLoc = varDecl? . nameLoc
344378 case . class( let refAddr) :
345- self . varDecl = refAddr. varDecl
346- self . sourceLoc = refAddr. location. sourceLoc
379+ self = . init( introducer: refAddr, context)
347380 case . tail( let refTail) :
348- self = Self ( introducer: refTail. instance)
381+ self = . init ( introducer: refTail. instance, context )
349382 case . argument( let arg) :
350- self . varDecl = arg. varDecl
351- self . sourceLoc = arg. sourceLoc
383+ self = . init( introducer: arg, context)
352384 case . yield( let result) :
353385 // TODO: bridge VarDecl for FunctionConvention.Yields
354- self . varDecl = nil
355- self . sourceLoc = result. parentInstruction. location. sourceLoc
386+ self = . init( introducer: result, context)
356387 case . storeBorrow( let sb) :
357388 self = . init( dependent: sb. source, context)
358389 case . pointer( let ptrToAddr) :
359- self . varDecl = nil
360- self . sourceLoc = ptrToAddr. location. sourceLoc
390+ self = . init( introducer: ptrToAddr, context)
361391 case . index, . unidentified:
362- self . varDecl = nil
363- self . sourceLoc = nil
392+ break
364393 }
365394 }
366395}
0 commit comments