From 0109ed292f8f5b23a067f94df215e62996958b1b Mon Sep 17 00:00:00 2001 From: Jake Drew <25826535+jakedrew@users.noreply.github.com> Date: Wed, 23 Sep 2026 01:14:29 +0200 Subject: [PATCH 1/6] BTree: make dropping an empty BTreeMap free --- library/alloc/src/collections/btree/map.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index e8ee6fa628bfa..9876e1c131122 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -205,8 +205,11 @@ pub struct BTreeMap< #[stable(feature = "btree_drop", since = "1.7.0")] unsafe impl<#[may_dangle] K, #[may_dangle] V, A: AllocatorClone> Drop for BTreeMap { fn drop(&mut self) { - // ignore-tidy-undocumented-unsafe - drop(unsafe { ptr::read(self) }.into_iter()) + if self.root.is_some() { + drop(unsafe { ptr::read(self) }.into_iter()) + } else { + unsafe { ManuallyDrop::drop(&mut self.alloc) } + } } } From 756abe8393782352f5badfbee2dcc88334c5f1e2 Mon Sep 17 00:00:00 2001 From: Jake Drew <25826535+jakedrew@users.noreply.github.com> Date: Wed, 23 Sep 2026 01:14:29 +0200 Subject: [PATCH 2/6] Adding codegen for the empty BTree drop --- tests/codegen-llvm/btree-empty-drop.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 tests/codegen-llvm/btree-empty-drop.rs diff --git a/tests/codegen-llvm/btree-empty-drop.rs b/tests/codegen-llvm/btree-empty-drop.rs new file mode 100644 index 0000000000000..4e08920f55dbf --- /dev/null +++ b/tests/codegen-llvm/btree-empty-drop.rs @@ -0,0 +1,15 @@ +// Regression test for https://github.com/rust-lang/rust/issues/161375 +// Checking that an empty BTree's drop is optimized away. +//@ compile-flags: -Copt-level=3 + +#![crate_type = "lib"] + +use std::collections::BTreeMap; + +// CHECK-LABEL: @drop_btree +// CHECK-NOT: dying_next +// CHECK: ret void +#[no_mangle] +pub fn drop_btree() { + let _ = BTreeMap::<(), ()>::new(); +} From 15831b0a64907b45e98f1b55eaa060cb1c368fce Mon Sep 17 00:00:00 2001 From: Jake Drew <25826535+jakedrew@users.noreply.github.com> Date: Wed, 23 Sep 2026 01:14:29 +0200 Subject: [PATCH 3/6] BTree: documenting the unsafe in the empty map drop --- library/alloc/src/collections/btree/map.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 9876e1c131122..1f479569705f8 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -206,8 +206,11 @@ pub struct BTreeMap< unsafe impl<#[may_dangle] K, #[may_dangle] V, A: AllocatorClone> Drop for BTreeMap { fn drop(&mut self) { if self.root.is_some() { + // ignore-tidy-undocumented-unsafe drop(unsafe { ptr::read(self) }.into_iter()) } else { + // SAFETY: With no root there are no nodes to free, so only the allocator needs + // dropping. `self` is not used after this, and `alloc` is dropped only here. unsafe { ManuallyDrop::drop(&mut self.alloc) } } } From 70ff620dc79853f245a9010de8773847e0512bcf Mon Sep 17 00:00:00 2001 From: Jake Drew <25826535+jakedrew@users.noreply.github.com> Date: Wed, 23 Sep 2026 01:14:29 +0200 Subject: [PATCH 4/6] Check for dealloc instead of dying_next --- tests/codegen-llvm/btree-empty-drop.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/codegen-llvm/btree-empty-drop.rs b/tests/codegen-llvm/btree-empty-drop.rs index 4e08920f55dbf..60e3c3b0c9a70 100644 --- a/tests/codegen-llvm/btree-empty-drop.rs +++ b/tests/codegen-llvm/btree-empty-drop.rs @@ -7,7 +7,7 @@ use std::collections::BTreeMap; // CHECK-LABEL: @drop_btree -// CHECK-NOT: dying_next +// CHECK-NOT: dealloc // CHECK: ret void #[no_mangle] pub fn drop_btree() { From 4cf035f6814d2c1f482487e9d671b4fd56a2c38f Mon Sep 17 00:00:00 2001 From: Jake Drew <25826535+jakedrew@users.noreply.github.com> Date: Wed, 23 Sep 2026 10:00:52 +0200 Subject: [PATCH 5/6] Checking that an empty BTreeMap drop compiles down to only a `ret void`, and that function only match drop_btree exactly. As the previous dealloc also passed on old code so didn't catch the regression --- tests/codegen-llvm/btree-empty-drop.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/codegen-llvm/btree-empty-drop.rs b/tests/codegen-llvm/btree-empty-drop.rs index 60e3c3b0c9a70..f1aaf52e95729 100644 --- a/tests/codegen-llvm/btree-empty-drop.rs +++ b/tests/codegen-llvm/btree-empty-drop.rs @@ -6,9 +6,9 @@ use std::collections::BTreeMap; -// CHECK-LABEL: @drop_btree -// CHECK-NOT: dealloc -// CHECK: ret void +// CHECK-LABEL: @drop_btree( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void #[no_mangle] pub fn drop_btree() { let _ = BTreeMap::<(), ()>::new(); From 9c77e1eaf7e342ab850b786c9ac61ea3e75aeba1 Mon Sep 17 00:00:00 2001 From: Jake Drew <25826535+jakedrew@users.noreply.github.com> Date: Thu, 24 Sep 2026 01:58:09 +0200 Subject: [PATCH 6/6] Adding safety comment on the unsafe in BTreeMap drop --- library/alloc/src/collections/btree/map.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 1f479569705f8..fc194639c5468 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -206,7 +206,8 @@ pub struct BTreeMap< unsafe impl<#[may_dangle] K, #[may_dangle] V, A: AllocatorClone> Drop for BTreeMap { fn drop(&mut self) { if self.root.is_some() { - // ignore-tidy-undocumented-unsafe + // SAFETY: `self` is not used after this and none of its fields are dropped again: + // `alloc` is `ManuallyDrop` and `root` has no drop glue. drop(unsafe { ptr::read(self) }.into_iter()) } else { // SAFETY: With no root there are no nodes to free, so only the allocator needs