@@ -66,6 +66,8 @@ private func log(prefix: Bool = true, _ message: @autoclosure () -> String) {
66
66
/// Walk up the value dependence chain to find the best-effort
67
67
/// variable declaration. Typically called while diagnosing an error.
68
68
///
69
+ /// Returns an array with at least one introducer value.
70
+ ///
69
71
/// The walk stops at:
70
72
/// - an address
71
73
/// - a variable declaration (begin_borrow [var_decl], move_value [var_decl])
@@ -85,6 +87,7 @@ func gatherVariableIntroducers(for value: Value, _ context: Context)
85
87
}
86
88
defer { useDefVisitor. deinitialize ( ) }
87
89
_ = useDefVisitor. walkUp ( valueOrAddress: value)
90
+ assert ( !introducers. isEmpty, " missing variable introducer " )
88
91
return introducers
89
92
}
90
93
@@ -104,7 +107,6 @@ func gatherVariableIntroducers(for value: Value, _ context: Context)
104
107
/// A lifetime dependence identifies its parent value, the kind of
105
108
/// scope that the parent value represents, and a dependent value.
106
109
struct LifetimeDependence : CustomStringConvertible {
107
- // TODO: handle trivial values based on variable binding
108
110
enum Scope : CustomStringConvertible {
109
111
/// A guaranteed or inout argument whose scope is provided by the caller
110
112
/// and covers the entire function and any dependent results or yields.
@@ -113,9 +115,10 @@ struct LifetimeDependence : CustomStringConvertible {
113
115
case access( BeginAccessInst )
114
116
/// A coroutine.
115
117
case yield( Value )
116
- /// An owned value whose OSSA lifetime encloses nonescapable values
118
+ /// An owned value whose OSSA lifetime encloses nonescapable values, or a trivial variable introduced by move_value.
117
119
case owned( Value )
118
- /// An borrowed value whose OSSA lifetime encloses nonescapable values
120
+ /// An borrowed value whose OSSA lifetime encloses nonescapable values, or a trivial variable introduced by
121
+ /// begin_borrow.
119
122
case borrowed( BeginBorrowValue )
120
123
/// Singly-initialized addressable storage (likely for an
121
124
/// immutable address-only value). The lifetime extends until the
@@ -128,8 +131,7 @@ struct LifetimeDependence : CustomStringConvertible {
128
131
/// If `initializingStore` is nil, then the `initialAddress` is
129
132
/// initialized on function entry.
130
133
case initialized( initialAddress: Value , initializingStore: Instruction ? )
131
- // TODO: Add SIL verification that no mark_depedence [unresolved] has an unknown LifetimeDependence.
132
- // This currently requires stack allocations to be singly initialized.
134
+ // Unknown includes: escapable values with local var_decl, stack allocations that are not singly initialized.
133
135
case unknown( Value )
134
136
135
137
var parentValue : Value {
@@ -202,7 +204,7 @@ extension LifetimeDependence {
202
204
if arg. isIndirectResult {
203
205
return nil
204
206
}
205
- self . scope = Scope ( base: arg, context) !
207
+ self . scope = Scope ( base: arg, context)
206
208
self . dependentValue = arg
207
209
}
208
210
@@ -219,7 +221,7 @@ extension LifetimeDependence {
219
221
return nil
220
222
}
221
223
assert ( value. ownership == . owned, " unsafe apply result must be owned " )
222
- self . scope = Scope ( base: value, context) !
224
+ self . scope = Scope ( base: value, context)
223
225
self . dependentValue = value
224
226
}
225
227
@@ -238,14 +240,11 @@ extension LifetimeDependence {
238
240
/// For any LifetimeDependence constructed from a mark_dependence, its `dependentValue` will be the result of the
239
241
/// mark_dependence.
240
242
///
241
- /// Returns 'nil' for dependence on a trivial value .
243
+ /// Returns 'nil' for unknown dependence .
242
244
init ? ( _ markDep: MarkDependenceInst , _ context: some Context ) {
243
245
switch markDep. dependenceKind {
244
246
case . Unresolved, . NonEscaping:
245
- guard let scope = Scope ( base: markDep. base, context) else {
246
- return nil
247
- }
248
- self . scope = scope
247
+ self . scope = Scope ( base: markDep. base, context)
249
248
self . dependentValue = markDep
250
249
case . Escaping:
251
250
return nil
@@ -294,65 +293,45 @@ extension LifetimeDependence.Scope {
294
293
/// directly defines the parent lifetime. If `base` is guaranteed, then it must have a single borrow introducer, which
295
294
/// defines the parent lifetime. `base` must not be derived from a guaranteed phi or forwarded (via struct/tuple) from
296
295
/// multiple guaranteed values.
297
- ///
298
- /// Returns 'nil' for dependence on a trivial value.
299
- init ? ( base: Value , _ context: some Context ) {
296
+ init ( base: Value , _ context: some Context ) {
300
297
if base. type. isAddress {
301
- guard let scope = Self ( address: base, context) else {
302
- return nil
303
- }
304
- self = scope
298
+ self = Self ( address: base, context)
305
299
return
306
300
}
307
301
switch base. ownership {
308
302
case . owned:
309
303
self = . owned( base)
310
304
return
311
305
case . guaranteed:
312
- guard let scope = Self ( guaranteed: base, context) else {
313
- return nil
314
- }
315
- self = scope
306
+ self = Self ( guaranteed: base, context)
316
307
case . none:
317
- // lifetime dependence requires a nontrivial value
318
- return nil
308
+ self = Self ( variable: base, context)
319
309
case . unowned:
320
310
self = . unknown( base)
321
311
}
322
312
}
323
313
324
- /// Returns 'nil' for dependence on a trivial value.
325
- private init ? ( address: Value , _ context: some Context ) {
314
+ private init ( address: Value , _ context: some Context ) {
326
315
switch address. enclosingAccessScope {
327
316
case let . scope( access) :
328
317
self = . access( access)
329
318
case let . base( accessBase) :
330
- guard let scope = Self ( accessBase: accessBase, address: address, context) else {
331
- return nil
332
- }
333
- self = scope
319
+ self = Self ( accessBase: accessBase, address: address, context)
334
320
}
335
321
}
336
322
337
- /// Returns 'nil' for dependence on a trivial value.
338
- init ? ( accessBase: AccessBase , address: Value , _ context: some Context ) {
323
+ init ( accessBase: AccessBase , address: Value , _ context: some Context ) {
339
324
switch accessBase {
340
325
case let . box( projectBox) :
341
326
// Note: the box may be in a borrow scope.
342
- guard let scope = Self ( base: projectBox. operand. value, context) else {
343
- return nil
344
- }
345
- self = scope
327
+ self = Self ( base: projectBox. operand. value, context)
346
328
case let . stack( allocStack) :
347
329
self = Self ( allocation: allocStack, context)
348
330
case . global:
349
331
self = . unknown( address)
350
332
case . class, . tail:
351
333
let refElt = address as! UnaryInstruction
352
- guard let scope = Self ( guaranteed: refElt. operand. value, context) else {
353
- return nil
354
- }
355
- self = scope
334
+ self = Self ( guaranteed: refElt. operand. value, context)
356
335
case let . argument( arg) :
357
336
if arg. convention. isIndirectIn {
358
337
self = . initialized( initialAddress: arg, initializingStore: nil )
@@ -368,24 +347,23 @@ extension LifetimeDependence.Scope {
368
347
case let . yield( result) :
369
348
self = Self ( yield: result)
370
349
case . storeBorrow( let sb) :
371
- guard let scope = Self ( base: sb. source, context) else {
372
- return nil
373
- }
374
- self = scope
350
+ self = Self ( base: sb. source, context)
375
351
case . pointer, . index, . unidentified:
376
352
self = . unknown( address)
377
353
}
378
354
}
379
355
380
- /// Returns 'nil' for dependence on a trivial value.
381
- private init ? ( guaranteed base: Value , _ context: some Context ) {
382
- // If introducers is empty, then the dependence is on a trivial value, so
383
- // there is no dependence scope.
384
- //
356
+ private init ( guaranteed base: Value , _ context: some Context ) {
385
357
// TODO: Add a SIL verifier check that a mark_dependence [nonescaping]
386
358
// base is never a guaranteed phi.
387
359
var iter = base. getBorrowIntroducers ( context) . makeIterator ( )
388
- guard let beginBorrow = iter. next ( ) else { return nil }
360
+ // If no borrow introducer was found, then this is a borrow of a trivial value. Since we can assume a single
361
+ // introducer here, then this is the only condition under which we have a trivial introducer.
362
+ guard let beginBorrow = iter. next ( ) else {
363
+ self = Self ( variable: base, context)
364
+ return
365
+ }
366
+ // TODO: will we need to handle tuple/struct with multiple scopes?
389
367
assert ( iter. next ( ) == nil ,
390
368
" guaranteed phis not allowed when diagnosing lifetime dependence " )
391
369
switch beginBorrow {
@@ -400,6 +378,24 @@ extension LifetimeDependence.Scope {
400
378
}
401
379
}
402
380
381
+ private init ( variable base: Value , _ context: some Context ) {
382
+ guard let introducer = gatherVariableIntroducers ( for: base, context) . singleElement else {
383
+ // TODO: do we need to handle multiple introducers in case of a tuple/struct?
384
+ self = . unknown( base)
385
+ return
386
+ }
387
+ switch introducer {
388
+ case let arg as FunctionArgument :
389
+ self = . caller( arg)
390
+ case let bbi as BeginBorrowInst :
391
+ self = . borrowed( BeginBorrowValue ( bbi) !)
392
+ case is MoveValueInst :
393
+ self = . owned( introducer)
394
+ default :
395
+ self = . unknown( introducer)
396
+ }
397
+ }
398
+
403
399
private init ( yield result: MultipleValueInstructionResult ) {
404
400
// Consider an @in yield an .initialized scope. We must find the destroys.
405
401
let apply = result. parentInstruction as! FullApplySite
@@ -431,7 +427,7 @@ extension LifetimeDependence.Scope {
431
427
if bb. isFromVarDecl {
432
428
return self
433
429
}
434
- return LifetimeDependence . Scope ( base: bb. borrowedValue, context) ? . ignoreBorrowScope ( context)
430
+ return LifetimeDependence . Scope ( base: bb. borrowedValue, context) . ignoreBorrowScope ( context)
435
431
case let . loadBorrow( lb) :
436
432
return LifetimeDependence . Scope ( base: lb. address, context)
437
433
default :
@@ -443,7 +439,7 @@ extension LifetimeDependence.Scope {
443
439
extension LifetimeDependence . Scope {
444
440
/// Compute the range of the dependence scope.
445
441
///
446
- /// Returns nil if the dependence scope covers the entire function.
442
+ /// Returns nil if the dependence scope covers the entire function. Returns an empty range for an unknown scope.
447
443
///
448
444
/// Note: The caller must deinitialize the returned range.
449
445
func computeRange( _ context: Context ) -> InstructionRange ? {
@@ -480,11 +476,12 @@ extension LifetimeDependence.Scope {
480
476
return InstructionRange ( for: value, context)
481
477
}
482
478
}
483
-
484
- private static func computeInitializedRange ( initialAddress : Value ,
485
- initializingStore: Instruction ? ,
479
+
480
+ // !!! - handle allocations of trivial values: no destroy. Use the dealloc in that case?
481
+ private static func computeInitializedRange ( initialAddress : Value , initializingStore: Instruction ? ,
486
482
_ context: Context )
487
483
-> InstructionRange {
484
+
488
485
assert ( initialAddress. type. isAddress)
489
486
490
487
var range : InstructionRange
@@ -619,7 +616,12 @@ struct VariableIntroducerUseDefWalker : LifetimeDependenceUseDefWalker {
619
616
620
617
mutating func walkUp( value: Value , _ owner: Value ? ) -> WalkResult {
621
618
if let inst = value. definingInstruction, VariableScopeInstruction ( inst) != nil {
622
- return introducer ( value, owner)
619
+ return visitorClosure ( value)
620
+ }
621
+ // Finding a variable introducer requires following the mark_dependence forwarded value, not the base value like the
622
+ // default LifetimeDependenceUseDefWalker.
623
+ if value is MarkDependenceInst {
624
+ return walkUpDefault ( forwarded: value, owner)
623
625
}
624
626
return walkUpDefault ( dependent: value, owner: owner)
625
627
}
@@ -685,6 +687,9 @@ struct VariableIntroducerUseDefWalker : LifetimeDependenceUseDefWalker {
685
687
protocol LifetimeDependenceUseDefWalker : ForwardingUseDefWalker where PathContext == Value ? {
686
688
var context : Context { get }
687
689
690
+ /// 'owner' is the most recently visited suitable base. Generally, this is the most recent owned value. When a
691
+ /// mark_dependence value operand is forwarded from its base operand, however, the owner is not updated because that
692
+ /// would could lead to introducing an illegal mark_dependence with the same value for both operands.
688
693
mutating func introducer( _ value: Value , _ owner: Value ? ) -> WalkResult
689
694
690
695
// Minimally, check a ValueSet. This walker may traverse chains of
@@ -1239,7 +1244,7 @@ let lifetimeDependenceScopeTest = FunctionTest("lifetime_dependence_scope") {
1239
1244
function, arguments, context in
1240
1245
let markDep = arguments. takeValue ( ) as! MarkDependenceInst
1241
1246
guard let dependence = LifetimeDependence ( markDep, context) else {
1242
- print ( " Trivial Dependence" )
1247
+ print ( " Invalid Dependence" )
1243
1248
return
1244
1249
}
1245
1250
print ( dependence)
0 commit comments