@@ -131,7 +131,7 @@ private struct DiagnoseDependence {
131131 reportEscaping ( operand: operand)
132132 }
133133
134- func checkInoutResult( argument inoutArg: FunctionArgument , result operand : Operand ) -> WalkResult {
134+ func checkInoutResult( argument inoutArg: FunctionArgument ) -> WalkResult {
135135 // Check that the parameter dependence for this inout argument is the same as the current dependence scope.
136136 if let sourceArg = dependence. scope. parentValue as? FunctionArgument {
137137 // If the inout result is also the inout source, then it's always ok.
@@ -140,13 +140,28 @@ private struct DiagnoseDependence {
140140 }
141141 if function. argumentConventions. getDependence ( target: inoutArg. index, source: sourceArg. index) != nil {
142142 // The inout result depends on a lifetime that is inherited or borrowed in the caller.
143+ log ( " has dependent inout argument: \( inoutArg) " )
143144 return . continueWalk
144145 }
145146 }
146- reportEscaping ( operand: operand)
147147 return . abortWalk
148148 }
149149
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+
150165 func checkFunctionResult( operand: Operand ) -> WalkResult {
151166
152167 if function. hasUnsafeNonEscapableResult {
@@ -174,9 +189,9 @@ private struct DiagnoseDependence {
174189 // The returned value depends on a lifetime that is inherited or
175190 // borrowed in the caller. The lifetime of the argument value
176191 // itself is irrelevant here.
192+ log ( " has dependent function result " )
177193 return . continueWalk
178194 }
179- reportEscaping ( operand: operand)
180195 return . abortWalk
181196 }
182197
@@ -328,6 +343,50 @@ private struct LifetimeVariable {
328343 }
329344}
330345
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+
331390/// Walk down lifetime depenence uses. For each check that all dependent
332391/// leaf uses are non-escaping and within the dependence scope. The walk
333392/// starts with add address for .access dependencies. The walk can
@@ -380,20 +439,44 @@ extension DiagnoseDependenceWalker : LifetimeDependenceDefUseWalker {
380439 }
381440
382441 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
384447 }
385448
386449 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
388455 }
389456
390457 mutating func returnedDependence( address: FunctionArgument ,
391458 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
393464 }
394465
395466 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
397480 }
398481
399482 // Override AddressUseVisitor here because LifetimeDependenceDefUseWalker
0 commit comments