@@ -328,6 +328,75 @@ private struct VisitForwardedUses : ForwardingDefUseWalker {
328
328
}
329
329
}
330
330
331
+ /// Walk all uses of partial_apply [on_stack] that may propagate the closure context. Gather each FullApplySite that
332
+ /// either invokes this closure as its callee, or passes the closure as an argument to another function.
333
+ ///
334
+ /// Start walk:
335
+ /// walkDown(closure:)
336
+ ///
337
+ /// This is a subset of the functionality in LifetimeDependenceDefUseWalker, but significantly simpler. This avoids
338
+ /// traversing lifetime dependencies that do not propagate context. For example, a mark_dependence on a closure extends
339
+ /// its lifetime but cannot introduce any new uses of the closure context.
340
+ struct NonEscapingClosureDefUseWalker {
341
+ let context : Context
342
+ var visitedValues : ValueSet
343
+ var applyOperandStack : Stack < Operand >
344
+
345
+ /// `visitor` takes an operand whose instruction is always a FullApplySite.
346
+ init ( _ context: Context ) {
347
+ self . context = context
348
+ self . visitedValues = ValueSet ( context)
349
+ self . applyOperandStack = Stack ( context)
350
+ }
351
+
352
+ mutating func deinitialize( ) {
353
+ visitedValues. deinitialize ( )
354
+ applyOperandStack. deinitialize ( )
355
+ }
356
+
357
+ mutating func walkDown( closure: PartialApplyInst ) -> WalkResult {
358
+ assert ( closure. isOnStack)
359
+ return walkDownUses ( of: closure, using: nil )
360
+ }
361
+
362
+ mutating func closureContextLeafUse( of operand: Operand ) -> WalkResult {
363
+ switch operand. instruction {
364
+ case is FullApplySite :
365
+ applyOperandStack. push ( operand)
366
+ return . continueWalk
367
+ case is MarkDependenceInst , is FixLifetimeInst , is DestroyValueInst :
368
+ return . continueWalk
369
+ default :
370
+ if operand. instruction. isIncidentalUse {
371
+ return . continueWalk
372
+ }
373
+ // Escaping or unexpected closure use. Expected escaping uses include ReturnInst with a lifetime-dependent result.
374
+ //
375
+ // TODO: Check in the SIL verifier that all uses are expected.
376
+ return . abortWalk
377
+ }
378
+ }
379
+ }
380
+
381
+ extension NonEscapingClosureDefUseWalker : ForwardingDefUseWalker {
382
+ mutating func needWalk( for value: Value ) -> Bool {
383
+ visitedValues. insert ( value)
384
+ }
385
+
386
+ mutating func nonForwardingUse( of operand: Operand ) -> WalkResult {
387
+ // Nonescaping closures may be moved, copied, or borrowed.
388
+ if let transition = operand. instruction as? OwnershipTransitionInstruction {
389
+ return walkDownUses ( of: transition, using: operand)
390
+ }
391
+ // Otherwise, assume the use cannot propagate the closure context.
392
+ return closureContextLeafUse ( of: operand)
393
+ }
394
+
395
+ mutating func deadValue( _ value: Value , using operand: Operand ? ) -> WalkResult {
396
+ return . continueWalk
397
+ }
398
+ }
399
+
331
400
let forwardingUseDefTest = FunctionTest ( " forwarding_use_def_test " ) {
332
401
function, arguments, context in
333
402
let value = arguments. takeValue ( )
0 commit comments