@@ -131,7 +131,7 @@ private struct DiagnoseDependence {
131
131
reportEscaping ( operand: operand)
132
132
}
133
133
134
- func checkInoutResult( argument inoutArg: FunctionArgument , result operand : Operand ) -> WalkResult {
134
+ func checkInoutResult( argument inoutArg: FunctionArgument ) -> WalkResult {
135
135
// Check that the parameter dependence for this inout argument is the same as the current dependence scope.
136
136
if let sourceArg = dependence. scope. parentValue as? FunctionArgument {
137
137
// If the inout result is also the inout source, then it's always ok.
@@ -140,13 +140,28 @@ private struct DiagnoseDependence {
140
140
}
141
141
if function. argumentConventions. getDependence ( target: inoutArg. index, source: sourceArg. index) != nil {
142
142
// The inout result depends on a lifetime that is inherited or borrowed in the caller.
143
+ log ( " has dependent inout argument: \( inoutArg) " )
143
144
return . continueWalk
144
145
}
145
146
}
146
- reportEscaping ( operand: operand)
147
147
return . abortWalk
148
148
}
149
149
150
+ func checkStoreToYield( address: Value ) -> WalkResult {
151
+ var walker = DependentAddressUseDefWalker ( context: context, diagnostics: self )
152
+ return walker. walkUp ( address: address)
153
+ }
154
+
155
+ func checkYield( operand: Operand ) -> WalkResult {
156
+ switch dependence. scope {
157
+ case . caller:
158
+ return checkFunctionResult ( operand: operand)
159
+ default :
160
+ // local scopes can be yielded without escaping.
161
+ return . continueWalk
162
+ }
163
+ }
164
+
150
165
func checkFunctionResult( operand: Operand ) -> WalkResult {
151
166
152
167
if function. hasUnsafeNonEscapableResult {
@@ -174,9 +189,9 @@ private struct DiagnoseDependence {
174
189
// The returned value depends on a lifetime that is inherited or
175
190
// borrowed in the caller. The lifetime of the argument value
176
191
// itself is irrelevant here.
192
+ log ( " has dependent function result " )
177
193
return . continueWalk
178
194
}
179
- reportEscaping ( operand: operand)
180
195
return . abortWalk
181
196
}
182
197
@@ -328,6 +343,50 @@ private struct LifetimeVariable {
328
343
}
329
344
}
330
345
346
+ /// Walk up an address into which a dependent value has been stored. If any address in the use-def chain is a
347
+ /// mark_dependence, follow the depenedence base rather than the forwarded value. If any of the dependence bases in
348
+ /// within the current scope is with (either local checkInoutResult), then storing a value into that address is
349
+ /// nonescaping.
350
+ ///
351
+ /// This supports store-to-yield. Storing to a yield is an escape unless the yielded memory location depends on another
352
+ /// lifetime that already depends on the current scope. When setter depends on 'newValue', 'newValue' is stored to the
353
+ /// yielded address, and the yielded addrses depends on the lifetime of 'self'. A mark_dependence should have already
354
+ /// been inserted for that lifetime depenence:
355
+ ///
356
+ /// (%a, %t) = begin_apply %f(%self)
357
+ /// : $@yield_once @convention(method) (@inout Self) -> _inherit(0) @yields @inout Self.field
358
+ /// %dep = mark_dependence [nonescaping] %yield_addr on %self
359
+ /// store %newValue to [assign] %dep : $*Self.field
360
+ ///
361
+ private struct DependentAddressUseDefWalker {
362
+ let context : Context
363
+ var diagnostics : DiagnoseDependence
364
+ }
365
+
366
+ extension DependentAddressUseDefWalker : AddressUseDefWalker {
367
+ // Follow the dependence base, not the forwarded value. Similar to the way LifetimeDependenceUseDefWalker handles
368
+ // MarkDependenceInst.
369
+ mutating func walkUp( address: Value , path: UnusedWalkingPath = UnusedWalkingPath ( ) ) -> WalkResult {
370
+ if let markDep = address as? MarkDependenceInst , let addressDep = LifetimeDependence ( markDep, context) {
371
+ switch addressDep. scope {
372
+ case let . caller( arg) :
373
+ return diagnostics. checkInoutResult ( argument: arg)
374
+ case . owned, . initialized:
375
+ // Storing a nonescaping value to local memory cannot escape.
376
+ return . abortWalk
377
+ default :
378
+ break
379
+ }
380
+ }
381
+ return walkUpDefault ( address: address, path: UnusedWalkingPath ( ) )
382
+ }
383
+
384
+ mutating func rootDef( address: Value , path: UnusedWalkingPath ) -> WalkResult {
385
+ // This only searches for mark_dependence scopes.
386
+ return . continueWalk
387
+ }
388
+ }
389
+
331
390
/// Walk down lifetime depenence uses. For each check that all dependent
332
391
/// leaf uses are non-escaping and within the dependence scope. The walk
333
392
/// starts with add address for .access dependencies. The walk can
@@ -380,20 +439,44 @@ extension DiagnoseDependenceWalker : LifetimeDependenceDefUseWalker {
380
439
}
381
440
382
441
mutating func inoutDependence( argument: FunctionArgument , on operand: Operand ) -> WalkResult {
383
- return diagnostics. checkInoutResult ( argument: argument, result: operand)
442
+ if diagnostics. checkInoutResult ( argument: argument) == . abortWalk {
443
+ diagnostics. reportEscaping ( operand: operand)
444
+ return . abortWalk
445
+ }
446
+ return . continueWalk
384
447
}
385
448
386
449
mutating func returnedDependence( result: Operand ) -> WalkResult {
387
- return diagnostics. checkFunctionResult ( operand: result)
450
+ if diagnostics. checkFunctionResult ( operand: result) == . abortWalk {
451
+ diagnostics. reportEscaping ( operand: result)
452
+ return . abortWalk
453
+ }
454
+ return . continueWalk
388
455
}
389
456
390
457
mutating func returnedDependence( address: FunctionArgument ,
391
458
on operand: Operand ) -> WalkResult {
392
- return diagnostics. checkFunctionResult ( operand: operand)
459
+ if diagnostics. checkFunctionResult ( operand: operand) == . abortWalk {
460
+ diagnostics. reportEscaping ( operand: operand)
461
+ return . abortWalk
462
+ }
463
+ return . continueWalk
393
464
}
394
465
395
466
mutating func yieldedDependence( result: Operand ) -> WalkResult {
396
- return diagnostics. checkFunctionResult ( operand: result)
467
+ if diagnostics. checkYield ( operand: result) == . abortWalk {
468
+ diagnostics. reportEscaping ( operand: result)
469
+ return . abortWalk
470
+ }
471
+ return . continueWalk
472
+ }
473
+
474
+ mutating func storeToYieldDependence( address: Value , of operand: Operand ) -> WalkResult {
475
+ if diagnostics. checkStoreToYield ( address: address) == . abortWalk {
476
+ diagnostics. reportEscaping ( operand: operand)
477
+ return . abortWalk
478
+ }
479
+ return . continueWalk
397
480
}
398
481
399
482
// Override AddressUseVisitor here because LifetimeDependenceDefUseWalker
0 commit comments