@@ -43,9 +43,22 @@ let lifetimeDependenceDiagnosticsPass = FunctionPass(
43
43
}
44
44
}
45
45
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
49
62
}
50
63
}
51
64
}
@@ -63,23 +76,35 @@ private func analyze(dependence: LifetimeDependence,
63
76
var range = dependence. computeRange ( context)
64
77
defer { range? . deinitialize ( ) }
65
78
79
+ var error = false
66
80
let diagnostics =
67
- DiagnoseDependence ( dependence: dependence, range: range, context: context)
81
+ DiagnoseDependence ( dependence: dependence, range: range,
82
+ onError: { error = true } , context: context)
68
83
69
84
// Check each lifetime-dependent use via a def-use visitor
70
85
var walker = DiagnoseDependenceWalker ( diagnostics, context)
71
86
defer { walker. deinitialize ( ) }
72
- _ = walker. walkDown ( root: dependence. parentValue)
87
+ _ = walker. walkDown ( root: dependence. dependentValue)
88
+
89
+ if !error {
90
+ dependence. resolve ( context)
91
+ }
73
92
}
74
93
75
94
/// Analyze and diagnose a single LifetimeDependence.
76
95
private struct DiagnoseDependence {
77
96
let dependence : LifetimeDependence
78
97
let range : InstructionRange ?
98
+ let onError : ( ) -> ( )
79
99
let context : FunctionPassContext
80
100
81
101
var function : Function { dependence. function }
82
102
103
+ func diagnose( _ position: SourceLoc ? , _ id: DiagID ,
104
+ _ args: DiagnosticArgument ... ) {
105
+ context. diagnosticEngine. diagnose ( position, id, args)
106
+ }
107
+
83
108
/// Check that this use is inside the dependence scope.
84
109
func checkInScope( operand: Operand ) -> WalkResult {
85
110
if let range, !range. inclusiveRangeContains ( operand. instruction) {
@@ -102,45 +127,73 @@ private struct DiagnoseDependence {
102
127
}
103
128
104
129
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
107
130
108
131
if function. hasUnsafeNonEscapableResult {
109
132
return . continueWalk
110
133
}
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
+ }
113
150
reportEscaping ( operand: operand)
114
151
return . abortWalk
115
152
}
116
153
117
154
func reportError( operand: Operand , diagID: DiagID ) {
155
+ onError ( )
156
+
118
157
// Identify the escaping variable.
119
158
let escapingVar = LifetimeVariable ( dependent: operand. value, context)
120
159
let varName = escapingVar. name
121
160
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)
125
163
} 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)
140
165
}
166
+ reportScope ( )
141
167
// Identify the use point.
142
168
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
+ }
144
197
}
145
198
}
146
199
@@ -170,18 +223,41 @@ private struct LifetimeVariable {
170
223
return varDecl? . userFacingName
171
224
}
172
225
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
184
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 ) {
185
261
if let arg = introducer as? Argument {
186
262
self . varDecl = arg. varDecl
187
263
} else {
@@ -193,17 +269,42 @@ private struct LifetimeVariable {
193
269
}
194
270
}
195
271
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
204
307
}
205
- self . varDecl = nil
206
- self . sourceLoc = nil
207
308
}
208
309
}
209
310
@@ -217,8 +318,8 @@ private struct LifetimeVariable {
217
318
///
218
319
/// TODO: handle stores to singly initialized temporaries like copies using a standard reaching-def analysis.
219
320
private struct DiagnoseDependenceWalker {
220
- let diagnostics : DiagnoseDependence
221
321
let context : Context
322
+ var diagnostics : DiagnoseDependence
222
323
var visitedValues : ValueSet
223
324
224
325
var function : Function { diagnostics. function }
0 commit comments