@@ -124,11 +124,102 @@ let lifetimeDependenceScopeFixupPass = FunctionPass(
124124 }
125125 let args = scopeExtension. findArgumentDependencies ( )
126126
127+ // If the scope cannot be extended to the caller, this must be the outermost dependency level.
128+ // Insert end_cow_mutation_addr if needed.
129+ if args. isEmpty {
130+ createEndCOWMutationIfNeeded ( lifetimeDep: newLifetimeDep, context)
131+ }
132+
127133 // Redirect the dependence base to the function arguments. This may create additional mark_dependence instructions.
128134 markDep. redirectFunctionReturn ( to: args, context)
129135 }
130136}
131137
138+ private extension Type {
139+ func mayHaveMutableSpan( in function: Function , _ context: FunctionPassContext ) -> Bool {
140+ if hasArchetype {
141+ return true
142+ }
143+ if isBuiltinType {
144+ return false
145+ }
146+ // Only result types that are nominal can have a MutableSpan derived from an inout array access.
147+ if nominal == nil {
148+ return false
149+ }
150+ if nominal == context. swiftMutableSpan {
151+ return true
152+ }
153+ if isStruct {
154+ guard let fields = getNominalFields ( in: function) else {
155+ return false
156+ }
157+ return fields. contains { $0. mayHaveMutableSpan ( in: function, context) }
158+ }
159+ if isTuple {
160+ return tupleElements. contains { $0. mayHaveMutableSpan ( in: function, context) }
161+ }
162+ if isEnum {
163+ guard let cases = getEnumCases ( in: function) else {
164+ return true
165+ }
166+ return cases. contains { $0. payload? . mayHaveMutableSpan ( in: function, context) ?? false }
167+ }
168+ // Classes cannot be ~Escapable, therefore cannot hold a MutableSpan.
169+ if isClass {
170+ return false
171+ }
172+ return false
173+ }
174+ }
175+
176+ /// Insert end_cow_mutation_addr for lifetime dependent values that maybe of type MutableSpan and depend on a mutable address.
177+ private func createEndCOWMutationIfNeeded( lifetimeDep: LifetimeDependence , _ context: FunctionPassContext ) {
178+ var scoped : ScopedInstruction
179+
180+ // Handle cases which generate mutable addresses: begin_access [modify] and yield &
181+ switch lifetimeDep. scope {
182+ case let . access( beginAccess) :
183+ if beginAccess. accessKind != . modify {
184+ return
185+ }
186+ scoped = beginAccess
187+ case let . yield( value) :
188+ let beginApply = value. definingInstruction as! BeginApplyInst
189+ if value == beginApply. token {
190+ return
191+ }
192+ if beginApply. convention ( of: value as! MultipleValueInstructionResult ) != . indirectInout {
193+ return
194+ }
195+ scoped = beginApply
196+ // None of the below cases can generate a mutable address.
197+ case let . owned:
198+ fallthrough
199+ case let . borrowed:
200+ fallthrough
201+ case let . local:
202+ fallthrough
203+ case let . initialized:
204+ fallthrough
205+ case let . caller:
206+ fallthrough
207+ case let . global:
208+ fallthrough
209+ case let . unknown:
210+ return
211+ }
212+
213+ guard lifetimeDep. dependentValue. type. mayHaveMutableSpan ( in: lifetimeDep. dependentValue. parentFunction, context) else {
214+ return
215+ }
216+
217+ for endInstruction in scoped. endInstructions {
218+ let builder = Builder ( before: endInstruction, context)
219+ builder. createEndCOWMutationAddr ( address: lifetimeDep. parentValue)
220+ }
221+ }
222+
132223private extension MarkDependenceInstruction {
133224 /// Rewrite the mark_dependence base operand to ignore inner borrow scopes (begin_borrow, load_borrow).
134225 ///
@@ -194,7 +285,7 @@ private extension MarkDependenceAddrInst {
194285 }
195286}
196287
197- /// A scope extension is a set of nested scopes and their owners. The owner is a value that represents ownerhip of
288+ /// A scope extension is a set of nested scopes and their owners. The owner is a value that represents ownership of
198289/// the outermost scopes, which cannot be extended; it limits how far the nested scopes can be extended.
199290private struct ScopeExtension {
200291 let context : FunctionPassContext
0 commit comments