SIL strong_release behavior

I am trying to understand memory deallocation behavior at the SIL level.

I have this simple swift program:

import Foundation
class A {
    let x: Int
    init(x: Int) {
        self.x = x
    }
}

var _ = A(x: 42)

The SIL that's generated for it looks as below:

 1 // Truncated only to show relevant code
 2 // main
 3 sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
 4 bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
 5   %2 = metatype $@thick A.Type                    // user: %6
 6   %3 = integer_literal $Builtin.Int64, 42         // user: %4
 7   %4 = struct $Int (%3 : $Builtin.Int64)          // user: %6
 8   // function_ref A.__allocating_init(x:)
 9   %5 = function_ref @$s4heap1AC1xACSi_tcfC : $@convention(method) (Int, @thick A.Type) -> @owned A // user: %6
10   %6 = apply %5(%4, %2) : $@convention(method) (Int, @thick A.Type) -> @owned A // user: %7
11   strong_release %6 : $A                          // id: %7
12   %8 = integer_literal $Builtin.Int32, 0          // user: %9
13   %9 = struct $Int32 (%8 : $Builtin.Int32)        // user: %10
14   return %9 : $Int32                              // id: %10
15 } // end sil function 'main'
16
17 // A.__deallocating_deinit
18 sil hidden @$s4heap1ACfD : $@convention(method) (@owned A) -> () {
19 // %0 "self"                                      // users: %3, %1
20 bb0(%0 : $A):
21   debug_value %0 : $A, let, name "self", argno 1, implicit // id: %1
22   // function_ref A.deinit
23   %2 = function_ref @$s4heap1ACfd : $@convention(method) (@guaranteed A) -> @owned Builtin.NativeObject // user: %3
24   %3 = apply %2(%0) : $@convention(method) (@guaranteed A) -> @owned Builtin.NativeObject // user: %4
25   %4 = unchecked_ref_cast %3 : $Builtin.NativeObject to $A // user: %5
26   dealloc_ref %4 : $A                             // id: %5
27   %6 = tuple ()                                   // user: %7
28   return %6 : $()                                 // id: %7
29 } // end sil function '$s4heap1ACfD'

I believe line 26 is responsible for freeing memory allocated for an object of type A. This makes sense as dealloc_ref bypasses the reference counting mechanism and deallocated the object on the heap.

However, if I comment out line 26 in the SIL code, leaks shows me that there is a memory leak. I would expect that strong_release on line 11 should achieve the same outcome -- since the object's reference count will go to zero and it should be cleaned up. In order to make sure the reference count is actually going to 0 I added multiple strong_release instructions and the memory still leaks.

Have I misunderstood what strong_release does?

When an object's reference count reaches zero, that triggers the invocation of its destructor, represented by the function A.__deallocating_deinit in your listing. That function is ultimately responsible for invoking deinit (if any), destroying the values of the stored properties in the object, and deallocating the memory. If you remove the deallocation from this destructor function, the object will never be deallocated.

2 Likes