Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
8cafe4c
std: add `fs::rename_noreplace`
valentynkit Aug 30, 2026
f09e0ef
std: add tests for `fs::rename_noreplace`
valentynkit Aug 30, 2026
d0d58b4
std: clarify rename_noreplace crash guarantees
valentynkit Sep 27, 2026
7b7ed95
std: take plain u32 move flags on Windows
valentynkit Sep 27, 2026
2ad8211
vectorized vec::dedup pre-scan
ywxt Sep 29, 2026
3fe7406
std: fix failing `test_rename_noreplace_directory`
valentynkit Oct 4, 2026
926a9d0
directly drop the tree instead of traversing every element when BTree…
ywxt Oct 8, 2026
2d21f3a
Add oneshot Sender::is_closed
connortsui20 Oct 6, 2026
a6bcbeb
bootstrap: always build llvm's dylib when linking to it
xSetech Aug 6, 2026
b60321b
Allow unary operand types to be inferred later
chenyukang Jul 22, 2026
9fce4c4
bless mismatch for unary-op-associated-output-mismatch-issue-106138
chenyukang Aug 24, 2026
722a9e4
add regression test for issue 26830
chenyukang Aug 24, 2026
01ae025
fix comment for check_expr_unop with Deref
chenyukang Sep 2, 2026
a97890a
add test unary operand autoderef behavior
chenyukang Oct 11, 2026
32fd83f
Nuke Unique
maxdexh Oct 11, 2026
a8d5c6c
minor: note dylib link and build vars are ideally equal
xSetech Oct 11, 2026
cf33c38
Rollup merge of #159744 - chenyukang:yukang-fix-106138-unary-op-infer…
jhpratt Oct 12, 2026
0679ba4
Rollup merge of #160597 - xSetech:bootstrap/build_llvm_shared, r=club…
jhpratt Oct 12, 2026
b35d551
Rollup merge of #162810 - ywxt:dedup, r=Amanieu
jhpratt Oct 12, 2026
0f547a9
Rollup merge of #162931 - ywxt:btree_map_drop, r=Amanieu
jhpratt Oct 12, 2026
60048ab
Rollup merge of #162027 - valentynkit:fs-rename-noreplace, r=joshtrip…
jhpratt Oct 12, 2026
d9f802f
Rollup merge of #163883 - connortsui20:oneshot-is-closed, r=clarfonthey
jhpratt Oct 12, 2026
2aad7af
Rollup merge of #164146 - maxdexh:nuke-unique, r=hanna-kruppe
jhpratt Oct 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_ssa/src/mir/retag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ impl<'a, 'tcx, V> RetagPlan<V> {
let boxed_ty = layout.ty.expect_boxed_ty();
let boxed_layout = bx.layout_of(boxed_ty);
if let Some(mut plan) = Self::emit_retag(bx, boxed_layout, None, is_fn_entry) {
// `Unique<T>`
// `BoxRaw<T>`
let unique = layout.field(bx, 0);
assert_eq!(unique.fields.count(), 2);
plan = plan.for_field(FieldIdx::ZERO);
Expand Down
55 changes: 33 additions & 22 deletions compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -426,35 +426,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return Ty::new_error(tcx, guar);
}

let oprnd_t = self.structurally_resolve_type(expr.span, oprnd_t);
// Resolve what we can for `Not` and `Neg`, but allow later constraints
// to determine an unresolved operand type,
// so we call `deeply_resolve_ignoring_regions_with_obligations` for them.
match unop {
hir::UnOp::Deref => self.lookup_derefing(expr, oprnd, oprnd_t).unwrap_or_else(|| {
let mut err =
self.dcx().create_err(CantDereference { span: expr.span, ty: oprnd_t });
let sp = tcx.sess.source_map().start_point(expr.span).with_parent(None);
if let Some(sp) =
tcx.sess.psess.complete_stmt_exprs_before_bin_op_lookalike.borrow().get(&sp)
{
err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
}
// The operand may be an uncalled function, in which case it is its return type
// the user meant to dereference. Only suggest the call when that return type is
// itself dereferenceable, mirroring the checks `lookup_derefing` just failed.
self.suggest_fn_call(&mut err, oprnd, oprnd_t, |output| {
output.builtin_deref(true).is_some()
|| self.tcx.lang_items().deref_trait().is_some_and(|deref_trait| {
self.type_implements_trait(deref_trait, [output], self.param_env)
.may_apply()
})
});
Ty::new_error(tcx, err.emit_err())
}),
hir::UnOp::Deref => {
// Dereferencing must distinguish builtin pointers from
// overloaded `Deref`, so it still requires a structurally resolved type.
//
// This is necessary as raw pointers do not implement `Deref`.
let oprnd_t = self.structurally_resolve_type(expr.span, oprnd_t);
self.lookup_derefing(expr, oprnd, oprnd_t).unwrap_or_else(|| {
let mut err =
self.dcx().create_err(CantDereference { span: expr.span, ty: oprnd_t });
let sp = tcx.sess.source_map().start_point(expr.span).with_parent(None);
if let Some(sp) =
tcx.sess.psess.complete_stmt_exprs_before_bin_op_lookalike.borrow().get(&sp)
{
err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
}
// The operand may be an uncalled function, in which case it is its return type
// the user meant to dereference. Only suggest the call when that return type is
// itself dereferenceable, mirroring the checks `lookup_derefing` just failed.
self.suggest_fn_call(&mut err, oprnd, oprnd_t, |output| {
output.builtin_deref(true).is_some()
|| self.tcx.lang_items().deref_trait().is_some_and(|deref_trait| {
self.type_implements_trait(deref_trait, [output], self.param_env)
.may_apply()
})
});
Ty::new_error(tcx, err.emit_err())
})
}
hir::UnOp::Not => {
let oprnd_t = self.deeply_resolve_ignoring_regions_with_obligations(oprnd_t);
let result = self.check_user_unop(expr, oprnd_t, unop, expected_inner);
// If it's builtin, we can reuse the type, this helps inference.
if oprnd_t.is_integral() || *oprnd_t.kind() == ty::Bool { oprnd_t } else { result }
}
hir::UnOp::Neg => {
let oprnd_t = self.deeply_resolve_ignoring_regions_with_obligations(oprnd_t);
let result = self.check_user_unop(expr, oprnd_t, unop, expected_inner);
// If it's builtin, we can reuse the type, this helps inference.
if oprnd_t.is_numeric() { oprnd_t } else { result }
Expand Down
15 changes: 6 additions & 9 deletions library/alloc/src/collections/btree/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,16 +205,13 @@ pub struct BTreeMap<
#[stable(feature = "btree_drop", since = "1.7.0")]
unsafe impl<#[may_dangle] K, #[may_dangle] V, A: AllocatorClone> Drop for BTreeMap<K, V, A> {
fn drop(&mut self) {
// Skip `into_iter` for an empty map: `dying_next` is too costly to inline, so the
// empty drop isn't optimised away (see #161375).
if self.root.is_some() {
// 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())
if let Some(root) = self.root.take() {
// SAFETY: The tree won't be used after this point.
// The allocator has not been moved out and will be moved into `drop_tree`.
unsafe { root.into_dying().drop_tree(ManuallyDrop::take(&mut self.alloc)) };
} 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) }
// SAFETY: The allocator has not been moved out and won't be used after this point.
unsafe { ManuallyDrop::drop(&mut self.alloc) };
}
}
}
Expand Down
108 changes: 108 additions & 0 deletions library/alloc/src/collections/btree/map/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2542,6 +2542,114 @@ fn test_into_iter_drop_leak_height_1() {
}
}

