22//
33// This source file is part of the Swift.org open source project
44//
5- // Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
5+ // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors
66// Licensed under Apache License v2.0 with Runtime Library Exception
77//
88// See https://swift.org/LICENSE.txt for license information
@@ -235,7 +235,7 @@ private func insertResultDependencies(for apply: LifetimeDependentApply, _ conte
235235 guard var sources = apply. getResultDependenceSources ( ) else {
236236 return
237237 }
238- log ( " Creating dependencies for \( apply. applySite) " )
238+ log ( " Creating result dependencies for \( apply. applySite) " )
239239
240240 // Find the dependence base for each source.
241241 sources. initializeBases ( context)
@@ -246,13 +246,7 @@ private func insertResultDependencies(for apply: LifetimeDependentApply, _ conte
246246 }
247247 for resultOper in apply. applySite. indirectResultOperands {
248248 let accessBase = resultOper. value. accessBase
249- guard let ( initialAddress, initializingStore) = accessBase. findSingleInitializer ( context) else {
250- continue
251- }
252- // TODO: This might bail-out on SIL that should be diagnosed. We should handle/cleanup projections and casts that
253- // occur before the initializingStore. Or check in the SIL verifier that all stores without an access scope follow
254- // this form. Then convert this bail-out to an assert.
255- guard initialAddress. usesOccurOnOrAfter ( instruction: initializingStore, context) else {
249+ guard case let . store( initializingStore, initialAddress) = accessBase. findSingleInitializer ( context) else {
256250 continue
257251 }
258252 assert ( initializingStore == resultOper. instruction, " an indirect result is a store " )
@@ -268,7 +262,7 @@ private func insertParameterDependencies(apply: LifetimeDependentApply, target:
268262 guard var sources = apply. getParameterDependenceSources ( target: target) else {
269263 return
270264 }
271- log ( " Creating dependencies for \( apply. applySite) " )
265+ log ( " Creating parameter dependencies for \( apply. applySite) " )
272266
273267 sources. initializeBases ( context)
274268
@@ -285,8 +279,13 @@ private func insertMarkDependencies(value: Value, initializer: Instruction?,
285279 let markDep = builder. createMarkDependence (
286280 value: currentValue, base: base, kind: . Unresolved)
287281
288- // Address dependencies cannot be represented as SSA values, so it doesn not make sense to replace any uses of the
289- // dependent address. TODO: consider a separate mark_dependence_addr instruction since the semantics are different.
282+ // Address dependencies cannot be represented as SSA values, so it does not make sense to replace any uses of the
283+ // dependent address.
284+ //
285+ // TODO: either (1) insert a separate mark_dependence_addr instruction with no return value, or (2) perform data
286+ // flow to replace all reachable address uses, and if any aren't dominated by base, then insert an extra
287+ // escaping mark_dependence at this apply site that directly uses the mark_dependence [nonescaping] to force
288+ // diagnostics to fail.
290289 if !value. type. isAddress {
291290 let uses = currentValue. uses. lazy. filter {
292291 if $0. isScopeEndingUse {
@@ -300,3 +299,247 @@ private func insertMarkDependencies(value: Value, initializer: Instruction?,
300299 currentValue = markDep
301300 }
302301}
302+
303+ /// Walk up the value dependence chain to find the best-effort variable declaration. Typically called while diagnosing
304+ /// an error.
305+ ///
306+ /// Returns an array with at least one introducer value.
307+ ///
308+ /// The walk stops at:
309+ /// - a variable declaration (begin_borrow [var_decl], move_value [var_decl])
310+ /// - a begin_access for a mutable variable access
311+ /// - the value or address "root" of the dependence chain
312+ func gatherVariableIntroducers( for value: Value , _ context: Context )
313+ -> SingleInlineArray < Value >
314+ {
315+ var introducers = SingleInlineArray < Value > ( )
316+ var useDefVisitor = VariableIntroducerUseDefWalker ( context, scopedValue: value) {
317+ introducers. push ( $0)
318+ return . continueWalk
319+ }
320+ defer { useDefVisitor. deinitialize ( ) }
321+ _ = useDefVisitor. walkUp ( valueOrAddress: value)
322+ assert ( !introducers. isEmpty, " missing variable introducer " )
323+ return introducers
324+ }
325+
326+ // =============================================================================
327+ // VariableIntroducerUseDefWalker - upward walk
328+ // =============================================================================
329+
330+ /// Walk up lifetime dependencies to the first value associated with a variable declaration.
331+ ///
332+ /// To start walking:
333+ /// walkUp(valueOrAddress: Value) -> WalkResult
334+ ///
335+ /// This utility finds the value or address associated with the lvalue (variable declaration) that is passed as the
336+ /// source of a lifetime dependent argument. If no lvalue is found, then it finds the "root" of the chain of temporary
337+ /// rvalues.
338+ ///
339+ /// This "looks through" projections: a property that is either visible as a stored property or access via
340+ /// unsafe[Mutable]Address.
341+ ///
342+ /// dependsOn(lvalue.field) // finds 'lvalue' when 'field' is a stored property
343+ ///
344+ /// dependsOn(lvalue.computed) // finds the temporary value directly returned by a getter.
345+ ///
346+ /// SILGen emits temporary copies that violate lifetime dependence semantcs. This utility looks through such temporary
347+ /// copies, stopping at a value that introduces an immutable variable: move_value [var_decl] or begin_borrow [var_decl],
348+ /// or at an access of a mutable variable: begin_access [read] or begin_access [modify].
349+ ///
350+ /// In this example, the dependence "root" is copied, borrowed, and forwarded before being used as the base operand of
351+ /// `mark_dependence`. The dependence "root" is the parent of the outer-most dependence scope.
352+ ///
353+ /// %root = apply // lifetime dependence root
354+ /// %copy = copy_value %root
355+ /// %parent = begin_borrow %copy // lifetime dependence parent value
356+ /// %base = struct_extract %parent // lifetime dependence base value
357+ /// %dependent = mark_dependence [nonescaping] %value on %base
358+ ///
359+ /// VariableIntroducerUseDefWalker extends the ForwardingUseDefWalker to follow copies, moves, and
360+ /// borrows. ForwardingUseDefWalker treats these as forward-extended lifetime introducers. But they inherit a lifetime
361+ /// dependency from their operand because non-escapable values can be copied, moved, and borrowed. Nonetheless, all of
362+ /// their uses must remain within original dependence scope.
363+ ///
364+ /// # owned lifetime dependence
365+ /// %parent = apply // begin dependence scope -+
366+ /// ... |
367+ /// %1 = mark_dependence [nonescaping] %value on %parent |
368+ /// ... |
369+ /// %2 = copy_value %1 -+ |
370+ /// # forwarding instruction | |
371+ /// %3 = struct $S (%2) | forward-extended lifetime |
372+ /// | | OSSA Lifetime
373+ /// %4 = move_value %3 -+ |
374+ /// ... | forward-extended lifetime |
375+ /// %5 = begin_borrow %4 | -+ |
376+ /// # dependent use of %1 | | forward-extended lifetime|
377+ /// end_borrow %5 | -+ |
378+ /// destroy_value %4 -+ |
379+ /// ... |
380+ /// destroy_value %parent // end dependence scope -+
381+ ///
382+ /// All of the dependent uses including `end_borrow %5` and `destroy_value %4` must be before the end of the dependence
383+ /// scope: `destroy_value %parent`. In this case, the dependence parent is an owned value, so the scope is simply the
384+ /// value's OSSA lifetime.
385+ struct VariableIntroducerUseDefWalker : ForwardingUseDefWalker {
386+ // The ForwardingUseDefWalker's context is the most recent lifetime owner.
387+ typealias PathContext = Value ?
388+
389+ let context : Context
390+
391+ // If the scoped value is trivial, then only the variable's lexical scope is relevant, and access scopes can be
392+ // ignored.
393+ let isTrivialScope : Bool
394+
395+ // This visited set is only really needed for instructions with
396+ // multiple results, including phis.
397+ private var visitedValues : ValueSet
398+
399+ // Call \p visit rather than calling this directly.
400+ private let visitorClosure : ( Value ) -> WalkResult
401+
402+ init ( _ context: Context , scopedValue: Value , _ visitor: @escaping ( Value ) -> WalkResult ) {
403+ self . context = context
404+ self . isTrivialScope = scopedValue. type. isAddress
405+ ? scopedValue. type. objectType. isTrivial ( in: scopedValue. parentFunction)
406+ : scopedValue. isTrivial ( context)
407+ self . visitedValues = ValueSet ( context)
408+ self . visitorClosure = visitor
409+ }
410+
411+ mutating func deinitialize( ) {
412+ visitedValues. deinitialize ( )
413+ }
414+
415+ mutating func needWalk( for value: Value , _ owner: Value ? ) -> Bool {
416+ visitedValues. insert ( value)
417+ }
418+
419+ mutating func introducer( _ value: Value , _ owner: Value ? ) -> WalkResult {
420+ return visitorClosure ( value)
421+ }
422+
423+ mutating func walkUp( valueOrAddress: Value ) -> WalkResult {
424+ if valueOrAddress. type. isAddress {
425+ return walkUp ( address: valueOrAddress)
426+ }
427+ return walkUp ( newLifetime: valueOrAddress)
428+ }
429+ }
430+
431+ // Helpers
432+ extension VariableIntroducerUseDefWalker {
433+ mutating func walkUp( newLifetime: Value ) -> WalkResult {
434+ let newOwner = newLifetime. ownership == . owned ? newLifetime : nil
435+ return walkUp ( value: newLifetime, newOwner)
436+ }
437+
438+ mutating func walkUp( value: Value , _ owner: Value ? ) -> WalkResult {
439+ // Check for variable introducers: move_value, begin_value, before following OwnershipTransitionInstruction.
440+ if let inst = value. definingInstruction, VariableScopeInstruction ( inst) != nil {
441+ return visitorClosure ( value)
442+ }
443+ switch value. definingInstruction {
444+ case let transition as OwnershipTransitionInstruction :
445+ return walkUp ( newLifetime: transition. operand. value)
446+ case let load as LoadInstruction :
447+ return walkUp ( address: load. address)
448+ default :
449+ break
450+ }
451+ // If the dependence chain has a phi, consider it a root. Dependence roots dominate all dependent values.
452+ if Phi ( value) != nil {
453+ return introducer ( value, owner)
454+ }
455+ // ForwardingUseDefWalker will callback to introducer() when it finds no forwarding instruction.
456+ return walkUpDefault ( forwarded: value, owner)
457+ }
458+
459+ // Handle temporary allocations and access scopes.
460+ mutating func walkUp( address: Value ) -> WalkResult {
461+ let accessBaseAndScopes = address. accessBaseWithScopes
462+ // Continue walking for some kinds of access base.
463+ switch accessBaseAndScopes. base {
464+ case . box, . global, . class, . tail, . pointer, . index, . unidentified:
465+ break
466+ case let . stack( allocStack) :
467+ if allocStack. varDecl == nil {
468+ // Ignore temporary stack locations. Their access scopes do not affect lifetime dependence.
469+ return walkUp ( stackInitializer: allocStack, at: address)
470+ }
471+ case let . argument( arg) :
472+ // Ignore access scopes for @in or @in_guaranteed arguments when all scopes are reads. Do not ignore a [read]
473+ // access of an inout argument or outer [modify]. Mutation later with the outer scope could invalidate the
474+ // borrowed state in this narrow scope. Do not ignore any mark_depedence on the address.
475+ if arg. convention. isIndirectIn && accessBaseAndScopes. isOnlyReadAccess {
476+ return introducer ( arg, nil )
477+ }
478+ // @inout arguments may be singly initialized (when no modification exists in this function), but this is not
479+ // relevant here because they require nested access scopes which can never be ignored.
480+ case let . yield( yieldedAddress) :
481+ // Ignore access scopes for @in or @in_guaranteed yields when all scopes are reads.
482+ let apply = yieldedAddress. definingInstruction as! FullApplySite
483+ if apply. convention ( of: yieldedAddress) . isIndirectIn && accessBaseAndScopes. isOnlyReadAccess {
484+ return introducer ( yieldedAddress, nil )
485+ }
486+ case . storeBorrow( let sb) :
487+ // Walk up through a store into a temporary.
488+ if accessBaseAndScopes. scopes. isEmpty,
489+ case . stack = sb. destinationOperand. value. accessBase {
490+ return walkUp ( newLifetime: sb. source)
491+ }
492+ }
493+ // Skip the access scope for unsafe[Mutable]Address. Treat it like a projection of 'self' rather than a separate
494+ // variable access.
495+ if case let . access( innerAccess) = accessBaseAndScopes. scopes. first,
496+ let addressorSelf = innerAccess. unsafeAddressorSelf {
497+ return walkUp ( valueOrAddress: addressorSelf)
498+ }
499+ // Ignore the acces scope for trivial values regardless of whether it is singly-initialized. Trivial values do not
500+ // need to be kept alive in memory and can be safely be overwritten in the same scope. Lifetime dependence only
501+ // cares that the loaded value is within the lexical scope of the trivial value's variable declaration. Rather than
502+ // skipping all access scopes, call 'walkUp' on each nested access in case one of them needs to redirect the walk,
503+ // as required for 'access.unsafeAddressorSelf'.
504+ if isTrivialScope {
505+ switch accessBaseAndScopes. scopes. first {
506+ case . none, . base:
507+ break
508+ case let . access( beginAccess) :
509+ return walkUp ( address: beginAccess. address)
510+ case let . dependence( markDep) :
511+ return walkUp ( address: markDep. value)
512+ }
513+ }
514+ return introducer ( accessBaseAndScopes. enclosingAccess. address ?? address, nil )
515+ }
516+
517+ // Handle singly-initialized temporary stack locations.
518+ mutating func walkUp( stackInitializer allocStack: AllocStackInst , at address: Value ) -> WalkResult {
519+ guard let initializer = allocStack. accessBase. findSingleInitializer ( context) else {
520+ return introducer ( address, nil )
521+ }
522+ if case let . store( store, _) = initializer {
523+ switch store {
524+ case let store as StoringInstruction :
525+ return walkUp ( newLifetime: store. source)
526+ case let srcDestInst as SourceDestAddrInstruction :
527+ return walkUp ( address: srcDestInst. destination)
528+ case let apply as FullApplySite :
529+ if let f = apply. referencedFunction, f. isConvertPointerToPointerArgument {
530+ return walkUp ( address: apply. parameterOperands [ 0 ] . value)
531+ }
532+ default :
533+ break
534+ }
535+ }
536+ return introducer ( address, nil )
537+ }
538+ }
539+
540+ let variableIntroducerTest = FunctionTest ( " variable_introducer " ) {
541+ function, arguments, context in
542+ let value = arguments. takeValue ( )
543+ print ( " Variable introducers of: \( value) " )
544+ print ( gatherVariableIntroducers ( for: value, context) )
545+ }
0 commit comments