Skip to content

Commit ac31a2f

Browse files
committed
LifetimeDependenceDiagnostics: extend temp alloc to unreachable.
When a non-Escapable value depends on the address of a trivial value, we use a special computeAddressableRange analysis to compute the trivial value's scope. Extend that analysis to include unreachable paths. Fixes this pattern: inlineStorage.span.withUnsafeBytes where inlineStorage is a trivial type defined in the user module. This does not reproduce directly with InlineArray, but it is a problem for user modules that have their own trivial wrapper around an InlineArray. Fixes rdar://161630684 (Incorrect diagnostic: lifetime-dependent value escapes its scope)
1 parent 7197f63 commit ac31a2f

File tree

3 files changed

+73
-0
lines changed

3 files changed

+73
-0
lines changed

SwiftCompilerSources/Sources/Optimizer/Utilities/LifetimeDependenceUtils.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,10 +558,27 @@ extension LifetimeDependence.Scope {
558558
}
559559
}
560560
guard isAddressable, !deallocInsts.isEmpty else {
561+
// Valid on all paths to function exit.
561562
return nil
562563
}
563564
var range = InstructionRange(begin: initializingStore, context)
564565
range.insert(contentsOf: deallocInsts)
566+
567+
// Insert unreachable paths with no dealloc_stack.
568+
var forwardUnreachableWalk = BasicBlockWorklist(context)
569+
defer { forwardUnreachableWalk.deinitialize() }
570+
571+
for exitBlock in range.exitBlocks {
572+
forwardUnreachableWalk.pushIfNotVisited(exitBlock)
573+
}
574+
while let b = forwardUnreachableWalk.pop() {
575+
if let unreachableInst = b.terminator as? UnreachableInst {
576+
range.insert(unreachableInst)
577+
}
578+
for succBlock in b.successors {
579+
forwardUnreachableWalk.pushIfNotVisited(succBlock)
580+
}
581+
}
565582
return range
566583
}
567584
}

test/SILOptimizer/lifetime_dependence/verify_diagnostics.sil

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ sil @addressInt : $@convention(thin) (@in_guaranteed InlineInt) -> @lifetime(bor
7070
sil @addressOfInt : $@convention(thin) (@in_guaranteed Int) -> @lifetime(borrow address 0) @owned Span<Int>
7171
sil @noAddressInt : $@convention(thin) (@in_guaranteed Int) -> @lifetime(borrow 0) @owned Span<Int>
7272
sil @useSpan : $@convention(thin) (@guaranteed Span<Int>) -> ()
73+
sil @useRawSpan : $@convention(thin) (@guaranteed RawSpan) -> @error any Error
74+
75+
sil @getInlineSpan : $@convention(thin) (@in_guaranteed InlineInt) -> @lifetime(borrow address 0) @owned RawSpan
7376

7477
// Test returning a owned dependence on a trivial value
7578
sil [ossa] @return_trivial_dependence : $@convention(thin) (@guaranteed C) -> @lifetime(borrow 0) @owned NE {
@@ -352,3 +355,29 @@ bb0(%0: $Int):
352355
%18 = tuple ()
353356
return %18
354357
}
358+
359+
// Test dependence on the temporary stack address of a trivial value. computeAddressableRange must extend the lifetime
360+
// of %tempAddr into the unreachable.
361+
sil hidden [ossa] @testTempAddressUnreachable : $@convention(thin) (@in_guaranteed InlineInt) -> () {
362+
bb0(%0 : $*InlineInt):
363+
%loadArg = load [trivial] %0
364+
%tempAddr = alloc_stack $InlineInt
365+
store %loadArg to [trivial] %tempAddr
366+
367+
%f1 = function_ref @getInlineSpan : $@convention(thin) (@in_guaranteed InlineInt) -> @lifetime(borrow address 0) @owned RawSpan
368+
%call = apply %f1(%tempAddr) : $@convention(thin) (@in_guaranteed InlineInt) -> @lifetime(borrow address 0) @owned RawSpan
369+
%md = mark_dependence [unresolved] %call on %tempAddr
370+
371+
%f2 = function_ref @useRawSpan : $@convention(thin) (@guaranteed RawSpan) -> @error any Error
372+
try_apply %f2(%md) : $@convention(thin) (@guaranteed RawSpan) -> @error any Error, normal bb1, error bb2
373+
374+
bb1(%void : $()):
375+
destroy_value %md
376+
dealloc_stack %tempAddr
377+
%99 = tuple ()
378+
return %99
379+
380+
bb2(%error : @owned $any Error):
381+
destroy_value [dead_end] %md
382+
unreachable
383+
}

test/SILOptimizer/lifetime_dependence/verify_diagnostics.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,19 @@ func testClosureCapture1(_ a: HasMethods) {
208208
*/
209209
}
210210

211+
public struct InlineInt: BitwiseCopyable {
212+
var val: UInt64
213+
214+
var span: RawSpan {
215+
@_addressableSelf
216+
@_lifetime(borrow self) borrowing get {
217+
let buf = UnsafeRawBufferPointer(start: UnsafeRawPointer(Builtin.addressOfBorrow(val)), count: 1)
218+
let span = RawSpan(_unsafeBytes: buf)
219+
return unsafe _overrideLifetime(span, borrowing: val)
220+
}
221+
}
222+
}
223+
211224
// =============================================================================
212225
// Indirect ~Escapable results
213226
// =============================================================================
@@ -382,3 +395,17 @@ func returnTempBorrow() -> Borrow<Int> {
382395
// expected-note@-1{{it depends on the lifetime of this parent value}}
383396
return span // expected-note{{this use causes the lifetime-dependent value to escape}}
384397
}
398+
399+
// Test dependence on the temporary stack address of a trivial value. computeAddressableRange must extend the lifetime
400+
// of 'inline' into the unreachable.
401+
//
402+
// This test requires InlineInt to be a trivial value defined in this module so that inline.span generates:
403+
//
404+
// %temp = alloc_stack
405+
// store %arg to [trivial] temp
406+
// apply %get_span(%temp)
407+
//
408+
// If we use InlineArray instead, we get a store_borrow, which is a completely different situatio.
409+
func test(inline: InlineInt) {
410+
inline.span.withUnsafeBytes { _ = $0 }
411+
}

0 commit comments

Comments
 (0)