From 84963f87e68007ad1adbd331661f6e0d28b55395 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Fri, 28 Aug 2026 13:04:20 +0200 Subject: [PATCH 01/15] run `extern "tail"` with `byval` argument test --- .../tailcc-no-signature-restriction.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/ui/explicit-tail-calls/tailcc-no-signature-restriction.rs b/tests/ui/explicit-tail-calls/tailcc-no-signature-restriction.rs index 9c9085ca1daca..64cea66c1d565 100644 --- a/tests/ui/explicit-tail-calls/tailcc-no-signature-restriction.rs +++ b/tests/ui/explicit-tail-calls/tailcc-no-signature-restriction.rs @@ -1,9 +1,9 @@ //@ run-pass //@ ignore-backends: gcc -//@ min-llvm-version: 22 -//@ revisions: x86_64 aarch64 +//@ min-llvm-version: 23 +//@ revisions: x86 x86_64 aarch64 // -// FIXME: enable x86 on LLVM 23. +//@ [x86] only-x86 //@ [x86_64] only-x86_64 //@ [aarch64] only-aarch64 #![feature(explicit_tail_calls, rust_tail_cc)] @@ -18,6 +18,7 @@ pub extern "tail" fn add() -> u64 { become add(1, 2); } +#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), not(windows)))] #[inline(never)] pub extern "tail" fn pass_struct(a: u64, d: u64) -> u64 { #[derive(Clone, Copy)] @@ -42,8 +43,7 @@ pub extern "tail" fn pass_struct(a: u64, d: u64) -> u64 { fn main() { assert_eq!(add(), 3); - // FIXME: LLVM 22 has a bug which makes this miscompile. - if false { - assert_eq!(pass_struct(5, 6), 5 + 6); - } + // Windows and Aarch64 in LLVM 23 does not support byval arguments. + #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), not(windows)))] + assert_eq!(pass_struct(5, 6), 5 + 6); } From a9a7fbdd11606c354e887baa2fa783f51b5aece7 Mon Sep 17 00:00:00 2001 From: Roland Xu Date: Fri, 28 Aug 2026 22:40:20 +0800 Subject: [PATCH 02/15] Only check allow-on-trait --- compiler/rustc_passes/src/dead.rs | 13 +++++-------- .../allow-adt-propagation-to-impls.rs | 18 ++++++++++++++++++ .../allow-adt-propagation-to-impls.stderr | 14 ++++++++++++++ 3 files changed, 37 insertions(+), 8 deletions(-) create mode 100644 tests/ui/lint/dead-code/allow-adt-propagation-to-impls.rs create mode 100644 tests/ui/lint/dead-code/allow-adt-propagation-to-impls.stderr diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index d6a8b2c2f5eb6..637052eb87530 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -601,15 +601,12 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { { if defer_seeds_come_from_allow { return ImplItemCheckResult::Dead { require: adt_def_id }; - } else { - let comes_from_allow = trait_comes_from_allow - .or_else(|| has_allow_dead_code_or_lang_attr(self.tcx, adt_def_id)); - - return match comes_from_allow { - Some(comes_from_allow) => ImplItemCheckResult::Live(comes_from_allow), - None => ImplItemCheckResult::Dead { require: adt_def_id }, - }; } + + return match trait_comes_from_allow { + Some(comes_from_allow) => ImplItemCheckResult::Live(comes_from_allow), + None => ImplItemCheckResult::Dead { require: adt_def_id }, + }; } ImplItemCheckResult::Live(ComesFromAllowExpect::No) diff --git a/tests/ui/lint/dead-code/allow-adt-propagation-to-impls.rs b/tests/ui/lint/dead-code/allow-adt-propagation-to-impls.rs new file mode 100644 index 0000000000000..72d68c3e91396 --- /dev/null +++ b/tests/ui/lint/dead-code/allow-adt-propagation-to-impls.rs @@ -0,0 +1,18 @@ +#![deny(dead_code)] + +pub trait Tr { + fn foo(&self); +} + +#[allow(dead_code)] +struct Foo; + +impl Tr for Foo { + fn foo(&self) { + bar(); + } +} + +fn bar() {} //~ ERROR function `bar` is never used + +fn main() {} diff --git a/tests/ui/lint/dead-code/allow-adt-propagation-to-impls.stderr b/tests/ui/lint/dead-code/allow-adt-propagation-to-impls.stderr new file mode 100644 index 0000000000000..ba0fe951e4e59 --- /dev/null +++ b/tests/ui/lint/dead-code/allow-adt-propagation-to-impls.stderr @@ -0,0 +1,14 @@ +error: function `bar` is never used + --> $DIR/allow-adt-propagation-to-impls.rs:16:4 + | +LL | fn bar() {} + | ^^^ + | +note: the lint level is defined here + --> $DIR/allow-adt-propagation-to-impls.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: aborting due to 1 previous error + From 77688344510b058e12a644b9e5f135e9d3919e46 Mon Sep 17 00:00:00 2001 From: Yukang Date: Mon, 7 Sep 2026 18:39:52 +0800 Subject: [PATCH 03/15] Add regression test for item-local diagnostic attribute lint levels --- .../local-lint-level-issue-135772.rs | 34 +++++++++++++++++++ .../local-lint-level-issue-135772.stderr | 10 ++++++ 2 files changed, 44 insertions(+) create mode 100644 tests/ui/diagnostic_namespace/local-lint-level-issue-135772.rs create mode 100644 tests/ui/diagnostic_namespace/local-lint-level-issue-135772.stderr diff --git a/tests/ui/diagnostic_namespace/local-lint-level-issue-135772.rs b/tests/ui/diagnostic_namespace/local-lint-level-issue-135772.rs new file mode 100644 index 0000000000000..093ebf0a266ec --- /dev/null +++ b/tests/ui/diagnostic_namespace/local-lint-level-issue-135772.rs @@ -0,0 +1,34 @@ +//! Regression test for . +// Unknown diagnostic attributes must respect item-local lint levels. + +//@ check-pass + +trait _Trait {} + +#[allow(unknown_or_malformed_diagnostic_attributes)] +#[diagnostic::abcdef] +impl _Trait for () {} + +#[diagnostic::abcdef] +#[allow(unknown_or_malformed_diagnostic_attributes)] +impl _Trait for bool {} + +#[expect(unknown_or_malformed_diagnostic_attributes)] +#[diagnostic::abcdef] +impl _Trait for u8 {} + +macro_rules! impl_trait { + ($ty:ty) => { + #[allow(unknown_or_malformed_diagnostic_attributes)] + #[diagnostic::abcdef] + impl _Trait for $ty {} + }; +} + +impl_trait!(u16); + +#[diagnostic::abcdef] +//~^ WARN unknown diagnostic attribute +impl _Trait for u32 {} + +fn main() {} diff --git a/tests/ui/diagnostic_namespace/local-lint-level-issue-135772.stderr b/tests/ui/diagnostic_namespace/local-lint-level-issue-135772.stderr new file mode 100644 index 0000000000000..9d3880fc4e6bd --- /dev/null +++ b/tests/ui/diagnostic_namespace/local-lint-level-issue-135772.stderr @@ -0,0 +1,10 @@ +warning: unknown diagnostic attribute + --> $DIR/local-lint-level-issue-135772.rs:30:15 + | +LL | #[diagnostic::abcdef] + | ^^^^^^ + | + = note: `#[warn(unknown_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +warning: 1 warning emitted + From 38e3714a7a7bedb70a5a2a2a20ba3a0c2c4710ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Miku=C5=82a?= Date: Mon, 7 Sep 2026 17:13:35 +0200 Subject: [PATCH 04/15] windows-gnu: document libgcc requirement --- src/doc/rustc/src/platform-support/windows-gnu.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/doc/rustc/src/platform-support/windows-gnu.md b/src/doc/rustc/src/platform-support/windows-gnu.md index d7aec5af21dec..595c2a42c81a4 100644 --- a/src/doc/rustc/src/platform-support/windows-gnu.md +++ b/src/doc/rustc/src/platform-support/windows-gnu.md @@ -34,6 +34,7 @@ The targets are built and tested using a reasonably modern C toolchain, and it s * GCC 14.2 * mingw-w64 12.0.0 * MSVCRT library as the default +* Libgcc with DWARF-2 exception handling for i686 and SEH for x86_64 Using older tools (especially Binutils) may not work properly, due to the number of issues plaguing older versions of Binutils. The supported toolchain versions are subject to change. From 9dee1dc06af405502f88fd2d162024802d9b85b1 Mon Sep 17 00:00:00 2001 From: rustbot <47979223+rustbot@users.noreply.github.com> Date: Mon, 7 Sep 2026 19:00:47 +0200 Subject: [PATCH 05/15] Update books --- src/doc/book | 2 +- src/doc/edition-guide | 2 +- src/doc/reference | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/book b/src/doc/book index 917544888a55e..1500248d8f230 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit 917544888a55e4da7109bdba8c88c893c0da70f4 +Subproject commit 1500248d8f230566e4ec9f27fcbb8fe9e2898ab1 diff --git a/src/doc/edition-guide b/src/doc/edition-guide index f5abcf137698e..ab8544aeed7b7 160000 --- a/src/doc/edition-guide +++ b/src/doc/edition-guide @@ -1 +1 @@ -Subproject commit f5abcf137698e5ad6ebed359d69654ff705346af +Subproject commit ab8544aeed7b792984366aa122ac19bd47ad9a2f diff --git a/src/doc/reference b/src/doc/reference index 3b38834b39f73..e24eecf97b0c9 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 3b38834b39f732c64686f7c64aa29dcf3cd83ba5 +Subproject commit e24eecf97b0c9a6dbac67191098204dc8a190aaa From fa2850ebe725abe8aa786e514a5b0ffcae57ba07 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 24 Aug 2026 11:43:21 +0200 Subject: [PATCH 06/15] misc typo fixes --- .../src/solve/sharing-crates-with-rust-analyzer.md | 10 +++++----- src/doc/rustc-dev-guide/src/solve/the-solver.md | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/solve/sharing-crates-with-rust-analyzer.md b/src/doc/rustc-dev-guide/src/solve/sharing-crates-with-rust-analyzer.md index 110fd1331bc44..bf9655c866ee0 100644 --- a/src/doc/rustc-dev-guide/src/solve/sharing-crates-with-rust-analyzer.md +++ b/src/doc/rustc-dev-guide/src/solve/sharing-crates-with-rust-analyzer.md @@ -115,7 +115,7 @@ For rust-analyzer, the corresponding implementations are located across several These two traits correspond to the role of [`InferCtxt`][rustc inferctxt] in rustc. [`InferCtxtLike`][ir inferctxtlike] must be defined in `rustc_infer` due to coherence -constraints(orphan rules). +constraints (orphan rules). As a result, it cannot provide functionality that lives in `rustc_trait_selection`. Instead, behavior that depends on trait-solving logic is abstracted into a separate trait, [`SolverDelegate`][ir solverdelegate]. @@ -214,9 +214,9 @@ non-obvious considerations: 1. The generic parameters `I` and `J` are reserved for `I: Interner` and `J` being the interner it is being lifted to. -2. `PhantomData` is handled automatically, creating a new `PhantomData` but - _has_ to be included in the file through; `use std::marker::PhantomData;` - you cannot use `std::marker::PhantomData` directly on the field of a struct. +2. `PhantomData` is handled automatically, creating a new `PhantomData`. But it + _has_ to be used in the fully unqualified form -- you cannot use + `std::marker::PhantomData` directly in the field. 3. The bounds are deliberately written as associated type bounds on the `Interner` trait rather than as `where` clauses on `LiftInto`. Given only `I: LiftInto`, Rust can then treat bounds such as the following as implied: @@ -310,4 +310,4 @@ There are still duplicated implementations between rustc and rust-analyzer—suc [r-a coerce]: https://github.com/rust-lang/rust-analyzer/blob/34f47d9298c478c12c6c4c0348771d1b05706e09/crates/hir-ty/src/infer/coerce.rs [rustc_lift]: https://github.com/rust-lang/rust/blob/0913b18e489ac1011b580e31fa5559654be12bfc/compiler/rustc_type_ir/src/lift.rs#L18 [rustc_typevisitable]: https://github.com/rust-lang/rust/blob/0913b18e489ac1011b580e31fa5559654be12bfc/compiler/rustc_type_ir/src/visit.rs#L62 -[rustc_typefoldable]: https://github.com/rust-lang/rust/blob/0913b18e489ac1011b580e31fa5559654be12bfc/compiler/rustc_type_ir/src/fold.rs#L71 \ No newline at end of file +[rustc_typefoldable]: https://github.com/rust-lang/rust/blob/0913b18e489ac1011b580e31fa5559654be12bfc/compiler/rustc_type_ir/src/fold.rs#L71 diff --git a/src/doc/rustc-dev-guide/src/solve/the-solver.md b/src/doc/rustc-dev-guide/src/solve/the-solver.md index 0151c0482d109..3d66ec272698d 100644 --- a/src/doc/rustc-dev-guide/src/solve/the-solver.md +++ b/src/doc/rustc-dev-guide/src/solve/the-solver.md @@ -7,7 +7,7 @@ as it is very similar to this implementation and also talks about limitations of ## A rough walkthrough -The entry-point of the solver is `InferCtxtEvalExt::evaluate_root_goal`. +The entry-point of the solver is `SolverDelegateEvalExt::evaluate_root_goal`. This function sets up the root `EvalCtxt` and then calls `EvalCtxt::evaluate_goal`, to actually enter the trait solver. From 2827a304fe7ce27405a6edae347c61e967409972 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 24 Aug 2026 11:55:40 +0200 Subject: [PATCH 07/15] remove outdated docs I ended up deciding not to add docs about `bounds` as it seems like a relatively minor feature of the derive, and there are docs at [1]. [1]: https://github.com/rust-lang/rust/blob/3ffb26fbf5bf232cf59e314e75ea325973f4f583/compiler/rustc_type_ir_macros/src/lib.rs#L21-L55 --- .../src/solve/sharing-crates-with-rust-analyzer.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/solve/sharing-crates-with-rust-analyzer.md b/src/doc/rustc-dev-guide/src/solve/sharing-crates-with-rust-analyzer.md index bf9655c866ee0..2b06f5b414c1b 100644 --- a/src/doc/rustc-dev-guide/src/solve/sharing-crates-with-rust-analyzer.md +++ b/src/doc/rustc-dev-guide/src/solve/sharing-crates-with-rust-analyzer.md @@ -255,12 +255,6 @@ There is intentionally no ignore attribute. The traversal must visit every field. This is a soundness requirement for rust-analyzer's use of the traversal when tracing and garbage-collecting interned types. -When the macro crate's `nightly` feature is enabled, the derive macro remains -registered but emits no tokens. The `GenericTypeVisitable` trait and its -traversal module are also excluded from the nightly configuration of -`rustc_type_ir`; they exist only in its non-nightly configuration. - - ## Long-term plans for supporting rust-analyzer In general, we aim to support rust-analyzer just as well as rustc in these shared crates—provided From 0db592d2919c1ca9cc3c1773005989df0e90ae75 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 24 Aug 2026 12:51:58 +0200 Subject: [PATCH 08/15] realize that `bounds` works a bit differently than advertised Just specifying `T: GenericTypeVisitable` doesn't work, as the trait has a generic: `V`, the visitor. `T: GenericTypeVisitable<__V>` is what actually works, as `__V` is the generic added to the impl generated by the derive macro. We discussed[1] different ways of making this nicer, but settled on not doing anything, as we don't expect people to need to specify any actual bounds. [1]: https://rust-lang.zulipchat.com/#narrow/channel/185405-t-compiler.2Frust-analyzer/topic/Updating.20next-solver/near/618331780 and below --- compiler/rustc_type_ir_macros/src/lib.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_type_ir_macros/src/lib.rs b/compiler/rustc_type_ir_macros/src/lib.rs index e1d2b53366066..8a437c4995724 100644 --- a/compiler/rustc_type_ir_macros/src/lib.rs +++ b/compiler/rustc_type_ir_macros/src/lib.rs @@ -48,11 +48,15 @@ decl_derive!( /// struct Foo { /// #[generic_type_visitable(bounds())] /// just_self: Box, - /// #[generic_type_visitable(bounds(Bar: GenericTypeVisitable))] + /// #[generic_type_visitable(bounds(Bar: GenericTypeVisitable<__V>))] /// contains_self: (Box, Bar), /// } /// struct Bar; /// ``` + /// + /// Note: the `__V` lifetime is an implementation detail of the derive macro. + /// We could probably handle this in a nicer way, but we don't expect this form + /// to really be necessary any time soon, so for now we don't. customizable_type_visitable_derive ); @@ -461,7 +465,7 @@ mod kw { /// Parses a bound like: /// /// ```ignore (would need to import GenericTypeVisitable to get this to compile) -/// #[generic_type_visitable(bounds(Foo: GenericTypeVisitable, Bar: GenericTypeVisitable))] +/// #[generic_type_visitable(bounds(Foo: GenericTypeVisitable<__V>, Bar: GenericTypeVisitable<__V>))] /// ``` fn parse_generic_type_visitable_bound( attr: &Attribute, From ada4f67f8aefa8707a65f87c4f9501e109b22333 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 7 Sep 2026 21:31:33 +0200 Subject: [PATCH 09/15] add tests for `#[derive(GenericTypeVisitable)]` and `bounds` --- ...ve-generic-type-visitable-missing-bound.rs | 19 +++ ...eneric-type-visitable-missing-bound.stderr | 18 +++ .../derive-generic-type-visitable.rs | 119 ++++++++++++++++++ 3 files changed, 156 insertions(+) create mode 100644 tests/ui-fulldeps/derive-generic-type-visitable-missing-bound.rs create mode 100644 tests/ui-fulldeps/derive-generic-type-visitable-missing-bound.stderr create mode 100644 tests/ui-fulldeps/derive-generic-type-visitable.rs diff --git a/tests/ui-fulldeps/derive-generic-type-visitable-missing-bound.rs b/tests/ui-fulldeps/derive-generic-type-visitable-missing-bound.rs new file mode 100644 index 0000000000000..af44ff7e92252 --- /dev/null +++ b/tests/ui-fulldeps/derive-generic-type-visitable-missing-bound.rs @@ -0,0 +1,19 @@ +//@ edition: 2024 +//@ check-fail + +#![crate_type = "rlib"] +#![feature(rustc_private)] + +extern crate rustc_type_ir; +extern crate rustc_type_ir_macros; + +use rustc_type_ir_macros::GenericTypeVisitable; + +#[derive(GenericTypeVisitable)] +struct MissingBound { + // This should fail, as `T: GenericTypeVisitable<__V>` wasn't specified + #[generic_type_visitable(bounds())] + //~^ ERROR: the trait bound `T: GenericTypeVisitable<__V>` is not satisfied + partially_rec: (Vec, T), + other: u32, +} diff --git a/tests/ui-fulldeps/derive-generic-type-visitable-missing-bound.stderr b/tests/ui-fulldeps/derive-generic-type-visitable-missing-bound.stderr new file mode 100644 index 0000000000000..44433984e4a7f --- /dev/null +++ b/tests/ui-fulldeps/derive-generic-type-visitable-missing-bound.stderr @@ -0,0 +1,18 @@ +error[E0277]: the trait bound `T: GenericTypeVisitable<__V>` is not satisfied + --> $DIR/derive-generic-type-visitable-missing-bound.rs:15:5 + | +LL | #[derive(GenericTypeVisitable)] + | -------------------- + | | + | required by a bound introduced by this call + | in this derive macro expansion +... +LL | #[generic_type_visitable(bounds())] + | ^ the nightly-only, unstable trait `GenericTypeVisitable<__V>` is not implemented for `T` + | + = note: required for `(Vec>, T)` to implement `GenericTypeVisitable<__V>` + = note: this error originates in the derive macro `GenericTypeVisitable` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui-fulldeps/derive-generic-type-visitable.rs b/tests/ui-fulldeps/derive-generic-type-visitable.rs new file mode 100644 index 0000000000000..91c7a502a87d8 --- /dev/null +++ b/tests/ui-fulldeps/derive-generic-type-visitable.rs @@ -0,0 +1,119 @@ +//@ edition: 2024 +//@ run-pass + +#![feature(rustc_private)] + +extern crate rustc_type_ir; +extern crate rustc_type_ir_macros; + +use rustc_type_ir::GenericTypeVisitable; +use rustc_type_ir_macros::GenericTypeVisitable; + +// Necessary to pull in object code as the rest of the rustc crates are shipped only as rmeta +// files. +#[expect(unused_extern_crates)] +extern crate rustc_driver; + +#[derive(GenericTypeVisitable)] +struct DerivesGenericTypeVisitable; + +#[derive(GenericTypeVisitable)] +struct Foo { + one: Incrementer, + two: Vec, +} + +#[derive(GenericTypeVisitable)] +enum Enum { + A, + B(Incrementer), + C { one: Incrementer, two: Vec }, +} + +#[derive(GenericTypeVisitable)] +struct Generic(Vec); + +#[derive(GenericTypeVisitable)] +struct Recursive { + #[generic_type_visitable(bounds())] + rec: Vec, + other: Incrementer, +} + +#[derive(GenericTypeVisitable)] +struct PartiallyRecursiveField { + #[generic_type_visitable(bounds(T: GenericTypeVisitable<__V>))] + partially_rec: (Vec, T), + other: Incrementer, +} + +// start testing setup + +use std::sync::atomic::{AtomicU8, Ordering}; + +static COUNT: AtomicU8 = AtomicU8::new(0); + +/// A type that, when visited, increments a global counter. +/// +/// Used to (weakly) test the correctness of the derive by making sure that +/// it traverses all the fields, and thus reaches all the incrementers. +#[derive(Clone)] +struct Incrementer; + +unsafe impl GenericTypeVisitable for Incrementer { + fn generic_visit_with(&self, _visitor: &mut V) { + COUNT.fetch_add(1, Ordering::Relaxed); + } +} + +// end testing setup + +fn main() { + use Incrementer as Inc; // for brevity + + #[track_caller] + fn check>(item: T, count: u8) { + let mut v = (); + item.generic_visit_with(&mut v); + assert_eq!(COUNT.swap(0, Ordering::Relaxed), count); + } + + check(DerivesGenericTypeVisitable, 0); + check(Foo { one: Inc, two: vec![] }, 1); + check(Foo { one: Inc, two: vec![Inc; 2] }, 1 + 2); + check(Enum::A, 0); + check(Enum::B(Inc), 1); + check(Enum::C { one: Inc, two: vec![] }, 1); + check(Enum::C { one: Inc, two: vec![Inc; 3] }, 1 + 3); + check(Generic::(vec![]), 0); + // visits each of the nested `Inc`s + check(Generic(vec![Inc; 5]), 5); + + // Every (nested) `rec!` adds another `Recursive`, and thus 1 more visited `Inc`. + macro_rules! rec { + [$($i:expr),* $(,)?] => { + Recursive { rec: vec![$($i),*], other: Inc } + } + } + check(rec![], 1); + check(rec![rec![]], 2); + check(rec![rec![], rec![]], 3); + check(rec![rec![rec![]]], 3); + + // Every (nested) `prec!` adds another `PartiallyRecursiveField`, and thus 1 more visited `Inc`. + macro_rules! prec { + ([$($i:expr),* $(,)?], $o:expr) => { + PartiallyRecursiveField { partially_rec: (vec![$($i),*], $o), other: Inc } + } + } + // Every nested `a()`, `b()`, and `c()` adds 0, 1, and 2 more visited `Inc`s, respectively. + let a = || Enum::A; + let b = || Enum::B(Inc); + let c = || Enum::C { one: Inc, two: vec![Inc] }; + check(prec!([], a()), 1 + 0); + check(prec!([], b()), 1 + 1); + check(prec!([], c()), 1 + 2); + check(prec!([prec!([], a())], a()), 1 + (1 + 0) + 0); + check(prec!([prec!([], b())], a()), 1 + (1 + 1) + 0); + check(prec!([prec!([], b())], b()), 1 + (1 + 1) + 1); +} From 2e331b1637fd915fe2a3610884d469ffde4d3f0a Mon Sep 17 00:00:00 2001 From: Hans Wennborg Date: Tue, 8 Sep 2026 08:59:13 +0200 Subject: [PATCH 10/15] Update codegen-llvm/inline-debuginfo.rs for distinct DILocation --- tests/codegen-llvm/inline-debuginfo.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/codegen-llvm/inline-debuginfo.rs b/tests/codegen-llvm/inline-debuginfo.rs index 1e1c9037f5c93..95fba918286a1 100644 --- a/tests/codegen-llvm/inline-debuginfo.rs +++ b/tests/codegen-llvm/inline-debuginfo.rs @@ -10,7 +10,7 @@ pub extern "C" fn callee(x: u32) -> u32 { // CHECK-LABEL: caller // CHECK: dbg{{.}}value({{(metadata )?}}i32 %y, {{(metadata )?}}!{{.*}}, {{(metadata )?}}!DIExpression(DW_OP_constu, 3, DW_OP_minus, DW_OP_stack_value){{.*}} [[A:![0-9]+]] -// CHECK: [[A]] = !DILocation(line: {{.*}}, scope: {{.*}}, inlinedAt: {{.*}}) +// CHECK: [[A]] = {{(distinct )?}}!DILocation(line: {{.*}}, scope: {{.*}}, inlinedAt: {{.*}}) #[no_mangle] pub extern "C" fn caller(y: u32) -> u32 { callee(y - 3) From 0847dae70368eeed7c4d1c772e29d3fc7e3e1f95 Mon Sep 17 00:00:00 2001 From: Hans Wennborg Date: Tue, 8 Sep 2026 09:39:47 +0200 Subject: [PATCH 11/15] call renumberMetadataForAssembly() in LLVMRustPrintModule() --- compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index e77d63d91703b..1bd5094c38f02 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -1165,7 +1165,9 @@ extern "C" LLVMRustResult LLVMRustPrintModule(LLVMModuleRef M, const char *Path, LLVMRustSetLastError(ErrorInfo.c_str()); return LLVMRustResult::Failure; } - +#if LLVM_VERSION_GE(24, 0) + unwrap(M)->renumberMetadataForAssembly(); +#endif auto AAW = RustAssemblyAnnotationWriter(Demangle); auto FOS = formatted_raw_ostream(OS); unwrap(M)->print(FOS, &AAW); From 8463342e91b9b0b19e97ebbf34e248564f333f28 Mon Sep 17 00:00:00 2001 From: Shun Sakai Date: Tue, 8 Sep 2026 17:18:39 +0900 Subject: [PATCH 12/15] docs(time): replace "method" with "function" --- library/core/src/time.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/core/src/time.rs b/library/core/src/time.rs index f9e2dc6b7f849..c123c66ee3bc0 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -345,7 +345,7 @@ impl Duration { /// Creates a new `Duration` from the specified number of weeks. /// - /// For this method, one week is defined as 7 days, or 604,800 seconds. + /// For this function, one week is defined as 7 days, or 604,800 seconds. /// /// # Panics /// @@ -375,7 +375,7 @@ impl Duration { /// Creates a new `Duration` from the specified number of days. /// - /// For this method, one day is defined as 24 hours, or 86,400 seconds. + /// For this function, one day is defined as 24 hours, or 86,400 seconds. /// /// # Panics /// @@ -405,7 +405,7 @@ impl Duration { /// Creates a new `Duration` from the specified number of hours. /// - /// For this method, one hour is defined as 60 minutes, or 3,600 seconds. + /// For this function, one hour is defined as 60 minutes, or 3,600 seconds. /// /// # Panics /// @@ -435,7 +435,7 @@ impl Duration { /// Creates a new `Duration` from the specified number of minutes. /// - /// For this method, one minute is defined as 60 seconds. + /// For this function, one minute is defined as 60 seconds. /// /// # Panics /// From 13f707fd296113af85111e3b05e31e9e994ff85a Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 7 Sep 2026 14:38:08 +1000 Subject: [PATCH 13/15] Rename `lower_let_expr` to `lower_fallible_let`, and clarify --- compiler/rustc_mir_build/src/builder/block.rs | 6 +- .../src/builder/matches/mod.rs | 65 ++++++++++--------- 2 files changed, 39 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/block.rs b/compiler/rustc_mir_build/src/builder/block.rs index 553b7af91e30c..591e4b096b416 100644 --- a/compiler/rustc_mir_build/src/builder/block.rs +++ b/compiler/rustc_mir_build/src/builder/block.rs @@ -207,12 +207,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let else_block_span = this.thir[*else_block].span; let (true_block, false_block) = this.in_if_then_scope(last_remainder_scope, else_block_span, |this| { - // Bypass `lower_if_condition` and call `lower_let_expr` directly, + // Bypass `lower_if_condition` and call `lower_fallible_let` directly, // since we don't have an actual THIR let-expression here. - this.lower_let_expr( + this.lower_fallible_let( block, - *initializer, pattern, + *initializer, None, initializer_span, DeclareLetBindings::No, diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 01505fb9ec8ae..687b03a2741dc 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -50,7 +50,7 @@ pub(crate) struct LowerIfCondArgs { pub(crate) variable_source_info: SourceInfo, /// Determines how bindings should be handled when lowering `let` expressions. /// - /// Forwarded to [`Builder::lower_let_expr`] when lowering [`ExprKind::Let`]. + /// Forwarded to [`Builder::lower_fallible_let`] when lowering [`ExprKind::Let`]. pub(crate) declare_let_bindings: DeclareLetBindings, } @@ -62,9 +62,9 @@ impl LowerIfCondArgs { } } -/// Should lowering a `let` expression also declare its bindings? +/// Should lowering a `let` also declare its bindings? /// -/// Used by [`Builder::lower_let_expr`] when lowering [`ExprKind::Let`]. +/// Used by [`Builder::lower_fallible_let`]. #[derive(Clone, Copy)] pub(crate) enum DeclareLetBindings { /// Yes, declare `let` bindings as normal for `if` conditions. @@ -169,10 +169,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }) } ExprKind::ValueExpr { source } => this.lower_if_condition(block, source, args), - ExprKind::Let { expr, ref pat } => this.lower_let_expr( + ExprKind::Let { ref pat, expr } => this.lower_fallible_let( block, - expr, pat, + expr, Some(args.variable_source_info.scope), args.variable_source_info.span, args.declare_let_bindings, @@ -2309,65 +2309,72 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Pat binding - used for `let` and function parameters as well. impl<'a, 'tcx> Builder<'a, 'tcx> { - /// Lowers a `let` expression that appears in a suitable context - /// (e.g. an `if` condition or match guard). - /// - /// Also used for lowering let-else statements, since they have similar - /// needs despite not actually using `let` expressions. + /// Lowers a fallible `let`, which is one of: + /// - A let-expression inside an `if` condition or match guard. + /// - A let-else statement. /// - /// Use [`DeclareLetBindings`] to control whether the `let` bindings are - /// declared or not. + /// (Strictly speaking, the underlying pattern might actually be infallible. + /// What matters here is that it is _allowed_ to be fallible.) /// /// Must be called within a [`Builder::in_if_then_scope`], to indicate where /// to break to if the `let` fails to match. - pub(crate) fn lower_let_expr( + pub(crate) fn lower_fallible_let( &mut self, mut block: BasicBlock, - expr_id: ExprId, pat: &Pat<'tcx>, + scrutinee_id: ExprId, source_scope: Option, scope_span: Span, + // Controls whether bindings are declared or not, as requested by the caller. declare_let_bindings: DeclareLetBindings, ) -> BlockAnd<()> { - let expr_span = self.thir[expr_id].span; - let scrutinee = unpack!(block = self.lower_scrutinee(block, expr_id)); + let scrutinee_span = self.thir[scrutinee_id].span; + let scrutinee_place_builder = unpack!(block = self.lower_scrutinee(block, scrutinee_id)); + + // Lower the scrutinee and pattern as though they were desugared to a `match`. let built_tree = self.lower_match_tree( block, - expr_span, - &scrutinee, + scrutinee_span, + &scrutinee_place_builder, pat.span, vec![(pat, HasMatchGuard::No)], Exhaustive::No, ); - let [branch] = built_tree.branches.try_into().unwrap(); + let [true_branch] = built_tree.branches.try_into().unwrap(); + let false_block = built_tree.otherwise_block; // If pattern-matching failed, break out of the enclosing if-then scope. - self.break_from_if_then_scope(built_tree.otherwise_block, self.source_info(expr_span)); + self.break_from_if_then_scope(false_block, self.source_info(scrutinee_span)); match declare_let_bindings { DeclareLetBindings::Yes => { - let expr_place = scrutinee.try_to_place(self); - let opt_expr_place = expr_place.as_ref().map(|place| (Some(place), expr_span)); + let scrutinee_place; + let opt_match_place = try { + scrutinee_place = scrutinee_place_builder.try_to_place(self)?; + (Some(&scrutinee_place), scrutinee_span) + }; self.declare_bindings( source_scope, pat.span.to(scope_span), pat, None, - opt_expr_place, + opt_match_place, ); } DeclareLetBindings::No => {} // Caller is responsible for bindings. - DeclareLetBindings::LetNotPermitted => { - self.tcx.dcx().span_bug(expr_span, "let expression not expected in this context") - } + DeclareLetBindings::LetNotPermitted => self + .tcx + .dcx() + .span_bug(scrutinee_span, "let expression not expected in this context"), } - let success = self.bind_pattern(self.source_info(pat.span), branch, &[], expr_span, None); + let true_block = + self.bind_pattern(self.source_info(pat.span), true_branch, &[], scrutinee_span, None); // If branch coverage is enabled, record this branch. - self.visit_coverage_conditional_let(pat, success, built_tree.otherwise_block); + self.visit_coverage_conditional_let(pat, true_block, false_block); - success.unit() + true_block.unit() } /// Initializes each of the bindings from the candidate by From 9bf6ad895ea4fea16d29722961974478c65d47f3 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 8 Sep 2026 12:04:57 +0200 Subject: [PATCH 14/15] Fix duplicate thanks entry --- .mailmap | 1 + 1 file changed, 1 insertion(+) diff --git a/.mailmap b/.mailmap index 2f7254ac7131c..037994875398a 100644 --- a/.mailmap +++ b/.mailmap @@ -351,6 +351,7 @@ John Van Enk Jon Gjengset Jonas Tepe Jonathan Bailey +Jonathan Brouwer Jonathan Chan Kwan Yin Jonathan L Jonathan S Jonathan S From 8bd0a5aff26444b137b61a0cb12a1fec67b8dcc7 Mon Sep 17 00:00:00 2001 From: Max Dexheimer Date: Tue, 8 Sep 2026 11:28:58 +0200 Subject: [PATCH 15/15] Refactor `vec::IntoIter::into_vecdeque` to fix unsoundness --- .../alloc/src/collections/vec_deque/mod.rs | 7 ++- library/alloc/src/vec/into_iter.rs | 52 ++++++++++++------- library/alloctests/tests/vec_deque.rs | 15 ++++++ 3 files changed, 54 insertions(+), 20 deletions(-) diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index b039fd587df47..5bd43806b1a09 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -949,6 +949,7 @@ impl VecDeque { /// `Vec::from_raw_parts_in`, but takes a *range* of elements that are /// initialized rather than only supporting `0..len`. Requires that /// `initialized.start` ≤ `initialized.end` ≤ `capacity`. + /// Also, `initialized.start` < `capacity`, unless both are 0. #[inline] #[cfg(not(test))] pub(crate) unsafe fn from_contiguous_raw_parts_in( @@ -959,9 +960,13 @@ impl VecDeque { ) -> Self { debug_assert!(initialized.start <= initialized.end); debug_assert!(initialized.end <= capacity); + debug_assert!(initialized.start == 0 && capacity == 0 || initialized.start < capacity); // SAFETY: Our safety precondition guarantees the range length won't wrap, - // and that the allocation is valid for use in `RawVec`. + // that the allocation is valid for use in `RawVec` with `alloc`, + // and that the range contains valid elements. + // We have `head`, `len` ≤ `cap`, since `start`, `end` ≤ `cap`. + // Also, `head` < `cap` unless `head` = `cap` = `0`. unsafe { VecDeque { head: WrappedIndex::from_arbitrary_number(initialized.start), diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs index 46874ff76c093..d99d2126810ba 100644 --- a/library/alloc/src/vec/into_iter.rs +++ b/library/alloc/src/vec/into_iter.rs @@ -213,25 +213,39 @@ impl IntoIter { // Keep our `Drop` impl from dropping the elements and the allocator let mut this = ManuallyDrop::new(self); - // SAFETY: This allocation originally came from a `Vec`, so it passes - // all those checks. We have `this.buf` ≤ `this.ptr` ≤ `this.end`, - // so the `offset_from_unsigned`s below cannot wrap, and will produce a well-formed - // range. `end` ≤ `buf + cap`, so the range will be in-bounds. - // Taking `alloc` is ok because nothing else is going to look at it, - // since our `Drop` impl isn't going to run so there's no more code. - unsafe { - let buf = this.buf.as_ptr(); - let initialized = if T::IS_ZST { - // All the pointers are the same for ZSTs, so it's fine to - // say that they're all at the beginning of the "allocation". - 0..this.len() - } else { - this.ptr.offset_from_unsigned(this.buf)..this.end.offset_from_unsigned(buf) - }; - let cap = this.cap; - let alloc = ManuallyDrop::take(&mut this.alloc); - VecDeque::from_contiguous_raw_parts_in(buf, initialized, cap, alloc) - } + let buf = this.buf.as_ptr(); + let initialized = if T::IS_ZST || this.len() == 0 { + // All the pointers are the same for ZSTs, so it's fine to + // say that they're all at the beginning of the "allocation". + // For non-ZSTs, we have length 0, so we can choose the (empty) + // range to be at the start of the buffer. + // + // Due to `0` ≤ `this.len()` ≤ `this.cap`, the range is well-formed, + // and due to the argument above it spans exactly the elements of + // this iterator. Because `init.start` = `0`, it follows that either + // `init.start` < `cap` or `cap` = `init.start` = `0`; thus the range + // satisfies the requirements of `from_contiguous_raw_parts_in`. + 0..this.len() + } else { + // SAFETY: `this.ptr` and `this.end` are created via offsets of `this.buf`, + // so they point to the same allocation. We have `this.buf` ≤ `this.ptr` ≤ `this.end`, + // so this cannot wrap, and will produce a well-formed range that spans exactly + // the elements of this iterator. + // + // Additionally, due to `end ≤ buf + cap`, we have `init.start` ≤ `init.end` ≤ `cap`. + // Due to the length check above, `init.start < cap`, so the range satisfies the + // requirements of `from_contiguous_raw_parts_in`. + unsafe { this.ptr.offset_from_unsigned(this.buf)..this.end.offset_from_unsigned(buf) } + }; + + let cap = this.cap; + // SAFETY: `this` is forgotten afterwards, so we can move out the allocator. + let alloc = unsafe { ManuallyDrop::take(&mut this.alloc) }; + + // SAFETY: This allocation originally came from a `Vec`, so it satisfies all + // requirements for the `buf` pointer with capacity `cap` allocated in `alloc`. + // Correctness of `initialized` was shown above. + unsafe { VecDeque::from_contiguous_raw_parts_in(buf, initialized, cap, alloc) } } } diff --git a/library/alloctests/tests/vec_deque.rs b/library/alloctests/tests/vec_deque.rs index 15cc156d6988f..00b2c2e34d569 100644 --- a/library/alloctests/tests/vec_deque.rs +++ b/library/alloctests/tests/vec_deque.rs @@ -2495,3 +2495,18 @@ fn truncate_to_range_inclusive_end_overflow() { let mut v: VecDeque<_> = (0..6).collect(); v.truncate_to_range(0..=usize::MAX); } + +#[test] +fn issue_162452_vec_deque_from_empty_vec_into_iter() { + for n in 1..20 { + let v = Vec::from_iter(0..n); + + let mut it = v.into_iter(); + for _ in &mut it {} + + let mut d: VecDeque<_> = it.collect(); + + d.push_back(n); + assert_eq!(Some(n), d.pop_front()); + } +}