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
+56 -56
View File
@@ -65,23 +65,41 @@ fn ConvertNullptrConstant() -> Core.Optional(i32*) {
// CHECK:STDOUT: store ptr %Cpp.nullptr_t.as.ImplicitAs.impl.Convert.call, ptr %.loc12_18.2.temp, align 8, !dbg !15
// CHECK:STDOUT: %.loc12_18.4 = load ptr, ptr %.loc12_18.2.temp, align 8, !dbg !15
// CHECK:STDOUT: call void @_Z7TakePtrPi(ptr %.loc12_18.4), !dbg !17
// CHECK:STDOUT: call void @"_COp.9a41840f34ccc47a:core.Destroy.Core"(ptr %.loc12_18.2.temp), !dbg !15
// CHECK:STDOUT: %Cpp.nullptr_t.as.Copy.impl.Op.call = call ptr @"_COp.NullptrT.CppCompat.Core:Copy.Core"(ptr poison), !dbg !16
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc14_22.1.temp), !dbg !16
// CHECK:STDOUT: store ptr %Cpp.nullptr_t.as.Copy.impl.Op.call, ptr %.loc14_22.1.temp, align 8, !dbg !16
// CHECK:STDOUT: call void @_Z11TakeNullptrDn.carbon_thunk._(ptr %.loc14_22.1.temp), !dbg !18
// CHECK:STDOUT: call void @"_COp.37a0be3fec6961f4:core.Destroy.Core"(ptr %.loc14_22.1.temp), !dbg !16
// CHECK:STDOUT: call void @"_COp.9a41840f34ccc47a:core.Destroy.Core"(ptr %.loc12_18.2.temp), !dbg !15
// CHECK:STDOUT: ret void, !dbg !19
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: declare void @_Z7TakePtrPi(ptr noundef) #1
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: nounwind
// CHECK:STDOUT: define weak_odr void @"_COp.32a070744df634d9:core.Destroy.Core"(ptr %self) #0 !dbg !20 {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: ret void, !dbg !26
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: nounwind
// CHECK:STDOUT: define weak_odr void @"_COp.958634845f762cfe:core.Destroy.Core"(ptr %self) #0 !dbg !27 {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: ret void, !dbg !30
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: nounwind
// CHECK:STDOUT: define weak_odr void @"_COp.9a41840f34ccc47a:core.Destroy.Core"(ptr %self) #0 !dbg !31 {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: ret void, !dbg !34
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress uwtable
// CHECK:STDOUT: define internal void @_Z11TakeNullptrDn.carbon_thunk._(ptr noundef %n) #2 {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: %n.addr = alloca ptr, align 8
// CHECK:STDOUT: store ptr %n, ptr %n.addr, align 8, !tbaa !20
// CHECK:STDOUT: %0 = load ptr, ptr %n.addr, align 8, !tbaa !20
// CHECK:STDOUT: store ptr %n, ptr %n.addr, align 8, !tbaa !35
// CHECK:STDOUT: %0 = load ptr, ptr %n.addr, align 8, !tbaa !35
// CHECK:STDOUT: call void @_Z11TakeNullptrDn(ptr null)
// CHECK:STDOUT: ret void
// CHECK:STDOUT: }
@@ -89,37 +107,19 @@ fn ConvertNullptrConstant() -> Core.Optional(i32*) {
// CHECK:STDOUT: declare ptr @"_COp.NullptrT.CppCompat.Core:Copy.Core"(ptr)
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: nounwind
// CHECK:STDOUT: define weak_odr void @"_COp.fd39a15e7d61228d:core.Destroy.Core"(ptr %self) #0 !dbg !23 {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: ret void, !dbg !29
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: nounwind
// CHECK:STDOUT: define weak_odr void @"_COp.125695b29f2cf97c:core.Destroy.Core"(ptr %self) #0 !dbg !30 {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: ret void, !dbg !33
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: nounwind
// CHECK:STDOUT: define weak_odr void @"_COp.37a0be3fec6961f4:core.Destroy.Core"(ptr %self) #0 !dbg !34 {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: ret void, !dbg !37
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: nounwind
// CHECK:STDOUT: define weak_odr void @"_COp.32a070744df634d9:core.Destroy.Core"(ptr %self) #0 !dbg !38 {
// CHECK:STDOUT: define weak_odr void @"_COp.fd39a15e7d61228d:core.Destroy.Core"(ptr %self) #0 !dbg !38 {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: ret void, !dbg !41
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: nounwind
// CHECK:STDOUT: define weak_odr void @"_COp.958634845f762cfe:core.Destroy.Core"(ptr %self) #0 !dbg !42 {
// CHECK:STDOUT: define weak_odr void @"_COp.125695b29f2cf97c:core.Destroy.Core"(ptr %self) #0 !dbg !42 {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: ret void, !dbg !45
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: nounwind
// CHECK:STDOUT: define weak_odr void @"_COp.9a41840f34ccc47a:core.Destroy.Core"(ptr %self) #0 !dbg !46 {
// CHECK:STDOUT: define weak_odr void @"_COp.37a0be3fec6961f4:core.Destroy.Core"(ptr %self) #0 !dbg !46 {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: ret void, !dbg !49
// CHECK:STDOUT: }
@@ -160,8 +160,8 @@ fn ConvertNullptrConstant() -> Core.Optional(i32*) {
// CHECK:STDOUT: define internal void @_Z13ReturnNullptrv.carbon_thunk.(ptr noundef %return) #2 {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: %return.addr = alloca ptr, align 8
// CHECK:STDOUT: store ptr %return, ptr %return.addr, align 8, !tbaa !20
// CHECK:STDOUT: %0 = load ptr, ptr %return.addr, align 8, !tbaa !20
// CHECK:STDOUT: store ptr %return, ptr %return.addr, align 8, !tbaa !35
// CHECK:STDOUT: %0 = load ptr, ptr %return.addr, align 8, !tbaa !35
// CHECK:STDOUT: %call = call ptr @_Z13ReturnNullptrv()
// CHECK:STDOUT: store ptr %call, ptr %0, align 8, !tbaa !65
// CHECK:STDOUT: ret void
@@ -239,39 +239,39 @@ fn ConvertNullptrConstant() -> Core.Optional(i32*) {
// CHECK:STDOUT: !17 = !DILocation(line: 12, column: 3, scope: !12)
// CHECK:STDOUT: !18 = !DILocation(line: 14, column: 3, scope: !12)
// CHECK:STDOUT: !19 = !DILocation(line: 11, column: 1, scope: !12)
// CHECK:STDOUT: !20 = !{!21, !21, i64 0}
// CHECK:STDOUT: !21 = !{!"p1 std::nullptr_t", !22, i64 0}
// CHECK:STDOUT: !22 = !{!"any pointer", !10, i64 0}
// CHECK:STDOUT: !23 = distinct !DISubprogram(name: "Op", linkageName: "_COp.fd39a15e7d61228d:core.Destroy.Core", scope: null, file: !1, line: 14, type: !24, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !27)
// CHECK:STDOUT: !24 = !DISubroutineType(types: !25)
// CHECK:STDOUT: !25 = !{null, !26}
// CHECK:STDOUT: !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64)
// CHECK:STDOUT: !27 = !{!28}
// CHECK:STDOUT: !28 = !DILocalVariable(arg: 1, scope: !23, type: !26)
// CHECK:STDOUT: !29 = !DILocation(line: 14, column: 19, scope: !23)
// CHECK:STDOUT: !30 = distinct !DISubprogram(name: "Op", linkageName: "_COp.125695b29f2cf97c:core.Destroy.Core", scope: null, file: !1, line: 14, type: !24, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !31)
// CHECK:STDOUT: !31 = !{!32}
// CHECK:STDOUT: !32 = !DILocalVariable(arg: 1, scope: !30, type: !26)
// CHECK:STDOUT: !33 = !DILocation(line: 14, column: 19, scope: !30)
// CHECK:STDOUT: !34 = distinct !DISubprogram(name: "Op", linkageName: "_COp.37a0be3fec6961f4:core.Destroy.Core", scope: null, file: !1, line: 14, type: !24, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !35)
// CHECK:STDOUT: !35 = !{!36}
// CHECK:STDOUT: !36 = !DILocalVariable(arg: 1, scope: !34, type: !26)
// CHECK:STDOUT: !37 = !DILocation(line: 14, column: 19, scope: !34)
// CHECK:STDOUT: !38 = distinct !DISubprogram(name: "Op", linkageName: "_COp.32a070744df634d9:core.Destroy.Core", scope: null, file: !1, line: 12, type: !24, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !39)
// CHECK:STDOUT: !20 = distinct !DISubprogram(name: "Op", linkageName: "_COp.32a070744df634d9:core.Destroy.Core", scope: null, file: !1, line: 12, type: !21, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !24)
// CHECK:STDOUT: !21 = !DISubroutineType(types: !22)
// CHECK:STDOUT: !22 = !{null, !23}
// CHECK:STDOUT: !23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64)
// CHECK:STDOUT: !24 = !{!25}
// CHECK:STDOUT: !25 = !DILocalVariable(arg: 1, scope: !20, type: !23)
// CHECK:STDOUT: !26 = !DILocation(line: 12, column: 15, scope: !20)
// CHECK:STDOUT: !27 = distinct !DISubprogram(name: "Op", linkageName: "_COp.958634845f762cfe:core.Destroy.Core", scope: null, file: !1, line: 12, type: !21, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !28)
// CHECK:STDOUT: !28 = !{!29}
// CHECK:STDOUT: !29 = !DILocalVariable(arg: 1, scope: !27, type: !23)
// CHECK:STDOUT: !30 = !DILocation(line: 12, column: 15, scope: !27)
// CHECK:STDOUT: !31 = distinct !DISubprogram(name: "Op", linkageName: "_COp.9a41840f34ccc47a:core.Destroy.Core", scope: null, file: !1, line: 12, type: !21, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !32)
// CHECK:STDOUT: !32 = !{!33}
// CHECK:STDOUT: !33 = !DILocalVariable(arg: 1, scope: !31, type: !23)
// CHECK:STDOUT: !34 = !DILocation(line: 12, column: 15, scope: !31)
// CHECK:STDOUT: !35 = !{!36, !36, i64 0}
// CHECK:STDOUT: !36 = !{!"p1 std::nullptr_t", !37, i64 0}
// CHECK:STDOUT: !37 = !{!"any pointer", !10, i64 0}
// CHECK:STDOUT: !38 = distinct !DISubprogram(name: "Op", linkageName: "_COp.fd39a15e7d61228d:core.Destroy.Core", scope: null, file: !1, line: 14, type: !21, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !39)
// CHECK:STDOUT: !39 = !{!40}
// CHECK:STDOUT: !40 = !DILocalVariable(arg: 1, scope: !38, type: !26)
// CHECK:STDOUT: !41 = !DILocation(line: 12, column: 15, scope: !38)
// CHECK:STDOUT: !42 = distinct !DISubprogram(name: "Op", linkageName: "_COp.958634845f762cfe:core.Destroy.Core", scope: null, file: !1, line: 12, type: !24, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !43)
// CHECK:STDOUT: !40 = !DILocalVariable(arg: 1, scope: !38, type: !23)
// CHECK:STDOUT: !41 = !DILocation(line: 14, column: 19, scope: !38)
// CHECK:STDOUT: !42 = distinct !DISubprogram(name: "Op", linkageName: "_COp.125695b29f2cf97c:core.Destroy.Core", scope: null, file: !1, line: 14, type: !21, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !43)
// CHECK:STDOUT: !43 = !{!44}
// CHECK:STDOUT: !44 = !DILocalVariable(arg: 1, scope: !42, type: !26)
// CHECK:STDOUT: !45 = !DILocation(line: 12, column: 15, scope: !42)
// CHECK:STDOUT: !46 = distinct !DISubprogram(name: "Op", linkageName: "_COp.9a41840f34ccc47a:core.Destroy.Core", scope: null, file: !1, line: 12, type: !24, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !47)
// CHECK:STDOUT: !44 = !DILocalVariable(arg: 1, scope: !42, type: !23)
// CHECK:STDOUT: !45 = !DILocation(line: 14, column: 19, scope: !42)
// CHECK:STDOUT: !46 = distinct !DISubprogram(name: "Op", linkageName: "_COp.37a0be3fec6961f4:core.Destroy.Core", scope: null, file: !1, line: 14, type: !21, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !47)
// CHECK:STDOUT: !47 = !{!48}
// CHECK:STDOUT: !48 = !DILocalVariable(arg: 1, scope: !46, type: !26)
// CHECK:STDOUT: !49 = !DILocation(line: 12, column: 15, scope: !46)
// CHECK:STDOUT: !48 = !DILocalVariable(arg: 1, scope: !46, type: !23)
// CHECK:STDOUT: !49 = !DILocation(line: 14, column: 19, scope: !46)
// CHECK:STDOUT: !50 = distinct !DISubprogram(name: "ReturnNullptr", linkageName: "_CReturnNullptr.Main", scope: null, file: !1, line: 17, type: !51, spFlags: DISPFlagDefinition, unit: !0)
// CHECK:STDOUT: !51 = !DISubroutineType(types: !52)
// CHECK:STDOUT: !52 = !{!26}
// CHECK:STDOUT: !52 = !{!23}
// CHECK:STDOUT: !53 = !DILocation(line: 18, column: 10, scope: !50)
// CHECK:STDOUT: !54 = !DILocation(line: 18, column: 3, scope: !50)
// CHECK:STDOUT: !55 = distinct !DISubprogram(name: "ReturnNullptrCopy", linkageName: "_CReturnNullptrCopy.Main", scope: null, file: !1, line: 21, type: !51, spFlags: DISPFlagDefinition, unit: !0)
@@ -294,9 +294,9 @@ fn ConvertNullptrConstant() -> Core.Optional(i32*) {
// CHECK:STDOUT: !72 = distinct !DISubprogram(name: "Convert", linkageName: "_CConvert.NullptrT.CppCompat.Core:ImplicitAs.f8357062f5b0e57c.Core.84588f41d61dafba", scope: null, file: !73, line: 51, type: !74, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !76)
// CHECK:STDOUT: !73 = !DIFile(filename: "{{.*}}/prelude/types/cpp/nullptr.carbon", directory: "")
// CHECK:STDOUT: !74 = !DISubroutineType(types: !75)
// CHECK:STDOUT: !75 = !{!26, !26}
// CHECK:STDOUT: !75 = !{!23, !23}
// CHECK:STDOUT: !76 = !{!77}
// CHECK:STDOUT: !77 = !DILocalVariable(arg: 1, scope: !72, type: !26)
// CHECK:STDOUT: !77 = !DILocalVariable(arg: 1, scope: !72, type: !23)
// CHECK:STDOUT: !78 = !DILocation(line: 52, column: 14, scope: !72)
// CHECK:STDOUT: !79 = !DILocation(line: 52, column: 7, scope: !72)
// CHECK:STDOUT: !80 = distinct !DISubprogram(name: "None", linkageName: "_CNone.Optional.Core.0a6122c87ac9ba0f", scope: null, file: !81, line: 30, type: !51, spFlags: DISPFlagDefinition, unit: !0)