@@ -43,9 +43,22 @@ let lifetimeDependenceDiagnosticsPass = FunctionPass(
4343 }
4444 }
4545 for instruction in function. instructions {
46- guard let markDep = instruction as? MarkDependenceInst else { continue }
47- if let lifetimeDep = LifetimeDependence ( markDep, context) {
48- analyze ( dependence: lifetimeDep, context)
46+ if let markDep = instruction as? MarkDependenceInst {
47+ if let lifetimeDep = LifetimeDependence ( markDep, context) {
48+ analyze ( dependence: lifetimeDep, context)
49+ }
50+ continue
51+ }
52+ if let apply = instruction as? FullApplySite {
53+ // Handle ~Escapable results that do not have a lifetime
54+ // dependence (@_unsafeNonescapableResult).
55+ apply. resultOrYields. forEach {
56+ if let lifetimeDep = LifetimeDependence ( unsafeApplyResult: $0,
57+ context) {
58+ analyze ( dependence: lifetimeDep, context)
59+ }
60+ }
61+ continue
4962 }
5063 }
5164}
@@ -63,23 +76,35 @@ private func analyze(dependence: LifetimeDependence,
6376 var range = dependence. computeRange ( context)
6477 defer { range? . deinitialize ( ) }
6578
79+ var error = false
6680 let diagnostics =
67- DiagnoseDependence ( dependence: dependence, range: range, context: context)
81+ DiagnoseDependence ( dependence: dependence, range: range,
82+ onError: { error = true } , context: context)
6883
6984 // Check each lifetime-dependent use via a def-use visitor
7085 var walker = DiagnoseDependenceWalker ( diagnostics, context)
7186 defer { walker. deinitialize ( ) }
72- _ = walker. walkDown ( root: dependence. parentValue)
87+ _ = walker. walkDown ( root: dependence. dependentValue)
88+
89+ if !error {
90+ dependence. resolve ( context)
91+ }
7392}
7493
7594/// Analyze and diagnose a single LifetimeDependence.
7695private struct DiagnoseDependence {
7796 let dependence : LifetimeDependence
7897 let range : InstructionRange ?
98+ let onError : ( ) -> ( )
7999 let context : FunctionPassContext
80100
81101 var function : Function { dependence. function }
82102
103+ func diagnose( _ position: SourceLoc ? , _ id: DiagID ,
104+ _ args: DiagnosticArgument ... ) {
105+ context. diagnosticEngine. diagnose ( position, id, args)
106+ }
107+
83108 /// Check that this use is inside the dependence scope.
84109 func checkInScope( operand: Operand ) -> WalkResult {
85110 if let range, !range. inclusiveRangeContains ( operand. instruction) {
@@ -102,45 +127,73 @@ private struct DiagnoseDependence {
102127 }
103128
104129 func checkFunctionResult( operand: Operand ) -> WalkResult {
105- // TODO: Get the argument dependence for this result. Check that it is the
106- // same as the current dependence scope
107130
108131 if function. hasUnsafeNonEscapableResult {
109132 return . continueWalk
110133 }
111- // TODO: Take ResultInfo as an argument and provide better
112- // 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+ }
113150 reportEscaping ( operand: operand)
114151 return . abortWalk
115152 }
116153
117154 func reportError( operand: Operand , diagID: DiagID ) {
155+ onError ( )
156+
118157 // Identify the escaping variable.
119158 let escapingVar = LifetimeVariable ( dependent: operand. value, context)
120159 let varName = escapingVar. name
121160 if let varName {
122- context. diagnosticEngine. diagnose ( escapingVar. sourceLoc,
123- . lifetime_variable_outside_scope,
124- varName)
161+ diagnose ( escapingVar. sourceLoc, . lifetime_variable_outside_scope,
162+ varName)
125163 } else {
126- context. diagnosticEngine. diagnose ( escapingVar. sourceLoc,
127- . lifetime_value_outside_scope)
128- }
129- // Identify the dependence scope.
130- //
131- // TODO: add bridging for function argument locations
132- // [SILArgument.getDecl().getLoc()]
133- //
134- // TODO: For clear diagnostics: switch on dependence.scope.
135- // For an access, report both the accessed variable, and the access.
136- if let parentSourceLoc =
137- dependence. parentValue. definingInstruction? . location. sourceLoc {
138- context. diagnosticEngine. diagnose ( parentSourceLoc,
139- . lifetime_outside_scope_parent)
164+ diagnose ( escapingVar. sourceLoc, . lifetime_value_outside_scope)
140165 }
166+ reportScope ( )
141167 // Identify the use point.
142168 let userSourceLoc = operand. instruction. location. sourceLoc
143- 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+ }
144197 }
145198}
146199
@@ -170,18 +223,41 @@ private struct LifetimeVariable {
170223 return varDecl? . userFacingName
171224 }
172225
173- init ( introducer: Value ) {
174- if introducer. type. isAddress {
175- switch introducer. enclosingAccessScope {
176- case let . scope( beginAccess) :
177- // TODO: report both the access point and original variable.
178- self = LifetimeVariable ( introducer: beginAccess. operand. value)
179- return
180- case . base( _) :
181- // TODO: use an address walker to get the allocation point.
182- break
183- }
226+ init ( dependent value: Value , _ context: some Context ) {
227+ if value. type. isAddress {
228+ self = Self ( accessBase: value. accessBase, context)
229+ return
230+ }
231+ if let firstIntroducer = getFirstBorrowIntroducer ( of: value, context) {
232+ self = Self ( introducer: firstIntroducer)
233+ return
184234 }
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 ) {
185261 if let arg = introducer as? Argument {
186262 self . varDecl = arg. varDecl
187263 } else {
@@ -193,17 +269,42 @@ private struct LifetimeVariable {
193269 }
194270 }
195271
196- init ( dependent value: Value , _ context: Context ) {
197- // TODO: consider diagnosing multiple variable introducers. It's
198- // unclear how more than one can happen.
199- var introducers = Stack < Value > ( context)
200- gatherBorrowIntroducers ( for: value, in: & introducers, context)
201- if let firstIntroducer = introducers. pop ( ) {
202- self = LifetimeVariable ( introducer: firstIntroducer)
203- 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
204307 }
205- self . varDecl = nil
206- self . sourceLoc = nil
207308 }
208309}
209310
@@ -217,8 +318,8 @@ private struct LifetimeVariable {
217318///
218319/// TODO: handle stores to singly initialized temporaries like copies using a standard reaching-def analysis.
219320private struct DiagnoseDependenceWalker {
220- let diagnostics : DiagnoseDependence
221321 let context : Context
322+ var diagnostics : DiagnoseDependence
222323 var visitedValues : ValueSet
223324
224325 var function : Function { diagnostics. function }
0 commit comments