From e427d29e3c9800c43fabbadd023fb0f75a59767e Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 26 Feb 2026 13:00:21 +0100 Subject: [PATCH 01/11] aarch64: fix UB in non-power-of-two reads and writes (cherry picked from commit 2fa37e7b3498a9cb46004fae5fd9d6d40835ad28) --- .../stdarch/crates/core_arch/src/macros.rs | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/macros.rs b/library/stdarch/crates/core_arch/src/macros.rs index 9f6922efeeb7d..54d4668e52fe3 100644 --- a/library/stdarch/crates/core_arch/src/macros.rs +++ b/library/stdarch/crates/core_arch/src/macros.rs @@ -226,12 +226,12 @@ macro_rules! deinterleaving_load { ($elem:ty, $lanes:literal, 2, $ptr:expr) => {{ use $crate::core_arch::macros::deinterleave_mask; use $crate::core_arch::simd::Simd; - use $crate::{mem::transmute, ptr}; + use $crate::mem::transmute; type V = Simd<$elem, $lanes>; type W = Simd<$elem, { $lanes * 2 }>; - let w: W = ptr::read_unaligned($ptr as *const W); + let w: W = $crate::ptr::read_unaligned($ptr as *const W); let v0: V = simd_shuffle!(w, w, deinterleave_mask::<$lanes, 2, 0>()); let v1: V = simd_shuffle!(w, w, deinterleave_mask::<$lanes, 2, 1>()); @@ -242,12 +242,20 @@ macro_rules! deinterleaving_load { ($elem:ty, $lanes:literal, 3, $ptr:expr) => {{ use $crate::core_arch::macros::deinterleave_mask; use $crate::core_arch::simd::Simd; - use $crate::{mem::transmute, ptr}; + use $crate::mem::{MaybeUninit, transmute}; type V = Simd<$elem, $lanes>; type W = Simd<$elem, { $lanes * 3 }>; - let w: W = ptr::read_unaligned($ptr as *const W); + // NOTE: repr(simd) adds padding to make the total size a power of two. + // Hence reading W from ptr might read out of bounds. + let mut mem = MaybeUninit::::uninit(); + $crate::ptr::copy_nonoverlapping( + $ptr.cast::<$elem>(), + mem.as_mut_ptr().cast::<$elem>(), + $lanes * 3, + ); + let w = mem.assume_init(); let v0: V = simd_shuffle!(w, w, deinterleave_mask::<$lanes, 3, 0>()); let v1: V = simd_shuffle!(w, w, deinterleave_mask::<$lanes, 3, 1>()); @@ -259,12 +267,12 @@ macro_rules! deinterleaving_load { ($elem:ty, $lanes:literal, 4, $ptr:expr) => {{ use $crate::core_arch::macros::deinterleave_mask; use $crate::core_arch::simd::Simd; - use $crate::{mem::transmute, ptr}; + use $crate::mem::transmute; type V = Simd<$elem, $lanes>; type W = Simd<$elem, { $lanes * 4 }>; - let w: W = ptr::read_unaligned($ptr as *const W); + let w: W = $crate::ptr::read_unaligned($ptr as *const W); let v0: V = simd_shuffle!(w, w, deinterleave_mask::<$lanes, 4, 0>()); let v1: V = simd_shuffle!(w, w, deinterleave_mask::<$lanes, 4, 1>()); From 73caca416f09147cf27eafbaa410da8434d653b1 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 19 Mar 2026 14:38:09 +0100 Subject: [PATCH 02/11] add neon load/store assembly test (cherry picked from commit 35f9cb6eeec95cd94c57c4b0403c227d8673835d) --- .../src/directives/directive_names.rs | 1 + tests/assembly-llvm/aarch64-arm-load-store.rs | 78 +++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 tests/assembly-llvm/aarch64-arm-load-store.rs diff --git a/src/tools/compiletest/src/directives/directive_names.rs b/src/tools/compiletest/src/directives/directive_names.rs index 230578d79ffbe..fa8c343fbce2d 100644 --- a/src/tools/compiletest/src/directives/directive_names.rs +++ b/src/tools/compiletest/src/directives/directive_names.rs @@ -210,6 +210,7 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "only-apple", "only-arm", "only-arm64ec", + "only-armv7-unknown-linux-gnueabihf", "only-avr", "only-beta", "only-bpf", diff --git a/tests/assembly-llvm/aarch64-arm-load-store.rs b/tests/assembly-llvm/aarch64-arm-load-store.rs new file mode 100644 index 0000000000000..8296194100d9f --- /dev/null +++ b/tests/assembly-llvm/aarch64-arm-load-store.rs @@ -0,0 +1,78 @@ +//@ assembly-output: emit-asm +// +//@ revisions: AARCH64 +//@[AARCH64] compile-flags: -Copt-level=3 +//@[AARCH64] only-aarch64-unknown-linux-gnu +// +//@ revisions: ARMV7 +//@[ARMV7] compile-flags: -Copt-level=3 +//@[ARMV7] only-armv7-unknown-linux-gnueabihf +//@[ARMV7] ignore-thumb +//@[ARMV7] ignore-android +#![crate_type = "lib"] +#![cfg_attr(target_arch = "arm", feature(arm_target_feature, stdarch_arm_neon_intrinsics))] + +#[cfg(target_arch = "aarch64")] +use std::arch::aarch64::*; +#[cfg(target_arch = "arm")] +use std::arch::arm::*; + +// Loads of 3 are error-prone because a `repr(simd)` type's size is always rounded up to the next +// power of 2. Hence, using `read_unaligned` and `write_unaligned` on such types is invalid, it +// would go out of bounds. +#[unsafe(no_mangle)] +#[cfg_attr(target_arch = "arm", target_feature(enable = "neon,v7"))] +fn test_vld3q_f32(ptr: *const f32) -> float32x4x3_t { + // AARCH64-LABEL: test_vld3q_f32 + // AARCH64: ld3 { v0.4s, v1.4s, v2.4s }, [x0] + // AARCH64: stp q0, q1, [x8] + // AARCH64: str q2, [x8, #32] + // AARCH64: ret + // + // ARMV7-LABEL: test_vld3q_f32 + // ARMV7: vld3.32 {d16, d18, d20}, [r1]! + // ARMV7: vld3.32 {d17, d19, d21}, [r1] + // ARMV7: vst1.32 {d16, d17}, [r0]! + // ARMV7: vst1.32 {d18, d19}, [r0]! + // ARMV7: vst1.64 {d20, d21}, [r0] + // ARMV7: bx lr + unsafe { vld3q_f32(ptr) } +} + +#[unsafe(no_mangle)] +#[cfg_attr(target_arch = "arm", target_feature(enable = "neon,v7"))] +fn test_vld3q_s32(ptr: *const i32) -> int32x4x3_t { + // AARCH64-LABEL: test_vld3q_s32 + // AARCH64: ld3 { v0.4s, v1.4s, v2.4s }, [x0] + // AARCH64: stp q0, q1, [x8] + // AARCH64: str q2, [x8, #32] + // AARCH64: ret + // + // ARMV7-LABEL: test_vld3q_s32 + // ARMV7: vld3.32 {d16, d18, d20}, [r1]! + // ARMV7: vld3.32 {d17, d19, d21}, [r1] + // ARMV7: vst1.32 {d16, d17}, [r0]! + // ARMV7: vst1.32 {d18, d19}, [r0]! + // ARMV7: vst1.64 {d20, d21}, [r0] + // ARMV7: bx lr + unsafe { vld3q_s32(ptr) } +} + +#[unsafe(no_mangle)] +#[cfg_attr(target_arch = "arm", target_feature(enable = "neon,v7"))] +fn test_vld3q_u32(ptr: *const u32) -> uint32x4x3_t { + // AARCH64-LABEL: test_vld3q_u32 + // AARCH64: ld3 { v0.4s, v1.4s, v2.4s }, [x0] + // AARCH64: stp q0, q1, [x8] + // AARCH64: str q2, [x8, #32] + // AARCH64: ret + // + // ARMV7-LABEL: test_vld3q_u32 + // ARMV7: vld3.32 {d16, d18, d20}, [r1]! + // ARMV7: vld3.32 {d17, d19, d21}, [r1] + // ARMV7: vst1.32 {d16, d17}, [r0]! + // ARMV7: vst1.32 {d18, d19}, [r0]! + // ARMV7: vst1.64 {d20, d21}, [r0] + // ARMV7: bx lr + unsafe { vld3q_u32(ptr) } +} From 4f50466be4802fb231200737fb0dc0933f421908 Mon Sep 17 00:00:00 2001 From: dianne Date: Wed, 18 Mar 2026 20:06:02 -0700 Subject: [PATCH 03/11] don't drop arguments' temporaries in `dbg!` (cherry picked from commit 51816efcc02d36d6f4c1a9149f655869ee6bc58d) --- library/std/src/macros.rs | 82 ++++++++++--------- library/std/src/macros/tests.rs | 13 +++ .../clippy/clippy_lints/src/dbg_macro.rs | 47 ++--------- .../dangling_primitive.stderr | 2 +- .../return_pointer_on_unwind.stderr | 2 +- tests/ui/borrowck/dbg-issue-120327.stderr | 40 ++++----- tests/ui/liveness/liveness-upvars.rs | 2 +- tests/ui/liveness/liveness-upvars.stderr | 10 ++- .../dbg-macro-move-semantics.stderr | 16 ++-- 9 files changed, 103 insertions(+), 111 deletions(-) create mode 100644 library/std/src/macros/tests.rs diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs index 0bb14552432d5..5f9b383c2da37 100644 --- a/library/std/src/macros.rs +++ b/library/std/src/macros.rs @@ -5,6 +5,9 @@ //! library. // ignore-tidy-dbg +#[cfg(test)] +mod tests; + #[doc = include_str!("../../core/src/macros/panic.md")] #[macro_export] #[rustc_builtin_macro(std_panic)] @@ -359,19 +362,16 @@ macro_rules! dbg { }; } -/// Internal macro that processes a list of expressions and produces a chain of -/// nested `match`es, one for each expression, before finally calling `eprint!` -/// with the collected information and returning all the evaluated expressions -/// in a tuple. +/// Internal macro that processes a list of expressions, binds their results +/// with `match`, calls `eprint!` with the collected information, and returns +/// all the evaluated expressions in a tuple. /// /// E.g. `dbg_internal!(() () (1, 2))` expands into /// ```rust, ignore -/// match 1 { -/// tmp_1 => match 2 { -/// tmp_2 => { -/// eprint!("...", &tmp_1, &tmp_2, /* some other arguments */); -/// (tmp_1, tmp_2) -/// } +/// match (1, 2) { +/// (tmp_1, tmp_2) => { +/// eprint!("...", &tmp_1, &tmp_2, /* some other arguments */); +/// (tmp_1, tmp_2) /// } /// } /// ``` @@ -380,37 +380,41 @@ macro_rules! dbg { #[doc(hidden)] #[rustc_macro_transparency = "semiopaque"] pub macro dbg_internal { - (($($piece:literal),+) ($($processed:expr => $bound:expr),+) ()) => {{ - $crate::eprint!( - $crate::concat!($($piece),+), - $( - $crate::stringify!($processed), - // The `&T: Debug` check happens here (not in the format literal desugaring) - // to avoid format literal related messages and suggestions. - &&$bound as &dyn $crate::fmt::Debug - ),+, - // The location returned here is that of the macro invocation, so - // it will be the same for all expressions. Thus, label these - // arguments so that they can be reused in every piece of the - // formatting template. - file=$crate::file!(), - line=$crate::line!(), - column=$crate::column!() - ); - // Comma separate the variables only when necessary so that this will - // not yield a tuple for a single expression, but rather just parenthesize - // the expression. - ($($bound),+) - }}, - (($($piece:literal),*) ($($processed:expr => $bound:expr),*) ($val:expr $(,$rest:expr)*)) => { + (($($piece:literal),+) ($($processed:expr => $bound:ident),+) ()) => { // Use of `match` here is intentional because it affects the lifetimes // of temporaries - https://stackoverflow.com/a/48732525/1063961 - match $val { - tmp => $crate::macros::dbg_internal!( - ($($piece,)* "[{file}:{line}:{column}] {} = {:#?}\n") - ($($processed => $bound,)* $val => tmp) - ($($rest),*) - ), + // Always put the arguments in a tuple to avoid an unused parens lint on the pattern. + match ($($processed,)+) { + ($($bound,)+) => { + $crate::eprint!( + $crate::concat!($($piece),+), + $( + $crate::stringify!($processed), + // The `&T: Debug` check happens here (not in the format literal desugaring) + // to avoid format literal related messages and suggestions. + &&$bound as &dyn $crate::fmt::Debug + ),+, + // The location returned here is that of the macro invocation, so + // it will be the same for all expressions. Thus, label these + // arguments so that they can be reused in every piece of the + // formatting template. + file=$crate::file!(), + line=$crate::line!(), + column=$crate::column!() + ); + // Comma separate the variables only when necessary so that this will + // not yield a tuple for a single expression, but rather just parenthesize + // the expression. + ($($bound),+) + + } } }, + (($($piece:literal),*) ($($processed:expr => $bound:ident),*) ($val:expr $(,$rest:expr)*)) => { + $crate::macros::dbg_internal!( + ($($piece,)* "[{file}:{line}:{column}] {} = {:#?}\n") + ($($processed => $bound,)* $val => tmp) + ($($rest),*) + ) + }, } diff --git a/library/std/src/macros/tests.rs b/library/std/src/macros/tests.rs new file mode 100644 index 0000000000000..db2be925ff30a --- /dev/null +++ b/library/std/src/macros/tests.rs @@ -0,0 +1,13 @@ +// ignore-tidy-dbg + +/// Test for : +/// `dbg!` shouldn't drop arguments' temporaries. +#[test] +fn no_dropping_temps() { + fn temp() {} + + *dbg!(&temp()); + *dbg!(&temp(), 1).0; + *dbg!(0, &temp()).1; + *dbg!(0, &temp(), 2).1; +} diff --git a/src/tools/clippy/clippy_lints/src/dbg_macro.rs b/src/tools/clippy/clippy_lints/src/dbg_macro.rs index f47d0d4835a9e..aed9b875fd093 100644 --- a/src/tools/clippy/clippy_lints/src/dbg_macro.rs +++ b/src/tools/clippy/clippy_lints/src/dbg_macro.rs @@ -5,7 +5,7 @@ use clippy_utils::macros::{MacroCall, macro_backtrace}; use clippy_utils::source::snippet_with_applicability; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::{Arm, Closure, ClosureKind, CoroutineKind, Expr, ExprKind, LetStmt, LocalSource, Node, Stmt, StmtKind}; +use rustc_hir::{Closure, ClosureKind, CoroutineKind, Expr, ExprKind, LetStmt, LocalSource, Node, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::{Span, SyntaxContext}; @@ -92,16 +92,15 @@ impl LateLintPass<'_> for DbgMacro { (macro_call.span, String::from("()")) } }, - ExprKind::Match(first, arms, _) => { - let vals = collect_vals(first, arms); - let suggestion = match *vals.as_slice() { + ExprKind::Match(args, _, _) => { + let suggestion = match args.kind { // dbg!(1) => 1 - [val] => { + ExprKind::Tup([val]) => { snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability) .to_string() }, // dbg!(2, 3) => (2, 3) - [first, .., last] => { + ExprKind::Tup([first, .., last]) => { let snippet = snippet_with_applicability( cx, first.span.source_callsite().to(last.span.source_callsite()), @@ -165,39 +164,3 @@ fn is_async_move_desugar<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx fn first_dbg_macro_in_expansion(cx: &LateContext<'_>, span: Span) -> Option { macro_backtrace(span).find(|mc| cx.tcx.is_diagnostic_item(sym::dbg_macro, mc.def_id)) } - -/// Extracts all value expressions from the `match`-tree generated by `dbg!`. -/// -/// E.g. from -/// ```rust, ignore -/// match 1 { -/// tmp_1 => match 2 { -/// tmp_2 => { -/// /* printing */ -/// (tmp_1, tmp_2) -/// } -/// } -/// } -/// ``` -/// this extracts `1` and `2`. -fn collect_vals<'hir>(first: &'hir Expr<'hir>, mut arms: &'hir [Arm<'hir>]) -> Vec<&'hir Expr<'hir>> { - let mut vals = vec![first]; - loop { - let [arm] = arms else { - unreachable!("dbg! macro expansion only has single-arm matches") - }; - - match is_async_move_desugar(arm.body) - .unwrap_or(arm.body) - .peel_drop_temps() - .kind - { - ExprKind::Block(..) => return vals, - ExprKind::Match(val, a, _) => { - vals.push(val); - arms = a; - }, - _ => unreachable!("dbg! macro expansion only results in block or match expressions"), - } - } -} diff --git a/src/tools/miri/tests/fail/dangling_pointers/dangling_primitive.stderr b/src/tools/miri/tests/fail/dangling_pointers/dangling_primitive.stderr index afc2cb214842e..4f06a1afa50fe 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/dangling_primitive.stderr +++ b/src/tools/miri/tests/fail/dangling_pointers/dangling_primitive.stderr @@ -2,7 +2,7 @@ error: Undefined Behavior: memory access failed: ALLOC has been freed, so this p --> tests/fail/dangling_pointers/dangling_primitive.rs:LL:CC | LL | dbg!(*ptr); - | ^^^^^^^^^^ Undefined Behavior occurred here + | ^^^^ Undefined Behavior occurred here | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr b/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr index 364a4b4a74418..88d5694c4736d 100644 --- a/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr +++ b/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr @@ -7,7 +7,7 @@ error: Undefined Behavior: reading memory at ALLOC[0x0..0x4], but memory is unin --> tests/fail/function_calls/return_pointer_on_unwind.rs:LL:CC | LL | dbg!(x.0); - | ^^^^^^^^^ Undefined Behavior occurred here + | ^^^ Undefined Behavior occurred here | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/tests/ui/borrowck/dbg-issue-120327.stderr b/tests/ui/borrowck/dbg-issue-120327.stderr index efacc0c3f1341..685a530a1fe5b 100644 --- a/tests/ui/borrowck/dbg-issue-120327.stderr +++ b/tests/ui/borrowck/dbg-issue-120327.stderr @@ -4,14 +4,14 @@ error[E0382]: use of moved value: `a` LL | let a = String::new(); | - move occurs because `a` has type `String`, which does not implement the `Copy` trait LL | dbg!(a); - | ------- value moved here + | - value moved here LL | return a; | ^ value used here after move | -help: consider borrowing instead of transferring ownership +help: consider cloning the value if the performance cost is acceptable | -LL | dbg!(&a); - | + +LL | dbg!(a.clone()); + | ++++++++ error[E0382]: use of moved value: `a` --> $DIR/dbg-issue-120327.rs:10:12 @@ -19,14 +19,14 @@ error[E0382]: use of moved value: `a` LL | let a = String::new(); | - move occurs because `a` has type `String`, which does not implement the `Copy` trait LL | dbg!(1, 2, a, 1, 2); - | ------------------- value moved here + | - value moved here LL | return a; | ^ value used here after move | -help: consider borrowing instead of transferring ownership +help: consider cloning the value if the performance cost is acceptable | -LL | dbg!(1, 2, &a, 1, 2); - | + +LL | dbg!(1, 2, a.clone(), 1, 2); + | ++++++++ error[E0382]: use of moved value: `b` --> $DIR/dbg-issue-120327.rs:16:12 @@ -34,14 +34,14 @@ error[E0382]: use of moved value: `b` LL | let b: String = "".to_string(); | - move occurs because `b` has type `String`, which does not implement the `Copy` trait LL | dbg!(a, b); - | ---------- value moved here + | - value moved here LL | return b; | ^ value used here after move | -help: consider borrowing instead of transferring ownership +help: consider cloning the value if the performance cost is acceptable | -LL | dbg!(a, &b); - | + +LL | dbg!(a, b.clone()); + | ++++++++ error[E0382]: use of moved value: `a` --> $DIR/dbg-issue-120327.rs:22:12 @@ -50,14 +50,14 @@ LL | fn x(a: String) -> String { | - move occurs because `a` has type `String`, which does not implement the `Copy` trait LL | let b: String = "".to_string(); LL | dbg!(a, b); - | ---------- value moved here + | - value moved here LL | return a; | ^ value used here after move | -help: consider borrowing instead of transferring ownership +help: consider cloning the value if the performance cost is acceptable | -LL | dbg!(&a, b); - | + +LL | dbg!(a.clone(), b); + | ++++++++ error[E0382]: use of moved value: `b` --> $DIR/dbg-issue-120327.rs:46:12 @@ -103,14 +103,14 @@ error[E0382]: borrow of moved value: `a` LL | let a: String = "".to_string(); | - move occurs because `a` has type `String`, which does not implement the `Copy` trait LL | let _res = get_expr(dbg!(a)); - | ------- value moved here + | - value moved here LL | let _l = a.len(); | ^ value borrowed here after move | -help: consider borrowing instead of transferring ownership +help: consider cloning the value if the performance cost is acceptable | -LL | let _res = get_expr(dbg!(&a)); - | + +LL | let _res = get_expr(dbg!(a.clone())); + | ++++++++ error: aborting due to 7 previous errors diff --git a/tests/ui/liveness/liveness-upvars.rs b/tests/ui/liveness/liveness-upvars.rs index be58b48a40576..0e198f1dea10b 100644 --- a/tests/ui/liveness/liveness-upvars.rs +++ b/tests/ui/liveness/liveness-upvars.rs @@ -98,7 +98,7 @@ pub fn g(mut v: T) { } pub fn h() { - let mut z = T::default(); + let mut z = T::default(); //~ WARN unused variable: `z` let _ = move |b| { loop { if b { diff --git a/tests/ui/liveness/liveness-upvars.stderr b/tests/ui/liveness/liveness-upvars.stderr index cfed2830164ad..0bb5786ab3e2e 100644 --- a/tests/ui/liveness/liveness-upvars.stderr +++ b/tests/ui/liveness/liveness-upvars.stderr @@ -156,6 +156,14 @@ LL | z = T::default(); | = help: maybe it is overwritten before being read? +warning: unused variable: `z` + --> $DIR/liveness-upvars.rs:101:9 + | +LL | let mut z = T::default(); + | ^^^^^ help: if this is intentional, prefix it with an underscore: `_z` + | + = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` + warning: value captured by `state` is never read --> $DIR/liveness-upvars.rs:131:9 | @@ -196,5 +204,5 @@ LL | s = yield (); | = help: maybe it is overwritten before being read? -warning: 24 warnings emitted +warning: 25 warnings emitted diff --git a/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr index f8ef315b9cc78..76b2d1be27795 100644 --- a/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr +++ b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr @@ -1,17 +1,21 @@ error[E0382]: use of moved value: `a` - --> $DIR/dbg-macro-move-semantics.rs:9:13 + --> $DIR/dbg-macro-move-semantics.rs:9:18 | LL | let a = NoCopy(0); | - move occurs because `a` has type `NoCopy`, which does not implement the `Copy` trait LL | let _ = dbg!(a); - | ------- value moved here + | - value moved here LL | let _ = dbg!(a); - | ^^^^^^^ value used here after move + | ^ value used here after move | -help: consider borrowing instead of transferring ownership +note: if `NoCopy` implemented `Clone`, you could clone the value + --> $DIR/dbg-macro-move-semantics.rs:4:1 | -LL | let _ = dbg!(&a); - | + +LL | struct NoCopy(usize); + | ^^^^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | let _ = dbg!(a); + | - you could clone this value error: aborting due to 1 previous error From 1374038f802353d235d89928fd605b271fb3a988 Mon Sep 17 00:00:00 2001 From: dianne Date: Sun, 29 Mar 2026 08:08:51 -0700 Subject: [PATCH 04/11] update diagnostic for variables moved by `dbg!` (cherry picked from commit 7d1b41cbb386fef7763885f738f7f89b48de10e2) --- .../src/diagnostics/conflict_errors.rs | 40 +++++---- compiler/rustc_span/src/symbol.rs | 1 + src/tools/clippy/clippy_utils/src/sym.rs | 1 - tests/ui/borrowck/dbg-issue-120327.rs | 53 ++++-------- tests/ui/borrowck/dbg-issue-120327.stderr | 81 ++++++++++++------- .../dbg-macro-move-semantics.stderr | 4 + 6 files changed, 96 insertions(+), 84 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index c2c07614bc0dd..3e34f2e5896ee 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -545,8 +545,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } // for dbg!(x) which may take ownership, suggest dbg!(&x) instead - // but here we actually do not check whether the macro name is `dbg!` - // so that we may extend the scope a bit larger to cover more cases fn suggest_ref_for_dbg_args( &self, body: &hir::Expr<'_>, @@ -560,29 +558,41 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { }); let Some(var_info) = var_info else { return }; let arg_name = var_info.name; - struct MatchArgFinder { - expr_span: Span, - match_arg_span: Option, + struct MatchArgFinder<'tcx> { + tcx: TyCtxt<'tcx>, + move_span: Span, arg_name: Symbol, + match_arg_span: Option = None, } - impl Visitor<'_> for MatchArgFinder { + impl Visitor<'_> for MatchArgFinder<'_> { fn visit_expr(&mut self, e: &hir::Expr<'_>) { // dbg! is expanded into a match pattern, we need to find the right argument span - if let hir::ExprKind::Match(expr, ..) = &e.kind - && let hir::ExprKind::Path(hir::QPath::Resolved( - _, - path @ Path { segments: [seg], .. }, - )) = &expr.kind - && seg.ident.name == self.arg_name - && self.expr_span.source_callsite().contains(expr.span) + if let hir::ExprKind::Match(scrutinee, ..) = &e.kind + && let hir::ExprKind::Tup(args) = scrutinee.kind + && e.span.macro_backtrace().any(|expn| { + expn.macro_def_id.is_some_and(|macro_def_id| { + self.tcx.is_diagnostic_item(sym::dbg_macro, macro_def_id) + }) + }) { - self.match_arg_span = Some(path.span); + for arg in args { + if let hir::ExprKind::Path(hir::QPath::Resolved( + _, + path @ Path { segments: [seg], .. }, + )) = &arg.kind + && seg.ident.name == self.arg_name + && self.move_span.source_equal(arg.span) + { + self.match_arg_span = Some(path.span); + return; + } + } } hir::intravisit::walk_expr(self, e); } } - let mut finder = MatchArgFinder { expr_span: move_span, match_arg_span: None, arg_name }; + let mut finder = MatchArgFinder { tcx: self.infcx.tcx, move_span, arg_name, .. }; finder.visit_expr(body); if let Some(macro_arg_span) = finder.match_arg_span { err.span_suggestion_verbose( diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 731a838530729..5b079bcaf0db8 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -763,6 +763,7 @@ symbols! { custom_test_frameworks, d, d32, + dbg_macro, dead_code, dealloc, debug, diff --git a/src/tools/clippy/clippy_utils/src/sym.rs b/src/tools/clippy/clippy_utils/src/sym.rs index 250e73afd4f00..a7171cff66f90 100644 --- a/src/tools/clippy/clippy_utils/src/sym.rs +++ b/src/tools/clippy/clippy_utils/src/sym.rs @@ -199,7 +199,6 @@ generate! { cx, cycle, cyclomatic_complexity, - dbg_macro, de, debug_struct, deprecated_in_future, diff --git a/tests/ui/borrowck/dbg-issue-120327.rs b/tests/ui/borrowck/dbg-issue-120327.rs index 2de43f634877a..45605acc34334 100644 --- a/tests/ui/borrowck/dbg-issue-120327.rs +++ b/tests/ui/borrowck/dbg-issue-120327.rs @@ -1,67 +1,44 @@ +//! Diagnostic test for : suggest borrowing +//! variables passed to `dbg!` that are later used. +//@ dont-require-annotations: HELP + fn s() -> String { let a = String::new(); - dbg!(a); + dbg!(a); //~ HELP consider borrowing instead of transferring ownership return a; //~ ERROR use of moved value: } fn m() -> String { let a = String::new(); - dbg!(1, 2, a, 1, 2); + dbg!(1, 2, a, 1, 2); //~ HELP consider borrowing instead of transferring ownership return a; //~ ERROR use of moved value: } fn t(a: String) -> String { let b: String = "".to_string(); - dbg!(a, b); + dbg!(a, b); //~ HELP consider borrowing instead of transferring ownership return b; //~ ERROR use of moved value: } fn x(a: String) -> String { let b: String = "".to_string(); - dbg!(a, b); + dbg!(a, b); //~ HELP consider borrowing instead of transferring ownership return a; //~ ERROR use of moved value: } -macro_rules! my_dbg { - () => { - eprintln!("[{}:{}:{}]", file!(), line!(), column!()) - }; - ($val:expr $(,)?) => { - match $val { - tmp => { - eprintln!("[{}:{}:{}] {} = {:#?}", - file!(), line!(), column!(), stringify!($val), &tmp); - tmp - } - } - }; - ($($val:expr),+ $(,)?) => { - ($(my_dbg!($val)),+,) - }; -} - -fn test_my_dbg() -> String { - let b: String = "".to_string(); - my_dbg!(b, 1); - return b; //~ ERROR use of moved value: -} - -fn test_not_macro() -> String { - let a = String::new(); - let _b = match a { - tmp => { - eprintln!("dbg: {}", tmp); - tmp - } - }; - return a; //~ ERROR use of moved value: +fn two_of_them(a: String) -> String { + dbg!(a, a); //~ ERROR use of moved value + //~| HELP consider borrowing instead of transferring ownership + //~| HELP consider borrowing instead of transferring ownership + return a; //~ ERROR use of moved value } fn get_expr(_s: String) {} +// The suggestion is purely syntactic; applying it here will result in a type error. fn test() { let a: String = "".to_string(); - let _res = get_expr(dbg!(a)); + let _res = get_expr(dbg!(a)); //~ HELP consider borrowing instead of transferring ownership let _l = a.len(); //~ ERROR borrow of moved value } diff --git a/tests/ui/borrowck/dbg-issue-120327.stderr b/tests/ui/borrowck/dbg-issue-120327.stderr index 685a530a1fe5b..e7a7151e541dd 100644 --- a/tests/ui/borrowck/dbg-issue-120327.stderr +++ b/tests/ui/borrowck/dbg-issue-120327.stderr @@ -1,5 +1,5 @@ error[E0382]: use of moved value: `a` - --> $DIR/dbg-issue-120327.rs:4:12 + --> $DIR/dbg-issue-120327.rs:8:12 | LL | let a = String::new(); | - move occurs because `a` has type `String`, which does not implement the `Copy` trait @@ -12,9 +12,13 @@ help: consider cloning the value if the performance cost is acceptable | LL | dbg!(a.clone()); | ++++++++ +help: consider borrowing instead of transferring ownership + | +LL | dbg!(&a); + | + error[E0382]: use of moved value: `a` - --> $DIR/dbg-issue-120327.rs:10:12 + --> $DIR/dbg-issue-120327.rs:14:12 | LL | let a = String::new(); | - move occurs because `a` has type `String`, which does not implement the `Copy` trait @@ -27,9 +31,13 @@ help: consider cloning the value if the performance cost is acceptable | LL | dbg!(1, 2, a.clone(), 1, 2); | ++++++++ +help: consider borrowing instead of transferring ownership + | +LL | dbg!(1, 2, &a, 1, 2); + | + error[E0382]: use of moved value: `b` - --> $DIR/dbg-issue-120327.rs:16:12 + --> $DIR/dbg-issue-120327.rs:20:12 | LL | let b: String = "".to_string(); | - move occurs because `b` has type `String`, which does not implement the `Copy` trait @@ -42,9 +50,13 @@ help: consider cloning the value if the performance cost is acceptable | LL | dbg!(a, b.clone()); | ++++++++ +help: consider borrowing instead of transferring ownership + | +LL | dbg!(a, &b); + | + error[E0382]: use of moved value: `a` - --> $DIR/dbg-issue-120327.rs:22:12 + --> $DIR/dbg-issue-120327.rs:26:12 | LL | fn x(a: String) -> String { | - move occurs because `a` has type `String`, which does not implement the `Copy` trait @@ -58,47 +70,52 @@ help: consider cloning the value if the performance cost is acceptable | LL | dbg!(a.clone(), b); | ++++++++ +help: consider borrowing instead of transferring ownership + | +LL | dbg!(&a, b); + | + -error[E0382]: use of moved value: `b` - --> $DIR/dbg-issue-120327.rs:46:12 +error[E0382]: use of moved value: `a` + --> $DIR/dbg-issue-120327.rs:30:13 | -LL | tmp => { - | --- value moved here -... -LL | let b: String = "".to_string(); - | - move occurs because `b` has type `String`, which does not implement the `Copy` trait -LL | my_dbg!(b, 1); -LL | return b; - | ^ value used here after move +LL | fn two_of_them(a: String) -> String { + | - move occurs because `a` has type `String`, which does not implement the `Copy` trait +LL | dbg!(a, a); + | - ^ value used here after move + | | + | value moved here | -help: consider borrowing instead of transferring ownership +help: consider cloning the value if the performance cost is acceptable | -LL | my_dbg!(&b, 1); - | + -help: borrow this binding in the pattern to avoid moving the value +LL | dbg!(a.clone(), a); + | ++++++++ +help: consider borrowing instead of transferring ownership | -LL | ref tmp => { - | +++ +LL | dbg!(&a, a); + | + error[E0382]: use of moved value: `a` - --> $DIR/dbg-issue-120327.rs:57:12 + --> $DIR/dbg-issue-120327.rs:33:12 | -LL | let a = String::new(); - | - move occurs because `a` has type `String`, which does not implement the `Copy` trait -LL | let _b = match a { -LL | tmp => { - | --- value moved here +LL | fn two_of_them(a: String) -> String { + | - move occurs because `a` has type `String`, which does not implement the `Copy` trait +LL | dbg!(a, a); + | - value moved here ... LL | return a; | ^ value used here after move | -help: borrow this binding in the pattern to avoid moving the value +help: consider cloning the value if the performance cost is acceptable + | +LL | dbg!(a, a.clone()); + | ++++++++ +help: consider borrowing instead of transferring ownership | -LL | ref tmp => { - | +++ +LL | dbg!(a, &a); + | + error[E0382]: borrow of moved value: `a` - --> $DIR/dbg-issue-120327.rs:65:14 + --> $DIR/dbg-issue-120327.rs:42:14 | LL | let a: String = "".to_string(); | - move occurs because `a` has type `String`, which does not implement the `Copy` trait @@ -111,6 +128,10 @@ help: consider cloning the value if the performance cost is acceptable | LL | let _res = get_expr(dbg!(a.clone())); | ++++++++ +help: consider borrowing instead of transferring ownership + | +LL | let _res = get_expr(dbg!(&a)); + | + error: aborting due to 7 previous errors diff --git a/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr index 76b2d1be27795..7ce5ebf81e310 100644 --- a/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr +++ b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr @@ -16,6 +16,10 @@ LL | struct NoCopy(usize); ... LL | let _ = dbg!(a); | - you could clone this value +help: consider borrowing instead of transferring ownership + | +LL | let _ = dbg!(&a); + | + error: aborting due to 1 previous error From 195f9764c0698126512b5b921d906f723b594ff9 Mon Sep 17 00:00:00 2001 From: mu001999 Date: Tue, 24 Mar 2026 20:07:23 +0800 Subject: [PATCH 05/11] Init self_decl with a correct vis (cherry picked from commit 01795c3eabbeca6e2a80c02385867ec055e3b7c7) --- compiler/rustc_resolve/src/lib.rs | 33 +++++++++++---- tests/ui/use/pub-use-self-super-crate.rs | 24 +++++++++++ tests/ui/use/pub-use-self-super-crate.stderr | 43 ++++++++++++++++++++ tests/ui/use/use-path-segment-kw.rs | 2 +- 4 files changed, 92 insertions(+), 10 deletions(-) create mode 100644 tests/ui/use/pub-use-self-super-crate.rs create mode 100644 tests/ui/use/pub-use-self-super-crate.stderr diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index d62a6fad02b01..c12281a9e0ef0 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -544,6 +544,13 @@ impl ModuleKind { ModuleKind::Def(.., name) => name, } } + + fn opt_def_id(&self) -> Option { + match self { + ModuleKind::Def(_, def_id, _) => Some(*def_id), + _ => None, + } + } } /// Combination of a symbol and its macros 2.0 normalized hygiene context. @@ -781,10 +788,7 @@ impl<'ra> Module<'ra> { } fn opt_def_id(self) -> Option { - match self.kind { - ModuleKind::Def(_, def_id, _) => Some(def_id), - _ => None, - } + self.kind.opt_def_id() } // `self` resolves to the first module ancestor that `is_normal`. @@ -1421,14 +1425,19 @@ impl<'ra> ResolverArenas<'ra> { &'ra self, parent: Option>, kind: ModuleKind, + vis: Visibility, expn_id: ExpnId, span: Span, no_implicit_prelude: bool, ) -> Module<'ra> { let self_decl = match kind { - ModuleKind::Def(def_kind, def_id, _) => { - Some(self.new_pub_def_decl(Res::Def(def_kind, def_id), span, LocalExpnId::ROOT)) - } + ModuleKind::Def(def_kind, def_id, _) => Some(self.new_def_decl( + Res::Def(def_kind, def_id), + vis, + span, + LocalExpnId::ROOT, + None, + )), ModuleKind::Block => None, }; Module(Interned::new_unchecked(self.modules.alloc(ModuleData::new( @@ -1610,6 +1619,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let graph_root = arenas.new_module( None, ModuleKind::Def(DefKind::Mod, root_def_id, None), + Visibility::Public, ExpnId::root(), crate_span, attr::contains_name(attrs, sym::no_implicit_prelude), @@ -1619,6 +1629,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let empty_module = arenas.new_module( None, ModuleKind::Def(DefKind::Mod, root_def_id, None), + Visibility::Public, ExpnId::root(), DUMMY_SP, true, @@ -1748,7 +1759,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { span: Span, no_implicit_prelude: bool, ) -> Module<'ra> { - let module = self.arenas.new_module(parent, kind, expn_id, span, no_implicit_prelude); + let vis = + kind.opt_def_id().map_or(Visibility::Public, |def_id| self.tcx.visibility(def_id)); + let module = self.arenas.new_module(parent, kind, vis, expn_id, span, no_implicit_prelude); self.local_modules.push(module); if let Some(def_id) = module.opt_def_id() { self.local_module_map.insert(def_id.expect_local(), module); @@ -1764,7 +1777,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { span: Span, no_implicit_prelude: bool, ) -> Module<'ra> { - let module = self.arenas.new_module(parent, kind, expn_id, span, no_implicit_prelude); + let vis = + kind.opt_def_id().map_or(Visibility::Public, |def_id| self.tcx.visibility(def_id)); + let module = self.arenas.new_module(parent, kind, vis, expn_id, span, no_implicit_prelude); self.extern_module_map.borrow_mut().insert(module.def_id(), module); module } diff --git a/tests/ui/use/pub-use-self-super-crate.rs b/tests/ui/use/pub-use-self-super-crate.rs new file mode 100644 index 0000000000000..1a799acb50fb9 --- /dev/null +++ b/tests/ui/use/pub-use-self-super-crate.rs @@ -0,0 +1,24 @@ +mod foo { + pub use self as this; + //~^ ERROR `self` is only public within the crate, and cannot be re-exported outside + + pub mod bar { + pub use super as parent; + //~^ ERROR `super` is only public within the crate, and cannot be re-exported outside + pub use self::super as parent2; + //~^ ERROR `super` is only public within the crate, and cannot be re-exported outside + pub use super::{self as parent3}; + //~^ ERROR `super` is only public within the crate, and cannot be re-exported outside + pub use self::{super as parent4}; + //~^ ERROR `super` is only public within the crate, and cannot be re-exported outside + + pub use crate as root; + pub use crate::{self as root2}; + pub use super::super as root3; + } +} + +pub use foo::*; +pub use foo::bar::*; + +pub fn main() {} diff --git a/tests/ui/use/pub-use-self-super-crate.stderr b/tests/ui/use/pub-use-self-super-crate.stderr new file mode 100644 index 0000000000000..3b336800a1800 --- /dev/null +++ b/tests/ui/use/pub-use-self-super-crate.stderr @@ -0,0 +1,43 @@ +error[E0365]: `self` is only public within the crate, and cannot be re-exported outside + --> $DIR/pub-use-self-super-crate.rs:2:13 + | +LL | pub use self as this; + | ^^^^^^^^^^^^ re-export of crate public `self` + | + = note: consider declaring type or module `self` with `pub` + +error[E0365]: `super` is only public within the crate, and cannot be re-exported outside + --> $DIR/pub-use-self-super-crate.rs:6:17 + | +LL | pub use super as parent; + | ^^^^^^^^^^^^^^^ re-export of crate public `super` + | + = note: consider declaring type or module `super` with `pub` + +error[E0365]: `super` is only public within the crate, and cannot be re-exported outside + --> $DIR/pub-use-self-super-crate.rs:8:17 + | +LL | pub use self::super as parent2; + | ^^^^^^^^^^^^^^^^^^^^^^ re-export of crate public `super` + | + = note: consider declaring type or module `super` with `pub` + +error[E0365]: `super` is only public within the crate, and cannot be re-exported outside + --> $DIR/pub-use-self-super-crate.rs:10:25 + | +LL | pub use super::{self as parent3}; + | ^^^^^^^^^^^^^^^ re-export of crate public `super` + | + = note: consider declaring type or module `super` with `pub` + +error[E0365]: `super` is only public within the crate, and cannot be re-exported outside + --> $DIR/pub-use-self-super-crate.rs:12:24 + | +LL | pub use self::{super as parent4}; + | ^^^^^^^^^^^^^^^^ re-export of crate public `super` + | + = note: consider declaring type or module `super` with `pub` + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0365`. diff --git a/tests/ui/use/use-path-segment-kw.rs b/tests/ui/use/use-path-segment-kw.rs index be64f239b9fc3..164f645dc5c54 100644 --- a/tests/ui/use/use-path-segment-kw.rs +++ b/tests/ui/use/use-path-segment-kw.rs @@ -70,7 +70,7 @@ macro_rules! macro_dollar_crate { fn outer() {} -mod foo { +pub mod foo { pub mod bar { pub mod foobar { pub mod qux { From 6db4dfefe16898b5dd8db7f65dff4b1268fc75f9 Mon Sep 17 00:00:00 2001 From: dianqk Date: Wed, 25 Mar 2026 07:32:01 +0800 Subject: [PATCH 06/11] Update LLVM to 22.1.2 (cherry picked from commit 283d705b314d8b393134f694ce005b5b5959ad60) --- src/llvm-project | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llvm-project b/src/llvm-project index 41f177ed26a52..05918363362b4 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit 41f177ed26a5252a30ea6090a4da66ce0a96bf44 +Subproject commit 05918363362b439b9b0bced3daa14badd75da790 From 533179a4379e27b65f8b28a8fb0a092f2e9930ba Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sat, 28 Mar 2026 17:43:29 +0100 Subject: [PATCH 07/11] [perf] Revert FastISel patch This change caused a significant compile-time regression for debug builds. (cherry picked from commit 1a320616e9d090e7ef7ac90e5ceabd7e2248d97f) --- src/llvm-project | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llvm-project b/src/llvm-project index 05918363362b4..1cb4e3833c191 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit 05918363362b439b9b0bced3daa14badd75da790 +Subproject commit 1cb4e3833c1919c2e6fb579a23ac0e2b22587b7e From b7bdc7aee26fbada150b5e9c84123dd26ba44fba Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Fri, 27 Mar 2026 09:31:38 +0000 Subject: [PATCH 08/11] core: Destabilize beta-stable `RangeInclusiveIter::remainder` As discussed, make this portion of the range API unstable again. This will now be tracked under `new_range_remainder`. Discussion: https://rust-lang.zulipchat.com/#narrow/channel/327149-t-libs-api.2Fapi-changes/topic/.60RangeFrom.3A.3Aremainder.60.20possible.20panic/with/582108913 (cherry picked from commit cbc94349d0ba20d6781cd3892794db55e630922a) --- library/core/src/range/iter.rs | 2 +- tests/ui/range/new_range_stability.rs | 2 +- tests/ui/range/new_range_stability.stderr | 12 +++++++++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/library/core/src/range/iter.rs b/library/core/src/range/iter.rs index c1d4fbbd23ad4..d162917bd842a 100644 --- a/library/core/src/range/iter.rs +++ b/library/core/src/range/iter.rs @@ -161,7 +161,7 @@ impl RangeInclusiveIter { /// Returns the remainder of the range being iterated over. /// /// If the iterator is exhausted or empty, returns `None`. - #[stable(feature = "new_range_inclusive_api", since = "1.95.0")] + #[unstable(feature = "new_range_remainder", issue = "154458")] pub fn remainder(self) -> Option> { if self.0.is_empty() { return None; diff --git a/tests/ui/range/new_range_stability.rs b/tests/ui/range/new_range_stability.rs index 7200e1ac95d27..98de98f9a7b70 100644 --- a/tests/ui/range/new_range_stability.rs +++ b/tests/ui/range/new_range_stability.rs @@ -11,7 +11,7 @@ fn range_inclusive(mut r: RangeInclusive) { let mut i = r.into_iter(); i.next(); - i.remainder(); + i.remainder(); //~ ERROR unstable } // Unstable module diff --git a/tests/ui/range/new_range_stability.stderr b/tests/ui/range/new_range_stability.stderr index 871d691794ff0..d34dbd35c9c26 100644 --- a/tests/ui/range/new_range_stability.stderr +++ b/tests/ui/range/new_range_stability.stderr @@ -48,6 +48,16 @@ LL | use std::range::RangeIter; = help: add `#![feature(new_range_api)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 5 previous errors +error[E0658]: use of unstable library feature `new_range_remainder` + --> $DIR/new_range_stability.rs:14:7 + | +LL | i.remainder(); + | ^^^^^^^^^ + | + = note: see issue #154458 for more information + = help: add `#![feature(new_range_remainder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0658`. From f68f5980c3834ea3e6be196afd1aabc535676f8e Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Fri, 27 Mar 2026 09:35:24 +0000 Subject: [PATCH 09/11] core: Move `{RangeIter, RangeFromIter}::remainder` to `new_range_remainder` Split the remainder functions from the rest of `std::range`. (cherry picked from commit 0bb3fe315e2130ce5229462d44ccc0ff09c41a4c) --- library/core/src/range/iter.rs | 4 ++-- tests/codegen-llvm/fromrangeiter-overflow-checks.rs | 1 + tests/ui/iterators/rangefrom-overflow-debug.rs | 1 + tests/ui/iterators/rangefrom-overflow-ndebug.rs | 1 + tests/ui/iterators/rangefrom-overflow-overflow-checks.rs | 1 + 5 files changed, 6 insertions(+), 2 deletions(-) diff --git a/library/core/src/range/iter.rs b/library/core/src/range/iter.rs index d162917bd842a..03adc2b22c58f 100644 --- a/library/core/src/range/iter.rs +++ b/library/core/src/range/iter.rs @@ -11,7 +11,7 @@ use crate::{intrinsics, mem}; pub struct RangeIter(legacy::Range); impl RangeIter { - #[unstable(feature = "new_range_api", issue = "125687")] + #[unstable(feature = "new_range_remainder", issue = "154458")] /// Returns the remainder of the range being iterated over. pub fn remainder(self) -> Range { Range { start: self.0.start, end: self.0.end } @@ -307,7 +307,7 @@ impl RangeFromIter { /// Returns the remainder of the range being iterated over. #[inline] #[rustc_inherit_overflow_checks] - #[unstable(feature = "new_range_api", issue = "125687")] + #[unstable(feature = "new_range_remainder", issue = "154458")] pub fn remainder(self) -> RangeFrom { if intrinsics::overflow_checks() { if !self.first { diff --git a/tests/codegen-llvm/fromrangeiter-overflow-checks.rs b/tests/codegen-llvm/fromrangeiter-overflow-checks.rs index 4d27f118ddd37..8c2fbebd0baa7 100644 --- a/tests/codegen-llvm/fromrangeiter-overflow-checks.rs +++ b/tests/codegen-llvm/fromrangeiter-overflow-checks.rs @@ -11,6 +11,7 @@ #![crate_type = "lib"] #![feature(new_range_api)] +#![feature(new_range_remainder)] use std::range::{RangeFrom, RangeFromIter}; // CHECK-LABEL: @iterrangefrom_remainder( diff --git a/tests/ui/iterators/rangefrom-overflow-debug.rs b/tests/ui/iterators/rangefrom-overflow-debug.rs index 9a1bc6910a044..ccb4a8ba8268c 100644 --- a/tests/ui/iterators/rangefrom-overflow-debug.rs +++ b/tests/ui/iterators/rangefrom-overflow-debug.rs @@ -3,6 +3,7 @@ //@ compile-flags: -O -C debug_assertions=yes #![feature(new_range_api)] +#![feature(new_range_remainder)] use std::panic; diff --git a/tests/ui/iterators/rangefrom-overflow-ndebug.rs b/tests/ui/iterators/rangefrom-overflow-ndebug.rs index 4ce9b0636383c..efdc757472d9d 100644 --- a/tests/ui/iterators/rangefrom-overflow-ndebug.rs +++ b/tests/ui/iterators/rangefrom-overflow-ndebug.rs @@ -2,6 +2,7 @@ //@ compile-flags: -O -C debug_assertions=no #![feature(new_range_api)] +#![feature(new_range_remainder)] fn main() { let mut it = core::range::RangeFrom::from(u8::MAX..).into_iter(); diff --git a/tests/ui/iterators/rangefrom-overflow-overflow-checks.rs b/tests/ui/iterators/rangefrom-overflow-overflow-checks.rs index 7e3b0fc308405..4692b041f4940 100644 --- a/tests/ui/iterators/rangefrom-overflow-overflow-checks.rs +++ b/tests/ui/iterators/rangefrom-overflow-overflow-checks.rs @@ -3,6 +3,7 @@ //@ compile-flags: -O -C overflow-checks=yes #![feature(new_range_api)] +#![feature(new_range_remainder)] use std::panic; From 5dacf5d54d2cc1a23ba5fc859fe7494115e8addd Mon Sep 17 00:00:00 2001 From: Wesley Wiser Date: Wed, 1 Apr 2026 21:29:42 -0500 Subject: [PATCH 10/11] Revert "Fix: On wasm targets, call `panic_in_cleanup` if panic occurs in cleanup" This reverts commit acbfd79acf662be04745dc0675558e75e0d30d87. (cherry picked from commit c9d3a00cd1a0a445a4563fd2ff3a7677cc783e1d) --- compiler/rustc_codegen_gcc/src/builder.rs | 4 - compiler/rustc_codegen_llvm/src/builder.rs | 4 - compiler/rustc_codegen_ssa/src/mir/block.rs | 96 ++++--------------- compiler/rustc_codegen_ssa/src/mir/mod.rs | 9 +- .../rustc_codegen_ssa/src/traits/builder.rs | 5 +- tests/codegen-llvm/double_panic_wasm.rs | 34 ------- tests/codegen-llvm/terminating-catchpad.rs | 10 -- 7 files changed, 24 insertions(+), 138 deletions(-) delete mode 100644 tests/codegen-llvm/double_panic_wasm.rs diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index f65da57317ce0..e1937f5c11eb5 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -1655,10 +1655,6 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { unimplemented!(); } - fn get_funclet_cleanuppad(&self, _funclet: &Funclet) -> RValue<'gcc> { - unimplemented!(); - } - // Atomic Operations fn atomic_cmpxchg( &mut self, diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 16c9d557d6b65..4ffc836f5559c 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1296,10 +1296,6 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { ret } - fn get_funclet_cleanuppad(&self, funclet: &Funclet<'ll>) -> &'ll Value { - funclet.cleanuppad() - } - // Atomic Operations fn atomic_cmpxchg( &mut self, diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 0d42ccc1a73c9..4c9a2bf663f94 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -215,18 +215,19 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { mir::UnwindAction::Continue => None, mir::UnwindAction::Unreachable => None, mir::UnwindAction::Terminate(reason) => { - if fx.mir[self.bb].is_cleanup && base::wants_wasm_eh(fx.cx.tcx().sess) { - // For wasm, we need to generate a nested `cleanuppad within %outer_pad` - // to catch exceptions during cleanup and call `panic_in_cleanup`. - Some(fx.terminate_block(reason, Some(self.bb))) - } else if fx.mir[self.bb].is_cleanup - && base::wants_new_eh_instructions(fx.cx.tcx().sess) - { + if fx.mir[self.bb].is_cleanup && base::wants_new_eh_instructions(fx.cx.tcx().sess) { // MSVC SEH will abort automatically if an exception tries to // propagate out from cleanup. + + // FIXME(@mirkootter): For wasm, we currently do not support terminate during + // cleanup, because this requires a few more changes: The current code + // caches the `terminate_block` for each function; funclet based code - however - + // requires a different terminate_block for each funclet + // Until this is implemented, we just do not unwind inside cleanup blocks + None } else { - Some(fx.terminate_block(reason, None)) + Some(fx.terminate_block(reason)) } } }; @@ -238,7 +239,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { if let Some(unwind_block) = unwind_block { let ret_llbb = if let Some((_, target)) = destination { - self.llbb_with_cleanup(fx, target) + fx.llbb(target) } else { fx.unreachable_block() }; @@ -309,7 +310,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { ) -> MergingSucc { let unwind_target = match unwind { mir::UnwindAction::Cleanup(cleanup) => Some(self.llbb_with_cleanup(fx, cleanup)), - mir::UnwindAction::Terminate(reason) => Some(fx.terminate_block(reason, None)), + mir::UnwindAction::Terminate(reason) => Some(fx.terminate_block(reason)), mir::UnwindAction::Continue => None, mir::UnwindAction::Unreachable => None, }; @@ -317,7 +318,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { if operands.iter().any(|x| matches!(x, InlineAsmOperandRef::Label { .. })) { assert!(unwind_target.is_none()); let ret_llbb = if let Some(target) = destination { - self.llbb_with_cleanup(fx, target) + fx.llbb(target) } else { fx.unreachable_block() }; @@ -334,7 +335,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { MergingSucc::False } else if let Some(cleanup) = unwind_target { let ret_llbb = if let Some(target) = destination { - self.llbb_with_cleanup(fx, target) + fx.llbb(target) } else { fx.unreachable_block() }; @@ -1915,39 +1916,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }) } - fn terminate_block( - &mut self, - reason: UnwindTerminateReason, - outer_catchpad_bb: Option, - ) -> Bx::BasicBlock { - // mb_funclet_bb should be present if and only if the target is wasm and - // we're terminating because of an unwind in a cleanup block. In that - // case we have nested funclets and the inner catch_switch needs to know - // what outer catch_pad it is contained in. - debug_assert!( - outer_catchpad_bb.is_some() - == (base::wants_wasm_eh(self.cx.tcx().sess) - && reason == UnwindTerminateReason::InCleanup) - ); - - // When we aren't in a wasm InCleanup block, there's only one terminate - // block needed so we cache at START_BLOCK index. - let mut cache_bb = mir::START_BLOCK; - // In wasm eh InCleanup, use the outer funclet's cleanup BB as the cache - // key. - if let Some(outer_bb) = outer_catchpad_bb { - let cleanup_kinds = - self.cleanup_kinds.as_ref().expect("cleanup_kinds required for funclets"); - cache_bb = cleanup_kinds[outer_bb] - .funclet_bb(outer_bb) - .expect("funclet_bb should be in a funclet"); - - // Ensure the outer funclet is created first - if self.funclets[cache_bb].is_none() { - self.landing_pad_for(cache_bb); - } - } - if let Some((cached_bb, cached_reason)) = self.terminate_blocks[cache_bb] + fn terminate_block(&mut self, reason: UnwindTerminateReason) -> Bx::BasicBlock { + if let Some((cached_bb, cached_reason)) = self.terminate_block && reason == cached_reason { return cached_bb; @@ -1985,35 +1955,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // cp_terminate: // %cp = catchpad within %cs [null, i32 64, null] // ... - // - // By contrast, on WebAssembly targets, we specifically _do_ want to - // catch foreign exceptions. The situation with MSVC is a - // regrettable hack which we don't want to extend to other targets - // unless necessary. For WebAssembly, to generate catch(...) and - // catch only C++ exception instead of generating a catch_all, we - // need to call the intrinsics @llvm.wasm.get.exception and - // @llvm.wasm.get.ehselector in the catch pad. Since we don't do - // this, we generate a catch_all. We originally got this behavior - // by accident but it luckily matches our intention. llbb = Bx::append_block(self.cx, self.llfn, "cs_terminate"); + let cp_llbb = Bx::append_block(self.cx, self.llfn, "cp_terminate"); let mut cs_bx = Bx::build(self.cx, llbb); - - // For wasm InCleanup blocks, our catch_switch is nested within the - // outer catchpad, so we need to provide it as the parent value to - // catch_switch. - let mut outer_cleanuppad = None; - if outer_catchpad_bb.is_some() { - // Get the outer funclet's catchpad - let outer_funclet = self.funclets[cache_bb] - .as_ref() - .expect("landing_pad_for didn't create funclet"); - outer_cleanuppad = Some(cs_bx.get_funclet_cleanuppad(outer_funclet)); - } - let cp_llbb = Bx::append_block(self.cx, self.llfn, "cp_terminate"); - let cs = cs_bx.catch_switch(outer_cleanuppad, None, &[cp_llbb]); - drop(cs_bx); + let cs = cs_bx.catch_switch(None, None, &[cp_llbb]); bx = Bx::build(self.cx, cp_llbb); let null = @@ -2034,18 +1981,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } else { // Specifying more arguments than necessary usually doesn't // hurt, but the `WasmEHPrepare` LLVM pass does not recognize - // anything other than a single `null` as a `catch_all` block, + // anything other than a single `null` as a `catch (...)` block, // leading to problems down the line during instruction // selection. &[null] as &[_] }; funclet = Some(bx.catch_pad(cs, args)); - // On wasm, if we wanted to generate a catch(...) and only catch C++ - // exceptions, we'd call @llvm.wasm.get.exception and - // @llvm.wasm.get.ehselector selectors here. We want a catch_all so - // we leave them out. This is intentionally diverging from the MSVC - // behavior. } else { llbb = Bx::append_block(self.cx, self.llfn, "terminate"); bx = Bx::build(self.cx, llbb); @@ -2071,7 +2013,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.unreachable(); - self.terminate_blocks[cache_bb] = Some((llbb, reason)); + self.terminate_block = Some((llbb, reason)); llbb } diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 1a0f66d31cca4..819abb9ae644d 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -90,11 +90,8 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> { /// Cached unreachable block unreachable_block: Option, - /// Cached terminate upon unwinding block and its reason. For non-wasm - /// targets, there is at most one such block per function, stored at index - /// `START_BLOCK`. For wasm targets, each funclet needs its own terminate - /// block, indexed by the cleanup block that is the funclet's head. - terminate_blocks: IndexVec>, + /// Cached terminate upon unwinding block and its reason + terminate_block: Option<(Bx::BasicBlock, UnwindTerminateReason)>, /// A bool flag for each basic block indicating whether it is a cold block. /// A cold block is a block that is unlikely to be executed at runtime. @@ -230,7 +227,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( personality_slot: None, cached_llbbs, unreachable_block: None, - terminate_blocks: IndexVec::from_elem(None, &mir.basic_blocks), + terminate_block: None, cleanup_kinds, landing_pads: IndexVec::from_elem(None, &mir.basic_blocks), funclets: IndexVec::from_fn_n(|_| None, mir.basic_blocks.len()), diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index edabd8188a3b6..3486bd140eceb 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -552,12 +552,12 @@ pub trait BuilderMethods<'a, 'tcx>: fn set_personality_fn(&mut self, personality: Self::Function); - // These are used by everyone except msvc and wasm EH + // These are used by everyone except msvc fn cleanup_landing_pad(&mut self, pers_fn: Self::Function) -> (Self::Value, Self::Value); fn filter_landing_pad(&mut self, pers_fn: Self::Function); fn resume(&mut self, exn0: Self::Value, exn1: Self::Value); - // These are used by msvc and wasm EH + // These are used only by msvc fn cleanup_pad(&mut self, parent: Option, args: &[Self::Value]) -> Self::Funclet; fn cleanup_ret(&mut self, funclet: &Self::Funclet, unwind: Option); fn catch_pad(&mut self, parent: Self::Value, args: &[Self::Value]) -> Self::Funclet; @@ -567,7 +567,6 @@ pub trait BuilderMethods<'a, 'tcx>: unwind: Option, handlers: &[Self::BasicBlock], ) -> Self::Value; - fn get_funclet_cleanuppad(&self, funclet: &Self::Funclet) -> Self::Value; fn atomic_cmpxchg( &mut self, diff --git a/tests/codegen-llvm/double_panic_wasm.rs b/tests/codegen-llvm/double_panic_wasm.rs deleted file mode 100644 index 1eafe60503809..0000000000000 --- a/tests/codegen-llvm/double_panic_wasm.rs +++ /dev/null @@ -1,34 +0,0 @@ -//@ compile-flags: -C panic=unwind -Copt-level=0 -//@ needs-unwind -//@ only-wasm32 - -#![crate_type = "lib"] - -// Test that `panic_in_cleanup` is called on webassembly targets when a panic -// occurs in a destructor during unwinding. - -extern "Rust" { - fn may_panic(); -} - -struct PanicOnDrop; - -impl Drop for PanicOnDrop { - fn drop(&mut self) { - unsafe { may_panic() } - } -} - -// CHECK-LABEL: @double_panic -// CHECK: invoke void @may_panic() -// CHECK: invoke void @{{.+}}drop_in_place{{.+}} -// CHECK: unwind label %[[TERMINATE:.*]] -// -// CHECK: [[TERMINATE]]: -// CHECK: call void @{{.*panic_in_cleanup}} -// CHECK: unreachable -#[no_mangle] -pub fn double_panic() { - let _guard = PanicOnDrop; - unsafe { may_panic() } -} diff --git a/tests/codegen-llvm/terminating-catchpad.rs b/tests/codegen-llvm/terminating-catchpad.rs index 7c98ea94fdc13..a2ec19871d1fc 100644 --- a/tests/codegen-llvm/terminating-catchpad.rs +++ b/tests/codegen-llvm/terminating-catchpad.rs @@ -9,10 +9,6 @@ // Ensure a catch-all generates: // - `catchpad ... [ptr null]` on Wasm (otherwise LLVM gets confused) // - `catchpad ... [ptr null, i32 64, ptr null]` on Windows (otherwise we catch SEH exceptions) -// -// Unlike on windows, on Wasm, we specifically do want to catch foreign -// exceptions. To catch only C++ exceptions we'd need to call -// @llvm.wasm.get.exception and @llvm.wasm.get.ehselector in the catchpad. #![feature(no_core, lang_items, rustc_attrs)] #![crate_type = "lib"] @@ -40,14 +36,8 @@ fn panic_cannot_unwind() -> ! { #[no_mangle] #[rustc_nounwind] pub fn doesnt_unwind() { - // CHECK: catchswitch within none [label %{{.*}}] unwind to caller // emscripten: %catchpad = catchpad within %catchswitch [ptr null] // wasi: %catchpad = catchpad within %catchswitch [ptr null] // seh: %catchpad = catchpad within %catchswitch [ptr null, i32 64, ptr null] - // - // We don't call these intrinsics on wasm targets so we generate a catch_all - // instruction which also picks up foreign exceptions - // NOT: @llvm.wasm.get.exception - // NOT: @llvm.wasm.get.ehselector unwinds(); } From 940fc4d3b3c42525450bb180a71149aba78c7940 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Wed, 1 Apr 2026 14:30:44 -0400 Subject: [PATCH 11/11] core: Update the feature gate on `TryFrom for bool` This implementation was added recently in f12288ec2632 ("TryFrom for bool") but used an old feature gate and stabilization version. Update to reflect when these were actually added. (cherry picked from commit 02b516afd99d5796672543e50529a002200858ff) --- library/core/src/convert/num.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/convert/num.rs b/library/core/src/convert/num.rs index 03650615e25a6..5bea429d52c53 100644 --- a/library/core/src/convert/num.rs +++ b/library/core/src/convert/num.rs @@ -332,7 +332,7 @@ macro_rules! impl_try_from_both_bounded { /// Implement `TryFrom` for `bool` macro_rules! impl_try_from_integer_for_bool { ($($int:ty)+) => {$( - #[stable(feature = "try_from", since = "1.34.0")] + #[stable(feature = "bool_try_from_int", since = "1.95.0")] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const TryFrom<$int> for bool { type Error = TryFromIntError;