@@ -52,8 +52,9 @@ let lifetimeDependenceDiagnosticsPass = FunctionPass(
5252 if let apply = instruction as? FullApplySite {
5353 // Handle ~Escapable results that do not have a lifetime
5454 // 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) {
5758 analyze ( dependence: lifetimeDep, context)
5859 }
5960 }
@@ -75,23 +76,35 @@ private func analyze(dependence: LifetimeDependence,
7576 var range = dependence. computeRange ( context)
7677 defer { range? . deinitialize ( ) }
7778
79+ var error = false
7880 let diagnostics =
79- DiagnoseDependence ( dependence: dependence, range: range, context: context)
81+ DiagnoseDependence ( dependence: dependence, range: range,
82+ onError: { error = true } , context: context)
8083
8184 // Check each lifetime-dependent use via a def-use visitor
8285 var walker = DiagnoseDependenceWalker ( diagnostics, context)
8386 defer { walker. deinitialize ( ) }
84- _ = walker. walkDown ( root: dependence. parentValue)
87+ _ = walker. walkDown ( root: dependence. dependentValue)
88+
89+ if !error {
90+ dependence. resolve ( context)
91+ }
8592}
8693
8794/// Analyze and diagnose a single LifetimeDependence.
8895private struct DiagnoseDependence {
8996 let dependence : LifetimeDependence
9097 let range : InstructionRange ?
98+ let onError : ( ) -> ( )
9199 let context : FunctionPassContext
92100
93101 var function : Function { dependence. function }
94102
103+ func diagnose( _ position: SourceLoc ? , _ id: DiagID ,
104+ _ args: DiagnosticArgument ... ) {
105+ context. diagnosticEngine. diagnose ( position, id, args)
106+ }
107+
95108 /// Check that this use is inside the dependence scope.
96109 func checkInScope( operand: Operand ) -> WalkResult {
97110 if let range, !range. inclusiveRangeContains ( operand. instruction) {
@@ -114,45 +127,73 @@ private struct DiagnoseDependence {
114127 }
115128
116129 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
119130
120131 if function. hasUnsafeNonEscapableResult {
121132 return . continueWalk
122133 }
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+ }
125150 reportEscaping ( operand: operand)
126151 return . abortWalk
127152 }
128153
129154 func reportError( operand: Operand , diagID: DiagID ) {
155+ onError ( )
156+
130157 // Identify the escaping variable.
131158 let escapingVar = LifetimeVariable ( dependent: operand. value, context)
132159 let varName = escapingVar. name
133160 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)
137163 } 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)
152165 }
166+ reportScope ( )
153167 // Identify the use point.
154168 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+ }
156197 }
157198}
158199
@@ -182,18 +223,41 @@ private struct LifetimeVariable {
182223 return varDecl? . userFacingName
183224 }
184225
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
196230 }
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 ) {
197261 if let arg = introducer as? Argument {
198262 self . varDecl = arg. varDecl
199263 } else {
@@ -205,17 +269,42 @@ private struct LifetimeVariable {
205269 }
206270 }
207271
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
216307 }
217- self . varDecl = nil
218- self . sourceLoc = nil
219308 }
220309}
221310
@@ -229,8 +318,8 @@ private struct LifetimeVariable {
229318///
230319/// TODO: handle stores to singly initialized temporaries like copies using a standard reaching-def analysis.
231320private struct DiagnoseDependenceWalker {
232- let diagnostics : DiagnoseDependence
233321 let context : Context
322+ var diagnostics : DiagnoseDependence
234323 var visitedValues : ValueSet
235324
236325 var function : Function { diagnostics. function }
0 commit comments