#[test]
fn test_drop_map_elements_multi_level() {
for size in [1, MIN_INSERTS_HEIGHT_1, MIN_INSERTS_HEIGHT_2] {
let keys = Vec::from_iter((0..size).map(CrashTestDummy::new));
let vals = Vec::from_iter((0..size).map(CrashTestDummy::new));
let map = {
let mut map = BTreeMap::new();

(0..size).map(|i| (keys[i].spawn(Panic::Never), vals[i].spawn(Panic::Never))).for_each(
|(k, v)| {
map.insert(k, v);
},
);
map
};

drop(map);

for i in 0..size {
assert_eq!(keys[i].dropped(), 1);
assert_eq!(vals[i].dropped(), 1);
}
}
}

#[test]
fn test_drop_empty_map_allocator() {
use core::alloc::{AllocError, Allocator, AllocatorClone, Layout};
use core::cell::Cell;
use core::ptr::NonNull;

struct CountedAllocator<'a>(&'a Cell<usize>);

impl Clone for CountedAllocator<'_> {
fn clone(&self) -> Self {
Self(self.0)
}
}

impl Drop for CountedAllocator<'_> {
fn drop(&mut self) {
self.0.set(self.0.get() + 1);
}
}

unsafe impl Allocator for CountedAllocator<'_> {
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
Global.allocate(layout)
}

unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
unsafe { Global.deallocate(ptr, layout) }
}
}

unsafe impl AllocatorClone for CountedAllocator<'_> {}

let dropped = Cell::new(0);
let map = BTreeMap::<i32, i32, _>::new_in(CountedAllocator(&dropped));
assert!(map.root.is_none());

drop(map);

assert_eq!(dropped.get(), 1);
}

#[test]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
fn test_drop_map_elements_multi_level_panic() {
for size in [1, MIN_INSERTS_HEIGHT_1, MIN_INSERTS_HEIGHT_2] {
for panic_point in [0, size / 2, size - 1] {
for panic_in_key in [true, false] {
let keys = Vec::from_iter((0..size).map(CrashTestDummy::new));
let vals = Vec::from_iter((0..size).map(CrashTestDummy::new));
let map = {
let mut map = BTreeMap::new();

(0..size)
.map(|i| {
let key_panic = if panic_in_key && i == panic_point {
Panic::InDrop
} else {
Panic::Never
};
let val_panic = if !panic_in_key && i == panic_point {
Panic::InDrop
} else {
Panic::Never
};
(keys[i].spawn(key_panic), vals[i].spawn(val_panic))
})
.for_each(|(k, v)| {
map.insert(k, v);
});
map
};

catch_unwind(move || drop(map)).unwrap_err();

for i in 0..size {
assert_eq!(keys[i].dropped(), 1);
assert_eq!(vals[i].dropped(), 1);
}
}
}
}
}

