From a5e4dc9a9abcf10b36f1fd1fef2051b13aa0098a Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Thu, 28 May 2026 16:23:43 +0000 Subject: [PATCH 01/22] Handle generic reborrow in expression-use adjustment walking * Handle generic reborrow in expression-use adjustment walking * Require generic reborrow to be terminal in adjustment walks --- .../rustc_hir_typeck/src/expr_use_visitor.rs | 17 +++++----- ...neric-reborrow-expr-use-visitor-closure.rs | 34 +++++++++++++++++++ .../generic-reborrow-expr-use-visitor.rs | 32 +++++++++++++++++ 3 files changed, 75 insertions(+), 8 deletions(-) create mode 100644 tests/ui/reborrow/generic-reborrow-expr-use-visitor-closure.rs create mode 100644 tests/ui/reborrow/generic-reborrow-expr-use-visitor.rs diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index a924a81f89b0d..7b39b953e3be0 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -727,7 +727,8 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx let typeck_results = self.cx.typeck_results(); let adjustments = typeck_results.expr_adjustments(expr); let mut place_with_id = self.cat_expr_unadjusted(expr)?; - for adjustment in adjustments { + for (adjustment_index, adjustment) in adjustments.iter().enumerate() { + let is_last_adjustment = adjustment_index + 1 == adjustments.len(); debug!("walk_adjustment expr={:?} adj={:?}", expr, adjustment); match adjustment.kind { adjustment::Adjust::NeverToAny | adjustment::Adjust::Pointer(_) => { @@ -752,13 +753,13 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx self.walk_autoref(expr, &place_with_id, autoref); } - adjustment::Adjust::GenericReborrow(_reborrow) => { - // To build an expression as a place expression, it needs to be a field - // projection or deref at the outmost layer. So it is field projection or deref - // on an adjusted value. But this means that adjustment is applied on a - // subexpression that is not the final operand/rvalue for function call or - // assignment. This is a contradiction. - unreachable!("Reborrow trait usage during adjustment walk"); + adjustment::Adjust::GenericReborrow(mutability) if is_last_adjustment => { + let bk = ty::BorrowKind::from_mutbl(mutability); + self.delegate.borrow_mut().borrow(&place_with_id, place_with_id.hir_id, bk); + } + + adjustment::Adjust::GenericReborrow(_) => { + span_bug!(expr.span, "generic reborrow adjustment must be terminal"); } } place_with_id = self.cat_expr_adjusted(expr, place_with_id, adjustment)?; diff --git a/tests/ui/reborrow/generic-reborrow-expr-use-visitor-closure.rs b/tests/ui/reborrow/generic-reborrow-expr-use-visitor-closure.rs new file mode 100644 index 0000000000000..c28cb1ef4fda4 --- /dev/null +++ b/tests/ui/reborrow/generic-reborrow-expr-use-visitor-closure.rs @@ -0,0 +1,34 @@ +//@ check-pass + +#![feature(reborrow)] + +use std::marker::{CoerceShared, Reborrow}; + +#[allow(unused)] +struct CustomMut<'a, T>(&'a mut T); +impl<'a, T> Reborrow for CustomMut<'a, T> {} +impl<'a, T> CoerceShared> for CustomMut<'a, T> {} + +#[allow(unused)] +struct CustomRef<'a, T>(&'a T); +impl<'a, T> Clone for CustomRef<'a, T> { + fn clone(&self) -> Self { + Self(self.0) + } +} +impl<'a, T> Copy for CustomRef<'a, T> {} + +fn takes_mut(_: CustomMut<'_, ()>) {} +fn takes_shared(_: CustomRef<'_, ()>) {} + +fn main() { + let a = CustomMut(&mut ()); + + let mut f = || { + takes_mut(a); + takes_shared(a); + }; + + f(); + f(); +} diff --git a/tests/ui/reborrow/generic-reborrow-expr-use-visitor.rs b/tests/ui/reborrow/generic-reborrow-expr-use-visitor.rs new file mode 100644 index 0000000000000..d513577e901a0 --- /dev/null +++ b/tests/ui/reborrow/generic-reborrow-expr-use-visitor.rs @@ -0,0 +1,32 @@ +//@ check-pass + +#![feature(reborrow)] + +use std::marker::{CoerceShared, Reborrow}; + +#[allow(unused)] +struct CustomMut<'a, T>(&'a mut T); +impl<'a, T> Reborrow for CustomMut<'a, T> {} +impl<'a, T> CoerceShared> for CustomMut<'a, T> {} + +#[allow(unused)] +struct CustomRef<'a, T>(&'a T); +impl<'a, T> Clone for CustomRef<'a, T> { + fn clone(&self) -> Self { + Self(self.0) + } +} +impl<'a, T> Copy for CustomRef<'a, T> {} + +fn takes_mut(_: CustomMut<'_, ()>) {} +fn takes_shared(_: CustomRef<'_, ()>) {} + +fn main() { + let a = CustomMut(&mut ()); + + takes_mut(a); + takes_mut(a); + + takes_shared(a); + takes_shared(a); +} From 640d7ae06d2d0f205bf3920d26fda730de5ff6aa Mon Sep 17 00:00:00 2001 From: XMH <970252187@qq.com> Date: Wed, 10 Jun 2026 14:56:00 +0800 Subject: [PATCH 02/22] Update task.rs --- library/alloc/src/task.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/alloc/src/task.rs b/library/alloc/src/task.rs index bc668f78bf740..45c22b845799d 100644 --- a/library/alloc/src/task.rs +++ b/library/alloc/src/task.rs @@ -37,6 +37,10 @@ use crate::sync::Arc; /// link ../../std/task/struct.Waker.html#impl-From%3CArc%3CW,+Global%3E%3E-for-Waker /// without getting a link-checking error in CI. --> /// +/// # Memory Ordering +/// +/// To avoid missed wakeups, all runtimes must adhere to the requirement described for [`Waker::wake`]. +/// /// # Examples /// /// A basic `block_on` function that takes a future and runs it to completion on From 4671f48aa62496dfbd6c017dd289f6a59447b2a9 Mon Sep 17 00:00:00 2001 From: XMH <970252187@qq.com> Date: Wed, 10 Jun 2026 15:02:29 +0800 Subject: [PATCH 03/22] Update wake.rs --- library/core/src/task/wake.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 14f0980ff6045..0d50f9151e181 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -432,6 +432,13 @@ impl Waker { /// executor’s choice which task to run and the executor may choose to run the /// current task again. /// + /// To avoid missed wakeups, runtimes must ensure that for any call to `wake`, + /// there is a subsequent call to `poll` such that the `wake()` return _happens-before_ + /// the beginning of the invocation of `poll`. + /// In particular, this means that if a task self-wakes (invokes `wake` on itself during `poll`), + /// then the `poll` must be invoked again because the call to `wake` _happens-after_ the beginning + /// of the current invocation of `poll`. + /// /// [`poll()`]: crate::future::Future::poll #[inline] #[stable(feature = "futures_api", since = "1.36.0")] From 452882786afde92ac75b87f6be05b83b2fe8f3be Mon Sep 17 00:00:00 2001 From: XMH <970252187@qq.com> Date: Wed, 10 Jun 2026 15:41:12 +0800 Subject: [PATCH 04/22] Update wake.rs --- library/core/src/task/wake.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 0d50f9151e181..c184225778f9d 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -433,7 +433,7 @@ impl Waker { /// current task again. /// /// To avoid missed wakeups, runtimes must ensure that for any call to `wake`, - /// there is a subsequent call to `poll` such that the `wake()` return _happens-before_ + /// there is a subsequent call to `poll` such that the `wake()` _happens-before_ /// the beginning of the invocation of `poll`. /// In particular, this means that if a task self-wakes (invokes `wake` on itself during `poll`), /// then the `poll` must be invoked again because the call to `wake` _happens-after_ the beginning From 61aa16370b53d07c397812040210aede961ef34d Mon Sep 17 00:00:00 2001 From: XMH <970252187@qq.com> Date: Wed, 10 Jun 2026 16:10:30 +0800 Subject: [PATCH 05/22] Update library/core/src/task/wake.rs Co-authored-by: Alice Ryhl --- library/core/src/task/wake.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index c184225778f9d..732a1855989ce 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -433,7 +433,7 @@ impl Waker { /// current task again. /// /// To avoid missed wakeups, runtimes must ensure that for any call to `wake`, - /// there is a subsequent call to `poll` such that the `wake()` _happens-before_ + /// there is a subsequent call to `poll` such that the call to `wake()` _happens-before_ /// the beginning of the invocation of `poll`. /// In particular, this means that if a task self-wakes (invokes `wake` on itself during `poll`), /// then the `poll` must be invoked again because the call to `wake` _happens-after_ the beginning From c5c5e814c6933a1d4a71332f6de68ba1e30c42d7 Mon Sep 17 00:00:00 2001 From: XMH <970252187@qq.com> Date: Wed, 10 Jun 2026 16:20:09 +0800 Subject: [PATCH 06/22] Update wake.rs --- library/core/src/task/wake.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 732a1855989ce..1428762db6234 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -434,7 +434,7 @@ impl Waker { /// /// To avoid missed wakeups, runtimes must ensure that for any call to `wake`, /// there is a subsequent call to `poll` such that the call to `wake()` _happens-before_ - /// the beginning of the invocation of `poll`. + /// the beginning of the invocation of `poll`. /// In particular, this means that if a task self-wakes (invokes `wake` on itself during `poll`), /// then the `poll` must be invoked again because the call to `wake` _happens-after_ the beginning /// of the current invocation of `poll`. From e65db4278bd9612d4145741dd9657ddcbea942cd Mon Sep 17 00:00:00 2001 From: XMH <970252187@qq.com> Date: Wed, 10 Jun 2026 17:51:02 +0800 Subject: [PATCH 07/22] Update library/core/src/task/wake.rs Co-authored-by: Alice Ryhl --- library/core/src/task/wake.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 1428762db6234..89c39e8b7836a 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -432,12 +432,13 @@ impl Waker { /// executor’s choice which task to run and the executor may choose to run the /// current task again. /// - /// To avoid missed wakeups, runtimes must ensure that for any call to `wake`, - /// there is a subsequent call to `poll` such that the call to `wake()` _happens-before_ - /// the beginning of the invocation of `poll`. - /// In particular, this means that if a task self-wakes (invokes `wake` on itself during `poll`), - /// then the `poll` must be invoked again because the call to `wake` _happens-after_ the beginning - /// of the current invocation of `poll`. + /// To avoid missed wakeups, runtimes must ensure that for any call to + /// `wake`, there is a subsequent call to `poll` such that the call to + /// `wake()` _happens-before_ the beginning of the invocation of `poll`. In + /// particular, this means that if a task self-wakes (invokes `wake` on + /// itself during `poll`), then the `poll` must be invoked again because the + /// call to `wake` _happens-after_ the beginning of the current invocation + /// of `poll`. /// /// [`poll()`]: crate::future::Future::poll #[inline] From 16ccf8e66ca9ef666f7ba76f863cc7c2a6f24ec2 Mon Sep 17 00:00:00 2001 From: XMH <970252187@qq.com> Date: Thu, 11 Jun 2026 09:46:23 +0800 Subject: [PATCH 08/22] Update library/alloc/src/task.rs Co-authored-by: Kevin Reid --- library/alloc/src/task.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/alloc/src/task.rs b/library/alloc/src/task.rs index 45c22b845799d..0e36c91f466fd 100644 --- a/library/alloc/src/task.rs +++ b/library/alloc/src/task.rs @@ -39,7 +39,7 @@ use crate::sync::Arc; /// /// # Memory Ordering /// -/// To avoid missed wakeups, all runtimes must adhere to the requirement described for [`Waker::wake`]. +/// To avoid missed wakeups, all executors must adhere to the requirement described for [`Waker::wake`]. /// /// # Examples /// From a0ecf25ca93d432786c0f5428eed76dd549ac8c8 Mon Sep 17 00:00:00 2001 From: XMH <970252187@qq.com> Date: Thu, 11 Jun 2026 09:46:38 +0800 Subject: [PATCH 09/22] Update library/core/src/task/wake.rs Co-authored-by: Kevin Reid --- library/core/src/task/wake.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 89c39e8b7836a..c3f7672a61a37 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -432,7 +432,7 @@ impl Waker { /// executor’s choice which task to run and the executor may choose to run the /// current task again. /// - /// To avoid missed wakeups, runtimes must ensure that for any call to + /// To avoid missed wakeups, executors must ensure that for any call to /// `wake`, there is a subsequent call to `poll` such that the call to /// `wake()` _happens-before_ the beginning of the invocation of `poll`. In /// particular, this means that if a task self-wakes (invokes `wake` on From 0954d4c64447fcce89c388af8d13e473db43b383 Mon Sep 17 00:00:00 2001 From: XMH <970252187@qq.com> Date: Fri, 12 Jun 2026 13:52:01 +0800 Subject: [PATCH 10/22] Update wake.rs --- library/core/src/task/wake.rs | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index c3f7672a61a37..bdf6c59a80b80 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -418,28 +418,21 @@ unsafe impl Sync for Waker {} impl Waker { /// Wakes up the task associated with this `Waker`. /// - /// As long as the executor keeps running and the task is not finished, it is - /// guaranteed that each invocation of [`wake()`](Self::wake) (or + /// As long as the executor keeps running and the task is not finished, + /// it is guaranteed that each invocation of [`wake()`](Self::wake) (or /// [`wake_by_ref()`](Self::wake_by_ref)) will be followed by at least one - /// [`poll()`] of the task to which this `Waker` belongs. This makes - /// it possible to temporarily yield to other tasks while running potentially - /// unbounded processing loops. + /// [`poll()`] of the task to which this `Waker` belongs, and the call to + /// [`wake()`](Self::wake) (or [`wake_by_ref()`](Self::wake_by_ref)) _happens-before_ + /// the beginning of the invocation of [`poll()`]. This makes it possible to temporarily + /// yield to other tasks while running potentially unbounded processing loops. /// /// Note that the above implies that multiple wake-ups may be coalesced into a - /// single [`poll()`] invocation by the runtime. + /// single [`poll()`] invocation by the executor. /// /// Also note that yielding to competing tasks is not guaranteed: it is the /// executor’s choice which task to run and the executor may choose to run the /// current task again. /// - /// To avoid missed wakeups, executors must ensure that for any call to - /// `wake`, there is a subsequent call to `poll` such that the call to - /// `wake()` _happens-before_ the beginning of the invocation of `poll`. In - /// particular, this means that if a task self-wakes (invokes `wake` on - /// itself during `poll`), then the `poll` must be invoked again because the - /// call to `wake` _happens-after_ the beginning of the current invocation - /// of `poll`. - /// /// [`poll()`]: crate::future::Future::poll #[inline] #[stable(feature = "futures_api", since = "1.36.0")] From 42538cac1a8c47f1635abe328a7f9fd997fa9b8d Mon Sep 17 00:00:00 2001 From: XMH <970252187@qq.com> Date: Mon, 15 Jun 2026 14:11:06 +0800 Subject: [PATCH 11/22] Update library/core/src/task/wake.rs Co-authored-by: Alice Ryhl --- library/core/src/task/wake.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index bdf6c59a80b80..63b7691582a7d 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -421,7 +421,7 @@ impl Waker { /// As long as the executor keeps running and the task is not finished, /// it is guaranteed that each invocation of [`wake()`](Self::wake) (or /// [`wake_by_ref()`](Self::wake_by_ref)) will be followed by at least one - /// [`poll()`] of the task to which this `Waker` belongs, and the call to + /// [`poll()`] of the task to which this `Waker` belongs, such that the call to /// [`wake()`](Self::wake) (or [`wake_by_ref()`](Self::wake_by_ref)) _happens-before_ /// the beginning of the invocation of [`poll()`]. This makes it possible to temporarily /// yield to other tasks while running potentially unbounded processing loops. From b8d7dcb0696417e0190e8740ee3d9da8f7a16e54 Mon Sep 17 00:00:00 2001 From: Kevin Valerio Date: Wed, 17 Jun 2026 17:06:42 +0200 Subject: [PATCH 12/22] fix(thir): visit reborrow source expressions --- compiler/rustc_middle/src/thir/visit.rs | 2 +- tests/ui/reborrow/reborrow-source-unsafety.rs | 28 +++++++++++++++++++ .../reborrow/reborrow-source-unsafety.stderr | 11 ++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 tests/ui/reborrow/reborrow-source-unsafety.rs create mode 100644 tests/ui/reborrow/reborrow-source-unsafety.stderr diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index a96f4e9457cb0..f1f3e2d98b226 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -187,7 +187,7 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( } ThreadLocalRef(_) => {} Yield { value } => visitor.visit_expr(&visitor.thir()[value]), - Reborrow { .. } => {} + Reborrow { source, .. } => visitor.visit_expr(&visitor.thir()[source]), } } diff --git a/tests/ui/reborrow/reborrow-source-unsafety.rs b/tests/ui/reborrow/reborrow-source-unsafety.rs new file mode 100644 index 0000000000000..e6f3540768a44 --- /dev/null +++ b/tests/ui/reborrow/reborrow-source-unsafety.rs @@ -0,0 +1,28 @@ +// Regression test for rust-lang/rust#158033. + +#![feature(reborrow)] +#![allow(dead_code)] +#![deny(unsafe_code)] + +use std::marker::Reborrow; + +struct Thing<'a> { + field: &'a mut usize, +} + +impl<'a> Reborrow for Thing<'a> {} + +fn takes(_: Thing<'_>) {} + +fn main() { + let mut x = 0; + let thing = Thing { field: &mut x }; + let y = 123usize; + + takes({ + let p: *const usize = &y; + std::hint::black_box(std::ptr::read(p)); + //~^ ERROR call to unsafe function `std::ptr::read` is unsafe + thing + }); +} diff --git a/tests/ui/reborrow/reborrow-source-unsafety.stderr b/tests/ui/reborrow/reborrow-source-unsafety.stderr new file mode 100644 index 0000000000000..431893a4d0ced --- /dev/null +++ b/tests/ui/reborrow/reborrow-source-unsafety.stderr @@ -0,0 +1,11 @@ +error[E0133]: call to unsafe function `std::ptr::read` is unsafe and requires unsafe function or block + --> $DIR/reborrow-source-unsafety.rs:24:30 + | +LL | std::hint::black_box(std::ptr::read(p)); + | ^^^^^^^^^^^^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0133`. From a487aec39ae75c92fe6f0e3dccb83f07c224c5ef Mon Sep 17 00:00:00 2001 From: Kevin Valerio <24193167+kevin-valerio@users.noreply.github.com> Date: Wed, 17 Jun 2026 17:58:52 +0200 Subject: [PATCH 13/22] Match all fields and removing `..` Co-authored-by: Timo <30553356+y21@users.noreply.github.com> --- compiler/rustc_middle/src/thir/visit.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index f1f3e2d98b226..e3366e74b5f26 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -187,7 +187,7 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( } ThreadLocalRef(_) => {} Yield { value } => visitor.visit_expr(&visitor.thir()[value]), - Reborrow { source, .. } => visitor.visit_expr(&visitor.thir()[source]), + Reborrow { source, mutability: _, target: _ } => visitor.visit_expr(&visitor.thir()[source]), } } From ef672ad0120cab59576e245df472bd662af29e45 Mon Sep 17 00:00:00 2001 From: Kevin Valerio Date: Wed, 17 Jun 2026 18:14:59 +0200 Subject: [PATCH 14/22] format reborrow visitor arm --- compiler/rustc_middle/src/thir/visit.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index e3366e74b5f26..24aa4ac513d45 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -187,7 +187,9 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( } ThreadLocalRef(_) => {} Yield { value } => visitor.visit_expr(&visitor.thir()[value]), - Reborrow { source, mutability: _, target: _ } => visitor.visit_expr(&visitor.thir()[source]), + Reborrow { source, mutability: _, target: _ } => { + visitor.visit_expr(&visitor.thir()[source]) + } } } From 36f5b3f260e6e16a5e32fd50f4946e760493b32a Mon Sep 17 00:00:00 2001 From: Valentyn Kit Date: Wed, 17 Jun 2026 22:58:37 +0300 Subject: [PATCH 15/22] Document transient connection errors from TcpListener::accept `accept` can return an error that belongs to a single incoming connection, not to the listener itself, for example a connection aborted by the peer before it could be accepted. The listener stays usable in that case, so code serving a long-lived listener usually wants to log the error and keep accepting connections rather than treat it as fatal. This was previously undocumented. - Add an `# Errors` section to `accept` that describes this behavior without listing specific error codes. - Note that `Interrupted` errors are retried internally on Unix. - Point `incoming` and `into_incoming` at `accept` for the same details. --- library/std/src/net/tcp.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs index 2e8779e05ca76..53bef0b4de609 100644 --- a/library/std/src/net/tcp.rs +++ b/library/std/src/net/tcp.rs @@ -876,6 +876,22 @@ impl TcpListener { /// is established. When established, the corresponding [`TcpStream`] and the /// remote peer's address will be returned. /// + /// # Errors + /// + /// Some errors returned by this function relate to a single incoming + /// connection that failed before it could be accepted, such as one aborted + /// by the peer ([`ConnectionAborted`]). Such an error does not indicate a + /// problem with the listener itself, which remains usable. Code serving a + /// long-lived listener will usually want to log the error and continue + /// accepting connections rather than treat it as fatal. Which errors can + /// occur this way is platform-specific. + /// + /// On Unix, [`Interrupted`] errors are retried internally rather than being + /// returned. + /// + /// [`ConnectionAborted`]: io::ErrorKind::ConnectionAborted + /// [`Interrupted`]: io::ErrorKind::Interrupted + /// /// # Examples /// /// ```no_run @@ -902,6 +918,11 @@ impl TcpListener { /// the peer's [`SocketAddr`] structure. Iterating over it is equivalent to /// calling [`TcpListener::accept`] in a loop. /// + /// # Errors + /// + /// Each connection yielded by the iterator can fail for the same reasons as + /// [`TcpListener::accept`]; see its documentation for details. + /// /// # Examples /// /// ```no_run @@ -937,6 +958,11 @@ impl TcpListener { /// the peer's [`SocketAddr`] structure. Iterating over it is equivalent to /// calling [`TcpListener::accept`] in a loop. /// + /// # Errors + /// + /// Each connection yielded by the iterator can fail for the same reasons as + /// [`TcpListener::accept`]; see its documentation for details. + /// /// # Examples /// /// ```no_run From 34d3eed3faa8250cb5ad0aea5e86b9c95eb4e0d9 Mon Sep 17 00:00:00 2001 From: Valentyn Kit Date: Thu, 18 Jun 2026 14:50:54 +0300 Subject: [PATCH 16/22] Document the file-descriptor-limit error from TcpListener::accept --- library/std/src/net/tcp.rs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs index 53bef0b4de609..4e67903540a65 100644 --- a/library/std/src/net/tcp.rs +++ b/library/std/src/net/tcp.rs @@ -878,16 +878,20 @@ impl TcpListener { /// /// # Errors /// - /// Some errors returned by this function relate to a single incoming - /// connection that failed before it could be accepted, such as one aborted - /// by the peer ([`ConnectionAborted`]). Such an error does not indicate a - /// problem with the listener itself, which remains usable. Code serving a - /// long-lived listener will usually want to log the error and continue - /// accepting connections rather than treat it as fatal. Which errors can - /// occur this way is platform-specific. - /// - /// On Unix, [`Interrupted`] errors are retried internally rather than being - /// returned. + /// Some errors this function returns do not indicate a problem with the + /// listener itself, and a program serving a long-lived listener will + /// usually want to handle them and keep accepting connections rather than + /// treat them as fatal. These include, but are not limited to: + /// + /// - An error specific to a single incoming connection that failed before + /// it could be accepted, such as one aborted by the peer + /// ([`ConnectionAborted`]). A later call may succeed immediately. + /// - An error from reaching the per-process or system-wide open file + /// descriptor limit. The call can be retried once other file descriptors + /// have been closed, typically after a short delay. + /// + /// Which errors can occur is platform-specific. On Unix, [`Interrupted`] + /// errors are retried internally rather than being returned. /// /// [`ConnectionAborted`]: io::ErrorKind::ConnectionAborted /// [`Interrupted`]: io::ErrorKind::Interrupted From 14475190e56c5b7fbf8f5d5c42327b89f65f4480 Mon Sep 17 00:00:00 2001 From: Valentyn Kit Date: Thu, 18 Jun 2026 16:04:05 +0300 Subject: [PATCH 17/22] Document the out-of-memory error from TcpListener::accept --- library/std/src/net/tcp.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs index 4e67903540a65..b673abdff7ba1 100644 --- a/library/std/src/net/tcp.rs +++ b/library/std/src/net/tcp.rs @@ -889,11 +889,14 @@ impl TcpListener { /// - An error from reaching the per-process or system-wide open file /// descriptor limit. The call can be retried once other file descriptors /// have been closed, typically after a short delay. + /// - An error from failing to allocate memory while accepting a connection + /// ([`OutOfMemory`]). /// /// Which errors can occur is platform-specific. On Unix, [`Interrupted`] /// errors are retried internally rather than being returned. /// /// [`ConnectionAborted`]: io::ErrorKind::ConnectionAborted + /// [`OutOfMemory`]: io::ErrorKind::OutOfMemory /// [`Interrupted`]: io::ErrorKind::Interrupted /// /// # Examples From dbf6d75478a36bd654a9e1138ddd03cc8a0384f4 Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Wed, 17 Jun 2026 14:34:53 +0200 Subject: [PATCH 18/22] `RegionValues`: disable unnecessary range check Currently, when adding liveness points to region values in the `RegionValues` struct, the locations of the points are checked for ranges. This is unnecessarily cautious because they always are in range by construction. This adds documentation (including debug assertions) to make this clearer and removes the checks, which should have a strictly positive impact on performance. --- .../rustc_borrowck/src/region_infer/values.rs | 42 +++++++++---------- compiler/rustc_mir_dataflow/src/points.rs | 7 +++- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index 27ee3f6176ee0..e96dc44fab7c4 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs @@ -9,7 +9,7 @@ use rustc_middle::bug; use rustc_middle::mir::{BasicBlock, Location}; use rustc_middle::ty::{self, RegionVid}; use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex}; -use tracing::debug; +use tracing::{debug, instrument}; use crate::BorrowIndex; use crate::polonius::LiveLoans; @@ -116,37 +116,43 @@ impl LivenessValues { /// Records `region` as being live at the given `location`. pub(crate) fn add_location(&mut self, region: RegionVid, location: Location) { let point = self.location_map.point_from_location(location); + // This is a debug assert despite being cheap because it drops + // the current `point_in_range()` uses to 0 when debugging is off. + debug_assert!( + self.location_map.point_in_range(point), + "Tried inserting region {region:?} whose location {location:?} does not belong to this body!" + ); debug!("LivenessValues::add_location(region={:?}, location={:?})", region, location); match &mut self.live_regions { LiveRegions::AtPoints(points) => { points.insert(region, point); } - LiveRegions::InBody(live_regions) if self.location_map.point_in_range(point) => { + LiveRegions::InBody(live_regions) => { live_regions.insert(region); } - - LiveRegions::InBody(_) => (), }; } /// Records `region` as being live at all the given `points`. pub(crate) fn add_points(&mut self, region: RegionVid, points: &IntervalSet) { + debug_assert!( + points.iter().all(|point| self.location_map.point_in_range(point)), + "Tried inserting region {region:?} with some points not belonging to this body!" + ); debug!("LivenessValues::add_points(region={:?}, points={:?})", region, points); match &mut self.live_regions { LiveRegions::AtPoints(these_points) => { these_points.union_row(region, points); } - LiveRegions::InBody(live_regions) - if points.iter().any(|point| self.location_map.point_in_range(point)) => - { + LiveRegions::InBody(live_regions) => { live_regions.insert(region); } - LiveRegions::InBody(_) => (), }; } /// Records `region` as being live at all the control-flow points. + #[instrument(skip(self))] pub(crate) fn add_all_points(&mut self, region: RegionVid) { match &mut self.live_regions { LiveRegions::AtPoints(points) => points.insert_all_into_row(region), @@ -172,10 +178,7 @@ impl LivenessValues { /// Returns an iterator of all the points where `region` is live. fn live_points(&self, region: RegionVid) -> impl Iterator { - self.point_liveness(region) - .into_iter() - .flat_map(|set| set.iter()) - .take_while(|&p| self.location_map.point_in_range(p)) + self.point_liveness(region).into_iter().flat_map(|set| set.iter()) } /// For debugging purposes, returns a pretty-printed string of the points where the `region` is @@ -343,11 +346,10 @@ impl<'tcx, N: Idx> RegionValues<'tcx, N> { /// Returns the locations contained within a given region `r`. pub(crate) fn locations_outlived_by(&self, r: N) -> impl Iterator { - self.points.row(r).into_iter().flat_map(move |set| { - set.iter() - .take_while(move |&p| self.location_map.point_in_range(p)) - .map(move |p| self.location_map.to_location(p)) - }) + self.points + .row(r) + .into_iter() + .flat_map(move |set| set.iter().map(move |p| self.location_map.to_location(p))) } /// Returns just the universal regions that are contained in a given region's value. @@ -413,11 +415,7 @@ pub(crate) fn pretty_print_points( points: impl IntoIterator, ) -> String { pretty_print_region_elements( - points - .into_iter() - .take_while(|&p| location_map.point_in_range(p)) - .map(|p| location_map.to_location(p)) - .map(RegionElement::Location), + points.into_iter().map(|p| location_map.to_location(p)).map(RegionElement::Location), ) } diff --git a/compiler/rustc_mir_dataflow/src/points.rs b/compiler/rustc_mir_dataflow/src/points.rs index e3d1e04a319ba..8568f325f1306 100644 --- a/compiler/rustc_mir_dataflow/src/points.rs +++ b/compiler/rustc_mir_dataflow/src/points.rs @@ -31,7 +31,8 @@ impl DenseLocationMap { for (bb, bb_data) in body.basic_blocks.iter_enumerated() { basic_blocks.extend((0..=bb_data.statements.len()).map(|_| bb)); } - + // Invariant: no block is preceded by more than all statements. + debug_assert!(*statements_before_block.iter().max().unwrap() < num_points); Self { statements_before_block, basic_blocks, num_points } } @@ -42,10 +43,14 @@ impl DenseLocationMap { } /// Converts a `Location` into a `PointIndex`. O(1). + /// [[`Self::point_in_range()`]] guaranteed for the returned index. #[inline] pub fn point_from_location(&self, location: Location) -> PointIndex { let Location { block, statement_index } = location; let start_index = self.statements_before_block[block]; + // Note the invariant in [`Self::new()`]; if the indexing + // operation above did not panic then this holds by construction. + debug_assert!(start_index < self.num_points); PointIndex::new(start_index + statement_index) } From ebea09f6f8779a189a6b16d026031a6913a398d3 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Thu, 18 Jun 2026 16:50:39 +0200 Subject: [PATCH 19/22] renovate: Skip dashboard approval for GitHub Actions updates --- .github/renovate.json5 | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index d6c7455fe14cb..c2df406c9dc18 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -9,6 +9,13 @@ "dependencyDashboard": true, // Require manual approval from the Dependency Dashboard before opening PRs "dependencyDashboardApproval": true, + "packageRules": [ + { + // No dashboard approval necessary for GitHub Actions updates + "matchManagers": ["github-actions"], + "dependencyDashboardApproval": false + } + ], // Don't manage dependencies inside subtrees. They are updated upstream and // synced in. See `src/doc/rustc-dev-guide/src/external-repos.md` for the list. "ignorePaths": [ From cfadf6ee6b0a11d88aa6bf84cf469c6fb6f228a3 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Thu, 18 Jun 2026 16:54:53 +0200 Subject: [PATCH 20/22] renovate: Enable monthly lock file maintenance --- .github/renovate.json5 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index c2df406c9dc18..69fc5216920c8 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -1,6 +1,9 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ + // Refresh lock files on the first day of each month + // (still gated by dashboard approval for now) + ":maintainLockFilesMonthly", // Pin GitHub Actions to their commit SHA digests, resolving floating tags // (e.g. `v4`) to the full SemVer version (e.g. `v4.1.2`) "helpers:pinGitHubActionDigestsToSemver" From 4e0bcfc4efcd77448aee4002a15cd5d0fc653ac4 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Thu, 18 Jun 2026 17:00:01 +0200 Subject: [PATCH 21/22] renovate: Extend `config:recommended` preset --- .github/renovate.json5 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 69fc5216920c8..a4930160612cb 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -1,6 +1,7 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ + "config:recommended", // Refresh lock files on the first day of each month // (still gated by dashboard approval for now) ":maintainLockFilesMonthly", @@ -8,8 +9,6 @@ // (e.g. `v4`) to the full SemVer version (e.g. `v4.1.2`) "helpers:pinGitHubActionDigestsToSemver" ], - // Let Renovatebot keep an opened issue that tracks our dependencies - "dependencyDashboard": true, // Require manual approval from the Dependency Dashboard before opening PRs "dependencyDashboardApproval": true, "packageRules": [ From 5db91c4c70cdfc1e3f28532c519c416be2112a63 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Thu, 18 Jun 2026 17:03:13 +0200 Subject: [PATCH 22/22] renovate: Enable config migration PRs --- .github/renovate.json5 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index a4930160612cb..6d45e9a81b2fe 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -2,6 +2,8 @@ "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ "config:recommended", + // Open a PR to migrate the config when Renovate deprecates syntax + ":configMigration", // Refresh lock files on the first day of each month // (still gated by dashboard approval for now) ":maintainLockFilesMonthly",