@@ -40,7 +40,10 @@ let lifetimeDependenceInsertionPass = FunctionPass(
4040
4141 for instruction in function. instructions {
4242 if let dependentApply = LifetimeDependentApply ( instruction) {
43- insertDependencies ( for: dependentApply, context)
43+ for operand in dependentApply. applySite. parameterOperands {
44+ insertParameterDependencies ( apply: dependentApply, target: operand, context)
45+ }
46+ insertResultDependencies ( for: dependentApply, context)
4447 }
4548 }
4649}
@@ -53,7 +56,7 @@ private struct LifetimeDependentApply {
5356 guard let apply = instruction as? FullApplySite else {
5457 return nil
5558 }
56- if !apply. hasResultDependence {
59+ if !apply. hasLifetimeDependence {
5760 return nil
5861 }
5962 self . applySite = apply
@@ -85,50 +88,145 @@ private struct LifetimeDependentApply {
8588}
8689
8790extension LifetimeDependentApply {
88- /// A lifetime argument copies, borrows, or mutatably borrows the
89- /// lifetime of the argument value.
90- struct LifetimeArgument {
91+ enum TargetKind {
92+ case result
93+ case inParameter
94+ case inoutParameter
95+ case yield
96+ case yieldAddress
97+ }
98+
99+ /// A lifetime argument that either inherits or creates a new scope for the lifetime of the argument value.
100+ struct LifetimeSource {
91101 let convention : LifetimeDependenceConvention
92102 let value : Value
93103 }
94104
95- func getLifetimeArguments( ) -> SingleInlineArray < LifetimeArgument > {
96- var args = SingleInlineArray < LifetimeArgument > ( )
105+ /// List of lifetime dependencies for a single target.
106+ struct LifetimeSources {
107+ let targetKind : TargetKind
108+ var sources = SingleInlineArray < LifetimeSource > ( )
109+ }
110+
111+ func getResultDependenceSources( ) -> LifetimeSources ? {
112+ guard applySite. hasResultDependence else { return nil }
113+ var sources : LifetimeSources
114+ switch applySite {
115+ case let beginApply as BeginApplyInst :
116+ if beginApply. yieldedValues. contains ( where: { $0. type. isAddress } ) {
117+ sources = LifetimeSources ( targetKind: . yieldAddress)
118+ } else {
119+ sources = LifetimeSources ( targetKind: . yield)
120+ }
121+ default :
122+ sources = LifetimeSources ( targetKind: . result)
123+ }
97124 for operand in applySite. parameterOperands {
98125 guard let dep = applySite. resultDependence ( on: operand) else {
99126 continue
100127 }
101- args. push ( LifetimeArgument ( convention: dep, value: operand. value) )
128+ sources. sources. push ( LifetimeSource ( convention: dep, value: operand. value) )
129+ }
130+ return sources
131+ }
132+
133+ func getParameterDependenceSources( target: Operand ) -> LifetimeSources ? {
134+ guard let deps = applySite. parameterDependencies ( target: target) else {
135+ return nil
136+ }
137+ var sources : LifetimeSources
138+ let convention = applySite. convention ( of: target) !
139+ switch convention {
140+ case . indirectInout, . indirectInoutAliasable, . packInout:
141+ sources = LifetimeSources ( targetKind: . inoutParameter)
142+ case . indirectIn, . indirectInGuaranteed, . indirectInCXX, . directOwned, . directUnowned, . directGuaranteed,
143+ . packOwned, . packGuaranteed:
144+ sources = LifetimeSources ( targetKind: . inParameter)
145+ case . indirectOut, . packOut:
146+ debugLog ( " \( applySite) " )
147+ fatalError ( " Lifetime dependencies cannot target \( convention) parameter " )
148+ }
149+ for (dep, operand) in zip ( deps, applySite. parameterOperands) {
150+ guard let dep = dep else {
151+ continue
152+ }
153+ sources. sources. push ( LifetimeSource ( convention: dep, value: operand. value) )
154+ }
155+ return sources
156+ }
157+
158+ // Scoped dependencies require a mark_dependence for every variable that introduces this scope.
159+ //
160+ // Inherited dependencies do not require a mark_dependence if the target is a result or yielded value. The inherited
161+ // lifetime is nonescapable, so either
162+ //
163+ // (a) the result or yield is never returned from this function
164+ //
165+ // (b) the inherited lifetime has a dependence root within this function (it comes from a dependent function argument
166+ // or scoped dependence). In this case, when that depedence root is diagnosed, the analysis will find transtive uses
167+ // of this apply's result.
168+ //
169+ // (c) the dependent value is passed to another call with a dependent inout argument, or it is stored to a yielded
170+ // address of a coroutine that has a dependent inout argument. In this case, a mark_dependence will already be created
171+ // for that inout argument.
172+ //
173+ // Parameter dependencies and yielded addresses always require a mark_dependence.
174+ static func findDependenceBases( sources: LifetimeSources , _ context: FunctionPassContext ) -> [ Value ] {
175+ var bases : [ Value ] = [ ]
176+ for source in sources. sources {
177+ switch source. convention {
178+ case . inherit:
179+ switch sources. targetKind {
180+ case . result, . yield:
181+ continue
182+ case . inParameter, . inoutParameter, . yieldAddress:
183+ _ = LifetimeDependence . visitDependenceRoots ( enclosing: source. value, context) { scope in
184+ log ( " Inherited lifetime from \( source. value) " )
185+ log ( " scope: \( scope) " )
186+ bases. append ( scope. parentValue)
187+ return . continueWalk
188+ }
189+ }
190+ case . scope:
191+ // Create a new dependence on the apply's access to the argument.
192+ for varIntoducer in gatherVariableIntroducers ( for: source. value, context) {
193+ if let scope = LifetimeDependence . Scope ( base: varIntoducer, context) {
194+ log ( " Scoped lifetime from \( source. value) " )
195+ log ( " scope: \( scope) " )
196+ bases. append ( scope. parentValue)
197+ }
198+ }
199+ }
102200 }
103- return args
201+ return bases
104202 }
105203}
106204
107205/// If the result of this apply depends on the scope of one or more
108206/// arguments, then insert a mark_dependence [unresolved] from the
109207/// result on each argument so that the result is recognized as a
110208/// dependent value within each scope.
111- private func insertDependencies( for apply: LifetimeDependentApply ,
112- _ context: FunctionPassContext ) {
113- let bases = findDependenceBases ( of: apply, context)
209+ private func insertResultDependencies( for apply: LifetimeDependentApply , _ context: FunctionPassContext ) {
210+ guard let sources = apply. getResultDependenceSources ( ) else {
211+ return
212+ }
213+ log ( " Creating dependencies for \( apply. applySite) " )
214+
215+ let bases = LifetimeDependentApply . findDependenceBases ( sources: sources, context)
216+
114217 for dependentValue in apply. applySite. resultOrYields {
115218 let builder = Builder ( before: dependentValue. nextInstruction, context)
116- insertMarkDependencies ( value: dependentValue, initializer: nil ,
117- bases: bases, builder: builder, context)
219+ insertMarkDependencies ( value: dependentValue, initializer: nil , bases: bases, builder: builder, context)
118220 }
119221 for resultOper in apply. applySite. indirectResultOperands {
120222 let accessBase = resultOper. value. accessBase
121- guard let ( initialAddress, initializingStore) =
122- accessBase. findSingleInitializer ( context) else {
223+ guard let ( initialAddress, initializingStore) = accessBase. findSingleInitializer ( context) else {
123224 continue
124225 }
125- // TODO: This is currently too strict for a diagnostic pass. We
126- // should handle/cleanup projections and casts that occur before
127- // the initializingStore. Or check in the SIL verifier that all
128- // stores without an access scope follow this form. Then convert
129- // this bail-out to an assert.
130- guard initialAddress. usesOccurOnOrAfter ( instruction: initializingStore,
131- context) else {
226+ // TODO: This might bail-out on SIL that should be diagnosed. We should handle/cleanup projections and casts that
227+ // occur before the initializingStore. Or check in the SIL verifier that all stores without an access scope follow
228+ // this form. Then convert this bail-out to an assert.
229+ guard initialAddress. usesOccurOnOrAfter ( instruction: initializingStore, context) else {
132230 continue
133231 }
134232 assert ( initializingStore == resultOper. instruction, " an indirect result is a store " )
@@ -139,29 +237,18 @@ private func insertDependencies(for apply: LifetimeDependentApply,
139237 }
140238}
141239
142- private func findDependenceBases( of apply: LifetimeDependentApply ,
143- _ context: FunctionPassContext )
144- -> [ Value ] {
240+ private func insertParameterDependencies( apply: LifetimeDependentApply , target: Operand ,
241+ _ context: FunctionPassContext ) {
242+ guard let sources = apply. getParameterDependenceSources ( target: target) else {
243+ return
244+ }
145245 log ( " Creating dependencies for \( apply. applySite) " )
146- var bases : [ Value ] = [ ]
147- for lifetimeArg in apply. getLifetimeArguments ( ) {
148- switch lifetimeArg. convention {
149- case . inherit:
150- continue
151- case . scope:
152- // Create a new dependence on the apply's access to the argument.
153- for varIntoducer in gatherVariableIntroducers ( for: lifetimeArg. value,
154- context) {
155- if let scope =
156- LifetimeDependence . Scope ( base: varIntoducer, context) {
157- log ( " Scoped lifetime from \( lifetimeArg. value) " )
158- log ( " scope: \( scope) " )
159- bases. append ( scope. parentValue)
160- }
161- }
162- }
246+
247+ let bases = LifetimeDependentApply . findDependenceBases ( sources: sources, context)
248+
249+ Builder . insert ( after: apply. applySite, context) {
250+ insertMarkDependencies ( value: target. value, initializer: nil , bases: bases, builder: $0, context)
163251 }
164- return bases
165252}
166253
167254private func insertMarkDependencies( value: Value , initializer: Instruction ? ,
0 commit comments