#[test]
fn test_into_keys() {
let map = BTreeMap::from([(1, 'a'), (2, 'b'), (3, 'c')]);
Expand Down
67 changes: 67 additions & 0 deletions library/alloc/src/collections/btree/navigate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -852,3 +852,70 @@ impl<BorrowType: marker::BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Lea
}
}
}

impl<K, V> NodeRef<marker::Dying, K, V, marker::LeafOrInternal> {
/// # Safety
/// - `self` must be the root.
/// - The tree shouldn't be accessed anymore.
pub(super) unsafe fn drop_tree<A: AllocatorClone>(self, alloc: A) {
struct DropGuard<K, V, A: AllocatorClone> {
node: Option<NodeRef<marker::Dying, K, V, marker::LeafOrInternal>>,
alloc: A,
}

impl<K, V, A: AllocatorClone> Drop for DropGuard<K, V, A> {
fn drop(&mut self) {
if let Some(cur) = self.node.take() {
let mut next =
// SAFETY: When unwinding reached this guard, all kvs in cur
// have already been dropped in `drop_kvs`, while the node itself has not
// yet been deallocated.
unsafe { deallocate_and_next_postorder_node(cur, self.alloc.clone()) };

while let Some(mut node) = next {
// SAFETY: Every node only calls the function once
unsafe { node.drop_kvs() };
next =
// SAFETY: `drop_kvs()` above only dropped kvs and the node is still accessible,
// and also each node is deallocated exactly once.
unsafe { deallocate_and_next_postorder_node(node, self.alloc.clone()) }
}
}
}
}
/// Deallocates `node` and returns the next node in postorder traversal
///
/// # Safety
///
/// The node must be initialized, all kvs in node must have already
/// been dropped or moved out and its children must have already been deallocated.
/// Also the node can't be touched anymore.
unsafe fn deallocate_and_next_postorder_node<K, V, A: AllocatorClone>(
node: NodeRef<marker::Dying, K, V, marker::LeafOrInternal>,
alloc: A,
) -> Option<NodeRef<marker::Dying, K, V, marker::LeafOrInternal>> {
// SAFETY: guaranteed by the caller
let parent = unsafe { node.deallocate_and_ascend(alloc) }?;

Some(match parent.right_kv() {
Ok(kv) => kv.right_edge().descend().first_leaf_edge().into_node().forget_type(),
Err(last_edge) => last_edge.into_node().forget_type(),
})
}

let node = self.first_leaf_edge().into_node().forget_type();
let mut guard = DropGuard { node: Some(node), alloc };
loop {
// SAFETY: all kvs of each node are dropped once
unsafe { guard.node.as_mut().unwrap().drop_kvs() };
// SAFETY: Nodes are deallocated in postorder, and each node is deallocated exactly once.
if let Some(next) = unsafe {
deallocate_and_next_postorder_node(guard.node.take().unwrap(), guard.alloc.clone())
} {
guard.node = Some(next);
} else {
return;
}
}
}
}
39 changes: 39 additions & 0 deletions library/alloc/src/collections/btree/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,45 @@ impl<K, V> NodeRef<marker::Dying, K, V, marker::LeafOrInternal> {
}
ret
}
/// Drops all kvs in this node without deallocating the node itself
///
/// # Safety
///
/// - kvs in the node must be initialized and must not be accessed after this call.
/// - No kvs in the node can be accessed through any other path while this function is running.
pub(super) unsafe fn drop_kvs(&mut self) {
struct DropGuard<'a, K, V> {
node: &'a NodeRef<marker::Dying, K, V, marker::LeafOrInternal>,
idx: usize,
}
impl<'a, K, V> Drop for DropGuard<'a, K, V> {
fn drop(&mut self) {
let len = self.node.len();
for i in self.idx..len {
// SAFETY: i < len and the kv has been initialized and
// it won't use the i-th kv more than once.
unsafe {
let kv = Handle::new_kv(ptr::read(self.node), i);
kv.drop_key_val();
}
}
}
}
let len = self.len();
for idx in 0..len {
// If `kv.drop_key_val()` panics, the guard would drop
// the rest and skip the panicked kv.
let guard = DropGuard { node: self, idx: idx + 1 };
// SAFETY: idx < len and the kv has been initialized and
// it won't touch the idx-th more than once
unsafe {
let kv = Handle::new_kv(ptr::read(self), idx);
kv.drop_key_val();
}

core::mem::forget(guard);
}
}
}

impl<'a, K, V, Type> NodeRef<marker::Mut<'a>, K, V, Type> {
Expand Down
1 change: 0 additions & 1 deletion library/alloc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,6 @@
#![feature(pin_coerce_unsized_trait)]
#![feature(ptr_alignment_type)]
#![feature(ptr_cast_slice)]
#![feature(ptr_internals)]
#![feature(ptr_metadata)]
#![feature(random)]
#![feature(raw_os_error_ty)]
Expand Down
Loading
Loading