Destroy temporaries at the end of expression statements. (#7513)

Instead of tracking the cleanup scope depth on entry to each scope,
track an "ambient" cleanup scope depth that's *after* the destructors of
local variables in that scope. This gets increased to include the
destructors of local variables when we create a name-binding
declaration. Then, when we reach a point where temporaries should be
destroyed, run cleanups that are after the ambient cleanup scope depth
on the stack. This happens:

* At the `;` of a statement expression.
* At the `)` of an `if` or `while` statement.
* After performing the implied `HasValue()` call in a `for` statement.

Per informal agreement with leads, this means we lifetime-extend all
temporaries created in the initializer of a name-binding declaration to
the full scope of that declaration, but that temporaries created in an
expression statement are destroyed at the `;`.
This commit is contained in:
Richard Smith
2026-07-16 15:12:39 +00:00
committed by GitHub
parent 81e495ee53
commit 703529fc55
37 changed files with 844 additions and 384 deletions
@@ -548,6 +548,8 @@ fn TestUnaryOperators(a: Cpp.Int16, b: Cpp.Int32) {
// CHECK:STDOUT: %result.patt.488: %pattern_type.785 = at_binding_pattern result, %result.param_patt.be6 [concrete]
// CHECK:STDOUT: %y.param_patt.418: %pattern_type.785 = value_param_pattern [concrete]
// CHECK:STDOUT: %y.patt.4e6: %pattern_type.785 = at_binding_pattern y, %y.param_patt.418 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.9d5: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet.2cc) [concrete]
// CHECK:STDOUT: %.6da: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.9d5, %Destroy.facet.2cc [concrete]
// CHECK:STDOUT: %facet_type.b15: type = facet_type <@Destroy & @SubWith.1, @SubWith.1(%Int16)> [concrete]
// CHECK:STDOUT: %.Self.415: %facet_type.b15 = symbolic_binding .Self [symbolic_self]
// CHECK:STDOUT: %SubWith.lookup_impl_witness.266: <witness> = lookup_impl_witness %.Self.415, @SubWith.1, @SubWith.1(%Int16) [symbolic_self]
@@ -760,6 +762,8 @@ fn TestUnaryOperators(a: Cpp.Int16, b: Cpp.Int32) {
// CHECK:STDOUT: %result.patt.830: %pattern_type.8b4 = at_binding_pattern result, %result.param_patt.e81 [concrete]
// CHECK:STDOUT: %y.param_patt.e0c: %pattern_type.8b4 = value_param_pattern [concrete]
// CHECK:STDOUT: %y.patt.0a3: %pattern_type.8b4 = at_binding_pattern y, %y.param_patt.e0c [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.b76: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet.099) [concrete]
// CHECK:STDOUT: %.043: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.b76, %Destroy.facet.099 [concrete]
// CHECK:STDOUT: %facet_type.44e: type = facet_type <@Destroy & @SubWith.1, @SubWith.1(%Int32)> [concrete]
// CHECK:STDOUT: %.Self.8ac: %facet_type.44e = symbolic_binding .Self [symbolic_self]
// CHECK:STDOUT: %SubWith.lookup_impl_witness.f41: <witness> = lookup_impl_witness %.Self.8ac, @SubWith.1, @SubWith.1(%Int32) [symbolic_self]
@@ -925,6 +929,8 @@ fn TestUnaryOperators(a: Cpp.Int16, b: Cpp.Int32) {
// CHECK:STDOUT: %T.patt.f5c: %pattern_type.613 = symbolic_binding_pattern T, 2 [symbolic]
// CHECK:STDOUT: %result.param_patt.1e1: %pattern_type.4aa = value_param_pattern [concrete]
// CHECK:STDOUT: %result.patt.41f: %pattern_type.4aa = at_binding_pattern result, %result.param_patt.1e1 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.c2c: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet.191) [concrete]
// CHECK:STDOUT: %.c2e: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.c2c, %Destroy.facet.191 [concrete]
// CHECK:STDOUT: %facet_type.502: type = facet_type <@Destroy & @SubWith.1, @SubWith.1(%Int64)> [concrete]
// CHECK:STDOUT: %.Self.074: %facet_type.502 = symbolic_binding .Self [symbolic_self]
// CHECK:STDOUT: %SubWith.lookup_impl_witness.0c4: <witness> = lookup_impl_witness %.Self.074, @SubWith.1, @SubWith.1(%Int64) [symbolic_self]
@@ -1042,12 +1048,6 @@ fn TestUnaryOperators(a: Cpp.Int16, b: Cpp.Int32) {
// CHECK:STDOUT: %facet_value.20a: %facet_type.546 = facet_value %Int64, (%custom_witness.e7a31c.3, %custom_witness.e66) [concrete]
// CHECK:STDOUT: %pattern_type.5db: type = pattern_type %facet_type.546 [concrete]
// CHECK:STDOUT: %T.patt.ed0: %pattern_type.5db = symbolic_binding_pattern T, 2 [symbolic]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.c2c: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet.191) [concrete]
// CHECK:STDOUT: %.c2e: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.c2c, %Destroy.facet.191 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.b76: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet.099) [concrete]
// CHECK:STDOUT: %.043: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.b76, %Destroy.facet.099 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.9d5: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet.2cc) [concrete]
// CHECK:STDOUT: %.6da: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.9d5, %Destroy.facet.2cc [concrete]
// CHECK:STDOUT: %U.patt.d47: %pattern_type.98f = symbolic_binding_pattern U, 0 [symbolic]
// CHECK:STDOUT: %U.67d: type = symbolic_binding U, 0 [symbolic]
// CHECK:STDOUT: %AddAssignWith.type.2c8890.1: type = facet_type <@AddAssignWith.1, @AddAssignWith.1(%Other)> [symbolic]