From f48f3ad228c5bb63ca3aa983e5de5512dc6461c6 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Wed, 5 Jan 2022 12:12:01 -0800 Subject: [PATCH 1/2] [Test] Checked variable lifetimes extend self. Verified that when a __consuming method calls a function which takes a closure that captures self weakly, self is not deallocated until the call returns. (Note that this is a behavioral change from what occurs when lexical borrow scopes are disabled; in that case, self is deallocated before the call to the function.) --- .../SILOptimizer/lexical-lifetimes.swift | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/validation-test/SILOptimizer/lexical-lifetimes.swift b/validation-test/SILOptimizer/lexical-lifetimes.swift index 9852da8487a18..c1191cb09b4d1 100644 --- a/validation-test/SILOptimizer/lexical-lifetimes.swift +++ b/validation-test/SILOptimizer/lexical-lifetimes.swift @@ -142,6 +142,31 @@ func test_localVar_keepsObjectAliveBeyondCallToSynchronizationPointFunction() { test_localVar_keepsObjectAliveBeyondCallToSynchronizationPointFunction_doit("blue") } +func do_foo(_ work: () -> ()) { + work() +} +class Fooer { + __consuming func foo() { + weak var weakSelf = self + do_foo { + weakSelf?.foo1() + weakSelf?.foo2() + } + } + func foo1() { + // CHECK: Fooer foo1 + print(type(of: self), #function) + } + func foo2() { + // CHECK: Fooer foo2 + print(type(of: self), #function) + } +} + +func test_self_keepsObjectAliveBeyond_callTo_functionTakingClosureCapturingWeakVar() { + Fooer().foo() +} + // ============================================================================= // = Tests }} = // ============================================================================= @@ -154,6 +179,8 @@ func run() { test_localVar_keepsObjectAliveBeyondCallToClassWithPointer() test_localLet_keepsObjectAliveBeyondCallToSynchronizationPointFunction() test_localVar_keepsObjectAliveBeyondCallToSynchronizationPointFunction() + + test_self_keepsObjectAliveBeyond_callTo_functionTakingClosureCapturingWeakVar() } run() From cb205a6ec0eb2ea42a0acac253d5315e91667924 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Wed, 5 Jan 2022 12:57:11 -0800 Subject: [PATCH 2/2] [Test] Checked variable lifetimes not extend weak. Verified that lexical lifetimes DO NOT result in a method call to a weak reference resulting in a strong reference to the object. Consequently, even with lexical lifetimes enabled, it is still possible, within a single scope, for the first method call to an object weakly referenced to occur but for the second such call not to because the object will have been deallocated. --- .../SILOptimizer/lexical-lifetimes.swift | 62 ++++++++++++++----- 1 file changed, 48 insertions(+), 14 deletions(-) diff --git a/validation-test/SILOptimizer/lexical-lifetimes.swift b/validation-test/SILOptimizer/lexical-lifetimes.swift index c1191cb09b4d1..7a43b63934fe2 100644 --- a/validation-test/SILOptimizer/lexical-lifetimes.swift +++ b/validation-test/SILOptimizer/lexical-lifetimes.swift @@ -1,5 +1,6 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-copy-propagation) | %FileCheck %s +// RUN: %target-run-simple-swift(-Xfrontend -disable-availability-checking -parse-as-library -Xfrontend -enable-copy-propagation) | %FileCheck %s +// REQUIRES: concurrency // REQUIRES: executable_test // ============================================================================= @@ -167,21 +168,54 @@ func test_self_keepsObjectAliveBeyond_callTo_functionTakingClosureCapturingWeakV Fooer().foo() } +func do_foo_async(_ work: @escaping () -> ()) -> Task { + Task { + work() + } +} +class FooerAsync { + var strongSelf: FooerAsync? + __consuming func foo() -> Task { + weak var weakSelf = self + strongSelf = self + return do_foo_async { + // At this point, strongSelf is keeping the object alive. + weakSelf?.foo1() + // By this point, strongSelf has been nil'd, dropping the object's retain + // count to 0, deallocating the object, so weakSelf is nil. + weakSelf?.foo2() + } + } + func foo1() { + // CHECK: FooerAsync foo1 + print(type(of: self), #function) + strongSelf = nil + } + func foo2() { + // CHECK-NOT: FooerAsync foo2 + print(type(of: self), #function) + } +} + +func test_repeatedLoadWeakSelf() -> Task { + FooerAsync().foo() +} + // ============================================================================= // = Tests }} = // ============================================================================= -func run() { - test_localLet_keepsObjectAliveBeyondCallToClassWithWeakReference() - // Reenable with rdar://86271875 - // test_localVar_keepsObjectAliveBeyondCallToClassWithWeakReference() - test_localLet_keepsObjectAliveBeyondCallToClassWithPointer() - test_localVar_keepsObjectAliveBeyondCallToClassWithPointer() - test_localLet_keepsObjectAliveBeyondCallToSynchronizationPointFunction() - test_localVar_keepsObjectAliveBeyondCallToSynchronizationPointFunction() - - test_self_keepsObjectAliveBeyond_callTo_functionTakingClosureCapturingWeakVar() +@main struct Main { + static func main() async { + test_localLet_keepsObjectAliveBeyondCallToClassWithWeakReference() + // Reenable with rdar://86271875 + // test_localVar_keepsObjectAliveBeyondCallToClassWithWeakReference() + test_localLet_keepsObjectAliveBeyondCallToClassWithPointer() + test_localVar_keepsObjectAliveBeyondCallToClassWithPointer() + test_localLet_keepsObjectAliveBeyondCallToSynchronizationPointFunction() + test_localVar_keepsObjectAliveBeyondCallToSynchronizationPointFunction() + + test_self_keepsObjectAliveBeyond_callTo_functionTakingClosureCapturingWeakVar() + await test_repeatedLoadWeakSelf().value + } } - -run() -