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
@@ -334,10 +334,10 @@ fn F(ref r: Cpp.A) {
// CHECK:STDOUT: %.loc8_14.3.temp = alloca i8, align 1, !dbg !19
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc7_14.3.temp), !dbg !18
// CHECK:STDOUT: call void @_ZNK9NeedThunk8ImplicitEa.carbon_thunk.__(ptr %n, ptr @int_1.f1e), !dbg !20
// CHECK:STDOUT: call void @"_COp.578b3934f85dbca6:core.Destroy.Core"(ptr @int_1.f1e), !dbg !18
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc8_14.3.temp), !dbg !19
// CHECK:STDOUT: call void @_ZNH9NeedThunk8ExplicitES_a.carbon_thunk.__(ptr %n, ptr @int_1.f1e), !dbg !21
// CHECK:STDOUT: call void @"_COp.578b3934f85dbca6:core.Destroy.Core"(ptr @int_1.f1e), !dbg !19
// CHECK:STDOUT: call void @"_COp.578b3934f85dbca6:core.Destroy.Core"(ptr @int_1.f1e), !dbg !18
// CHECK:STDOUT: ret void, !dbg !22
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -355,6 +355,12 @@ fn F(ref r: Cpp.A) {
// CHECK:STDOUT: ret void
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: nounwind
// CHECK:STDOUT: define weak_odr void @"_COp.578b3934f85dbca6:core.Destroy.Core"(ptr %self) #0 !dbg !30 {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: ret void, !dbg !36
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress uwtable
// CHECK:STDOUT: define internal void @_ZNH9NeedThunk8ExplicitES_a.carbon_thunk.__(ptr noundef %0, ptr noundef %c) #1 {
// CHECK:STDOUT: entry:
@@ -370,12 +376,6 @@ fn F(ref r: Cpp.A) {
// CHECK:STDOUT: ret void
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: nounwind
// CHECK:STDOUT: define weak_odr void @"_COp.578b3934f85dbca6:core.Destroy.Core"(ptr %self) #0 !dbg !30 {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: ret void, !dbg !36
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #2
// CHECK:STDOUT:
@@ -426,13 +426,13 @@ fn F(ref r: Cpp.A) {
// CHECK:STDOUT: !27 = !{!"p1 omnipotent char", !25, i64 0}
// CHECK:STDOUT: !28 = !{}
// CHECK:STDOUT: !29 = !{!10, !10, i64 0}
// CHECK:STDOUT: !30 = distinct !DISubprogram(name: "Op", linkageName: "_COp.578b3934f85dbca6:core.Destroy.Core", scope: null, file: !1, line: 8, type: !31, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !34)
// CHECK:STDOUT: !30 = distinct !DISubprogram(name: "Op", linkageName: "_COp.578b3934f85dbca6:core.Destroy.Core", scope: null, file: !1, line: 7, type: !31, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !34)
// CHECK:STDOUT: !31 = !DISubroutineType(types: !32)
// CHECK:STDOUT: !32 = !{null, !33}
// CHECK:STDOUT: !33 = !DIBasicType(name: "int", size: 8, encoding: DW_ATE_signed)
// CHECK:STDOUT: !34 = !{!35}
// CHECK:STDOUT: !35 = !DILocalVariable(arg: 1, scope: !30, type: !33)
// CHECK:STDOUT: !36 = !DILocation(line: 8, column: 14, scope: !30)
// CHECK:STDOUT: !36 = !DILocation(line: 7, column: 14, scope: !30)
// CHECK:STDOUT:
// CHECK:STDOUT: ; ---
// CHECK:STDOUT: ; ModuleID = 'call_with_ref_self.carbon'