From a401851cfc985171de7617ae6b1b7e4998f80ccd Mon Sep 17 00:00:00 2001 From: Valentyn Kit Date: Fri, 24 Jul 2026 20:19:54 +0300 Subject: [PATCH 01/66] std: retry waitid on EINTR in the pidfd wait path --- library/std/src/sys/process/unix/pidfd.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/sys/process/unix/pidfd.rs b/library/std/src/sys/process/unix/pidfd.rs index ef8433068c967..c586354861dcd 100644 --- a/library/std/src/sys/process/unix/pidfd.rs +++ b/library/std/src/sys/process/unix/pidfd.rs @@ -2,7 +2,7 @@ use super::ExitStatus; use crate::io; use crate::os::fd::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use crate::sys::fd::FileDesc; -use crate::sys::{AsInner, FromInner, IntoInner, cvt}; +use crate::sys::{AsInner, FromInner, IntoInner, cvt, cvt_r}; #[cfg(test)] mod tests; @@ -61,7 +61,7 @@ impl PidFd { fn waitid(&self, options: libc::c_int) -> io::Result> { let mut siginfo: libc::siginfo_t = unsafe { crate::mem::zeroed() }; - let r = cvt(unsafe { + let r = cvt_r(|| unsafe { libc::waitid(libc::P_PIDFD, self.0.as_raw_fd() as u32, &mut siginfo, options) }); match r { From e96993c68f6f41c6fd9746e3c3cf2ec7f50ace7e Mon Sep 17 00:00:00 2001 From: David Carlier Date: Mon, 3 Aug 2026 06:51:24 +0100 Subject: [PATCH 02/66] std: fix unix socket address truncation without a trailing NUL getsockname(2) and friends do not count the trailing NUL in the length they report on freebsd, netbsd and qnx, and a caller may bind(2) without one anywhere, so shortening the path by one byte dropped its last character. scan for the NUL within the reported length instead, which is the rule unix(7) gives and subsumes the qnx case the old cfg handled. Fixes rust-lang/rust#118925 --- library/std/src/os/unix/net/addr.rs | 10 ++++------ library/std/src/os/unix/net/tests.rs | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/library/std/src/os/unix/net/addr.rs b/library/std/src/os/unix/net/addr.rs index e13f44d6fc9bd..92f30ae4605cb 100644 --- a/library/std/src/os/unix/net/addr.rs +++ b/library/std/src/os/unix/net/addr.rs @@ -255,12 +255,10 @@ impl SocketAddr { } else if self.addr.sun_path[0] == 0 { AddressKind::Abstract(ByteStr::from_bytes(&path[1..len])) } else { - // the value returned by getsockname(2) and similar on QNX7.1 and - // QNX8 does not count the NUL byte terminator of the path string, - // which matches the behavior of the SUN_LEN macro in libc, but - // other OSes do count the NUL byte so adjust accordingly - let end = - if cfg!(any(target_os = "qnx", target_env = "nto71")) { len } else { len - 1 }; + // linux adds a trailing NUL and counts it in the length, freebsd, netbsd + // and qnx do not, and a caller may bind(2) without one either. unix(7) + // gives the portable rule: strnlen(sun_path, len - offsetof(sun_path)) + let end = core::slice::memchr::memchr(0, &path[..len]).unwrap_or(len); AddressKind::Pathname(OsStr::from_bytes(&path[..end]).as_ref()) } } diff --git a/library/std/src/os/unix/net/tests.rs b/library/std/src/os/unix/net/tests.rs index 3ba4b44d2f1ef..9c3119e787b33 100644 --- a/library/std/src/os/unix/net/tests.rs +++ b/library/std/src/os/unix/net/tests.rs @@ -29,6 +29,29 @@ fn sock_addr_from_pathname() { assert_eq!(address.as_pathname(), Some(Path::new("/path/to/socket"))); } +// the trailing NUL is not counted in the reported length on freebsd, netbsd +// and qnx, and a caller may bind(2) without one anywhere +#[test] +fn sock_addr_without_trailing_nul() { + const PATH: &[u8] = b"/path/to/socket"; + + // SAFETY: all zeros is a valid representation for `sockaddr_un`. + let mut addr: libc::sockaddr_un = unsafe { crate::mem::zeroed() }; + addr.sun_family = libc::AF_UNIX as libc::sa_family_t; + for (dst, &src) in addr.sun_path.iter_mut().zip(PATH) { + *dst = src as _; + } + let offset = crate::mem::offset_of!(libc::sockaddr_un, sun_path); + + // length excluding the NUL, as reported by freebsd, netbsd and qnx + let address = or_panic!(SocketAddr::from_parts(addr, (offset + PATH.len()) as _)); + assert_eq!(address.as_pathname(), Some(Path::new("/path/to/socket"))); + + // length including the NUL, as reported by linux + let address = or_panic!(SocketAddr::from_parts(addr, (offset + PATH.len() + 1) as _)); + assert_eq!(address.as_pathname(), Some(Path::new("/path/to/socket"))); +} + #[test] #[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets #[cfg_attr(target_os = "vxworks", ignore = "Unix sockets are not implemented in VxWorks")] From 99f42a775411283d372cf5154107577f3266e372 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 5 Aug 2026 11:05:48 +0200 Subject: [PATCH 03/66] genmc: rename 'invalid' to 'moot' --- src/tools/miri/src/concurrency/genmc/helper.rs | 3 ++- src/tools/miri/src/concurrency/genmc/mod.rs | 2 +- src/tools/miri/src/concurrency/genmc/scheduling.rs | 8 ++------ src/tools/miri/src/diagnostics.rs | 10 +++++----- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/tools/miri/src/concurrency/genmc/helper.rs b/src/tools/miri/src/concurrency/genmc/helper.rs index 1870a8e4e2f3a..34314c84db4bc 100644 --- a/src/tools/miri/src/concurrency/genmc/helper.rs +++ b/src/tools/miri/src/concurrency/genmc/helper.rs @@ -16,7 +16,8 @@ pub(super) const MAX_ACCESS_SIZE: u64 = 8; // FIXME(genmc): improve error handling. pub(super) fn get_outcome<'tcx, T>(result: GenmcHandlerResult) -> InterpResult<'tcx, T> { match result { - GenmcHandlerResult::Invalid => throw_machine_stop!(TerminationInfo::GenmcInvalid), + // A handler producing an invalid result means that the execution is moot. + GenmcHandlerResult::Invalid => throw_machine_stop!(TerminationInfo::GenmcMoot), GenmcHandlerResult::Error(e) => throw_ub_format!("{e}"), GenmcHandlerResult::Ok(outcome) => interp_ok(outcome), } diff --git a/src/tools/miri/src/concurrency/genmc/mod.rs b/src/tools/miri/src/concurrency/genmc/mod.rs index dfd7d4b52678d..a4eab95fbef5d 100644 --- a/src/tools/miri/src/concurrency/genmc/mod.rs +++ b/src/tools/miri/src/concurrency/genmc/mod.rs @@ -527,7 +527,7 @@ impl GenmcCtx { alignment.bytes(), ); let chosen_address = match malloc_result.into_genmc_result() { - GenmcHandlerResult::Invalid => throw_machine_stop!(TerminationInfo::GenmcInvalid), + GenmcHandlerResult::Invalid => throw_machine_stop!(TerminationInfo::GenmcMoot), GenmcHandlerResult::Error(_e) => throw_exhaust!(AddressSpaceFull), GenmcHandlerResult::Ok(a) => a, }; diff --git a/src/tools/miri/src/concurrency/genmc/scheduling.rs b/src/tools/miri/src/concurrency/genmc/scheduling.rs index 253446e3eb91d..e704346ad2ea4 100644 --- a/src/tools/miri/src/concurrency/genmc/scheduling.rs +++ b/src/tools/miri/src/concurrency/genmc/scheduling.rs @@ -120,12 +120,8 @@ impl GenmcCtx { match result.exec_status { ExecutionStatus::Ok => interp_ok(Some(thread_infos.get_miri_tid(result.next_thread))), ExecutionStatus::Blocked => { - // This execution doesn't need further exploration. We treat this as "success, no - // leak check needed", which makes it a NOP in the big outer loop. - throw_machine_stop!(TerminationInfo::Exit { - code: 0, // success - leak_check: false, - }); + // This execution is "moot", it doesn't need further exploration. + throw_machine_stop!(TerminationInfo::GenmcMoot); } ExecutionStatus::Finished => { let exit_status = self.exec_state.exit_status.get().expect( diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 7e8c49bf9fba0..787a213df9f6f 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -32,9 +32,9 @@ pub enum TerminationInfo { history: tree_diagnostics::HistoryData, }, Int2PtrWithStrictProvenance, - /// GenMC deemed this execution invalid, so Miri drops it, i.e., it skips to the next execution - /// (mirrors GenMC's `Invalid` result). - GenmcInvalid, + /// GenMC deemed this execution "moot" or invalid, so Miri drops it, i.e., it skips to the next + /// execution. Mirrors GenMC's `Invalid` result or a "moot" result from the scheduler. + GenmcMoot, /// All threads are blocked. GlobalDeadlock, /// Some thread discovered a deadlock condition (e.g. in a mutex with reentrancy checking). @@ -84,7 +84,7 @@ impl fmt::Display for TerminationInfo { TreeBorrowsUb { title, .. } => write!(f, "{title}"), GlobalDeadlock => write!(f, "the evaluated program deadlocked"), LocalDeadlock => write!(f, "a thread deadlocked"), - GenmcInvalid => write!(f, "GenMC wants to skip this execution"), + GenmcMoot => write!(f, "GenMC wants to skip this execution"), MultipleSymbolDefinitions { link_name, .. } => write!(f, "multiple definitions of symbol `{link_name}`"), SymbolShimClashing { link_name, .. } => @@ -258,7 +258,7 @@ pub fn report_result<'tcx>( Some("unsupported operation"), StackedBorrowsUb { .. } | TreeBorrowsUb { .. } | DataRace { .. } => Some("Undefined Behavior"), - GenmcInvalid => { + GenmcMoot => { assert!(ecx.machine.data_race.as_genmc_ref().is_some()); return Some((0, false)); } From a6ed3afb8d6e8bdec2e91a1e95f5530d747ec2d7 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Sat, 8 Aug 2026 04:41:50 +0000 Subject: [PATCH 04/66] Prepare for merging from rust-lang/rust This updates the rust-version file to 7dfb9d0c40d3338e1a27f0f787182c6b90e6791e. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 8ab1fcaae5225..01be744b88a74 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -f73951df0a5566d94d13b7954acd9f4ab1fa3734 +7dfb9d0c40d3338e1a27f0f787182c6b90e6791e From ca7945670434f7287368d9d0eaef69ac073f95d1 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Sun, 9 Aug 2026 04:46:55 +0000 Subject: [PATCH 05/66] Prepare for merging from rust-lang/rust This updates the rust-version file to 4667d75565e47ba5df36c0df598c556b543e8624. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 01be744b88a74..fdcf0a5aff4d4 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -7dfb9d0c40d3338e1a27f0f787182c6b90e6791e +4667d75565e47ba5df36c0df598c556b543e8624 From 6c2d6a65ccae5d91cf6721dbde0e15416ba3ab33 Mon Sep 17 00:00:00 2001 From: Joao Roberto Date: Mon, 3 Aug 2026 22:37:58 -0300 Subject: [PATCH 06/66] Suggest moving for<> closure binders onto fn binding types When `for<'a>` appears on a closure without `closure_lifetime_binder`, suggest rewriting a simple let-bound closure to a stable `for<'a> fn(...)` binding type annotation when that rewrite is safe. --- compiler/rustc_ast_passes/src/diagnostics.rs | 32 ++ compiler/rustc_ast_passes/src/feature_gate.rs | 392 +++++++++++++++++- compiler/rustc_parse/src/parser/expr.rs | 2 + 3 files changed, 414 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_ast_passes/src/diagnostics.rs b/compiler/rustc_ast_passes/src/diagnostics.rs index 0814c79d339bd..9ab9c1d9f40d9 100644 --- a/compiler/rustc_ast_passes/src/diagnostics.rs +++ b/compiler/rustc_ast_passes/src/diagnostics.rs @@ -1264,3 +1264,35 @@ pub(crate) struct VarargsWithoutPattern { #[primary_span] pub span: Span, } + +#[derive(Subdiagnostic)] +pub(crate) enum ClosureLifetimeBinderBindingTypeSugg { + #[multipart_suggestion( + "consider setting the binding type instead", + applicability = "machine-applicable", + style = "verbose" + )] + MachineApplicable { + #[suggestion_part(code = ": {ty}")] + binding: Span, + ty: String, + #[suggestion_part(code = "{closure}")] + closure_header: Span, + closure: String, + }, + /// Used when the body references other simple paths: they may be captures (or free items). + /// Without name resolution we can't tell, so rustfix must not auto-apply. + #[multipart_suggestion( + "consider setting the binding type instead", + applicability = "maybe-incorrect", + style = "verbose" + )] + MaybeIncorrect { + #[suggestion_part(code = ": {ty}")] + binding: Span, + ty: String, + #[suggestion_part(code = "{closure}")] + closure_header: Span, + closure: String, + }, +} diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index bd92f32e24b68..c8c7a828ea281 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -1,12 +1,17 @@ use rustc_ast::visit::{self, AssocCtxt, FnKind, Visitor}; -use rustc_ast::{self as ast, AttrVec, GenericBound, NodeId, PatKind, attr, token}; +use rustc_ast::{ + self as ast, AttrVec, BindingMode, ByRef, GenericBound, GenericParamKind, NodeId, PatKind, + attr, token, +}; +use rustc_ast_pretty::pprust; use rustc_attr_ir::{Attribute, AttributeKind}; use rustc_attr_parsing::AttributeParser; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::msg; use rustc_feature::Features; use rustc_session::Session; use rustc_session::diagnostics::{feature_err, feature_warn}; -use rustc_span::{Span, Spanned, Symbol, sym}; +use rustc_span::{Ident, Span, Spanned, Symbol, sym}; use crate::diagnostics; @@ -47,7 +52,13 @@ macro_rules! gate_multi { } pub fn check_attribute(attr: &ast::Attribute, sess: &Session, features: &Features) { - PostExpansionVisitor { sess, features }.visit_attribute(attr) + PostExpansionVisitor { + sess, + features, + let_binding: None, + handled_closure_lifetime_binders: FxHashSet::default(), + } + .visit_attribute(attr) } struct PostExpansionVisitor<'a> { @@ -55,6 +66,14 @@ struct PostExpansionVisitor<'a> { // `sess` contains a `Features`, but this might not be that one. features: &'a Features, + + /// Set while visiting the initializer of a `let` binding whose RHS is directly a closure. + /// Used to suggest moving `for<...>` binders onto the binding's type. + let_binding: Option<&'a ast::Local>, + + /// Binder spans for which we already emitted the `closure_lifetime_binder` gate while walking + /// the live AST. Remaining pre-expansion spans (e.g. under `#[cfg(false)]`) are gated later. + handled_closure_lifetime_binders: FxHashSet, } // ----------------------------------------------------------------------------- @@ -67,6 +86,34 @@ struct PostExpansionVisitor<'a> { // Instead, register a pre-expansion feature gate using `gate_all` in fn `check_crate`. impl<'a> PostExpansionVisitor<'a> { + /// Gate `for<...>` binders on closures, suggesting a `fn` pointer binding type when possible. + fn gate_closure_lifetime_binder(&mut self, closure: &ast::Closure, binder_span: Span) { + self.handled_closure_lifetime_binders.insert(binder_span); + + if self.features.closure_lifetime_binder() + || binder_span.allows_unstable(sym::closure_lifetime_binder) + { + return; + } + + let mut err = feature_err( + self.sess, + sym::closure_lifetime_binder, + binder_span, + "`for<...>` binders for closures are experimental", + ); + + if let Some(sugg) = + closure_lifetime_binder_binding_type_sugg(self.sess, self.let_binding, closure) + { + err.subdiagnostic(sugg); + } else { + err.help("consider removing `for<...>`"); + } + + err.emit(); + } + /// Feature gate `impl Trait` inside `type Alias = $type_expr;`. fn check_impl_trait(&self, ty: &ast::Ty, in_associated_ty: bool) { struct ImplTraitVisitor<'a> { @@ -306,8 +353,32 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { visit::walk_generic_args(self, args); } + fn visit_local(&mut self, local: &'a ast::Local) { + // Only track direct `let pat = for<'a> |...| ...` inits; parenthesized or otherwise + // wrapped closures fall back to the simpler help. + if let Some(init) = local.kind.init() + && matches!(init.kind, ast::ExprKind::Closure(_)) + { + let prev = self.let_binding.replace(local); + visit::walk_local(self, local); + self.let_binding = prev; + } else { + visit::walk_local(self, local); + } + } + fn visit_expr(&mut self, e: &'a ast::Expr) { - match e.kind { + match &e.kind { + ast::ExprKind::Closure(closure) => { + if let ast::ClosureBinder::For { span, .. } = &closure.binder { + self.gate_closure_lifetime_binder(closure, *span); + } + // Nested expressions inside the closure are not the `let` initializer. + let prev = self.let_binding.take(); + visit::walk_expr(self, e); + self.let_binding = prev; + return; + } ast::ExprKind::TryBlock(_, None) => { // `try { ... }` is old and is only gated post-expansion here. gate!(self, try_blocks, e.span, "`try` expression is experimental"); @@ -319,14 +390,14 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { kind: token::LitKind::Float | token::LitKind::Integer, suffix, .. - }) => match suffix { + }) => match *suffix { Some(sym::f16) => { gate!(self, f16, e.span, "the type `f16` is unstable") } Some(sym::f128) => { gate!(self, f128, e.span, "the type `f128` is unstable") } - _ => (), + _ => {} }, _ => {} } @@ -439,7 +510,12 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { check_new_solver_banned_features(sess, features); check_features_requiring_new_solver(sess, features); - let mut visitor = PostExpansionVisitor { sess, features }; + let mut visitor = PostExpansionVisitor { + sess, + features, + let_binding: None, + handled_closure_lifetime_binders: FxHashSet::default(), + }; // ----------------------------------------------------------------------------- // PRE-EXPANSION FEATURE GATES FOR UNSTABLE SYNTAX @@ -502,11 +578,8 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { "`async` trait bounds are unstable", "use the desugared name of the async trait, such as `AsyncFn`" ); - gate_all!( - closure_lifetime_binder, - "`for<...>` binders for closures are experimental", - "consider removing `for<...>`" - ); + // `closure_lifetime_binder` is gated in `PostExpansionVisitor` (with a richer suggestion when + // possible). Spans not seen there — notably under `#[cfg(false)]` — are handled after the walk. gate_all!( half_open_range_patterns_in_slices, "half-open range patterns in slices are unstable" @@ -628,6 +701,301 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { // ----------------------------------------------------------------------------- visit::walk_crate(&mut visitor, krate); + + // Reject `for<...>` closure binders that never reached the AST walk (e.g. `#[cfg(false)]`). + if !visitor.features.closure_lifetime_binder() { + for &span in spans.get(&sym::closure_lifetime_binder).into_flat_iter() { + if span.allows_unstable(sym::closure_lifetime_binder) + || visitor.handled_closure_lifetime_binders.contains(&span) + { + continue; + } + feature_err( + sess, + sym::closure_lifetime_binder, + span, + "`for<...>` binders for closures are experimental", + ) + .with_help("consider removing `for<...>`") + .emit(); + } + } +} + +/// Build a suggestion rewriting +/// `let cl = for<'a> |x: &'a T| -> U { ... }` into +/// `let cl: for<'a> fn(&'a T) -> U = |x| { ... }` when that is a reasonable alternative. +fn closure_lifetime_binder_binding_type_sugg( + sess: &Session, + local: Option<&ast::Local>, + closure: &ast::Closure, +) -> Option { + let local = local?; + if local.ty.is_some() { + return None; + } + // Only by-value `let ident = ...` / `let mut ident = ...` bindings. + if !matches!(&local.pat.kind, PatKind::Ident(BindingMode(ByRef::No, _), _, None)) { + return None; + } + + // Explicit `move`/`use`/`async`/`const`/`static` closures are not `fn` pointers. + if !matches!(closure.capture_clause, ast::CaptureBy::Ref) + || closure.coroutine_kind.is_some() + || matches!(closure.constness, ast::Const::Yes(_)) + || matches!(closure.movability, ast::Movability::Static) + { + return None; + } + + let ast::ClosureBinder::For { span: binder_span, generic_params } = &closure.binder else { + return None; + }; + + // `for` / `for<'a: 'static>` are not valid on `fn` pointer types. + if !generic_params + .iter() + .all(|param| matches!(param.kind, GenericParamKind::Lifetime) && param.bounds.is_empty()) + { + return None; + } + + // Need fully explicit parameter and return types to form a useful `fn` type. A top-level or + // nested `_` (e.g. `-> _`, `&'a _`) must not be copied into a MachineApplicable suggestion. + let ast::FnRetTy::Ty(ret_ty) = &closure.fn_decl.output else { + return None; + }; + if ty_contains_infer(ret_ty) + || closure.fn_decl.inputs.iter().any(|param| ty_contains_infer(¶m.ty)) + { + return None; + } + + // Only by-value binding patterns (and `_`) can be rewritten safely. + if !closure.fn_decl.inputs.iter().all(|param| { + matches!( + ¶m.pat.kind, + PatKind::Wild | PatKind::Ident(BindingMode(ByRef::No, _), _, None) + ) + }) { + return None; + } + + // `pprust::pat_to_string` drops parameter attributes; don't emit a lossy rewrite. + if closure.fn_decl.inputs.iter().any(|param| !param.attrs.is_empty()) { + return None; + } + + // Don't rewrite macro-expanded closures; hygiene makes capture analysis unreliable and the + // suggestion would point into the macro definition. + if binder_span.from_expansion() || closure.fn_decl_span.from_expansion() { + return None; + } + + let binder = sess.source_map().span_to_snippet(*binder_span).ok()?; + let inputs: String = closure + .fn_decl + .inputs + .iter() + .map(|param| pprust::ty_to_string(¶m.ty)) + .intersperse(", ".to_string()) + .collect(); + let ty = format!("{binder} fn({inputs}) -> {}", pprust::ty_to_string(ret_ty)); + + let closure_pats: String = closure + .fn_decl + .inputs + .iter() + .map(|param| pprust::pat_to_string(¶m.pat)) + .intersperse(", ".to_string()) + .collect(); + + let binding = local.pat.span.shrink_to_hi(); + let closure_header = binder_span.to(closure.fn_decl_span); + let closure_code = format!("|{closure_pats}|"); + + // `CaptureBy::Ref` only means no `move`/`use`. Without name resolution, any other simple + // path may be an env capture (including uppercase locals) or a free item. Offer the rewrite + // only as maybe-incorrect in that case so rustfix won't auto-apply a breaking change. + // Paths bound locally in the body (e.g. `let n = ...; n`) are fine for `fn` pointers. + if closure_body_has_free_simple_path(closure) { + Some(diagnostics::ClosureLifetimeBinderBindingTypeSugg::MaybeIncorrect { + binding, + ty, + closure_header, + closure: closure_code, + }) + } else { + Some(diagnostics::ClosureLifetimeBinderBindingTypeSugg::MachineApplicable { + binding, + ty, + closure_header, + closure: closure_code, + }) + } +} + +/// Returns true if `ty` contains any `_` inference placeholder, including nested forms like +/// `&'a _` or `(_, u8)`. +fn ty_contains_infer(ty: &ast::Ty) -> bool { + struct InferVisitor { + found: bool, + } + + impl<'a> Visitor<'a> for InferVisitor { + fn visit_ty(&mut self, ty: &'a ast::Ty) { + if self.found { + return; + } + if matches!(ty.kind, ast::TyKind::Infer) { + self.found = true; + return; + } + visit::walk_ty(self, ty); + } + } + + let mut visitor = InferVisitor { found: false }; + visitor.visit_ty(ty); + visitor.found +} + +/// Returns true if the closure body contains a single-segment value path that is neither a +/// parameter nor a name bound inside the body. +/// +/// Locals are tracked as hygiene-aware [`Ident`]s (name + `SyntaxContext`) so a macro parameter +/// `$x` is not confused with a closure parameter `x` that happens to share a spelling. +/// +/// This is intentionally AST-only and conservative: free functions and constructors look the same +/// as captures here. Callers should downgrade suggestion applicability when this is true. +fn closure_body_has_free_simple_path(closure: &ast::Closure) -> bool { + let mut known_locals = FxHashSet::default(); + for param in &closure.fn_decl.inputs { + if let PatKind::Ident(_, ident, _) = param.pat.kind { + known_locals.insert(ident); + } + } + + struct FreePathVisitor { + known_locals: FxHashSet, + has_free_path: bool, + } + + impl FreePathVisitor { + fn bind_pat(&mut self, pat: &ast::Pat) { + match &pat.kind { + PatKind::Ident(_, ident, sub) => { + self.known_locals.insert(*ident); + if let Some(sub) = sub { + self.bind_pat(sub); + } + } + PatKind::Tuple(pats) + | PatKind::TupleStruct(_, _, pats) + | PatKind::Slice(pats) + | PatKind::Or(pats) => { + for pat in pats { + self.bind_pat(pat); + } + } + PatKind::Struct(_, _, fields, _) => { + for field in fields { + self.bind_pat(&field.pat); + } + } + PatKind::Box(pat) + | PatKind::Deref(pat) + | PatKind::Ref(pat, ..) + | PatKind::Paren(pat) => self.bind_pat(pat), + _ => {} + } + } + } + + impl<'a> Visitor<'a> for FreePathVisitor { + fn visit_ty(&mut self, _: &'a ast::Ty) { + // Paths in types are not value captures. + } + + fn visit_block(&mut self, block: &'a ast::Block) { + let old = self.known_locals.clone(); + visit::walk_block(self, block); + self.known_locals = old; + } + + fn visit_local(&mut self, local: &'a ast::Local) { + // Visit the initializer (and `else` block) before binding names from the pattern. + // Bindings are not in scope in the `else` block. + if let Some((init, els)) = local.kind.init_else_opt() { + self.visit_expr(init); + if let Some(els) = els { + // Must go through `visit_block` so locals declared in the `else` block do not + // leak into `known_locals` for code after the `let else`. + self.visit_block(els); + } + } + self.bind_pat(&local.pat); + } + + fn visit_arm(&mut self, arm: &'a ast::Arm) { + let old = self.known_locals.clone(); + self.bind_pat(&arm.pat); + visit::walk_arm(self, arm); + self.known_locals = old; + } + + fn visit_expr(&mut self, expr: &'a ast::Expr) { + if self.has_free_path { + return; + } + if let ast::ExprKind::Path(None, path) = &expr.kind + && let [seg] = path.segments.as_slice() + && seg.args.is_none() + && !self.known_locals.contains(&seg.ident) + { + self.has_free_path = true; + return; + } + match &expr.kind { + // `let` bindings from let-chains / `if let` / `while let` conditions. The enclosing + // `If` / `While` arms restore `known_locals` so these do not escape that scope. + ast::ExprKind::Let(pat, scrutinee, _, _) => { + self.visit_expr(scrutinee); + self.bind_pat(pat); + } + // `if`/`if let`/`if` let-chains: condition bindings are in scope for the then + // branch only, not the else branch or anything after the `if`. + ast::ExprKind::If(cond, then_block, else_opt) => { + let old = self.known_locals.clone(); + self.visit_expr(cond); + self.visit_block(then_block); + self.known_locals = old; + if let Some(els) = else_opt { + self.visit_expr(els); + } + } + // `while`/`while let`: condition bindings are in scope for the loop body only. + ast::ExprKind::While(cond, body, _) => { + let old = self.known_locals.clone(); + self.visit_expr(cond); + self.visit_block(body); + self.known_locals = old; + } + ast::ExprKind::ForLoop(for_loop) => { + self.visit_expr(&for_loop.iter); + let old = self.known_locals.clone(); + self.bind_pat(&for_loop.pat); + self.visit_block(&for_loop.body); + self.known_locals = old; + } + _ => visit::walk_expr(self, expr), + } + } + } + + let mut visitor = FreePathVisitor { known_locals, has_free_path: false }; + visitor.visit_expr(&closure.body); + visitor.has_free_path } fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate) { diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index f81727eda4fb6..0650491ba8fbe 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2463,6 +2463,8 @@ impl<'a> Parser<'a> { let (bound_vars, _) = self.parse_higher_ranked_binder()?; let span = lo.to(self.prev_token.span); + // Pre-expansion gate so `#[cfg(false)]` code is still rejected. The post-expansion + // visitor may replace this with a richer diagnostic when the AST is available. self.psess.gated_spans.gate(sym::closure_lifetime_binder, span); ClosureBinder::For { span, generic_params: bound_vars } From ef3532620c1776dde4c8bbd490110fb2cd0217e6 Mon Sep 17 00:00:00 2001 From: Joao Roberto Date: Mon, 3 Aug 2026 22:37:58 -0300 Subject: [PATCH 07/66] Add UI coverage for closure for<> binder binding suggestions Cover MachineApplicable rewrites, MaybeIncorrect capture cases, and macro-expanded binders where structured suggestions must be suppressed. --- ...ture-gate-closure_lifetime_binder-macro.rs | 20 ++ ...-gate-closure_lifetime_binder-macro.stderr | 43 +++ ...closure_lifetime_binder-maybe-incorrect.rs | 34 +++ ...ure_lifetime_binder-maybe-incorrect.stderr | 119 ++++++++ ...gate-closure_lifetime_binder-rustfix.fixed | 10 + ...re-gate-closure_lifetime_binder-rustfix.rs | 10 + ...ate-closure_lifetime_binder-rustfix.stderr | 18 ++ .../feature-gate-closure_lifetime_binder.rs | 119 ++++++++ ...eature-gate-closure_lifetime_binder.stderr | 285 +++++++++++++++++- 9 files changed, 654 insertions(+), 4 deletions(-) create mode 100644 tests/ui/feature-gates/feature-gate-closure_lifetime_binder-macro.rs create mode 100644 tests/ui/feature-gates/feature-gate-closure_lifetime_binder-macro.stderr create mode 100644 tests/ui/feature-gates/feature-gate-closure_lifetime_binder-maybe-incorrect.rs create mode 100644 tests/ui/feature-gates/feature-gate-closure_lifetime_binder-maybe-incorrect.stderr create mode 100644 tests/ui/feature-gates/feature-gate-closure_lifetime_binder-rustfix.fixed create mode 100644 tests/ui/feature-gates/feature-gate-closure_lifetime_binder-rustfix.rs create mode 100644 tests/ui/feature-gates/feature-gate-closure_lifetime_binder-rustfix.stderr diff --git a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-macro.rs b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-macro.rs new file mode 100644 index 0000000000000..c1cc4cc03aa18 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-macro.rs @@ -0,0 +1,20 @@ +//@ compile-flags: --error-format=json +//@ forbid-output: MachineApplicable +//@ forbid-output: MaybeIncorrect + +// Macro-expanded closures must not get a structured fn-pointer rewrite (hygiene + spans point +// into the macro). Expect only the simple help. + +macro_rules! make { + ($x:ident) => { + for<'a> |x: &'a i32| -> i32 { *x + $x } + //~^ ERROR `for<...>` binders for closures are experimental + //~| HELP add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + //~| HELP consider removing `for<...>` + }; +} + +fn main() { + let x = 1; + let _cl = make!(x); +} diff --git a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-macro.stderr b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-macro.stderr new file mode 100644 index 0000000000000..1f960c02be929 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-macro.stderr @@ -0,0 +1,43 @@ +{"$message_type":"diagnostic","message":"`for<...>` binders for closures are experimental","code":{"code":"E0658","explanation":"An unstable feature was used. + +Erroneous code example: + +```compile_fail,E0658 +use std::intrinsics; // error: use of unstable library feature `core_intrinsics` +``` + +If you're using a stable or a beta version of rustc, you won't be able to use +any unstable features. In order to do so, please switch to a nightly version of +rustc (by using [rustup]). + +If you're using a nightly version of rustc, just add the corresponding feature +to be able to use it: + +``` +#![feature(core_intrinsics)] + +use std::intrinsics; // ok! +``` + +[rustup]: https://rust-lang.github.io/rustup/concepts/channels.html +"},"level":"error","spans":[{"file_name":"$DIR/feature-gate-closure_lifetime_binder-macro.rs","byte_start":304,"byte_end":311,"line_start":10,"line_end":10,"column_start":9,"column_end":16,"is_primary":true,"text":[{"text":" for<'a> |x: &'a i32| -> i32 { *x + $x }","highlight_start":9,"highlight_end":16}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":{"span":{"file_name":"$DIR/feature-gate-closure_lifetime_binder-macro.rs","byte_start":605,"byte_end":613,"line_start":19,"line_end":19,"column_start":15,"column_end":23,"is_primary":false,"text":[{"text":" let _cl = make!(x);","highlight_start":15,"highlight_end":23}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null},"macro_decl_name":"make!","def_site_span":{"file_name":"$DIR/feature-gate-closure_lifetime_binder-macro.rs","byte_start":256,"byte_end":273,"line_start":8,"line_end":8,"column_start":1,"column_end":18,"is_primary":false,"text":[{"text":"macro_rules! make {","highlight_start":1,"highlight_end":18}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}}}],"children":[{"message":"see issue #97362 for more information","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable","code":null,"level":"help","spans":[],"children":[],"rendered":null},{"message":"this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"consider removing `for<...>`","code":null,"level":"help","spans":[],"children":[],"rendered":null}],"rendered":"error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder-macro.rs:10:9 + | +LL | for<'a> |x: &'a i32| -> i32 { *x + $x } + | ^^^^^^^ +... +LL | let _cl = make!(x); + | -------- in this macro invocation + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider removing `for<...>` + = note: this error originates in the macro `make` (in Nightly builds, run with -Z macro-backtrace for more info) + +"} +{"$message_type":"diagnostic","message":"aborting due to 1 previous error","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to 1 previous error + +"} +{"$message_type":"diagnostic","message":"For more information about this error, try `rustc --explain E0658`.","code":null,"level":"failure-note","spans":[],"children":[],"rendered":"For more information about this error, try `rustc --explain E0658`. +"} diff --git a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-maybe-incorrect.rs b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-maybe-incorrect.rs new file mode 100644 index 0000000000000..8d7ab9cc2b54c --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-maybe-incorrect.rs @@ -0,0 +1,34 @@ +//@ edition: 2024 +//@ compile-flags: --error-format=json +//@ error-pattern: "suggestion_applicability":"MaybeIncorrect" + +// Capturing closures must not get a MachineApplicable rewrite. Cover plain captures, let-else +// leakage, and let-chain shadowing — all should report MaybeIncorrect in JSON. + +fn main() { + let y = 1; + let _capture = for<'a> |x: &'a i32| -> i32 { *x + y }; + //~^ ERROR `for<...>` binders for closures are experimental + + let let_else_env = 1; + let _let_else = for<'a> |x: &'a i32| -> i32 { + //~^ ERROR `for<...>` binders for closures are experimental + let Some(_) = None:: else { + let let_else_env = 0; + return let_else_env; + }; + *x + let_else_env + }; + + let chain_env = 1; + let _let_chain = for<'a> |x: &'a i32| -> i32 { + //~^ ERROR `for<...>` binders for closures are experimental + if let Some(chain_env) = None:: + && chain_env == 0 + { + 0 + } else { + *x + chain_env + } + }; +} diff --git a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-maybe-incorrect.stderr b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-maybe-incorrect.stderr new file mode 100644 index 0000000000000..6cf49f6effdbd --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-maybe-incorrect.stderr @@ -0,0 +1,119 @@ +{"$message_type":"diagnostic","message":"`for<...>` binders for closures are experimental","code":{"code":"E0658","explanation":"An unstable feature was used. + +Erroneous code example: + +```compile_fail,E0658 +use std::intrinsics; // error: use of unstable library feature `core_intrinsics` +``` + +If you're using a stable or a beta version of rustc, you won't be able to use +any unstable features. In order to do so, please switch to a nightly version of +rustc (by using [rustup]). + +If you're using a nightly version of rustc, just add the corresponding feature +to be able to use it: + +``` +#![feature(core_intrinsics)] + +use std::intrinsics; // ok! +``` + +[rustup]: https://rust-lang.github.io/rustup/concepts/channels.html +"},"level":"error","spans":[{"file_name":"$DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs","byte_start":345,"byte_end":352,"line_start":10,"line_end":10,"column_start":20,"column_end":27,"is_primary":true,"text":[{"text":" let _capture = for<'a> |x: &'a i32| -> i32 { *x + y };","highlight_start":20,"highlight_end":27}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"see issue #97362 for more information","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable","code":null,"level":"help","spans":[],"children":[],"rendered":null},{"message":"this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"consider setting the binding type instead","code":null,"level":"help","spans":[{"file_name":"$DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs","byte_start":342,"byte_end":342,"line_start":10,"line_end":10,"column_start":17,"column_end":17,"is_primary":true,"text":[{"text":" let _capture = for<'a> |x: &'a i32| -> i32 { *x + y };","highlight_start":17,"highlight_end":17}],"label":null,"suggested_replacement":": for<'a> fn(&'a i32) -> i32","suggestion_applicability":"MaybeIncorrect","expansion":null},{"file_name":"$DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs","byte_start":345,"byte_end":372,"line_start":10,"line_end":10,"column_start":20,"column_end":47,"is_primary":true,"text":[{"text":" let _capture = for<'a> |x: &'a i32| -> i32 { *x + y };","highlight_start":20,"highlight_end":47}],"label":null,"suggested_replacement":"|x|","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs:10:20 + | +LL | let _capture = for<'a> |x: &'a i32| -> i32 { *x + y }; + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +help: consider setting the binding type instead + | +LL - let _capture = for<'a> |x: &'a i32| -> i32 { *x + y }; +LL + let _capture: for<'a> fn(&'a i32) -> i32 = |x| { *x + y }; + | + +"} +{"$message_type":"diagnostic","message":"`for<...>` binders for closures are experimental","code":{"code":"E0658","explanation":"An unstable feature was used. + +Erroneous code example: + +```compile_fail,E0658 +use std::intrinsics; // error: use of unstable library feature `core_intrinsics` +``` + +If you're using a stable or a beta version of rustc, you won't be able to use +any unstable features. In order to do so, please switch to a nightly version of +rustc (by using [rustup]). + +If you're using a nightly version of rustc, just add the corresponding feature +to be able to use it: + +``` +#![feature(core_intrinsics)] + +use std::intrinsics; // ok! +``` + +[rustup]: https://rust-lang.github.io/rustup/concepts/channels.html +"},"level":"error","spans":[{"file_name":"$DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs","byte_start":496,"byte_end":503,"line_start":14,"line_end":14,"column_start":21,"column_end":28,"is_primary":true,"text":[{"text":" let _let_else = for<'a> |x: &'a i32| -> i32 {","highlight_start":21,"highlight_end":28}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"see issue #97362 for more information","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable","code":null,"level":"help","spans":[],"children":[],"rendered":null},{"message":"this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"consider setting the binding type instead","code":null,"level":"help","spans":[{"file_name":"$DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs","byte_start":493,"byte_end":493,"line_start":14,"line_end":14,"column_start":18,"column_end":18,"is_primary":true,"text":[{"text":" let _let_else = for<'a> |x: &'a i32| -> i32 {","highlight_start":18,"highlight_end":18}],"label":null,"suggested_replacement":": for<'a> fn(&'a i32) -> i32","suggestion_applicability":"MaybeIncorrect","expansion":null},{"file_name":"$DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs","byte_start":496,"byte_end":523,"line_start":14,"line_end":14,"column_start":21,"column_end":48,"is_primary":true,"text":[{"text":" let _let_else = for<'a> |x: &'a i32| -> i32 {","highlight_start":21,"highlight_end":48}],"label":null,"suggested_replacement":"|x|","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs:14:21 + | +LL | let _let_else = for<'a> |x: &'a i32| -> i32 { + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +help: consider setting the binding type instead + | +LL - let _let_else = for<'a> |x: &'a i32| -> i32 { +LL + let _let_else: for<'a> fn(&'a i32) -> i32 = |x| { + | + +"} +{"$message_type":"diagnostic","message":"`for<...>` binders for closures are experimental","code":{"code":"E0658","explanation":"An unstable feature was used. + +Erroneous code example: + +```compile_fail,E0658 +use std::intrinsics; // error: use of unstable library feature `core_intrinsics` +``` + +If you're using a stable or a beta version of rustc, you won't be able to use +any unstable features. In order to do so, please switch to a nightly version of +rustc (by using [rustup]). + +If you're using a nightly version of rustc, just add the corresponding feature +to be able to use it: + +``` +#![feature(core_intrinsics)] + +use std::intrinsics; // ok! +``` + +[rustup]: https://rust-lang.github.io/rustup/concepts/channels.html +"},"level":"error","spans":[{"file_name":"$DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs","byte_start":791,"byte_end":798,"line_start":24,"line_end":24,"column_start":22,"column_end":29,"is_primary":true,"text":[{"text":" let _let_chain = for<'a> |x: &'a i32| -> i32 {","highlight_start":22,"highlight_end":29}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"see issue #97362 for more information","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable","code":null,"level":"help","spans":[],"children":[],"rendered":null},{"message":"this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"consider setting the binding type instead","code":null,"level":"help","spans":[{"file_name":"$DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs","byte_start":788,"byte_end":788,"line_start":24,"line_end":24,"column_start":19,"column_end":19,"is_primary":true,"text":[{"text":" let _let_chain = for<'a> |x: &'a i32| -> i32 {","highlight_start":19,"highlight_end":19}],"label":null,"suggested_replacement":": for<'a> fn(&'a i32) -> i32","suggestion_applicability":"MaybeIncorrect","expansion":null},{"file_name":"$DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs","byte_start":791,"byte_end":818,"line_start":24,"line_end":24,"column_start":22,"column_end":49,"is_primary":true,"text":[{"text":" let _let_chain = for<'a> |x: &'a i32| -> i32 {","highlight_start":22,"highlight_end":49}],"label":null,"suggested_replacement":"|x|","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs:24:22 + | +LL | let _let_chain = for<'a> |x: &'a i32| -> i32 { + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +help: consider setting the binding type instead + | +LL - let _let_chain = for<'a> |x: &'a i32| -> i32 { +LL + let _let_chain: for<'a> fn(&'a i32) -> i32 = |x| { + | + +"} +{"$message_type":"diagnostic","message":"aborting due to 3 previous errors","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to 3 previous errors + +"} +{"$message_type":"diagnostic","message":"For more information about this error, try `rustc --explain E0658`.","code":null,"level":"failure-note","spans":[],"children":[],"rendered":"For more information about this error, try `rustc --explain E0658`. +"} diff --git a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-rustfix.fixed b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-rustfix.fixed new file mode 100644 index 0000000000000..30250eca548ad --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-rustfix.fixed @@ -0,0 +1,10 @@ +//@ run-rustfix +//@ rustfix-only-machine-applicable + +// Verify the #160431 rewrite is MachineApplicable: rustfix applies it and the result compiles +// without `#![feature(closure_lifetime_binder)]`. + +fn main() { + let _cl: for<'a> fn(&'a str) -> (&'a str, &'a str) = |x| { x.split_at(0) }; + //~^ ERROR `for<...>` binders for closures are experimental +} diff --git a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-rustfix.rs b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-rustfix.rs new file mode 100644 index 0000000000000..372a03e6e5d99 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-rustfix.rs @@ -0,0 +1,10 @@ +//@ run-rustfix +//@ rustfix-only-machine-applicable + +// Verify the #160431 rewrite is MachineApplicable: rustfix applies it and the result compiles +// without `#![feature(closure_lifetime_binder)]`. + +fn main() { + let _cl = for<'a> |x: &'a str| -> (&'a str, &'a str) { x.split_at(0) }; + //~^ ERROR `for<...>` binders for closures are experimental +} diff --git a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-rustfix.stderr b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-rustfix.stderr new file mode 100644 index 0000000000000..afdf6ece14144 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-rustfix.stderr @@ -0,0 +1,18 @@ +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder-rustfix.rs:8:15 + | +LL | let _cl = for<'a> |x: &'a str| -> (&'a str, &'a str) { x.split_at(0) }; + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +help: consider setting the binding type instead + | +LL - let _cl = for<'a> |x: &'a str| -> (&'a str, &'a str) { x.split_at(0) }; +LL + let _cl: for<'a> fn(&'a str) -> (&'a str, &'a str) = |x| { x.split_at(0) }; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder.rs b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder.rs index b0b494fa3ff13..cb62f426083f4 100644 --- a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder.rs +++ b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder.rs @@ -1,3 +1,5 @@ +//@ edition: 2024 + fn main() { for<> || -> () {}; //~^ ERROR `for<...>` binders for closures are experimental @@ -5,4 +7,121 @@ fn main() { //~^ ERROR `for<...>` binders for closures are experimental for<'a, 'b> |_: &'a ()| -> () {}; //~^ ERROR `for<...>` binders for closures are experimental + + // Issue #160431: suggest moving the binder onto a `fn` pointer binding type. + let _cl = for<'a> |x: &'a str| -> (&'a str, &'a str) { x.split_at(0) }; + //~^ ERROR `for<...>` binders for closures are experimental + + // Local temporaries in the body are fine for `fn` pointers (machine-applicable). + let _tmp = for<'a> |x: &'a str| -> usize { + //~^ ERROR `for<...>` binders for closures are experimental + let n = x.len(); + n + }; + + // Already has a type ascription — fall back to the simple help. + let _typed: _ = for<'a> |x: &'a str| -> &'a str { x }; + //~^ ERROR `for<...>` binders for closures are experimental + + // Infer placeholders must not be copied into a MachineApplicable `fn` type. + let _ret_infer = for<'a> |x: &'a str| -> _ { x }; + //~^ ERROR `for<...>` binders for closures are experimental + //~| ERROR implicit types in closure signatures are forbidden when `for<...>` is present + let _nested_infer = for<'a> |x: &'a _| -> &'a str { x }; + //~^ ERROR `for<...>` binders for closures are experimental + //~| ERROR implicit types in closure signatures are forbidden when `for<...>` is present + + // Explicit `move` closures are not `fn` pointers. + let y = 1; + let _move = for<'a> move |x: &'a i32| -> i32 { *x + y }; + //~^ ERROR `for<...>` binders for closures are experimental + + // Possible captures (any case) still get a suggestion, but only as maybe-incorrect. + let z = 1; + let _capture = for<'a> |x: &'a i32| -> i32 { *x + z }; + //~^ ERROR `for<...>` binders for closures are experimental + let Y = 1; + let _upper = for<'a> |x: &'a i32| -> i32 { *x + Y }; + //~^ ERROR `for<...>` binders for closures are experimental + + // `if let` bindings must not escape into the `else` branch (or past the `if`). + let if_let_env = 1; + let _if_let = for<'a> |x: &'a i32| -> i32 { + //~^ ERROR `for<...>` binders for closures are experimental + if let Some(if_let_env) = None:: { + if_let_env + } else { + *x + if_let_env + } + }; + + // Same for `while let`. + let while_let_env = 1; + let _while_let = for<'a> |x: &'a i32| -> i32 { + //~^ ERROR `for<...>` binders for closures are experimental + while let Some(while_let_env) = None:: { + let _ = while_let_env; + break; + } + *x + while_let_env + }; + + // Let-chain bindings are scoped to the `if` as well (same name as the outer capture). + let chain_env = 1; + let _let_chain = for<'a> |x: &'a i32| -> i32 { + //~^ ERROR `for<...>` binders for closures are experimental + if let Some(chain_env) = None:: + && chain_env == 0 + { + 0 + } else { + *x + chain_env + } + }; + + // Locals declared in a `let else` block must not leak past it. + let let_else_env = 1; + let _let_else = for<'a> |x: &'a i32| -> i32 { + //~^ ERROR `for<...>` binders for closures are experimental + let Some(_) = None:: else { + let let_else_env = 0; + return let_else_env; + }; + *x + let_else_env + }; + + // Free functions look like captures to the AST heuristic; suggestion is maybe-incorrect. + let _freefn = for<'a> |x: &'a i32| -> i32 { add(*x, 1) }; + //~^ ERROR `for<...>` binders for closures are experimental + + // `ref` bindings on the `let` are not rewritten. + let ref _ref_cl = for<'a> |x: &'a str| -> &'a str { x }; + //~^ ERROR `for<...>` binders for closures are experimental + + // `ref` closure parameters are not rewritten. + let _ref_param = for<'a> |ref x: &'a str| -> &'a str { *x }; + //~^ ERROR `for<...>` binders for closures are experimental + + // Parameter attributes would be dropped by the rewrite — fall back. + let _attrs = for<'a> |#[allow(unused)] x: &'a str| -> &'a str { x }; + //~^ ERROR `for<...>` binders for closures are experimental + + // Non-lifetime binders are not valid on `fn` pointers. + let _ty_binder = for |x: T| -> T { x }; + //~^ ERROR `for<...>` binders for closures are experimental + //~| ERROR only lifetime parameters can be used in this context + + // Bounded lifetime binders are not valid on `fn` pointers. + let _bound = for<'a: 'static> |x: &'a str| -> &'a str { x }; + //~^ ERROR `for<...>` binders for closures are experimental + //~| ERROR bounds cannot be used in this context + + // Pre-expansion gating still rejects binders under `#[cfg(false)]`. + #[cfg(false)] + let _cfg = for<'a> |x: &'a str| -> &'a str { x }; + //~^ ERROR `for<...>` binders for closures are experimental +} + +fn add(a: i32, b: i32) -> i32 { + a + b } diff --git a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder.stderr b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder.stderr index 96e428fb9a37e..cc703db9c61e8 100644 --- a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder.stderr +++ b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder.stderr @@ -1,5 +1,5 @@ error[E0658]: `for<...>` binders for closures are experimental - --> $DIR/feature-gate-closure_lifetime_binder.rs:2:5 + --> $DIR/feature-gate-closure_lifetime_binder.rs:4:5 | LL | for<> || -> () {}; | ^^^^^ @@ -10,7 +10,7 @@ LL | for<> || -> () {}; = help: consider removing `for<...>` error[E0658]: `for<...>` binders for closures are experimental - --> $DIR/feature-gate-closure_lifetime_binder.rs:4:5 + --> $DIR/feature-gate-closure_lifetime_binder.rs:6:5 | LL | for<'a> || -> () {}; | ^^^^^^^ @@ -21,7 +21,7 @@ LL | for<'a> || -> () {}; = help: consider removing `for<...>` error[E0658]: `for<...>` binders for closures are experimental - --> $DIR/feature-gate-closure_lifetime_binder.rs:6:5 + --> $DIR/feature-gate-closure_lifetime_binder.rs:8:5 | LL | for<'a, 'b> |_: &'a ()| -> () {}; | ^^^^^^^^^^^ @@ -31,6 +31,283 @@ LL | for<'a, 'b> |_: &'a ()| -> () {}; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date = help: consider removing `for<...>` -error: aborting due to 3 previous errors +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:12:15 + | +LL | let _cl = for<'a> |x: &'a str| -> (&'a str, &'a str) { x.split_at(0) }; + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +help: consider setting the binding type instead + | +LL - let _cl = for<'a> |x: &'a str| -> (&'a str, &'a str) { x.split_at(0) }; +LL + let _cl: for<'a> fn(&'a str) -> (&'a str, &'a str) = |x| { x.split_at(0) }; + | + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:16:16 + | +LL | let _tmp = for<'a> |x: &'a str| -> usize { + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +help: consider setting the binding type instead + | +LL - let _tmp = for<'a> |x: &'a str| -> usize { +LL + let _tmp: for<'a> fn(&'a str) -> usize = |x| { + | + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:23:21 + | +LL | let _typed: _ = for<'a> |x: &'a str| -> &'a str { x }; + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider removing `for<...>` + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:27:22 + | +LL | let _ret_infer = for<'a> |x: &'a str| -> _ { x }; + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider removing `for<...>` + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:30:25 + | +LL | let _nested_infer = for<'a> |x: &'a _| -> &'a str { x }; + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider removing `for<...>` + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:36:17 + | +LL | let _move = for<'a> move |x: &'a i32| -> i32 { *x + y }; + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider removing `for<...>` + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:41:20 + | +LL | let _capture = for<'a> |x: &'a i32| -> i32 { *x + z }; + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +help: consider setting the binding type instead + | +LL - let _capture = for<'a> |x: &'a i32| -> i32 { *x + z }; +LL + let _capture: for<'a> fn(&'a i32) -> i32 = |x| { *x + z }; + | + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:44:18 + | +LL | let _upper = for<'a> |x: &'a i32| -> i32 { *x + Y }; + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +help: consider setting the binding type instead + | +LL - let _upper = for<'a> |x: &'a i32| -> i32 { *x + Y }; +LL + let _upper: for<'a> fn(&'a i32) -> i32 = |x| { *x + Y }; + | + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:49:19 + | +LL | let _if_let = for<'a> |x: &'a i32| -> i32 { + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +help: consider setting the binding type instead + | +LL - let _if_let = for<'a> |x: &'a i32| -> i32 { +LL + let _if_let: for<'a> fn(&'a i32) -> i32 = |x| { + | + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:60:22 + | +LL | let _while_let = for<'a> |x: &'a i32| -> i32 { + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +help: consider setting the binding type instead + | +LL - let _while_let = for<'a> |x: &'a i32| -> i32 { +LL + let _while_let: for<'a> fn(&'a i32) -> i32 = |x| { + | + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:71:22 + | +LL | let _let_chain = for<'a> |x: &'a i32| -> i32 { + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +help: consider setting the binding type instead + | +LL - let _let_chain = for<'a> |x: &'a i32| -> i32 { +LL + let _let_chain: for<'a> fn(&'a i32) -> i32 = |x| { + | + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:84:21 + | +LL | let _let_else = for<'a> |x: &'a i32| -> i32 { + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +help: consider setting the binding type instead + | +LL - let _let_else = for<'a> |x: &'a i32| -> i32 { +LL + let _let_else: for<'a> fn(&'a i32) -> i32 = |x| { + | + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:94:19 + | +LL | let _freefn = for<'a> |x: &'a i32| -> i32 { add(*x, 1) }; + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +help: consider setting the binding type instead + | +LL - let _freefn = for<'a> |x: &'a i32| -> i32 { add(*x, 1) }; +LL + let _freefn: for<'a> fn(&'a i32) -> i32 = |x| { add(*x, 1) }; + | + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:98:23 + | +LL | let ref _ref_cl = for<'a> |x: &'a str| -> &'a str { x }; + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider removing `for<...>` + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:102:22 + | +LL | let _ref_param = for<'a> |ref x: &'a str| -> &'a str { *x }; + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider removing `for<...>` + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:106:18 + | +LL | let _attrs = for<'a> |#[allow(unused)] x: &'a str| -> &'a str { x }; + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider removing `for<...>` + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:110:22 + | +LL | let _ty_binder = for |x: T| -> T { x }; + | ^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider removing `for<...>` + +error[E0658]: only lifetime parameters can be used in this context + --> $DIR/feature-gate-closure_lifetime_binder.rs:110:26 + | +LL | let _ty_binder = for |x: T| -> T { x }; + | ^ + | + = note: see issue #108185 for more information + = help: add `#![feature(non_lifetime_binders)]` 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[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:115:18 + | +LL | let _bound = for<'a: 'static> |x: &'a str| -> &'a str { x }; + | ^^^^^^^^^^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider removing `for<...>` + +error: bounds cannot be used in this context + --> $DIR/feature-gate-closure_lifetime_binder.rs:115:26 + | +LL | let _bound = for<'a: 'static> |x: &'a str| -> &'a str { x }; + | ^^^^^^^ + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:121:16 + | +LL | let _cfg = for<'a> |x: &'a str| -> &'a str { x }; + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider removing `for<...>` + +error: implicit types in closure signatures are forbidden when `for<...>` is present + --> $DIR/feature-gate-closure_lifetime_binder.rs:27:46 + | +LL | let _ret_infer = for<'a> |x: &'a str| -> _ { x }; + | ------- ^ + | | + | `for<...>` is here + +error: implicit types in closure signatures are forbidden when `for<...>` is present + --> $DIR/feature-gate-closure_lifetime_binder.rs:30:41 + | +LL | let _nested_infer = for<'a> |x: &'a _| -> &'a str { x }; + | ------- ^ + | | + | `for<...>` is here + +error: aborting due to 26 previous errors For more information about this error, try `rustc --explain E0658`. From bdbf95a46b764cb016b0bb3db3e68894f5c805d0 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Wed, 12 Aug 2026 02:23:34 +0300 Subject: [PATCH 08/66] [Priroda] Generalize DAP session transport --- src/tools/miri/priroda/src/frontend/dap.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/tools/miri/priroda/src/frontend/dap.rs b/src/tools/miri/priroda/src/frontend/dap.rs index 6e48510cadc5c..285011f4c7c0f 100644 --- a/src/tools/miri/priroda/src/frontend/dap.rs +++ b/src/tools/miri/priroda/src/frontend/dap.rs @@ -1,4 +1,4 @@ -use std::io::{self, BufReader, BufWriter}; +use std::io::{self, BufReader, BufWriter, Read, Write}; use emmy_dap_types::errors::ServerError; use emmy_dap_types::prelude::events::{ExitedEventBody, StoppedEventBody}; @@ -72,15 +72,13 @@ impl Dap { } } -type DapServer = Server, io::StdoutLock<'static>>; - -/// Owns the DAP stdio transport and dispatches requests into Priroda handlers. -struct DapSession { - server: DapServer, +/// Owns a DAP transport and dispatches requests into Priroda handlers. +struct DapSession { + server: Server, state: DapState, } -impl DapSession { +impl DapSession, io::StdoutLock<'static>> { fn stdio() -> Self { Self { server: Server::new( @@ -90,7 +88,9 @@ impl DapSession { state: DapState::Fresh, } } +} +impl DapSession { fn run_requests<'tcx>( &mut self, session: &mut PrirodaContext<'tcx>, From 4f63153f498f2bea5d76f6457da62cc229114c79 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Wed, 12 Aug 2026 02:25:57 +0300 Subject: [PATCH 09/66] [Priroda] Add DAP TCP frontend transport --- src/tools/miri/priroda/src/frontend/dap.rs | 29 +++++++++++++++++++--- src/tools/miri/priroda/src/main.rs | 2 +- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/tools/miri/priroda/src/frontend/dap.rs b/src/tools/miri/priroda/src/frontend/dap.rs index 285011f4c7c0f..e14251340c78f 100644 --- a/src/tools/miri/priroda/src/frontend/dap.rs +++ b/src/tools/miri/priroda/src/frontend/dap.rs @@ -1,4 +1,5 @@ use std::io::{self, BufReader, BufWriter, Read, Write}; +use std::net::{TcpListener, TcpStream}; use emmy_dap_types::errors::ServerError; use emmy_dap_types::prelude::events::{ExitedEventBody, StoppedEventBody}; @@ -56,15 +57,23 @@ enum ExecutionOutcome { } /// Debug Adapter Protocol frontend. -pub(crate) struct Dap; +pub(crate) struct Dap { + pub(crate) port: Option, +} impl Dap { - /// Serve DAP requests on stdin/stdout. + /// Serve DAP requests on stdin/stdout, or on a TCP socket if `port` is set. pub(crate) fn run_dap_loop<'tcx>( &self, session: &mut PrirodaContext<'tcx>, ) -> InterpResult<'tcx> { - if let Err(err) = DapSession::stdio().run_requests(session) { + let result = if let Some(port) = self.port { + DapSession::tcp(port).run_requests(session) + } else { + DapSession::stdio().run_requests(session) + }; + + if let Err(err) = result { eprintln!("priroda dap error: {err:?}"); } @@ -90,6 +99,20 @@ impl DapSession, io::StdoutLock<'static>> { } } +impl DapSession { + fn tcp(port: u16) -> Self { + let listener = + TcpListener::bind(("127.0.0.1", port)).expect("failed to listen on DAP TCP socket"); + let (stream, _) = listener.accept().expect("failed to accept DAP TCP connection"); + let reader = stream.try_clone().expect("failed to clone DAP TCP stream"); + + Self { + server: Server::new(BufReader::new(reader), BufWriter::new(stream)), + state: DapState::Fresh, + } + } +} + impl DapSession { fn run_requests<'tcx>( &mut self, diff --git a/src/tools/miri/priroda/src/main.rs b/src/tools/miri/priroda/src/main.rs index 9b0efdf9fadb8..facdca777ff78 100644 --- a/src/tools/miri/priroda/src/main.rs +++ b/src/tools/miri/priroda/src/main.rs @@ -107,7 +107,7 @@ impl rustc_driver::Callbacks for PrirodaCompilerCalls { let mut session = PrirodaContext::new(ecx); let result = match self.frontend { Frontend::Cli => frontend::Cli {}.run_cli_loop(&mut session), - Frontend::Dap => frontend::Dap {}.run_dap_loop(&mut session), + Frontend::Dap => frontend::Dap { port: None }.run_dap_loop(&mut session), }; match result.report_err() { From 2c7b68213a70c53e6fd5f8c1ac55b1234bb50959 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Wed, 12 Aug 2026 02:27:22 +0300 Subject: [PATCH 10/66] [Priroda] Wire --port to DAP TCP mode --- src/tools/miri/priroda/README.md | 5 +-- src/tools/miri/priroda/src/main.rs | 50 ++++++++++++++++++++++++------ 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/src/tools/miri/priroda/README.md b/src/tools/miri/priroda/README.md index 6283bc28bb7c2..d322746e89db1 100644 --- a/src/tools/miri/priroda/README.md +++ b/src/tools/miri/priroda/README.md @@ -40,8 +40,9 @@ cargo run -- ../tests/pass/empty_main.rs ## DAP Prototype -Priroda's `--dap` mode speaks a bounded Debug Adapter Protocol prototype over -stdio. It currently supports the startup handshake, stops at the first +Priroda speaks a bounded Debug Adapter Protocol prototype over stdio with +`--dap`, or over TCP with `--port N`. It currently supports the startup +handshake, stops at the first user-relevant source location after `configurationDone`, reports one current stack frame, exposes one flat Locals scope, and maps `list_locals()` into DAP variables with no child expansion. diff --git a/src/tools/miri/priroda/src/main.rs b/src/tools/miri/priroda/src/main.rs index facdca777ff78..f67aab3b3dfea 100644 --- a/src/tools/miri/priroda/src/main.rs +++ b/src/tools/miri/priroda/src/main.rs @@ -54,7 +54,7 @@ fn main() { #[derive(Clone, Copy)] enum Frontend { Cli, - Dap, + Dap { port: Option }, } impl Frontend { @@ -64,14 +64,36 @@ impl Frontend { let mut rustc_args = Vec::with_capacity(args.len()); let mut parsing_priroda_args = true; - for (idx, arg) in args.drain(..).enumerate() { - if idx != 0 && parsing_priroda_args && arg == "--dap" { - frontend = Frontend::Dap; - continue; - } + let mut arg_iter = std::mem::take(args).into_iter(); + if let Some(program) = arg_iter.next() { + rustc_args.push(program); + } - if arg == "--" { - parsing_priroda_args = false; + while let Some(arg) = arg_iter.next() { + if parsing_priroda_args { + if arg == "--dap" { + if matches!(frontend, Frontend::Cli) { + frontend = Frontend::Dap { port: None }; + } + continue; + } + + if arg == "--port" { + let port_str = arg_iter + .next() + .unwrap_or_else(|| Self::fatal_arg_error("--port requires a value")); + frontend = Frontend::Dap { port: Some(Self::parse_port(&port_str)) }; + continue; + } + + if let Some(port_str) = arg.strip_prefix("--port=") { + frontend = Frontend::Dap { port: Some(Self::parse_port(port_str)) }; + continue; + } + + if arg == "--" { + parsing_priroda_args = false; + } } rustc_args.push(arg); @@ -80,6 +102,16 @@ impl Frontend { *args = rustc_args; frontend } + + fn parse_port(port: &str) -> u16 { + port.parse() + .unwrap_or_else(|_| Self::fatal_arg_error("--port requires a valid u16 port number")) + } + + fn fatal_arg_error(message: &str) -> ! { + eprintln!("priroda: {message}"); + std::process::exit(1); + } } struct PrirodaCompilerCalls { @@ -107,7 +139,7 @@ impl rustc_driver::Callbacks for PrirodaCompilerCalls { let mut session = PrirodaContext::new(ecx); let result = match self.frontend { Frontend::Cli => frontend::Cli {}.run_cli_loop(&mut session), - Frontend::Dap => frontend::Dap { port: None }.run_dap_loop(&mut session), + Frontend::Dap { port } => frontend::Dap { port }.run_dap_loop(&mut session), }; match result.report_err() { From d93b7a80dcdd38200f2bac72c26e293b153fd4c6 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Wed, 12 Aug 2026 02:35:50 +0300 Subject: [PATCH 11/66] [Priroda] Add VS Code DAP launch template --- src/tools/miri/priroda/README.md | 84 ++++++++++++++++++++++ src/tools/miri/priroda/src/frontend/dap.rs | 1 + src/tools/miri/priroda/vscode_launch.json | 15 ++++ src/tools/miri/priroda/vscode_tasks.json | 38 ++++++++++ 4 files changed, 138 insertions(+) create mode 100644 src/tools/miri/priroda/vscode_launch.json create mode 100644 src/tools/miri/priroda/vscode_tasks.json diff --git a/src/tools/miri/priroda/README.md b/src/tools/miri/priroda/README.md index d322746e89db1..14ec0681163e7 100644 --- a/src/tools/miri/priroda/README.md +++ b/src/tools/miri/priroda/README.md @@ -51,6 +51,90 @@ The `next` and `stepIn` requests are wired to Priroda's existing source-line step so VS Code can drive one visible step. They are not true DAP step-over or step-in semantics yet. +### VS Code + +VS Code can start Priroda as a TCP DAP server and then attach to that server +when you run the debugger configuration. The launch configuration does not spawn +Priroda directly; it starts a background task and then connects through +`debugServer`. + +This requires a VS Code debug extension that contributes the `priroda` debugger +type. The `debugServer` setting only tells VS Code to connect to an +already-running adapter; it does not register a new debugger type. On a clean VS +Code install, copying these JSON files is not enough for the launch +configuration to be accepted. + +After that debugger type is registered, copy the example files into the +workspace you want to debug: + +```sh +mkdir -p /path/to/project/.vscode +cp vscode_launch.json /path/to/project/.vscode/launch.json +cp vscode_tasks.json /path/to/project/.vscode/tasks.json +``` + +Before running the debugger configuration, make sure: + +- Priroda has been built, so the binary path in `command` exists. +- `MIRI_SYSROOT` points at a Miri sysroot, for example from + `cargo +miri miri setup --print-sysroot`. +- If running the `priroda` binary directly, `LD_LIBRARY_PATH` may need to point + at the pinned `miri` toolchain's `lib` directory. +- The Rust file path at the end of `args` is the file you want Priroda to run. +- Port `4711` is free, or both `--port` and `debugServer` use the same different + port. +- VS Code has a debugger contribution installed that accepts + `type: "priroda"` debug configurations. + +Then edit `.vscode/tasks.json` for your local paths. Set `command` to the +Priroda binary you want VS Code to run: + +```json +"command": "${workspaceFolder}/target/debug/priroda" +``` + +If the binary cannot find rustc libraries, add an `env` block under +`options`: + +```json +"options": { + "cwd": "${workspaceFolder}", + "env": { + "LD_LIBRARY_PATH": "/path/to/miri-toolchain/lib", + "MIRI_SYSROOT": "/path/to/miri-sysroot" + } +} +``` + +Also edit the final argument in `args` to point at the Rust file you want +Priroda to run. This task argument, not `launch.json`, selects the interpreted +program: + +```json +"${workspaceFolder}/src/main.rs" +``` + +The task runs Priroda like this: + +```sh +cargo run -- --dap --port 4711 /path/to/project/src/main.rs +``` + +Once Priroda prints `priroda dap listening on 127.0.0.1:4711`, VS Code treats +the background task as ready and connects with: + +```json +{ + "type": "priroda", + "request": "launch", + "preLaunchTask": "Priroda: Start DAP Server", + "debugServer": 4711 +} +``` + +Priroda accepts one TCP connection and waits for VS Code before running the DAP +handshake. + ## Test Priroda's CLI tests also need `MIRI_SYSROOT`. Run them from `miri/priroda/`: diff --git a/src/tools/miri/priroda/src/frontend/dap.rs b/src/tools/miri/priroda/src/frontend/dap.rs index e14251340c78f..744fd8675f8d0 100644 --- a/src/tools/miri/priroda/src/frontend/dap.rs +++ b/src/tools/miri/priroda/src/frontend/dap.rs @@ -103,6 +103,7 @@ impl DapSession { fn tcp(port: u16) -> Self { let listener = TcpListener::bind(("127.0.0.1", port)).expect("failed to listen on DAP TCP socket"); + eprintln!("priroda dap listening on 127.0.0.1:{port}"); let (stream, _) = listener.accept().expect("failed to accept DAP TCP connection"); let reader = stream.try_clone().expect("failed to clone DAP TCP stream"); diff --git a/src/tools/miri/priroda/vscode_launch.json b/src/tools/miri/priroda/vscode_launch.json new file mode 100644 index 0000000000000..c78ad524d9b3e --- /dev/null +++ b/src/tools/miri/priroda/vscode_launch.json @@ -0,0 +1,15 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Priroda: Run and Attach", + // Requires a VS Code debug extension that contributes the "priroda" type. + // The debugServer field only tells VS Code to connect to an already-running + // adapter; it does not register a new debugger type by itself. + "type": "priroda", + "request": "launch", + "preLaunchTask": "Priroda: Start DAP Server", + "debugServer": 4711 + } + ] +} diff --git a/src/tools/miri/priroda/vscode_tasks.json b/src/tools/miri/priroda/vscode_tasks.json new file mode 100644 index 0000000000000..bfd0a04000716 --- /dev/null +++ b/src/tools/miri/priroda/vscode_tasks.json @@ -0,0 +1,38 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Priroda: Start DAP Server", + "type": "process", + "command": "${workspaceFolder}/target/debug/priroda", + "args": [ + "--dap", + "--port", + "4711", + "--sysroot", + "${env:MIRI_SYSROOT}", + "${workspaceFolder}/src/main.rs" + ], + "isBackground": true, + "options": { + "cwd": "${workspaceFolder}" + }, + "presentation": { + "echo": true, + "reveal": "always", + "focus": true, + "panel": "dedicated" + }, + "problemMatcher": { + "pattern": { + "regexp": "^(.*)$" + }, + "background": { + "activeOnStart": true, + "beginsPattern": ".", + "endsPattern": "priroda dap listening on 127\\.0\\.0\\.1:4711" + } + } + } + ] +} From 377ef9f028c7853b70e21fdecf5f6d376d9031b5 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 12 Aug 2026 23:52:29 +0200 Subject: [PATCH 12/66] Build newer binutils before building gcc --- src/ci/docker/scripts/build-gcc.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ci/docker/scripts/build-gcc.sh b/src/ci/docker/scripts/build-gcc.sh index 6a96b82d3f924..bef169fafe38c 100755 --- a/src/ci/docker/scripts/build-gcc.sh +++ b/src/ci/docker/scripts/build-gcc.sh @@ -4,6 +4,13 @@ set -eux source shared.sh +BINUTILS="2.47" +curl https://ci-mirrors.rust-lang.org/rustc/gcc/binutils-$BINUTILS.tar.xz | xzcat | tar xf - +cd binutils-$BINUTILS +hide_output ./configure +hide_output make +hide_output make install + # Note: in the future when bumping to version 10.1.0, also take care of the sed block below. # This version is specified in the Dockerfile GCC=$GCC_VERSION From 46eb4fde6a9eee926c46550031d8bd1b49c03d97 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Wed, 12 Aug 2026 18:52:28 +0000 Subject: [PATCH 13/66] clippy::needless_borrow --- library/alloc/src/boxed.rs | 2 +- library/alloc/src/collections/btree/map.rs | 6 +++--- library/alloc/src/collections/btree/node.rs | 2 +- library/alloc/src/collections/btree/set.rs | 4 ++-- library/alloc/src/io/impls.rs | 4 ++-- library/alloc/src/vec/splice.rs | 2 +- library/alloc/src/wtf8/mod.rs | 4 ++-- library/core/src/ffi/c_str.rs | 4 ++-- library/core/src/iter/traits/iterator.rs | 2 +- library/core/src/slice/ascii.rs | 2 +- library/core/src/slice/iter.rs | 2 +- library/core/src/str/iter.rs | 2 +- library/core/src/task/wake.rs | 4 ++-- library/std/src/ffi/os_str.rs | 10 +++++----- library/std/src/net/socket_addr.rs | 2 +- library/std/src/os/unix/net/ancillary.rs | 4 ++-- library/std/src/path.rs | 2 +- library/std/src/sync/nonpoison/rwlock.rs | 8 ++++---- library/std/src/sync/poison/rwlock.rs | 8 ++++---- library/std/src/sys/fs/common.rs | 2 +- library/std/src/sys/fs/unix.rs | 4 ++-- library/std/src/sys/fs/unix/dir.rs | 2 +- library/std/src/sys/fs/windows.rs | 10 +++++----- library/std/src/sys/process/unix/common.rs | 2 +- 24 files changed, 47 insertions(+), 47 deletions(-) diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index cd2508a76a10e..c25a06968e293 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -2141,7 +2141,7 @@ impl Clone for Box<[T], A> { /// ``` fn clone_from(&mut self, source: &Self) { if self.len() == source.len() { - self.clone_from_slice(&source); + self.clone_from_slice(source); } else { *self = source.clone(); } diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 0a1f7738632c1..d8421d3c3f70a 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -1572,7 +1572,7 @@ impl BTreeMap { let right_root = left_root.split_off(key, (*self.alloc).clone()); - let (new_left_len, right_len) = Root::calc_split_length(total_num, &left_root, &right_root); + let (new_left_len, right_len) = Root::calc_split_length(total_num, left_root, &right_root); self.length = new_left_len; BTreeMap { @@ -2208,8 +2208,8 @@ impl<'a, K, V, R> ExtractIfInner<'a, K, V, R> { // On creation, we navigated directly to the left bound, so we need only check the // right bound here to decide whether to stop. match self.range.end_bound() { - Bound::Included(ref end) if (*k).le(end) => (), - Bound::Excluded(ref end) if (*k).lt(end) => (), + Bound::Included(end) if (*k).le(end) => (), + Bound::Excluded(end) if (*k).lt(end) => (), Bound::Unbounded => (), _ => return None, } diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs index 84dd4b7e49def..0c7afcc63b9b7 100644 --- a/library/alloc/src/collections/btree/node.rs +++ b/library/alloc/src/collections/btree/node.rs @@ -1430,7 +1430,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { left_node.val_area_mut(old_left_len + 1..new_left_len), ); - slice_remove(&mut parent_node.edge_area_mut(..old_parent_len + 1), parent_idx + 1); + slice_remove(parent_node.edge_area_mut(..old_parent_len + 1), parent_idx + 1); parent_node.correct_childrens_parent_links(parent_idx + 1..old_parent_len); *parent_node.len_mut() -= 1; diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index 2a483b3d3982e..d06daa7c6c1b7 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -1971,7 +1971,7 @@ impl<'a, T: Ord, A: Allocator + Clone> Iterator for Difference<'a, T, A> { } DifferenceInner::Search { self_iter, other_set } => loop { let self_next = self_iter.next()?; - if !other_set.contains(&self_next) { + if !other_set.contains(self_next) { return Some(self_next); } }, @@ -2068,7 +2068,7 @@ impl<'a, T: Ord, A: Allocator + Clone> Iterator for Intersection<'a, T, A> { } IntersectionInner::Search { small_iter, large_set } => loop { let small_next = small_iter.next()?; - if large_set.contains(&small_next) { + if large_set.contains(small_next) { return Some(small_next); } }, diff --git a/library/alloc/src/io/impls.rs b/library/alloc/src/io/impls.rs index a6c9428ba62dc..dae6b3aa3371d 100644 --- a/library/alloc/src/io/impls.rs +++ b/library/alloc/src/io/impls.rs @@ -625,7 +625,7 @@ where #[inline] fn is_read_vectored(&self) -> bool { - (&**self).is_read_vectored() + (**self).is_read_vectored() } #[inline] @@ -667,7 +667,7 @@ where #[inline] fn is_write_vectored(&self) -> bool { - (&**self).is_write_vectored() + (**self).is_write_vectored() } #[inline] diff --git a/library/alloc/src/vec/splice.rs b/library/alloc/src/vec/splice.rs index 99ebcb4ada296..6436afd1ba12f 100644 --- a/library/alloc/src/vec/splice.rs +++ b/library/alloc/src/vec/splice.rs @@ -59,7 +59,7 @@ impl Drop for Splice<'_, I, A> { // Which means we can replace the slice::Iter with pointers that won't point to deallocated // memory, so that Drain::drop is still allowed to call iter.len(), otherwise it would break // the ptr.offset_from_unsigned contract. - self.drain.iter = (&[]).iter(); + self.drain.iter = [].iter(); unsafe { if self.drain.tail_len == 0 { diff --git a/library/alloc/src/wtf8/mod.rs b/library/alloc/src/wtf8/mod.rs index 394c41bf36727..36ec32c549763 100644 --- a/library/alloc/src/wtf8/mod.rs +++ b/library/alloc/src/wtf8/mod.rs @@ -284,7 +284,7 @@ impl Wtf8Buf { /// like concatenating ill-formed UTF-16 strings effectively would. #[inline] pub fn push_wtf8(&mut self, other: &Wtf8) { - match ((&*self).final_lead_surrogate(), other.initial_trail_surrogate()) { + match ((*self).final_lead_surrogate(), other.initial_trail_surrogate()) { // Replace newly paired surrogates by a supplementary code point. (Some(lead), Some(trail)) => { let len_without_lead_surrogate = self.len() - 3; @@ -322,7 +322,7 @@ impl Wtf8Buf { #[inline] pub fn push(&mut self, code_point: CodePoint) { if let Some(trail) = code_point.to_trail_surrogate() { - if let Some(lead) = (&*self).final_lead_surrogate() { + if let Some(lead) = (*self).final_lead_surrogate() { let len_without_lead_surrogate = self.len() - 3; self.bytes.truncate(len_without_lead_surrogate); self.push_char(decode_surrogate_pair(lead, trail)); diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index d3318b0863e6e..ae25e09230faa 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -682,7 +682,7 @@ impl PartialEq<&Self> for CStr { impl PartialOrd for CStr { #[inline] fn partial_cmp(&self, other: &CStr) -> Option { - self.to_bytes().partial_cmp(&other.to_bytes()) + self.to_bytes().partial_cmp(other.to_bytes()) } } @@ -690,7 +690,7 @@ impl PartialOrd for CStr { impl Ord for CStr { #[inline] fn cmp(&self, other: &CStr) -> Ordering { - self.to_bytes().cmp(&other.to_bytes()) + self.to_bytes().cmp(other.to_bytes()) } } diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 3867a44099f6d..e3fadd2363911 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -4082,7 +4082,7 @@ pub const trait Iterator { mut compare: impl FnMut(&T, &T) -> bool + 'a, ) -> impl FnMut(T) -> bool + 'a { move |curr| { - if !compare(&last, &curr) { + if !compare(last, &curr) { return false; } *last = curr; diff --git a/library/core/src/slice/ascii.rs b/library/core/src/slice/ascii.rs index bc99290a38dfc..2b6037b2ee53e 100644 --- a/library/core/src/slice/ascii.rs +++ b/library/core/src/slice/ascii.rs @@ -91,7 +91,7 @@ impl [u8] { let mut b = other; while let ([first_a, rest_a @ ..], [first_b, rest_b @ ..]) = (a, b) { - if first_a.eq_ignore_ascii_case(&first_b) { + if first_a.eq_ignore_ascii_case(first_b) { a = rest_a; b = rest_b; } else { diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index 6f75808015e71..a054c9d742c88 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -424,7 +424,7 @@ impl<'a, T: 'a, P: FnMut(&T) -> bool> Split<'a, T, P> { /// ``` #[unstable(feature = "split_as_slice", issue = "96137")] pub fn as_slice(&self) -> &'a [T] { - if self.finished { &[] } else { &self.v } + if self.finished { &[] } else { self.v } } } diff --git a/library/core/src/str/iter.rs b/library/core/src/str/iter.rs index 70d9c7aef2a74..26c48d48d211e 100644 --- a/library/core/src/str/iter.rs +++ b/library/core/src/str/iter.rs @@ -1410,7 +1410,7 @@ impl<'a> SplitAsciiWhitespace<'a> { } // SAFETY: Slice is created from str. - Some(unsafe { crate::str::from_utf8_unchecked(&self.inner.iter.iter.v) }) + Some(unsafe { crate::str::from_utf8_unchecked(self.inner.iter.iter.v) }) } } diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 63b7691582a7d..473b185d24652 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -245,14 +245,14 @@ impl<'a> Context<'a> { #[stable(feature = "futures_api", since = "1.36.0")] #[rustc_const_stable(feature = "const_waker", since = "1.82.0")] pub const fn waker(&self) -> &'a Waker { - &self.waker + self.waker } /// Returns a reference to the [`LocalWaker`] for the current task. #[inline] #[unstable(feature = "local_waker", issue = "118959")] pub const fn local_waker(&self) -> &'a LocalWaker { - &self.local_waker + self.local_waker } /// Returns a reference to the extension data for the current task. diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 73fb3f54097fa..937bf0f119d3d 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -762,7 +762,7 @@ impl Eq for OsString {} impl PartialOrd for OsString { #[inline] fn partial_cmp(&self, other: &OsString) -> Option { - (&**self).partial_cmp(&**other) + (**self).partial_cmp(&**other) } #[inline] fn lt(&self, other: &OsString) -> bool { @@ -786,7 +786,7 @@ impl PartialOrd for OsString { impl PartialOrd for OsString { #[inline] fn partial_cmp(&self, other: &str) -> Option { - (&**self).partial_cmp(other) + (**self).partial_cmp(other) } } @@ -794,7 +794,7 @@ impl PartialOrd for OsString { impl Ord for OsString { #[inline] fn cmp(&self, other: &OsString) -> cmp::Ordering { - (&**self).cmp(&**other) + (**self).cmp(&**other) } } @@ -802,7 +802,7 @@ impl Ord for OsString { impl Hash for OsString { #[inline] fn hash(&self, state: &mut H) { - (&**self).hash(state) + (**self).hash(state) } } @@ -1777,7 +1777,7 @@ impl AsRef for str { impl AsRef for String { #[inline] fn as_ref(&self) -> &OsStr { - (&**self).as_ref() + (**self).as_ref() } } diff --git a/library/std/src/net/socket_addr.rs b/library/std/src/net/socket_addr.rs index 2dab8c26f1f6b..6aa625d66a363 100644 --- a/library/std/src/net/socket_addr.rs +++ b/library/std/src/net/socket_addr.rs @@ -257,6 +257,6 @@ impl ToSocketAddrs for &T { impl ToSocketAddrs for String { type Iter = vec::IntoIter; fn to_socket_addrs(&self) -> io::Result> { - (&**self).to_socket_addrs() + (**self).to_socket_addrs() } } diff --git a/library/std/src/os/unix/net/ancillary.rs b/library/std/src/os/unix/net/ancillary.rs index a9029f7fa0bfb..5d2cbd403ad20 100644 --- a/library/std/src/os/unix/net/ancillary.rs +++ b/library/std/src/os/unix/net/ancillary.rs @@ -744,7 +744,7 @@ impl<'a> SocketAncillary<'a> { pub fn add_fds(&mut self, fds: &[RawFd]) -> bool { self.truncated = false; add_to_ancillary_data( - &mut self.buffer, + self.buffer, &mut self.length, fds, libc::SOL_SOCKET, @@ -771,7 +771,7 @@ impl<'a> SocketAncillary<'a> { pub fn add_creds(&mut self, creds: &[SocketCred]) -> bool { self.truncated = false; add_to_ancillary_data( - &mut self.buffer, + self.buffer, &mut self.length, creds, libc::SOL_SOCKET, diff --git a/library/std/src/path.rs b/library/std/src/path.rs index be216d87f3241..8b41a3792ac9a 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -2673,7 +2673,7 @@ impl Path { #[stable(feature = "path_ancestors", since = "1.28.0")] #[inline] pub fn ancestors(&self) -> Ancestors<'_> { - Ancestors { next: Some(&self) } + Ancestors { next: Some(self) } } /// Returns the final component of the `Path`, if there is one. diff --git a/library/std/src/sync/nonpoison/rwlock.rs b/library/std/src/sync/nonpoison/rwlock.rs index dc5d9479ba5a9..19064fdd1ce10 100644 --- a/library/std/src/sync/nonpoison/rwlock.rs +++ b/library/std/src/sync/nonpoison/rwlock.rs @@ -636,7 +636,7 @@ impl<'rwlock, T: ?Sized> RwLockReadGuard<'rwlock, T> { // reference passed to it. If the closure panics, the guard will be dropped. let data = NonNull::from(f(unsafe { orig.data.as_ref() })); let orig = ManuallyDrop::new(orig); - MappedRwLockReadGuard { data, inner_lock: &orig.inner_lock } + MappedRwLockReadGuard { data, inner_lock: orig.inner_lock } } /// Makes a [`MappedRwLockReadGuard`] for a component of the borrowed data. The @@ -668,7 +668,7 @@ impl<'rwlock, T: ?Sized> RwLockReadGuard<'rwlock, T> { Some(data) => { let data = NonNull::from(data); let orig = ManuallyDrop::new(orig); - Ok(MappedRwLockReadGuard { data, inner_lock: &orig.inner_lock }) + Ok(MappedRwLockReadGuard { data, inner_lock: orig.inner_lock }) } None => Err(orig), } @@ -861,7 +861,7 @@ impl<'rwlock, T: ?Sized> MappedRwLockReadGuard<'rwlock, T> { // reference passed to it. If the closure panics, the guard will be dropped. let data = NonNull::from(f(unsafe { orig.data.as_ref() })); let orig = ManuallyDrop::new(orig); - MappedRwLockReadGuard { data, inner_lock: &orig.inner_lock } + MappedRwLockReadGuard { data, inner_lock: orig.inner_lock } } /// Makes a [`MappedRwLockReadGuard`] for a component of the borrowed data. @@ -893,7 +893,7 @@ impl<'rwlock, T: ?Sized> MappedRwLockReadGuard<'rwlock, T> { Some(data) => { let data = NonNull::from(data); let orig = ManuallyDrop::new(orig); - Ok(MappedRwLockReadGuard { data, inner_lock: &orig.inner_lock }) + Ok(MappedRwLockReadGuard { data, inner_lock: orig.inner_lock }) } None => Err(orig), } diff --git a/library/std/src/sync/poison/rwlock.rs b/library/std/src/sync/poison/rwlock.rs index 4cfd9d19df74a..de1fedf88f63a 100644 --- a/library/std/src/sync/poison/rwlock.rs +++ b/library/std/src/sync/poison/rwlock.rs @@ -770,7 +770,7 @@ impl<'rwlock, T: ?Sized> RwLockReadGuard<'rwlock, T> { // reference passed to it. If the closure panics, the guard will be dropped. let data = NonNull::from(f(unsafe { orig.data.as_ref() })); let orig = ManuallyDrop::new(orig); - MappedRwLockReadGuard { data, inner_lock: &orig.inner_lock } + MappedRwLockReadGuard { data, inner_lock: orig.inner_lock } } /// Makes a [`MappedRwLockReadGuard`] for a component of the borrowed data. The @@ -802,7 +802,7 @@ impl<'rwlock, T: ?Sized> RwLockReadGuard<'rwlock, T> { Some(data) => { let data = NonNull::from(data); let orig = ManuallyDrop::new(orig); - Ok(MappedRwLockReadGuard { data, inner_lock: &orig.inner_lock }) + Ok(MappedRwLockReadGuard { data, inner_lock: orig.inner_lock }) } None => Err(orig), } @@ -996,7 +996,7 @@ impl<'rwlock, T: ?Sized> MappedRwLockReadGuard<'rwlock, T> { // reference passed to it. If the closure panics, the guard will be dropped. let data = NonNull::from(f(unsafe { orig.data.as_ref() })); let orig = ManuallyDrop::new(orig); - MappedRwLockReadGuard { data, inner_lock: &orig.inner_lock } + MappedRwLockReadGuard { data, inner_lock: orig.inner_lock } } /// Makes a [`MappedRwLockReadGuard`] for a component of the borrowed data. @@ -1028,7 +1028,7 @@ impl<'rwlock, T: ?Sized> MappedRwLockReadGuard<'rwlock, T> { Some(data) => { let data = NonNull::from(data); let orig = ManuallyDrop::new(orig); - Ok(MappedRwLockReadGuard { data, inner_lock: &orig.inner_lock }) + Ok(MappedRwLockReadGuard { data, inner_lock: orig.inner_lock }) } None => Err(orig), } diff --git a/library/std/src/sys/fs/common.rs b/library/std/src/sys/fs/common.rs index 68aed39d1dcdf..edc31d21ca1fa 100644 --- a/library/std/src/sys/fs/common.rs +++ b/library/std/src/sys/fs/common.rs @@ -72,7 +72,7 @@ impl Dir { } pub fn open_file(&self, path: &Path, opts: &OpenOptions) -> io::Result { - File::open(&self.path.join(path), &opts) + File::open(&self.path.join(path), opts) } pub fn metadata(&self) -> io::Result { diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index cdd1ef6146fd9..393a4d13b603b 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -2415,7 +2415,7 @@ mod remove_dir_impl { fn remove_dir_all_recursive(parent_fd: Option, path: &CStr) -> io::Result<()> { // try opening as directory - let fd = match openat_nofollow_dironly(parent_fd, &path) { + let fd = match openat_nofollow_dironly(parent_fd, path) { Err(err) if matches!(err.raw_os_error(), Some(libc::ENOTDIR | libc::ELOOP)) => { // not a directory - don't traverse further // (for symlinks, older Linux kernels may return ELOOP instead of ENOTDIR) @@ -2485,7 +2485,7 @@ mod remove_dir_impl { if attr.file_type().is_symlink() { super::unlink(p) } else { - remove_dir_all_recursive(None, &p) + remove_dir_all_recursive(None, p) } } diff --git a/library/std/src/sys/fs/unix/dir.rs b/library/std/src/sys/fs/unix/dir.rs index f3f612a225ed1..13a17350ff7b9 100644 --- a/library/std/src/sys/fs/unix/dir.rs +++ b/library/std/src/sys/fs/unix/dir.rs @@ -38,7 +38,7 @@ impl Dir { } pub fn open_file(&self, path: &Path, opts: &OpenOptions) -> io::Result { - run_path_with_cstr(path.as_ref(), &|path| self.open_file_c(path, &opts)) + run_path_with_cstr(path.as_ref(), &|path| self.open_file_c(path, opts)) } pub fn metadata(&self) -> io::Result { diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs index ef76a038c1fb5..9023d1f1c8338 100644 --- a/library/std/src/sys/fs/windows.rs +++ b/library/std/src/sys/fs/windows.rs @@ -339,7 +339,7 @@ impl File { let path = maybe_verbatim(path)?; // SAFETY: maybe_verbatim returns null-terminated strings let path = unsafe { WCStr::from_wchars_with_null_unchecked(&path) }; - Self::open_native(&path, opts) + Self::open_native(path, opts) } fn open_native(path: &WCStr, opts: &OpenOptions) -> io::Result { @@ -1305,7 +1305,7 @@ pub fn unlink(path: &WCStr) -> io::Result<()> { let mut opts = OpenOptions::new(); opts.access_mode(c::DELETE); opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT); - if let Ok(f) = File::open_native(&path, &opts) { + if let Ok(f) = File::open_native(path, &opts) { if f.posix_delete().is_ok() { return Ok(()); } @@ -1328,7 +1328,7 @@ pub fn rename(old: &WCStr, new: &WCStr) -> io::Result<()> { let mut opts = OpenOptions::new(); opts.access_mode(c::DELETE); opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS); - let Ok(f) = File::open_native(&old, &opts) else { return Err(err).io_result() }; + let Ok(f) = File::open_native(old, &opts) else { return Err(err).io_result() }; // Calculate the layout of the `FILE_RENAME_INFO` we pass to `SetFileInformation` // This is a dynamically sized struct so we need to get the position of the last field to calculate the actual size. @@ -1419,7 +1419,7 @@ pub fn readlink(path: &WCStr) -> io::Result { let mut opts = OpenOptions::new(); opts.access_mode(0); opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS); - let file = File::open_native(&path, &opts)?; + let file = File::open_native(path, &opts)?; file.readlink() } @@ -1506,7 +1506,7 @@ fn metadata(path: &WCStr, reparse: ReparsePoint) -> io::Result { // Attempt to open the file normally. // If that fails with `ERROR_SHARING_VIOLATION` then retry using `FindFirstFileExW`. // If the fallback fails for any reason we return the original error. - match File::open_native(&path, &opts) { + match File::open_native(path, &opts) { Ok(file) => file.file_attr(), Err(e) if [Some(c::ERROR_SHARING_VIOLATION as _), Some(c::ERROR_ACCESS_DENIED as _)] diff --git a/library/std/src/sys/process/unix/common.rs b/library/std/src/sys/process/unix/common.rs index 2e32770e90e77..a67c14b58faf1 100644 --- a/library/std/src/sys/process/unix/common.rs +++ b/library/std/src/sys/process/unix/common.rs @@ -218,7 +218,7 @@ impl Command { pub fn chroot(&mut self, dir: &Path) { self.chroot = Some(os2c(dir.as_os_str(), &mut self.saw_nul)); if self.cwd.is_none() { - self.cwd(&OsStr::new("/")); + self.cwd(OsStr::new("/")); } } pub fn setsid(&mut self, setsid: bool) { From ddd8fa08135bb6d006fcc7e3f2c5bd4be849c426 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Wed, 12 Aug 2026 14:16:16 +0000 Subject: [PATCH 14/66] clippy::op_ref --- library/core/src/bstr/traits.rs | 2 +- library/std/src/ffi/os_str.rs | 10 +++++----- library/std/src/sys/path/windows.rs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/library/core/src/bstr/traits.rs b/library/core/src/bstr/traits.rs index bcfffd52d7419..1d8d0e29e9a5a 100644 --- a/library/core/src/bstr/traits.rs +++ b/library/core/src/bstr/traits.rs @@ -25,7 +25,7 @@ impl PartialOrd for ByteStr { impl PartialEq for ByteStr { #[inline] fn eq(&self, other: &ByteStr) -> bool { - &self.0 == &other.0 + self.0 == other.0 } } diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 937bf0f119d3d..27039f0d3d2eb 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -719,7 +719,7 @@ impl fmt::Debug for OsString { impl PartialEq for OsString { #[inline] fn eq(&self, other: &OsString) -> bool { - &**self == &**other + **self == **other } } @@ -766,19 +766,19 @@ impl PartialOrd for OsString { } #[inline] fn lt(&self, other: &OsString) -> bool { - &**self < &**other + **self < **other } #[inline] fn le(&self, other: &OsString) -> bool { - &**self <= &**other + **self <= **other } #[inline] fn gt(&self, other: &OsString) -> bool { - &**self > &**other + **self > **other } #[inline] fn ge(&self, other: &OsString) -> bool { - &**self >= &**other + **self >= **other } } diff --git a/library/std/src/sys/path/windows.rs b/library/std/src/sys/path/windows.rs index 1c7bf50d1907f..2dbe33e2cf8b0 100644 --- a/library/std/src/sys/path/windows.rs +++ b/library/std/src/sys/path/windows.rs @@ -251,6 +251,6 @@ pub(crate) fn is_absolute_exact(path: &[u16]) -> bool { unsafe { new_path.set_len((result as usize) + 1); } - path == &new_path + path == new_path } } From fafcb2be486f2c24bbeb8417e5d32335df5d9a34 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Thu, 13 Aug 2026 01:51:53 +0000 Subject: [PATCH 15/66] clippy::borrow_deref_ref --- library/core/src/cell.rs | 2 +- library/core/src/fmt/mod.rs | 2 +- library/core/src/mem/maybe_uninit.rs | 2 +- library/core/src/pin.rs | 2 +- library/std/src/fs.rs | 4 ++-- library/std/src/io/stdio.rs | 4 ++-- library/std/src/os/unix/net/stream.rs | 4 ++-- library/std/src/process.rs | 2 +- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 2dc2c5981cafd..e8cd3a500084a 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -704,7 +704,7 @@ impl AsRef<[Cell; N]> for Cell<[T; N]> { impl AsRef<[Cell]> for Cell<[T; N]> { #[inline] fn as_ref(&self) -> &[Cell] { - &*self.as_array_of_cells() + self.as_array_of_cells() } } diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 47886aa7165d9..6a4c58afc16f3 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -3189,7 +3189,7 @@ impl Debug for Ref<'_, T> { #[stable(feature = "rust1", since = "1.0.0")] impl Debug for RefMut<'_, T> { fn fmt(&self, f: &mut Formatter<'_>) -> Result { - Debug::fmt(&*(self.deref()), f) + Debug::fmt(self.deref(), f) } } diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 6275d7cd59a2c..94703940baa1f 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -1639,7 +1639,7 @@ impl AsRef<[MaybeUninit; N]> for MaybeUninit<[T; N]> { impl AsRef<[MaybeUninit]> for MaybeUninit<[T; N]> { #[inline] fn as_ref(&self) -> &[MaybeUninit] { - &*AsRef::<[MaybeUninit; N]>::as_ref(self) + AsRef::<[MaybeUninit; N]>::as_ref(self) } } diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 52a84082f3b92..58e63ff04af15 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -1540,7 +1540,7 @@ impl<'a, T: ?Sized> Pin<&'a T> { U: ?Sized, F: FnOnce(&T) -> &U, { - let pointer = &*self.pointer; + let pointer = self.pointer; let new_pointer = func(pointer); // SAFETY: the safety contract for `new_unchecked` must be diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 58874a27f8ae5..61e62e2064952 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -1497,7 +1497,7 @@ impl Read for File { } #[inline] fn is_read_vectored(&self) -> bool { - (&&*self).is_read_vectored() + (&self).is_read_vectored() } fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { (&*self).read_to_end(buf) @@ -1516,7 +1516,7 @@ impl Write for File { } #[inline] fn is_write_vectored(&self) -> bool { - (&&*self).is_write_vectored() + (&self).is_write_vectored() } #[inline] fn flush(&mut self) -> io::Result<()> { diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 957235f9f3fb0..b104ea69cd1fc 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -796,7 +796,7 @@ impl Write for Stdout { } #[inline] fn is_write_vectored(&self) -> bool { - io::Write::is_write_vectored(&&*self) + io::Write::is_write_vectored(&self) } fn flush(&mut self) -> io::Result<()> { (&*self).flush() @@ -1028,7 +1028,7 @@ impl Write for Stderr { } #[inline] fn is_write_vectored(&self) -> bool { - io::Write::is_write_vectored(&&*self) + io::Write::is_write_vectored(&self) } fn flush(&mut self) -> io::Result<()> { (&*self).flush() diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index 8567e2fbb783d..9a17f9e0b8b9b 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -642,7 +642,7 @@ impl io::Read for UnixStream { #[inline] fn is_read_vectored(&self) -> bool { - io::Read::is_read_vectored(&&*self) + io::Read::is_read_vectored(&self) } } @@ -678,7 +678,7 @@ impl io::Write for UnixStream { #[inline] fn is_write_vectored(&self) -> bool { - io::Write::is_write_vectored(&&*self) + io::Write::is_write_vectored(&self) } fn flush(&mut self) -> io::Result<()> { diff --git a/library/std/src/process.rs b/library/std/src/process.rs index a398363cf4bf9..59480aa79fe9e 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -333,7 +333,7 @@ impl Write for ChildStdin { } fn is_write_vectored(&self) -> bool { - io::Write::is_write_vectored(&&*self) + io::Write::is_write_vectored(&self) } #[inline] From d9d6acfad73e941bcf08fbbb278e3c77903f6c31 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Thu, 13 Aug 2026 02:01:47 +0000 Subject: [PATCH 16/66] clippy::explicit_auto_deref --- library/alloc/src/borrow.rs | 2 +- library/alloc/src/boxed.rs | 12 ++++++------ library/alloc/src/io/impls.rs | 4 ++-- library/alloc/src/rc.rs | 12 ++++++------ library/alloc/src/sync.rs | 14 +++++++------- library/alloc/src/vec/mod.rs | 2 +- library/core/src/clone.rs | 2 +- library/core/src/mem/drop_guard.rs | 4 ++-- library/core/src/str/pattern.rs | 2 +- library/std/src/os/unix/net/ancillary.rs | 8 ++++---- library/std/src/sync/lazy_lock.rs | 2 +- 11 files changed, 32 insertions(+), 32 deletions(-) diff --git a/library/alloc/src/borrow.rs b/library/alloc/src/borrow.rs index d1c7cd47da0f0..b6a7a1eae2a70 100644 --- a/library/alloc/src/borrow.rs +++ b/library/alloc/src/borrow.rs @@ -188,7 +188,7 @@ impl<'a, B: ?Sized + ToOwned> Borrow for Cow<'a, B> // B::Owned: [const] Borrow, { fn borrow(&self) -> &B { - &**self + self } } diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index c25a06968e293..8c790d1105050 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -2293,14 +2293,14 @@ impl Deref for Box { type Target = T; fn deref(&self) -> &T { - &**self + self } } #[stable(feature = "rust1", since = "1.0.0")] impl DerefMut for Box { fn deref_mut(&mut self) -> &mut T { - &mut **self + self } } @@ -2396,28 +2396,28 @@ impl, U: ?Sized> DispatchFromDyn> for Box Borrow for Box { fn borrow(&self) -> &T { - &**self + self } } #[stable(feature = "box_borrow", since = "1.1.0")] impl BorrowMut for Box { fn borrow_mut(&mut self) -> &mut T { - &mut **self + self } } #[stable(since = "1.5.0", feature = "smart_ptr_as_ref")] impl AsRef for Box { fn as_ref(&self) -> &T { - &**self + self } } #[stable(since = "1.5.0", feature = "smart_ptr_as_ref")] impl AsMut for Box { fn as_mut(&mut self) -> &mut T { - &mut **self + self } } diff --git a/library/alloc/src/io/impls.rs b/library/alloc/src/io/impls.rs index dae6b3aa3371d..0296cada74171 100644 --- a/library/alloc/src/io/impls.rs +++ b/library/alloc/src/io/impls.rs @@ -327,7 +327,7 @@ impl Read for &[u8] { fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_, u8>) -> io::Result<()> { if cursor.capacity() > self.len() { // Append everything we can to the cursor. - cursor.append(*self); + cursor.append(self); *self = &self[self.len()..]; return Err(io::Error::READ_EXACT_EOF); } @@ -349,7 +349,7 @@ impl Read for &[u8] { buf.try_extend_from_slice_of_bytes(*self)?; } _ => { - buf.extend_from_slice(*self); + buf.extend_from_slice(self); } } diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 38ba8d64f900e..73dabb3b02b60 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -3858,14 +3858,14 @@ impl<'a> RcInnerPtr for WeakInner<'a> { #[stable(feature = "rust1", since = "1.0.0")] impl borrow::Borrow for Rc { fn borrow(&self) -> &T { - &**self + self } } #[stable(since = "1.5.0", feature = "smart_ptr_as_ref")] impl AsRef for Rc { fn as_ref(&self) -> &T { - &**self + self } } @@ -3990,28 +3990,28 @@ impl fmt::Pointer for UniqueRc { #[unstable(feature = "unique_rc_arc", issue = "112566")] impl borrow::Borrow for UniqueRc { fn borrow(&self) -> &T { - &**self + self } } #[unstable(feature = "unique_rc_arc", issue = "112566")] impl borrow::BorrowMut for UniqueRc { fn borrow_mut(&mut self) -> &mut T { - &mut **self + self } } #[unstable(feature = "unique_rc_arc", issue = "112566")] impl AsRef for UniqueRc { fn as_ref(&self) -> &T { - &**self + self } } #[unstable(feature = "unique_rc_arc", issue = "112566")] impl AsMut for UniqueRc { fn as_mut(&mut self) -> &mut T { - &mut **self + self } } diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 5e1344c0994cb..a3356b423288f 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -3836,7 +3836,7 @@ impl Default for Arc { #[inline] fn default() -> Self { let arc: Arc<[u8]> = Default::default(); - debug_assert!(core::str::from_utf8(&*arc).is_ok()); + debug_assert!(core::str::from_utf8(&arc).is_ok()); let (ptr, alloc) = Arc::into_inner_with_allocator(arc); unsafe { Arc::from_ptr_in(ptr.as_ptr() as *mut ArcInner, alloc) } } @@ -4246,14 +4246,14 @@ impl> ToArcSlice for I { #[stable(feature = "rust1", since = "1.0.0")] impl borrow::Borrow for Arc { fn borrow(&self) -> &T { - &**self + self } } #[stable(since = "1.5.0", feature = "smart_ptr_as_ref")] impl AsRef for Arc { fn as_ref(&self) -> &T { - &**self + self } } @@ -4463,28 +4463,28 @@ impl fmt::Pointer for UniqueArc { #[unstable(feature = "unique_rc_arc", issue = "112566")] impl borrow::Borrow for UniqueArc { fn borrow(&self) -> &T { - &**self + self } } #[unstable(feature = "unique_rc_arc", issue = "112566")] impl borrow::BorrowMut for UniqueArc { fn borrow_mut(&mut self) -> &mut T { - &mut **self + self } } #[unstable(feature = "unique_rc_arc", issue = "112566")] impl AsRef for UniqueArc { fn as_ref(&self) -> &T { - &**self + self } } #[unstable(feature = "unique_rc_arc", issue = "112566")] impl AsMut for UniqueArc { fn as_mut(&mut self) -> &mut T { - &mut **self + self } } diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index a619aa6e5427b..898ed0378bb80 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -3875,7 +3875,7 @@ impl Clone for Vec { /// capacity of the original. fn clone(&self) -> Self { let alloc = self.allocator().clone(); - <[T]>::to_vec_in(&**self, alloc) + <[T]>::to_vec_in(self, alloc) } /// Overwrites the contents of `self` with a clone of the contents of `source`. diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index 2996c753faea4..f124b8bceaded 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -781,7 +781,7 @@ mod impls { #[inline(always)] #[rustc_diagnostic_item = "noop_method_clone"] fn clone(&self) -> Self { - *self + self } } diff --git a/library/core/src/mem/drop_guard.rs b/library/core/src/mem/drop_guard.rs index 70658f0efb242..8e6655f785466 100644 --- a/library/core/src/mem/drop_guard.rs +++ b/library/core/src/mem/drop_guard.rs @@ -116,7 +116,7 @@ where type Target = T; fn deref(&self) -> &T { - &*self.inner + &self.inner } } @@ -127,7 +127,7 @@ where F: FnOnce(T), { fn deref_mut(&mut self) -> &mut T { - &mut *self.inner + &mut self.inner } } diff --git a/library/core/src/str/pattern.rs b/library/core/src/str/pattern.rs index 38006b638fdcd..e157ab588e701 100644 --- a/library/core/src/str/pattern.rs +++ b/library/core/src/str/pattern.rs @@ -1051,7 +1051,7 @@ impl<'b> Pattern for &'b str { #[inline] fn as_utf8_pattern(&self) -> Option> { - Some(Utf8Pattern::StringPattern(*self)) + Some(Utf8Pattern::StringPattern(self)) } } diff --git a/library/std/src/os/unix/net/ancillary.rs b/library/std/src/os/unix/net/ancillary.rs index 5d2cbd403ad20..bdf0384e34f80 100644 --- a/library/std/src/os/unix/net/ancillary.rs +++ b/library/std/src/os/unix/net/ancillary.rs @@ -506,12 +506,12 @@ impl<'a> AncillaryData<'a> { fn try_from_cmsghdr(cmsg: &'a libc::cmsghdr) -> Result { unsafe { let cmsg_len_zero = libc::CMSG_LEN(0) as usize; - let data_len = (*cmsg).cmsg_len as usize - cmsg_len_zero; + let data_len = cmsg.cmsg_len as usize - cmsg_len_zero; let data = libc::CMSG_DATA(cmsg).cast(); let data = from_raw_parts(data, data_len); - match (*cmsg).cmsg_level { - libc::SOL_SOCKET => match (*cmsg).cmsg_type { + match cmsg.cmsg_level { + libc::SOL_SOCKET => match cmsg.cmsg_type { libc::SCM_RIGHTS => Ok(AncillaryData::as_rights(data)), #[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))] libc::SCM_CREDENTIALS => Ok(AncillaryData::as_credentials(data)), @@ -524,7 +524,7 @@ impl<'a> AncillaryData<'a> { } }, cmsg_level => { - Err(AncillaryError::Unknown { cmsg_level, cmsg_type: (*cmsg).cmsg_type }) + Err(AncillaryError::Unknown { cmsg_level, cmsg_type: cmsg.cmsg_type }) } } } diff --git a/library/std/src/sync/lazy_lock.rs b/library/std/src/sync/lazy_lock.rs index 9bb25287275b2..f150d42a3137c 100644 --- a/library/std/src/sync/lazy_lock.rs +++ b/library/std/src/sync/lazy_lock.rs @@ -258,7 +258,7 @@ impl T> LazyLock { // * the closure was not called, but a previous call initialized `value`. // * the closure was not called because the Once is poisoned, which we handled above. // So `value` has definitely been initialized and will not be modified again. - unsafe { &*(*this.data.get()).value } + unsafe { &(*this.data.get()).value } } } From 5653e8f78d16dfe364de15d217d535c90bb3c4f5 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Thu, 13 Aug 2026 02:22:22 +0000 Subject: [PATCH 17/66] Library: enforce clippy deref lints in CI --- src/bootstrap/src/core/build_steps/clippy.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index 6f6c76d23a454..95a850e3984f2 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -588,6 +588,10 @@ impl CommandLineStep for CI { "clippy::ptr_offset_with_cast".into(), "clippy::let_and_return".into(), "clippy::needless_return".into(), + "clippy::needless_borrow".into(), + "clippy::op_ref".into(), + "clippy::borrow_deref_ref".into(), + "clippy::explicit_auto_deref".into(), ], forbid: vec![], }; From 7f74e595a84d917c0b8bcbf8e6ed64fac4c3ef4d Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Thu, 13 Aug 2026 03:22:53 +0000 Subject: [PATCH 18/66] Ignore clippy lint in backtrace submodule --- library/std/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 22ac7443f464b..d3bbb7c2353cd 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -740,7 +740,7 @@ mod panicking; #[path = "../../backtrace/src/lib.rs"] #[allow(dead_code, unused_attributes, implicit_provenance_casts, unsafe_op_in_unsafe_fn)] -#[allow(clippy::len_zero)] // FIXME +#[allow(clippy::len_zero, clippy::needless_borrow)] // FIXME mod backtrace_rs; #[stable(feature = "cfg_select", since = "1.95.0")] From d7526fc35cb4c0f971b2d408a88c83637a9480cb Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Thu, 13 Aug 2026 13:36:02 +0300 Subject: [PATCH 19/66] [Priroda] Exit cleanly on DAP TCP setup failures --- src/tools/miri/priroda/src/frontend/dap.rs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/tools/miri/priroda/src/frontend/dap.rs b/src/tools/miri/priroda/src/frontend/dap.rs index 744fd8675f8d0..ea87afe2406cc 100644 --- a/src/tools/miri/priroda/src/frontend/dap.rs +++ b/src/tools/miri/priroda/src/frontend/dap.rs @@ -101,11 +101,19 @@ impl DapSession, io::StdoutLock<'static>> { impl DapSession { fn tcp(port: u16) -> Self { - let listener = - TcpListener::bind(("127.0.0.1", port)).expect("failed to listen on DAP TCP socket"); + let listener = match TcpListener::bind(("127.0.0.1", port)) { + Ok(listener) => listener, + Err(err) => fatal(&format!("failed to listen on DAP TCP socket: {err}")), + }; eprintln!("priroda dap listening on 127.0.0.1:{port}"); - let (stream, _) = listener.accept().expect("failed to accept DAP TCP connection"); - let reader = stream.try_clone().expect("failed to clone DAP TCP stream"); + let (stream, _) = match listener.accept() { + Ok(conn) => conn, + Err(err) => fatal(&format!("failed to accept DAP TCP connection: {err}")), + }; + let reader = match stream.try_clone() { + Ok(clone) => clone, + Err(err) => fatal(&format!("failed to clone DAP TCP stream: {err}")), + }; Self { server: Server::new(BufReader::new(reader), BufWriter::new(stream)), @@ -114,6 +122,11 @@ impl DapSession { } } +fn fatal(message: &str) -> ! { + eprintln!("priroda dap: {message}"); + std::process::exit(1); +} + impl DapSession { fn run_requests<'tcx>( &mut self, From df2fe1f5f1eae0dbd3835b497edcb1445052f8a4 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Thu, 13 Aug 2026 13:37:05 +0300 Subject: [PATCH 20/66] [Priroda] Clarify VS Code debug type requirement --- src/tools/miri/priroda/README.md | 14 ++++++-------- src/tools/miri/priroda/vscode_launch.json | 7 ++++--- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/tools/miri/priroda/README.md b/src/tools/miri/priroda/README.md index 14ec0681163e7..810d02a785241 100644 --- a/src/tools/miri/priroda/README.md +++ b/src/tools/miri/priroda/README.md @@ -58,14 +58,12 @@ when you run the debugger configuration. The launch configuration does not spawn Priroda directly; it starts a background task and then connects through `debugServer`. -This requires a VS Code debug extension that contributes the `priroda` debugger -type. The `debugServer` setting only tells VS Code to connect to an -already-running adapter; it does not register a new debugger type. On a clean VS -Code install, copying these JSON files is not enough for the launch -configuration to be accepted. - -After that debugger type is registered, copy the example files into the -workspace you want to debug: +`debugServer` only tells VS Code to connect to an already-running adapter; it +does not register a debug type. VS Code still needs a registered `priroda` +debug type, which must come from a debug extension. A custom Priroda extension +is deferred to future graphical features. + +Copy the example files into the workspace you want to debug: ```sh mkdir -p /path/to/project/.vscode diff --git a/src/tools/miri/priroda/vscode_launch.json b/src/tools/miri/priroda/vscode_launch.json index c78ad524d9b3e..15141a4c47528 100644 --- a/src/tools/miri/priroda/vscode_launch.json +++ b/src/tools/miri/priroda/vscode_launch.json @@ -3,9 +3,10 @@ "configurations": [ { "name": "Priroda: Run and Attach", - // Requires a VS Code debug extension that contributes the "priroda" type. - // The debugServer field only tells VS Code to connect to an already-running - // adapter; it does not register a new debugger type by itself. + // Attaches to an already-running Priroda DAP server started by the + // preLaunchTask below. VS Code still needs a registered "priroda" debug + // type from a debug extension; a custom Priroda extension is deferred + // to future graphical features. "type": "priroda", "request": "launch", "preLaunchTask": "Priroda: Start DAP Server", From 0af72e44ee3949da9b857f5508fb9d19f0f19573 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Thu, 13 Aug 2026 13:37:20 +0300 Subject: [PATCH 21/66] [Priroda] Run the VS Code task through cargo --- src/tools/miri/priroda/README.md | 56 +++++++++--------------- src/tools/miri/priroda/vscode_tasks.json | 8 +++- 2 files changed, 26 insertions(+), 38 deletions(-) diff --git a/src/tools/miri/priroda/README.md b/src/tools/miri/priroda/README.md index 810d02a785241..5f49cabca68f0 100644 --- a/src/tools/miri/priroda/README.md +++ b/src/tools/miri/priroda/README.md @@ -63,60 +63,44 @@ does not register a debug type. VS Code still needs a registered `priroda` debug type, which must come from a debug extension. A custom Priroda extension is deferred to future graphical features. -Copy the example files into the workspace you want to debug: +The templates assume `${workspaceFolder}` is the `miri/priroda` directory that +contains them. Copy them into that directory's `.vscode/`, or edit +`--manifest-path` and the final `args` entry when using them from elsewhere: ```sh -mkdir -p /path/to/project/.vscode -cp vscode_launch.json /path/to/project/.vscode/launch.json -cp vscode_tasks.json /path/to/project/.vscode/tasks.json +mkdir -p /path/to/miri/priroda/.vscode +cp vscode_launch.json /path/to/miri/priroda/.vscode/launch.json +cp vscode_tasks.json /path/to/miri/priroda/.vscode/tasks.json ``` Before running the debugger configuration, make sure: -- Priroda has been built, so the binary path in `command` exists. - `MIRI_SYSROOT` points at a Miri sysroot, for example from `cargo +miri miri setup --print-sysroot`. -- If running the `priroda` binary directly, `LD_LIBRARY_PATH` may need to point - at the pinned `miri` toolchain's `lib` directory. +- The `cargo` in `command` resolves to the `miri` toolchain's cargo, so the + task builds Priroda with `rustc_private`. - The Rust file path at the end of `args` is the file you want Priroda to run. - Port `4711` is free, or both `--port` and `debugServer` use the same different port. - VS Code has a debugger contribution installed that accepts `type: "priroda"` debug configurations. -Then edit `.vscode/tasks.json` for your local paths. Set `command` to the -Priroda binary you want VS Code to run: +The task runs Priroda through `cargo run` against the Priroda crate: -```json -"command": "${workspaceFolder}/target/debug/priroda" -``` - -If the binary cannot find rustc libraries, add an `env` block under -`options`: - -```json -"options": { - "cwd": "${workspaceFolder}", - "env": { - "LD_LIBRARY_PATH": "/path/to/miri-toolchain/lib", - "MIRI_SYSROOT": "/path/to/miri-sysroot" - } -} -``` - -Also edit the final argument in `args` to point at the Rust file you want -Priroda to run. This task argument, not `launch.json`, selects the interpreted -program: - -```json -"${workspaceFolder}/src/main.rs" +```sh +cargo run --manifest-path /path/to/miri/priroda/Cargo.toml -- \ + --dap --port 4711 --sysroot "$MIRI_SYSROOT" /path/to/project/src/main.rs ``` -The task runs Priroda like this: +Edit the final argument in `args` to point at the Rust file you want Priroda to +run (the checked-in default is `../tests/pass/empty_main.rs`). This task +argument, not `launch.json`, selects the interpreted program. -```sh -cargo run -- --dap --port 4711 /path/to/project/src/main.rs -``` +Running through `cargo run` sets the dynamic library path automatically. The +`priroda` binary links rustc's shared libraries, so running it directly, or via +`cargo install`, still needs `LD_LIBRARY_PATH` to point at the pinned `miri` +toolchain's `lib` directory; a future packaging step (an rpath, or shipping +Priroda next to Miri) will remove that requirement. Once Priroda prints `priroda dap listening on 127.0.0.1:4711`, VS Code treats the background task as ready and connects with: diff --git a/src/tools/miri/priroda/vscode_tasks.json b/src/tools/miri/priroda/vscode_tasks.json index bfd0a04000716..9663bb3169bae 100644 --- a/src/tools/miri/priroda/vscode_tasks.json +++ b/src/tools/miri/priroda/vscode_tasks.json @@ -4,14 +4,18 @@ { "label": "Priroda: Start DAP Server", "type": "process", - "command": "${workspaceFolder}/target/debug/priroda", + "command": "cargo", "args": [ + "run", + "--manifest-path", + "${workspaceFolder}/Cargo.toml", + "--", "--dap", "--port", "4711", "--sysroot", "${env:MIRI_SYSROOT}", - "${workspaceFolder}/src/main.rs" + "../tests/pass/empty_main.rs" ], "isBackground": true, "options": { From 7bbe5d690f54caebdbaf4345e5fc8dfa35489c42 Mon Sep 17 00:00:00 2001 From: zakrad <49591476+zakrad@users.noreply.github.com> Date: Thu, 13 Aug 2026 02:12:01 +0330 Subject: [PATCH 22/66] Add regression test for static lifetime of borrowed array of Drop type in const --- ...omotion-drop-type-static-lifetime-86672.rs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/ui/consts/promotion-drop-type-static-lifetime-86672.rs diff --git a/tests/ui/consts/promotion-drop-type-static-lifetime-86672.rs b/tests/ui/consts/promotion-drop-type-static-lifetime-86672.rs new file mode 100644 index 0000000000000..5fc8d06708451 --- /dev/null +++ b/tests/ui/consts/promotion-drop-type-static-lifetime-86672.rs @@ -0,0 +1,23 @@ +// Regression test for . +// Borrowing an array of a Drop type in a const used to fail with E0493 and E0716 +// unless the borrow went through another const. +//@ check-pass + +#![allow(dead_code)] + +pub struct Foo<'a, B: ?Sized>(&'a B); + +struct Bar; +impl Drop for Bar { + fn drop(&mut self) {} +} + +// These always worked. +const BAR0: Bar = Bar; +const BAR1: &'static [Bar] = &[Bar]; +const BAR2: Foo<'static, [Bar]> = Foo(BAR1); +// These used to fail. +const BAR3: Foo<'static, [Bar]> = Foo(&[Bar]); +const BAR4: Foo<'static, [Bar]> = Foo(&[Bar] as &'static [Bar]); + +fn main() {} From 748a319ee14347ddb87aa7be35a51c3fcc3bbb0a Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 13 Aug 2026 15:05:15 +0200 Subject: [PATCH 23/66] Add extra checks to ensure GCC and binutils have needed support for the `retain` attribute --- src/ci/docker/scripts/build-gcc.sh | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/ci/docker/scripts/build-gcc.sh b/src/ci/docker/scripts/build-gcc.sh index bef169fafe38c..41bc3495e0b2e 100755 --- a/src/ci/docker/scripts/build-gcc.sh +++ b/src/ci/docker/scripts/build-gcc.sh @@ -11,6 +11,15 @@ hide_output ./configure hide_output make hide_output make install +if echo '.section .test,"awR",@progbits' | as - -o /dev/null 2>/dev/null; then + echo "binutils assembler supports SHF_GNU_RETAIN" +else + echo "binutils assembler DOES NOT support SHF_GNU_RETAIN" + exit 1 +fi + +cd .. + # Note: in the future when bumping to version 10.1.0, also take care of the sed block below. # This version is specified in the Dockerfile GCC=$GCC_VERSION @@ -54,6 +63,12 @@ hide_output ../gcc-$GCC/configure \ hide_output make -j$(nproc) hide_output make install ln -s gcc /rustroot/bin/cc +if echo 'int x __attribute__((used, retain));' | gcc -S -x c -o - - | grep -i '"a.*R"'; then + echo "retain attribute is supported" +else + echo "retain attribute is not supported" + exit 1 +fi cd .. rm -rf gcc-build From 487e0e0b00837f573c076cd238db9fa819b5309d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 13 Aug 2026 17:47:16 +0200 Subject: [PATCH 24/66] Display generated asm in case it failed --- src/ci/docker/scripts/build-gcc.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ci/docker/scripts/build-gcc.sh b/src/ci/docker/scripts/build-gcc.sh index 41bc3495e0b2e..6ac0514b6569f 100755 --- a/src/ci/docker/scripts/build-gcc.sh +++ b/src/ci/docker/scripts/build-gcc.sh @@ -67,6 +67,8 @@ if echo 'int x __attribute__((used, retain));' | gcc -S -x c -o - - | grep -i '" echo "retain attribute is supported" else echo "retain attribute is not supported" + # We display the generated asm just in case... + echo 'int x __attribute__((used, retain));' | gcc -S -x c -o - - exit 1 fi From 4c097cfeda80dd0fc8307f804f946106330cb18a Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 13 Aug 2026 21:56:22 +0200 Subject: [PATCH 25/66] Use `--prefix` to generate binaries in the right location --- src/ci/docker/scripts/build-gcc.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ci/docker/scripts/build-gcc.sh b/src/ci/docker/scripts/build-gcc.sh index 6ac0514b6569f..bb2c97b475ece 100755 --- a/src/ci/docker/scripts/build-gcc.sh +++ b/src/ci/docker/scripts/build-gcc.sh @@ -7,8 +7,8 @@ source shared.sh BINUTILS="2.47" curl https://ci-mirrors.rust-lang.org/rustc/gcc/binutils-$BINUTILS.tar.xz | xzcat | tar xf - cd binutils-$BINUTILS -hide_output ./configure -hide_output make +hide_output ./configure --prefix=/rustroot +hide_output make -j$(nproc) hide_output make install if echo '.section .test,"awR",@progbits' | as - -o /dev/null 2>/dev/null; then From 3c395a999b49ca4e1c2d54524951bacacc4997c4 Mon Sep 17 00:00:00 2001 From: zakrad <49591476+zakrad@users.noreply.github.com> Date: Fri, 14 Aug 2026 01:04:04 +0330 Subject: [PATCH 26/66] Add regression test for path printing with infinitely many visible names --- tests/ui/imports/auxiliary/pathloop.rs | 6 ++++++ .../path-with-infinite-visible-names-57500.rs | 12 ++++++++++++ .../path-with-infinite-visible-names-57500.stderr | 11 +++++++++++ 3 files changed, 29 insertions(+) create mode 100644 tests/ui/imports/auxiliary/pathloop.rs create mode 100644 tests/ui/imports/path-with-infinite-visible-names-57500.rs create mode 100644 tests/ui/imports/path-with-infinite-visible-names-57500.stderr diff --git a/tests/ui/imports/auxiliary/pathloop.rs b/tests/ui/imports/auxiliary/pathloop.rs new file mode 100644 index 0000000000000..f69f0e0c8c056 --- /dev/null +++ b/tests/ui/imports/auxiliary/pathloop.rs @@ -0,0 +1,6 @@ +pub struct AStruct; + +pub mod prelude { + pub use crate as pathloop; + pub use crate::AStruct; +} diff --git a/tests/ui/imports/path-with-infinite-visible-names-57500.rs b/tests/ui/imports/path-with-infinite-visible-names-57500.rs new file mode 100644 index 0000000000000..74a3bb1969b19 --- /dev/null +++ b/tests/ui/imports/path-with-infinite-visible-names-57500.rs @@ -0,0 +1,12 @@ +// Regression test for . +// An item reachable under infinitely many paths used to hang path printing +// while rendering this error. +//@ aux-build: pathloop.rs + +extern crate pathloop; + +use pathloop::prelude::*; + +fn main() { + let _x: AStruct = 42; //~ ERROR mismatched types +} diff --git a/tests/ui/imports/path-with-infinite-visible-names-57500.stderr b/tests/ui/imports/path-with-infinite-visible-names-57500.stderr new file mode 100644 index 0000000000000..2f7b1e6ad106c --- /dev/null +++ b/tests/ui/imports/path-with-infinite-visible-names-57500.stderr @@ -0,0 +1,11 @@ +error[E0308]: mismatched types + --> $DIR/path-with-infinite-visible-names-57500.rs:11:23 + | +LL | let _x: AStruct = 42; + | ------- ^^ expected `AStruct`, found integer + | | + | expected due to this + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. From 88cfd2278aac08e6f0a6098d3c3a0acd5ef4be08 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 14 Aug 2026 01:58:50 +0200 Subject: [PATCH 27/66] Use `--with-as` and `--with-ld` options for `gcc` configure --- src/ci/docker/scripts/build-gcc.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/ci/docker/scripts/build-gcc.sh b/src/ci/docker/scripts/build-gcc.sh index bb2c97b475ece..24598f62e05e2 100755 --- a/src/ci/docker/scripts/build-gcc.sh +++ b/src/ci/docker/scripts/build-gcc.sh @@ -11,13 +11,16 @@ hide_output ./configure --prefix=/rustroot hide_output make -j$(nproc) hide_output make install -if echo '.section .test,"awR",@progbits' | as - -o /dev/null 2>/dev/null; then +if echo '.section .test,"awR",@progbits' | /rustroot/bin/as - -o /dev/null 2>/dev/null; then echo "binutils assembler supports SHF_GNU_RETAIN" else echo "binutils assembler DOES NOT support SHF_GNU_RETAIN" exit 1 fi +AS_PATH="/rustroot/bin/as" +LD_PATH="/rustroot/bin/ld" + cd .. # Note: in the future when bumping to version 10.1.0, also take care of the sed block below. @@ -57,6 +60,8 @@ cd ../gcc-build # which is included in librustc_driver.so hide_output ../gcc-$GCC/configure \ --prefix=/rustroot \ + --with-as=$AS_PATH \ + --with-ld=$LD_PATH \ --enable-languages=c,c++ \ --disable-gnu-unique-object \ --enable-cxx-flags='-fno-reorder-blocks-and-partition' From bcc35a8e30a0061d07df3973fffbd7d8ffa85d2f Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 14 Aug 2026 14:22:38 +0200 Subject: [PATCH 28/66] More settings targetting binutils --- src/ci/docker/scripts/build-gcc.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ci/docker/scripts/build-gcc.sh b/src/ci/docker/scripts/build-gcc.sh index 24598f62e05e2..9a70071242d83 100755 --- a/src/ci/docker/scripts/build-gcc.sh +++ b/src/ci/docker/scripts/build-gcc.sh @@ -55,11 +55,13 @@ sed -i'' 's|ftp://gcc\.gnu\.org/pub/gcc/infrastructure|https://ci-mirrors.rust-l mkdir ../gcc-build cd ../gcc-build +export PATH=/rustroot/bin:$PATH # '-fno-reorder-blocks-and-partition' is required to # enable BOLT optimization of the C++ standard library, # which is included in librustc_driver.so hide_output ../gcc-$GCC/configure \ --prefix=/rustroot \ + --with-bintuils=/rustroot/bin \ --with-as=$AS_PATH \ --with-ld=$LD_PATH \ --enable-languages=c,c++ \ @@ -68,12 +70,13 @@ hide_output ../gcc-$GCC/configure \ hide_output make -j$(nproc) hide_output make install ln -s gcc /rustroot/bin/cc -if echo 'int x __attribute__((used, retain));' | gcc -S -x c -o - - | grep -i '"a.*R"'; then + +if echo 'int x __attribute__((used, retain));' | /rustroot/bin/gcc -S -x c -o - - | grep -i '"a.*R"'; then echo "retain attribute is supported" else echo "retain attribute is not supported" # We display the generated asm just in case... - echo 'int x __attribute__((used, retain));' | gcc -S -x c -o - - + echo 'int x __attribute__((used, retain));' | /rustroot/bin/gcc -S -x c -o - - exit 1 fi From 5b93f5472432b546a9a7e71e68a432e2229f9325 Mon Sep 17 00:00:00 2001 From: zakrad <49591476+zakrad@users.noreply.github.com> Date: Fri, 14 Aug 2026 11:53:11 +0330 Subject: [PATCH 29/66] Add regression test for unstable def_ident_span fingerprint with incremental recompilation --- .../macros/lib.rs | 73 +++++++++++++++++++ .../rmake.rs | 71 ++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 tests/run-make/incr-unstable-fingerprint-def-ident-span/macros/lib.rs create mode 100644 tests/run-make/incr-unstable-fingerprint-def-ident-span/rmake.rs diff --git a/tests/run-make/incr-unstable-fingerprint-def-ident-span/macros/lib.rs b/tests/run-make/incr-unstable-fingerprint-def-ident-span/macros/lib.rs new file mode 100644 index 0000000000000..5eee82170edb1 --- /dev/null +++ b/tests/run-make/incr-unstable-fingerprint-def-ident-span/macros/lib.rs @@ -0,0 +1,73 @@ +extern crate proc_macro; + +use proc_macro::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree}; + +// Re-emits each enum variant's identifier as an associated constant, reusing the +// original `Ident` tokens so the generated items keep the variants' spans. +#[proc_macro_derive(Bar)] +pub fn derive_bar(input: TokenStream) -> TokenStream { + let mut it = input.into_iter(); + let mut name: Option = None; + let mut body: Option = None; + + while let Some(tt) = it.next() { + match tt { + TokenTree::Ident(id) => { + if id.to_string() == "enum" { + if let Some(TokenTree::Ident(n)) = it.next() { + name = Some(n); + } + } + } + TokenTree::Group(g) => { + if g.delimiter() == Delimiter::Brace { + body = Some(g); + break; + } + } + _ => {} + } + } + + let name = name.expect("enum name"); + let body = body.expect("enum body"); + + // Collect variant idents (skip commas / discriminants). + let mut variants: Vec = Vec::new(); + let mut expect_ident = true; + for tt in body.stream() { + match tt { + TokenTree::Ident(id) => { + if expect_ident { + variants.push(id); + expect_ident = false; + } + } + TokenTree::Punct(p) => { + if p.as_char() == ',' { + expect_ident = true; + } + } + _ => {} + } + } + + // Build: impl Name { const V: () = (); ... } + let mut inner: Vec = Vec::new(); + for v in variants { + inner.push(TokenTree::Ident(Ident::new("const", Span::call_site()))); + inner.push(TokenTree::Ident(v)); // original ident + span + inner.push(TokenTree::Punct(Punct::new(':', Spacing::Alone))); + inner.push(TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new()))); + inner.push(TokenTree::Punct(Punct::new('=', Spacing::Alone))); + inner.push(TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new()))); + inner.push(TokenTree::Punct(Punct::new(';', Spacing::Alone))); + } + + let mut out: Vec = Vec::new(); + out.push(TokenTree::Ident(Ident::new("impl", Span::call_site()))); + out.push(TokenTree::Ident(name)); + out.push(TokenTree::Group(Group::new(Delimiter::Brace, inner.into_iter().collect()))); + + out.into_iter().collect() +} diff --git a/tests/run-make/incr-unstable-fingerprint-def-ident-span/rmake.rs b/tests/run-make/incr-unstable-fingerprint-def-ident-span/rmake.rs new file mode 100644 index 0000000000000..8be1525ad2991 --- /dev/null +++ b/tests/run-make/incr-unstable-fingerprint-def-ident-span/rmake.rs @@ -0,0 +1,71 @@ +//@ ignore-cross-compile +//@ needs-crate-type: proc-macro + +// Regression test for . +// Recompiling incrementally after inserting an enum variant before an existing one used +// to ICE with "Found unstable fingerprints for def_ident_span". The derive re-emits the +// variant identifiers as associated constants, so their spans move while the surrounding +// generated tokens keep call-site hygiene. +// +// This cannot use the `revisions` system: the `#[cfg]`-based revisions keep both versions +// of the text in the file, so the identifier spans never move. The source has to actually +// be rewritten between the two compilations. + +use std::fs; +use std::path::PathBuf; + +use run_make_support::{rfs, rustc}; + +fn main() { + rustc().input("macros/lib.rs").crate_name("macros").crate_type("proc-macro").run(); + let macros_dylib = find_proc_macro_dylib("macros"); + + rfs::write("lib.rs", "#[derive(macros::Bar)]\npub enum FooEnum { One }\n"); + rustc() + .input("lib.rs") + .crate_type("lib") + .incremental("incr") + .arg("-Zincremental-verify-ich") + .extern_("macros", ¯os_dylib) + .run(); + + // Insert a variant *before* the existing one, moving `One`'s span. + rfs::write("lib.rs", "#[derive(macros::Bar)]\npub enum FooEnum { Zero, One }\n"); + let out = rustc() + .input("lib.rs") + .crate_type("lib") + .incremental("incr") + .arg("-Zincremental-verify-ich") + .extern_("macros", ¯os_dylib) + .run(); + + out.assert_stderr_not_contains("internal compiler error"); + out.assert_stderr_not_contains("Found unstable fingerprints"); +} + +fn find_proc_macro_dylib(name: &str) -> PathBuf { + let prefix = if cfg!(target_os = "windows") { "" } else { "lib" }; + + let ext: &str = if cfg!(target_os = "macos") { + "dylib" + } else if cfg!(target_os = "windows") { + "dll" + } else if cfg!(target_os = "aix") { + "a" + } else { + "so" + }; + + let lib_name = format!("{prefix}{name}.{ext}"); + + for entry in fs::read_dir(".").unwrap() { + let entry = entry.unwrap(); + let name = entry.file_name(); + let name = name.to_str().unwrap(); + if name == lib_name { + return entry.path(); + } + } + + panic!("could not find proc-macro dylib for `{name}`"); +} From 9126d556628b04fd4b72443817d8d1aef9a8d372 Mon Sep 17 00:00:00 2001 From: Makai Date: Fri, 14 Aug 2026 23:54:11 +0800 Subject: [PATCH 30/66] cleanup: rip out unnecessary `iter().last()` and `iter().next()` --- compiler/rustc_ast_passes/src/ast_validation.rs | 2 +- compiler/rustc_hir_typeck/src/lib.rs | 2 +- compiler/rustc_parse/src/parser/pat.rs | 2 +- compiler/rustc_resolve/src/diagnostics/impls.rs | 6 ++---- compiler/rustc_resolve/src/late.rs | 5 ++--- 5 files changed, 7 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 3270fb940bab3..fb33f2823372b 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1180,7 +1180,7 @@ impl<'a> AstValidator<'a> { self.dcx().emit_err(diagnostics::ArgsBeforeConstraint { arg_spans: arg_spans.clone(), constraints: constraint_spans[0], - args: *arg_spans.iter().last().unwrap(), + args: *arg_spans.last().unwrap(), data: data.span, constraint_spans: diagnostics::EmptyLabelManySpans(constraint_spans), arg_spans2: diagnostics::EmptyLabelManySpans(arg_spans), diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 7a2670f3b1b78..7f38aeb5ac3fa 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -324,7 +324,7 @@ fn extend_err_with_const_context( { // `foo()`, point at the const parameter in the definition of `foo`. if let Some(i) = - path.segments.iter().last().and_then(|segment| segment.args).and_then(|args| { + path.segments.last().and_then(|segment| segment.args).and_then(|args| { args.args.iter().position(|arg| { matches!(arg, hir::GenericArg::Const(arg) if arg.hir_id == parent.hir_id) }) diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index d017a27e8f77f..266a2d134199c 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -1688,7 +1688,7 @@ impl<'a> Parser<'a> { /// If the user writes `S { ref field: name }` instead of `S { field: ref name }`, we suggest /// the correct code. fn recover_misplaced_pattern_modifiers(&self, fields: &ThinVec, err: &mut Diag<'a>) { - if let Some(last) = fields.iter().last() + if let Some(last) = fields.last() && last.is_shorthand && let PatKind::Ident(binding, ident, None) = last.pat.kind && binding != BindingMode::NONE diff --git a/compiler/rustc_resolve/src/diagnostics/impls.rs b/compiler/rustc_resolve/src/diagnostics/impls.rs index 9fa762c87ef3e..149f34cb6c35d 100644 --- a/compiler/rustc_resolve/src/diagnostics/impls.rs +++ b/compiler/rustc_resolve/src/diagnostics/impls.rs @@ -256,9 +256,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { for note in notes { diag.note(note); } - } else if let Some((_, UnresolvedImportError { note: Some(note), .. })) = - errors.iter().last() - { + } else if let Some((_, UnresolvedImportError { note: Some(note), .. })) = errors.last() { diag.note(note.clone()); } @@ -2876,7 +2874,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if struct_expr.fields.is_empty() { return; } - let last_span = struct_expr.fields.iter().last().unwrap().span; + let last_span = struct_expr.fields.last().unwrap().span; let mut iter = struct_expr.fields.iter().peekable(); let mut prev: Option = None; while let Some(field) = iter.next() { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index c21d3653a13be..543082a6af7fc 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -4092,7 +4092,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { v.could_be_path = false; } self.report_error( - v.origin.iter().next().unwrap().0, + v.origin.first().unwrap().0, ResolutionError::VariableNotBoundInPattern(v, self.parent_scope), ); } @@ -4757,8 +4757,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.resolve_path(&std_path, Some(ns), None, source) { // Check if we wrote `str::from_utf8` instead of `std::str::from_utf8` - let item_span = - path.iter().last().map_or(path_span, |segment| segment.ident.span); + let item_span = path.last().map_or(path_span, |segment| segment.ident.span); self.r.confused_type_with_std_module.insert(item_span, path_span); self.r.confused_type_with_std_module.insert(path_span, path_span); From 80cc60a7ea59a2edf33c7cbbc3c6cfa2a0bcb78b Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 14 Aug 2026 21:26:24 +0200 Subject: [PATCH 31/66] Correctly test the custom GCC built in CI --- .../host-x86_64/dist-x86_64-linux/dist.sh | 11 ++++++++ src/ci/docker/scripts/build-gcc.sh | 26 +++++-------------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh b/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh index 46d34cd001a95..0ccfd0d765946 100755 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh @@ -18,4 +18,15 @@ if [ "${DIST_TRY_BUILD:-0}" == "0" ]; then CC=/rustroot/bin/cc CXX=/rustroot/bin/c++ python3 ../x.py dist \ gcc-dev \ gcc + # We confirm that the built GCC has support for the `retain` attribute. + # FIXME: Maybe get the path from `.x.py` instead? + gcc_path="./build/$HOSTS/gcc/$HOSTS/install/bin/gcc" + if echo 'int x __attribute__((used, retain));' | "$gcc_path" -S -x c -o - - | grep -i '"a.*R"'; then + echo "retain attribute is supported" + else + echo "retain attribute is not supported" + # We display the generated asm just in case... + echo 'int x __attribute__((used, retain));' | "$gcc_path" -S -x c -o - - + exit 1 + fi fi diff --git a/src/ci/docker/scripts/build-gcc.sh b/src/ci/docker/scripts/build-gcc.sh index 9a70071242d83..29502ec56a767 100755 --- a/src/ci/docker/scripts/build-gcc.sh +++ b/src/ci/docker/scripts/build-gcc.sh @@ -6,22 +6,22 @@ source shared.sh BINUTILS="2.47" curl https://ci-mirrors.rust-lang.org/rustc/gcc/binutils-$BINUTILS.tar.xz | xzcat | tar xf - -cd binutils-$BINUTILS -hide_output ./configure --prefix=/rustroot +mkdir binutils-build +cd binutils-build +hide_output ../binutils-$BINUTILS/configure --prefix=/rustroot hide_output make -j$(nproc) hide_output make install -if echo '.section .test,"awR",@progbits' | /rustroot/bin/as - -o /dev/null 2>/dev/null; then +cd .. +rm -rf binutils-build binutils-$BINUTILS + +if echo '.section .test,"awR",@progbits' | as - -o /dev/null 2>/dev/null; then echo "binutils assembler supports SHF_GNU_RETAIN" else echo "binutils assembler DOES NOT support SHF_GNU_RETAIN" exit 1 fi -AS_PATH="/rustroot/bin/as" -LD_PATH="/rustroot/bin/ld" - -cd .. # Note: in the future when bumping to version 10.1.0, also take care of the sed block below. # This version is specified in the Dockerfile @@ -61,9 +61,6 @@ export PATH=/rustroot/bin:$PATH # which is included in librustc_driver.so hide_output ../gcc-$GCC/configure \ --prefix=/rustroot \ - --with-bintuils=/rustroot/bin \ - --with-as=$AS_PATH \ - --with-ld=$LD_PATH \ --enable-languages=c,c++ \ --disable-gnu-unique-object \ --enable-cxx-flags='-fno-reorder-blocks-and-partition' @@ -71,15 +68,6 @@ hide_output make -j$(nproc) hide_output make install ln -s gcc /rustroot/bin/cc -if echo 'int x __attribute__((used, retain));' | /rustroot/bin/gcc -S -x c -o - - | grep -i '"a.*R"'; then - echo "retain attribute is supported" -else - echo "retain attribute is not supported" - # We display the generated asm just in case... - echo 'int x __attribute__((used, retain));' | /rustroot/bin/gcc -S -x c -o - - - exit 1 -fi - cd .. rm -rf gcc-build rm -rf gcc-$GCC From 942fe31efedf5fee6f3d5dfdf21be2296704abab Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 14 Aug 2026 21:34:37 +0200 Subject: [PATCH 32/66] Fix tidy error --- src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh b/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh index 0ccfd0d765946..835fb6ee5e724 100755 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh @@ -21,12 +21,13 @@ if [ "${DIST_TRY_BUILD:-0}" == "0" ]; then # We confirm that the built GCC has support for the `retain` attribute. # FIXME: Maybe get the path from `.x.py` instead? gcc_path="./build/$HOSTS/gcc/$HOSTS/install/bin/gcc" - if echo 'int x __attribute__((used, retain));' | "$gcc_path" -S -x c -o - - | grep -i '"a.*R"'; then + c_code='int x __attribute__((used, retain));' + if echo "$c_code" | "$gcc_path" -S -x c -o - - | grep -i '"a.*R"'; then echo "retain attribute is supported" else echo "retain attribute is not supported" # We display the generated asm just in case... - echo 'int x __attribute__((used, retain));' | "$gcc_path" -S -x c -o - - + echo "$c_code" | "$gcc_path" -S -x c -o - - exit 1 fi fi From 78dd93d2a50757c527dee305bf96718b7aba0d84 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Sat, 15 Aug 2026 01:52:29 +0300 Subject: [PATCH 33/66] [Priroda] Add DAP attach startup fixtures Document the current startup behavior for clients that send `attach` after `initialize`. The VS Code debugServer template needs this path once it uses an editor-provided debug type with an attach request, so make the existing rejection visible before changing the adapter state machine. --- .../miri/priroda/tests/ui/dap/dap_initialize_attach.rs | 3 +++ .../priroda/tests/ui/dap/dap_initialize_attach.stdin | 5 +++++ .../priroda/tests/ui/dap/dap_initialize_attach.stdout | 7 +++++++ .../ui/dap/dap_initialize_attach_configuration_done.rs | 3 +++ .../dap/dap_initialize_attach_configuration_done.stdin | 7 +++++++ .../dap/dap_initialize_attach_configuration_done.stdout | 9 +++++++++ 6 files changed, 34 insertions(+) create mode 100644 src/tools/miri/priroda/tests/ui/dap/dap_initialize_attach.rs create mode 100644 src/tools/miri/priroda/tests/ui/dap/dap_initialize_attach.stdin create mode 100644 src/tools/miri/priroda/tests/ui/dap/dap_initialize_attach.stdout create mode 100644 src/tools/miri/priroda/tests/ui/dap/dap_initialize_attach_configuration_done.rs create mode 100644 src/tools/miri/priroda/tests/ui/dap/dap_initialize_attach_configuration_done.stdin create mode 100644 src/tools/miri/priroda/tests/ui/dap/dap_initialize_attach_configuration_done.stdout diff --git a/src/tools/miri/priroda/tests/ui/dap/dap_initialize_attach.rs b/src/tools/miri/priroda/tests/ui/dap/dap_initialize_attach.rs new file mode 100644 index 0000000000000..c1f1ed6f67bea --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/dap/dap_initialize_attach.rs @@ -0,0 +1,3 @@ +//@ compile-flags: --dap + +fn main() {} diff --git a/src/tools/miri/priroda/tests/ui/dap/dap_initialize_attach.stdin b/src/tools/miri/priroda/tests/ui/dap/dap_initialize_attach.stdin new file mode 100644 index 0000000000000..65bfc06876624 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/dap/dap_initialize_attach.stdin @@ -0,0 +1,5 @@ +Content-Length: 85 + +{"seq":1,"type":"request","command":"initialize","arguments":{"adapterID":"priroda"}}Content-Length: 60 + +{"seq":2,"type":"request","command":"attach","arguments":{}} \ No newline at end of file diff --git a/src/tools/miri/priroda/tests/ui/dap/dap_initialize_attach.stdout b/src/tools/miri/priroda/tests/ui/dap/dap_initialize_attach.stdout new file mode 100644 index 0000000000000..b750e3d3c7d54 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/dap/dap_initialize_attach.stdout @@ -0,0 +1,7 @@ +Content-Length: {CONTENT_LENGTH} + +{"seq":1,"type":"response","request_seq":1,"success":true,"command":"initialize","body":{"supportsConfigurationDoneRequest":true,"supportsSingleThreadExecutionRequests":true},"error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":2,"type":"event","event":"initialized"}Content-Length: {CONTENT_LENGTH} + +{"seq":3,"type":"response","request_seq":2,"success":false,"message":"unsupported request in Priroda DAP demo mode: attach","command":"attach","error":null} diff --git a/src/tools/miri/priroda/tests/ui/dap/dap_initialize_attach_configuration_done.rs b/src/tools/miri/priroda/tests/ui/dap/dap_initialize_attach_configuration_done.rs new file mode 100644 index 0000000000000..c1f1ed6f67bea --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/dap/dap_initialize_attach_configuration_done.rs @@ -0,0 +1,3 @@ +//@ compile-flags: --dap + +fn main() {} diff --git a/src/tools/miri/priroda/tests/ui/dap/dap_initialize_attach_configuration_done.stdin b/src/tools/miri/priroda/tests/ui/dap/dap_initialize_attach_configuration_done.stdin new file mode 100644 index 0000000000000..bd6f954820320 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/dap/dap_initialize_attach_configuration_done.stdin @@ -0,0 +1,7 @@ +Content-Length: 85 + +{"seq":1,"type":"request","command":"initialize","arguments":{"adapterID":"priroda"}}Content-Length: 60 + +{"seq":2,"type":"request","command":"attach","arguments":{}}Content-Length: 56 + +{"seq":3,"type":"request","command":"configurationDone"} \ No newline at end of file diff --git a/src/tools/miri/priroda/tests/ui/dap/dap_initialize_attach_configuration_done.stdout b/src/tools/miri/priroda/tests/ui/dap/dap_initialize_attach_configuration_done.stdout new file mode 100644 index 0000000000000..64f8a0740a765 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/dap/dap_initialize_attach_configuration_done.stdout @@ -0,0 +1,9 @@ +Content-Length: {CONTENT_LENGTH} + +{"seq":1,"type":"response","request_seq":1,"success":true,"command":"initialize","body":{"supportsConfigurationDoneRequest":true,"supportsSingleThreadExecutionRequests":true},"error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":2,"type":"event","event":"initialized"}Content-Length: {CONTENT_LENGTH} + +{"seq":3,"type":"response","request_seq":2,"success":false,"message":"unsupported request in Priroda DAP demo mode: attach","command":"attach","error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":4,"type":"response","request_seq":3,"success":false,"message":"configurationDone requires launch","command":"configurationDone","error":null} From bf99cbc3ab1cb569117f56ed6c501ea8aa2784fd Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Sat, 15 Aug 2026 01:54:31 +0300 Subject: [PATCH 34/66] [Priroda] Accept DAP attach at startup Treat DAP `attach` as the same startup transition as `launch`. Priroda is already running by the time a `debugServer` client connects, so this does not add a second session model or any target-selection semantics. This makes the VS Code template usable with `request: "attach"`, which is the extension-free path through VS Code's built-in debug type. --- src/tools/miri/priroda/src/frontend/dap.rs | 21 +++++++++++++++---- .../tests/ui/dap/dap_initialize_attach.stdout | 2 +- ...nitialize_attach_configuration_done.stdout | 6 ++++-- ...ts_configuration_done_before_launch.stdout | 2 +- ...rejects_repeated_configuration_done.stdout | 2 +- 5 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/tools/miri/priroda/src/frontend/dap.rs b/src/tools/miri/priroda/src/frontend/dap.rs index ea87afe2406cc..f0dd32d252d67 100644 --- a/src/tools/miri/priroda/src/frontend/dap.rs +++ b/src/tools/miri/priroda/src/frontend/dap.rs @@ -175,6 +175,7 @@ impl DapSession { match &request.command { Command::Initialize(_) => self.handle_initialize(), Command::Launch(_) => self.handle_launch(), + Command::Attach(_) => self.handle_attach(), Command::ConfigurationDone => self.handle_configuration_done(session), Command::Threads => self.handle_threads(), Command::StackTrace(args) => self.handle_stack_trace(args.thread_id, session), @@ -186,8 +187,7 @@ impl DapSession { Command::StepIn(args) => self.handle_step(ResponseBody::StepIn, args.thread_id, session), Command::Disconnect(_) => self.handle_disconnect(), - Command::Attach(_) - | Command::BreakpointLocations(_) + Command::BreakpointLocations(_) | Command::Cancel(_) | Command::Completions(_) | Command::DataBreakpointInfo(_) @@ -231,6 +231,19 @@ impl DapSession { }) } + fn handle_attach(&self) -> Result { + self.require_state(DapState::Initialized)?; + + // VS Code's extension-free `debugServer` template uses `attach`. + // Priroda still starts the same single interpreted session as `launch`. + Ok(HandlerSuccess { + response: HandlerResponse::Success(ResponseBody::Attach), + state: Some(DapState::Launched), + events: Vec::new(), + outcome: HandlerOutcome::Continue, + }) + } + fn handle_scopes<'tcx>( &self, frame_id: i64, @@ -592,8 +605,8 @@ impl DapSession { fn require_state(&self, expected: DapState) -> Result<(), &'static str> { if self.state != expected { return Err(match expected { - DapState::Initialized => "launch requires initialize", - DapState::Launched => "configurationDone requires launch", + DapState::Initialized => "launch or attach requires initialize", + DapState::Launched => "configurationDone requires launch or attach", _ => "invalid session state for request", }); } diff --git a/src/tools/miri/priroda/tests/ui/dap/dap_initialize_attach.stdout b/src/tools/miri/priroda/tests/ui/dap/dap_initialize_attach.stdout index b750e3d3c7d54..ccf336953510b 100644 --- a/src/tools/miri/priroda/tests/ui/dap/dap_initialize_attach.stdout +++ b/src/tools/miri/priroda/tests/ui/dap/dap_initialize_attach.stdout @@ -4,4 +4,4 @@ Content-Length: {CONTENT_LENGTH} {"seq":2,"type":"event","event":"initialized"}Content-Length: {CONTENT_LENGTH} -{"seq":3,"type":"response","request_seq":2,"success":false,"message":"unsupported request in Priroda DAP demo mode: attach","command":"attach","error":null} +{"seq":3,"type":"response","request_seq":2,"success":true,"command":"attach","error":null} \ No newline at end of file diff --git a/src/tools/miri/priroda/tests/ui/dap/dap_initialize_attach_configuration_done.stdout b/src/tools/miri/priroda/tests/ui/dap/dap_initialize_attach_configuration_done.stdout index 64f8a0740a765..5fd81e96dd22c 100644 --- a/src/tools/miri/priroda/tests/ui/dap/dap_initialize_attach_configuration_done.stdout +++ b/src/tools/miri/priroda/tests/ui/dap/dap_initialize_attach_configuration_done.stdout @@ -4,6 +4,8 @@ Content-Length: {CONTENT_LENGTH} {"seq":2,"type":"event","event":"initialized"}Content-Length: {CONTENT_LENGTH} -{"seq":3,"type":"response","request_seq":2,"success":false,"message":"unsupported request in Priroda DAP demo mode: attach","command":"attach","error":null}Content-Length: {CONTENT_LENGTH} +{"seq":3,"type":"response","request_seq":2,"success":true,"command":"attach","error":null}Content-Length: {CONTENT_LENGTH} -{"seq":4,"type":"response","request_seq":3,"success":false,"message":"configurationDone requires launch","command":"configurationDone","error":null} +{"seq":4,"type":"response","request_seq":3,"success":true,"command":"configurationDone","error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":5,"type":"event","event":"stopped","body":{"reason":"entry","description":null,"threadId":1,"preserveFocusHint":null,"text":null,"allThreadsStopped":true,"hitBreakpointIds":null}} \ No newline at end of file diff --git a/src/tools/miri/priroda/tests/ui/dap_rejects_configuration_done_before_launch.stdout b/src/tools/miri/priroda/tests/ui/dap_rejects_configuration_done_before_launch.stdout index 4a4df53ea5889..79b3ccde8cca1 100644 --- a/src/tools/miri/priroda/tests/ui/dap_rejects_configuration_done_before_launch.stdout +++ b/src/tools/miri/priroda/tests/ui/dap_rejects_configuration_done_before_launch.stdout @@ -4,7 +4,7 @@ Content-Length: {CONTENT_LENGTH} {"seq":2,"type":"event","event":"initialized"}Content-Length: {CONTENT_LENGTH} -{"seq":3,"type":"response","request_seq":2,"success":false,"message":"configurationDone requires launch","command":"configurationDone","error":null}Content-Length: {CONTENT_LENGTH} +{"seq":3,"type":"response","request_seq":2,"success":false,"message":"configurationDone requires launch or attach","command":"configurationDone","error":null}Content-Length: {CONTENT_LENGTH} {"seq":4,"type":"response","request_seq":3,"success":true,"command":"disconnect","error":null}Content-Length: {CONTENT_LENGTH} diff --git a/src/tools/miri/priroda/tests/ui/dap_rejects_repeated_configuration_done.stdout b/src/tools/miri/priroda/tests/ui/dap_rejects_repeated_configuration_done.stdout index abc6e1cf7d694..d137ae0997eb3 100644 --- a/src/tools/miri/priroda/tests/ui/dap_rejects_repeated_configuration_done.stdout +++ b/src/tools/miri/priroda/tests/ui/dap_rejects_repeated_configuration_done.stdout @@ -10,7 +10,7 @@ Content-Length: {CONTENT_LENGTH} {"seq":5,"type":"event","event":"stopped","body":{"reason":"entry","description":null,"threadId":1,"preserveFocusHint":null,"text":null,"allThreadsStopped":true,"hitBreakpointIds":null}}Content-Length: {CONTENT_LENGTH} -{"seq":6,"type":"response","request_seq":4,"success":false,"message":"configurationDone requires launch","command":"configurationDone","error":null}Content-Length: {CONTENT_LENGTH} +{"seq":6,"type":"response","request_seq":4,"success":false,"message":"configurationDone requires launch or attach","command":"configurationDone","error":null}Content-Length: {CONTENT_LENGTH} {"seq":7,"type":"response","request_seq":5,"success":true,"command":"disconnect","error":null}Content-Length: {CONTENT_LENGTH} From d928cafc6268bd2cfa4b04c218c85e21cb5e281d Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Sat, 15 Aug 2026 01:55:18 +0300 Subject: [PATCH 35/66] [Priroda] Add DAP unknown-command fixture Document how the DAP frontend currently reacts when VS Code's JavaScript debugger sends an extension request that `emmy_dap_types` cannot deserialize. The following valid DAP messages are included in the input so the next commit can show that Priroda keeps serving after the unknown command instead of ending the session. --- .../priroda/tests/ui/dap/dap_skips_unknown_request.rs | 3 +++ .../tests/ui/dap/dap_skips_unknown_request.stderr | 1 + .../priroda/tests/ui/dap/dap_skips_unknown_request.stdin | 9 +++++++++ .../tests/ui/dap/dap_skips_unknown_request.stdout | 0 4 files changed, 13 insertions(+) create mode 100644 src/tools/miri/priroda/tests/ui/dap/dap_skips_unknown_request.rs create mode 100644 src/tools/miri/priroda/tests/ui/dap/dap_skips_unknown_request.stderr create mode 100644 src/tools/miri/priroda/tests/ui/dap/dap_skips_unknown_request.stdin create mode 100644 src/tools/miri/priroda/tests/ui/dap/dap_skips_unknown_request.stdout diff --git a/src/tools/miri/priroda/tests/ui/dap/dap_skips_unknown_request.rs b/src/tools/miri/priroda/tests/ui/dap/dap_skips_unknown_request.rs new file mode 100644 index 0000000000000..c1f1ed6f67bea --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/dap/dap_skips_unknown_request.rs @@ -0,0 +1,3 @@ +//@ compile-flags: --dap + +fn main() {} diff --git a/src/tools/miri/priroda/tests/ui/dap/dap_skips_unknown_request.stderr b/src/tools/miri/priroda/tests/ui/dap/dap_skips_unknown_request.stderr new file mode 100644 index 0000000000000..a3053a87a3ea0 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/dap/dap_skips_unknown_request.stderr @@ -0,0 +1 @@ +priroda dap error: ParseError(Error("unknown variant `enableNetworking`, expected one of `attach`, `breakpointLocations`, `cancel`, `completions`, `configurationDone`, `continue`, `dataBreakpointInfo`, `disassemble`, `disconnect`, `evaluate`, `exceptionInfo`, `goto`, `gotoTargets`, `initialize`, `launch`, `loadedSources`, `modules`, `next`, `pause`, `readMemory`, `restart`, `restartFrame`, `reverseContinue`, `scopes`, `setBreakpoints`, `setDataBreakpoints`, `setExceptionBreakpoints`, `setExpression`, `setFunctionBreakpoints`, `setInstructionBreakpoints`, `setVariable`, `source`, `stackTrace`, `stepBack`, `stepIn`, `stepInTargets`, `stepOut`, `terminate`, `terminateThreads`, `threads`, `variables`, `writeMemory`", line: 1, column: 86)) diff --git a/src/tools/miri/priroda/tests/ui/dap/dap_skips_unknown_request.stdin b/src/tools/miri/priroda/tests/ui/dap/dap_skips_unknown_request.stdin new file mode 100644 index 0000000000000..fac8fbc4c1d94 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/dap/dap_skips_unknown_request.stdin @@ -0,0 +1,9 @@ +Content-Length: 89 + +{"seq":1,"type":"request","command":"enableNetworking","arguments":{"mirrorEvents":true}}Content-Length: 85 + +{"seq":2,"type":"request","command":"initialize","arguments":{"adapterID":"priroda"}}Content-Length: 60 + +{"seq":3,"type":"request","command":"launch","arguments":{}}Content-Length: 56 + +{"seq":4,"type":"request","command":"configurationDone"} \ No newline at end of file diff --git a/src/tools/miri/priroda/tests/ui/dap/dap_skips_unknown_request.stdout b/src/tools/miri/priroda/tests/ui/dap/dap_skips_unknown_request.stdout new file mode 100644 index 0000000000000..e69de29bb2d1d From 9c36d9a6a080eeaf44db2c682f42904e2d5bb64d Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Sat, 15 Aug 2026 01:55:44 +0300 Subject: [PATCH 36/66] [Priroda] Keep DAP sessions alive after unknown commands Skip requests that `emmy_dap_types` cannot deserialize after reading their length-prefixed message body. That leaves the transport positioned at the next message, so Priroda can keep serving the DAP subset it understands. This is needed by the extension-free VS Code debugServer flow because the built-in JavaScript debugger can send extension commands such as `enableNetworking` before the ordinary Priroda handshake. The handler still records this as a FIXME so we can replace the stderr-only skip with a proper DAP error response once the protocol crate can represent unknown commands. --- src/tools/miri/priroda/src/frontend/dap.rs | 9 +++++++++ .../tests/ui/dap/dap_skips_unknown_request.stderr | 2 +- .../tests/ui/dap/dap_skips_unknown_request.stdout | 11 +++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/priroda/src/frontend/dap.rs b/src/tools/miri/priroda/src/frontend/dap.rs index f0dd32d252d67..c2935213a6ea6 100644 --- a/src/tools/miri/priroda/src/frontend/dap.rs +++ b/src/tools/miri/priroda/src/frontend/dap.rs @@ -136,6 +136,15 @@ impl DapSession { let request = match self.server.poll_request() { Ok(Some(request)) => request, Ok(None) => return Ok(()), + // The message body has already been consumed. js-debug can send + // commands like `enableNetworking`, which `emmy_dap_types` reports + // as parse errors because it has no unknown-command variant. + // FIXME: send a DAP error response once unknown commands are + // representable. + Err(ServerError::ParseError(_)) => { + eprintln!("priroda dap: skipping request that could not be deserialized"); + continue; + } Err(err) => return Err(err), }; diff --git a/src/tools/miri/priroda/tests/ui/dap/dap_skips_unknown_request.stderr b/src/tools/miri/priroda/tests/ui/dap/dap_skips_unknown_request.stderr index a3053a87a3ea0..6be8b1c29f43e 100644 --- a/src/tools/miri/priroda/tests/ui/dap/dap_skips_unknown_request.stderr +++ b/src/tools/miri/priroda/tests/ui/dap/dap_skips_unknown_request.stderr @@ -1 +1 @@ -priroda dap error: ParseError(Error("unknown variant `enableNetworking`, expected one of `attach`, `breakpointLocations`, `cancel`, `completions`, `configurationDone`, `continue`, `dataBreakpointInfo`, `disassemble`, `disconnect`, `evaluate`, `exceptionInfo`, `goto`, `gotoTargets`, `initialize`, `launch`, `loadedSources`, `modules`, `next`, `pause`, `readMemory`, `restart`, `restartFrame`, `reverseContinue`, `scopes`, `setBreakpoints`, `setDataBreakpoints`, `setExceptionBreakpoints`, `setExpression`, `setFunctionBreakpoints`, `setInstructionBreakpoints`, `setVariable`, `source`, `stackTrace`, `stepBack`, `stepIn`, `stepInTargets`, `stepOut`, `terminate`, `terminateThreads`, `threads`, `variables`, `writeMemory`", line: 1, column: 86)) +priroda dap: skipping request that could not be deserialized diff --git a/src/tools/miri/priroda/tests/ui/dap/dap_skips_unknown_request.stdout b/src/tools/miri/priroda/tests/ui/dap/dap_skips_unknown_request.stdout index e69de29bb2d1d..d20901238838a 100644 --- a/src/tools/miri/priroda/tests/ui/dap/dap_skips_unknown_request.stdout +++ b/src/tools/miri/priroda/tests/ui/dap/dap_skips_unknown_request.stdout @@ -0,0 +1,11 @@ +Content-Length: {CONTENT_LENGTH} + +{"seq":1,"type":"response","request_seq":2,"success":true,"command":"initialize","body":{"supportsConfigurationDoneRequest":true,"supportsSingleThreadExecutionRequests":true},"error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":2,"type":"event","event":"initialized"}Content-Length: {CONTENT_LENGTH} + +{"seq":3,"type":"response","request_seq":3,"success":true,"command":"launch","error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":4,"type":"response","request_seq":4,"success":true,"command":"configurationDone","error":null}Content-Length: {CONTENT_LENGTH} + +{"seq":5,"type":"event","event":"stopped","body":{"reason":"entry","description":null,"threadId":1,"preserveFocusHint":null,"text":null,"allThreadsStopped":true,"hitBreakpointIds":null}} \ No newline at end of file From 513bf1a4e4a6ccda8a4069198b3b7c39a6392a00 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Sat, 15 Aug 2026 01:56:24 +0300 Subject: [PATCH 37/66] [Priroda] Make the VS Code template extension-free Use VS Code's built-in node debug type as the editor-side entry point for the debugServer template. The debugServer setting still connects VS Code to Priroda's already-running DAP server, so the Node adapter is not spawned. Local testing had been masked by an experimental Priroda VS Code extension that contributed a `priroda` debug type. After removing that extension, the previous template depended on hidden editor state and was not reproducible from the checked-in files alone. Switching the template to `type: "node"` and `request: "attach"` makes the setup work on a clean VS Code install, together with Priroda accepting DAP `attach` and skipping js-debug extension requests that are not part of Priroda's protocol. --- src/tools/miri/priroda/README.md | 36 +++++++++++++++-------- src/tools/miri/priroda/vscode_launch.json | 10 +++---- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/tools/miri/priroda/README.md b/src/tools/miri/priroda/README.md index 5f49cabca68f0..daab65fc3ad00 100644 --- a/src/tools/miri/priroda/README.md +++ b/src/tools/miri/priroda/README.md @@ -58,10 +58,15 @@ when you run the debugger configuration. The launch configuration does not spawn Priroda directly; it starts a background task and then connects through `debugServer`. -`debugServer` only tells VS Code to connect to an already-running adapter; it -does not register a debug type. VS Code still needs a registered `priroda` -debug type, which must come from a debug extension. A custom Priroda extension -is deferred to future graphical features. +`debugServer` tells VS Code to connect to an already-running adapter, but VS Code +still requires the launch configuration's `type` to be one it knows. Priroda has +no installed extension, so the configuration uses VS Code's built-in `node` +debug type as the registered editor-side type. `debugServer` redirects the DAP +transport to Priroda before the Node adapter is spawned, so no custom Priroda +extension is needed. The configuration uses `request: "attach"`, which makes VS +Code send the DAP `attach` request; Priroda accepts `attach` as the same startup +transition as `launch`. A richer custom Priroda extension is deferred to future +graphical features. The templates assume `${workspaceFolder}` is the `miri/priroda` directory that contains them. Copy them into that directory's `.vscode/`, or edit @@ -75,15 +80,18 @@ cp vscode_tasks.json /path/to/miri/priroda/.vscode/tasks.json Before running the debugger configuration, make sure: -- `MIRI_SYSROOT` points at a Miri sysroot, for example from - `cargo +miri miri setup --print-sysroot`. -- The `cargo` in `command` resolves to the `miri` toolchain's cargo, so the - task builds Priroda with `rustc_private`. - The Rust file path at the end of `args` is the file you want Priroda to run. - Port `4711` is free, or both `--port` and `debugServer` use the same different port. -- VS Code has a debugger contribution installed that accepts - `type: "priroda"` debug configurations. +- `MIRI_SYSROOT` points at a Miri sysroot, for example from + `cargo +miri miri setup --print-sysroot`. VS Code resolves `${env:MIRI_SYSROOT}` + from the environment it was started with, not from the task's `env`, so export + it in your shell before launching VS Code, or replace the argument with the + absolute sysroot path. +- The `cargo` in `command` resolves to the `miri` toolchain's cargo, so the task + builds Priroda with `rustc_private`. That is automatic when the workspace is + `miri/priroda`; when using the templates from another project, pass `+miri` as + the first `cargo` argument. The task runs Priroda through `cargo run` against the Priroda crate: @@ -107,15 +115,17 @@ the background task as ready and connects with: ```json { - "type": "priroda", - "request": "launch", + "type": "node", + "request": "attach", "preLaunchTask": "Priroda: Start DAP Server", "debugServer": 4711 } ``` Priroda accepts one TCP connection and waits for VS Code before running the DAP -handshake. +handshake. VS Code's built-in JavaScript debugger may also send extension +requests of its own, such as `enableNetworking` for its network preview; Priroda +skips unrecognized requests rather than failing, so those are ignored. ## Test diff --git a/src/tools/miri/priroda/vscode_launch.json b/src/tools/miri/priroda/vscode_launch.json index 15141a4c47528..b9aa77f689ba6 100644 --- a/src/tools/miri/priroda/vscode_launch.json +++ b/src/tools/miri/priroda/vscode_launch.json @@ -4,11 +4,11 @@ { "name": "Priroda: Run and Attach", // Attaches to an already-running Priroda DAP server started by the - // preLaunchTask below. VS Code still needs a registered "priroda" debug - // type from a debug extension; a custom Priroda extension is deferred - // to future graphical features. - "type": "priroda", - "request": "launch", + // preLaunchTask below, using VS Code's built-in "node" debug type as + // the registered editor-side type so no custom Priroda extension is + // required. + "type": "node", + "request": "attach", "preLaunchTask": "Priroda: Start DAP Server", "debugServer": 4711 } From cdca303d0c478414043ded72a390e5c6bd15204b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 15 Aug 2026 16:16:15 +0200 Subject: [PATCH 38/66] add some more missing io::Error variants --- src/tools/miri/src/lib.rs | 1 + src/tools/miri/src/shims/io_error.rs | 14 +++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index d90f543b4905e..c74b786eada85 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -18,6 +18,7 @@ #![feature(try_blocks)] #![feature(io_error_more)] #![feature(io_error_inprogress)] +#![cfg_attr(not(bootstrap), feature(io_error_input_output_error))] #![cfg_attr(not(bootstrap), feature(io_error_too_many_open_files))] #![feature(variant_count)] #![feature(yeet_expr)] diff --git a/src/tools/miri/src/shims/io_error.rs b/src/tools/miri/src/shims/io_error.rs index dc0a504ff3c4d..d94862f88d4e9 100644 --- a/src/tools/miri/src/shims/io_error.rs +++ b/src/tools/miri/src/shims/io_error.rs @@ -79,7 +79,6 @@ const UNIX_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = { ("ENOENT", NotFound), ("ENOMEM", OutOfMemory), ("ENOSPC", StorageFull), - ("ENOSYS", Unsupported), ("EMLINK", TooManyLinks), ("ENAMETOOLONG", InvalidFilename), ("ENETDOWN", NetworkDown), @@ -95,12 +94,17 @@ const UNIX_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = { ("ETXTBSY", ExecutableFileBusy), ("EXDEV", CrossesDevices), ("EINPROGRESS", InProgress), + #[cfg(not(bootstrap))] + ("EIO", InputOutputError), // The following have two valid options. We have both for the forwards mapping; only the // first one will be used for the backwards mapping. ("EPERM", PermissionDenied), ("EACCES", PermissionDenied), ("EWOULDBLOCK", WouldBlock), ("EAGAIN", WouldBlock), + ("ENOSYS", Unsupported), + ("EOPNOTSUPP", Unsupported), + ("ENOTSUP", Unsupported), #[cfg(not(bootstrap))] ("EMFILE", TooManyOpenFiles), #[cfg(not(bootstrap))] @@ -258,10 +262,12 @@ const WINDOWS_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = { ("ERROR_RUNLEVEL_SWITCH_TIMEOUT", TimedOut), ("ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT", TimedOut), ("ERROR_TOO_MANY_LINKS", TooManyLinks), - #[cfg(not(bootstrap))] - ("ERROR_TOO_MANY_OPEN_FILES", TooManyOpenFiles), ("ERROR_CALL_NOT_IMPLEMENTED", Unsupported), ("WSAEWOULDBLOCK", WouldBlock), + #[cfg(not(bootstrap))] + ("ERROR_TOO_MANY_OPEN_FILES", TooManyOpenFiles), + #[cfg(not(bootstrap))] + ("ERROR_IO_DEVICE", InputOutputError), ] }; @@ -347,6 +353,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let target = &this.tcx.sess.target; if target.families.iter().any(|f| f == "unix") { + // FIXME: consult UNIX_ERRNO_TABLE. for &(name, kind) in UNIX_IO_ERROR_TABLE { if err.kind() == kind { return interp_ok(this.eval_libc(name)); @@ -369,6 +376,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } /// The inverse of `io_error_to_errnum`: it converts target errors to host errors. + /// This is used to render such errors as user-visible strings. /// This is done in a best-effort way. #[expect(clippy::needless_return)] fn try_errnum_to_io_error( From 2fbe4c96fec2a3257984a5aad751ce1707fcdc13 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 15 Aug 2026 16:34:49 +0200 Subject: [PATCH 39/66] host_error_to_errnum: avoid ErrorKind detour when host and target are both Unix --- src/tools/miri/src/shims/io_error.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/src/shims/io_error.rs b/src/tools/miri/src/shims/io_error.rs index d94862f88d4e9..346b6423ca176 100644 --- a/src/tools/miri/src/shims/io_error.rs +++ b/src/tools/miri/src/shims/io_error.rs @@ -353,7 +353,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let target = &this.tcx.sess.target; if target.families.iter().any(|f| f == "unix") { - // FIXME: consult UNIX_ERRNO_TABLE. + // If the host is also Unix, we can use the raw OS error and avoid a potentially lossy + // trip through `ErrorKind`. + #[cfg(unix)] + if let Some(host_errno) = err.raw_os_error() { + for &(name, errno) in UNIX_ERRNO_TABLE { + if host_errno == errno { + return interp_ok(this.eval_libc(name)); + } + } + } + // For other hosts or other constants, we fall back to translating via `ErrorKind`. for &(name, kind) in UNIX_IO_ERROR_TABLE { if err.kind() == kind { return interp_ok(this.eval_libc(name)); From 581ac1c9eaace91ad57963875e78c53593aa92fc Mon Sep 17 00:00:00 2001 From: vad Date: Sat, 15 Aug 2026 17:40:43 +0200 Subject: [PATCH 40/66] Add documentation for BPF targets BPF targets, despite being Tier 3, were lacking documentation. Add it, describing how the targets work. Nominate @nagisa and myself as maintainers. --- src/doc/rustc/src/SUMMARY.md | 1 + src/doc/rustc/src/platform-support.md | 4 +- .../src/platform-support/bpf-unknown-none.md | 155 ++++++++++++++++++ 3 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 src/doc/rustc/src/platform-support/bpf-unknown-none.md diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index bedfa65ac894d..b9c79ab0128e9 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -87,6 +87,7 @@ - [\*-unknown-l4re](platform-support/l4re.md) - [\*-unknown-trusty](platform-support/trusty.md) - [\*-kmc-solid_\*](platform-support/kmc-solid.md) + - [bpf\*-unknown-none](platform-support/bpf-unknown-none.md) - [csky-unknown-linux-gnuabiv2\*](platform-support/csky-unknown-linux-gnuabiv2.md) - [hexagon-unknown-linux-musl](platform-support/hexagon-unknown-linux-musl.md) - [hexagon-unknown-none-elf](platform-support/hexagon-unknown-none-elf.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index c8ae02b091034..2643c7fd12891 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -330,8 +330,8 @@ target | std | host | notes [`armv7a-nuttx-eabi`](platform-support/nuttx.md) | ✓ | | ARMv7-A with NuttX [`armv7a-nuttx-eabihf`](platform-support/nuttx.md) | ✓ | | ARMv7-A with NuttX, hardfloat [`avr-none`](platform-support/avr-none.md) | * | | AVR; requires `-Zbuild-std=core` and `-Ctarget-cpu=...` -`bpfeb-unknown-none` | * | | BPF (big endian) -`bpfel-unknown-none` | * | | BPF (little endian) +[`bpfeb-unknown-none`](platform-support/bpf-unknown-none.md) | * | | BPF (big endian) +[`bpfel-unknown-none`](platform-support/bpf-unknown-none.md) | * | | BPF (little endian) [`csky-unknown-linux-gnuabiv2`](platform-support/csky-unknown-linux-gnuabiv2.md) | ✓ | | C-SKY abiv2 Linux (little endian) [`csky-unknown-linux-gnuabiv2hf`](platform-support/csky-unknown-linux-gnuabiv2.md) | ✓ | | C-SKY abiv2 Linux, hardfloat (little endian) [`hexagon-unknown-linux-musl`](platform-support/hexagon-unknown-linux-musl.md) | ✓ | | Hexagon Linux with musl 1.2.5 diff --git a/src/doc/rustc/src/platform-support/bpf-unknown-none.md b/src/doc/rustc/src/platform-support/bpf-unknown-none.md new file mode 100644 index 0000000000000..430b5bc28646c --- /dev/null +++ b/src/doc/rustc/src/platform-support/bpf-unknown-none.md @@ -0,0 +1,155 @@ +# `bpf*-unknown-none` + +**Tier: 3** + +* `bpfeb-unknown-none` (big endian) +* `bpfel-unknown-none` (little endian) + +Targets for the 64-bit [BPF virtual machine][ebpf]. + +## Target maintainers + +[@nagisa](https://github.com/nagisa) [@vadorovsky](https://github.com/vadorovsky) + +## Requirements + +BPF targets require a Rust toolchain with the `rust-src` component. In +addition, you must install the [bpf-linker]. + +They don't support std and alloc and are meant for a `no_std` environment. + +`extern "C"` uses the [BPF ABI calling convention][bpf-abi]. + +Produced binaries use the ELF format. + +## Building the target + +You can build Rust with support for BPF targets by adding them to the `target` +list in `config.toml`: + +```toml +[build] +target = ["bpfeb-unknown-none", "bpfel-unknown-none"] +``` + +## Building Rust programs + +Rust does not yet ship pre-compiled artifacts for this target. To compile for +this target, you will either need to build Rust with the target enabled (see +"Building the target" above), or build your own copy of `core` by using +`build-std` or similar. + +Building the BPF target requires specifying it explicitly. Users can either +add it to the `target` list in `config.toml`: + +```toml +[build] +target = ["bpfel-unknown-none"] +``` + +Or specify it directly in the `cargo build` invocation: + +```console +cargo +nightly build -Z build-std=core --target bpfel-unknown-none +``` + +BPF has its own debug info format called [BTF][btf]. + +BPF targets use [bpf-linker], an LLVM bitcode linker. + +## Error handling + +There is no concept of stack unwinding in BPF, therefore BPF programs are +expected to handle errors in a recoverable manner. Therefore most BPF programs +written in Rust use the following no-op panic handler implementation: + +```rust,ignore (a panic handler implementation specific to BPF targets) +#[cfg(not(test))] +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} +``` + +Infinite loops are forbidden by the BPF verifier. Therefore, if the program +contains any code which can panic, the BPF VM refuses to load it. + +## Testing + +BPF bytecode needs to be executed on a BPF virtual machine, like the one +provided by the Linux kernel or one of the user-space implementations like +[rbpf][rbpf]. None of them support running Rust `#[test]` functions. One of the +reasons is the lack of support for panicking. + +Therefore, unit tests need to run on the host system. That requirement can be +enforced by the following conditional check: + +```rust +#[cfg(all(not(target_arch = "bpf"), test))] +mod test {} +``` + +## Cross-compilation toolchains + +BPF programs are always cross-compiled from a host (e.g. +`x86_64-unknown-linux-*`) for a BPF target (e.g. `bpfel-unknown-none`). + +The endianness of a chosen BPF target needs to match the endianness of the BPF +VM host on which the program is supposed to run. + +The architecture of the BPF VM host often has an impact on types that the BPF +programs should use. For example [kprobes][kprobe], [fprobes][fprobe] and +[uprobes][uprobe] allow dynamic function tracing and lookup into host registers +through the [`pt_regs`][pt-regs] struct, which differs across architectures. + +That difference is still not a concern of the compiler. Instead, it should be +handled by the developers. [Aya][aya] (the library for writing Linux BPF +programs and the main consumer of BPF targets in Rust) handles that by +providing the [`aya-ebpf-cty`][aya-ebpf-cty] crate, with type aliases similar +to those provided by [`core:ffi`][core-ffi]. [`aya-ebpf-cty`][aya-ebpf-cty] +allows to specify the VM target through the `CARGO_CFG_BPF_TARGET_ARCH` +environment variable (e.g. `CARGO_CFG_BPF_TARGET_ARCH=aarch64`). + +## C code + +It's possible to link a Rust BPF project to bitcode or object files which are +built from C code with [clang][clang]. It can be done using a `rustc-link-lib` +instruction in `build.rs`. Example: + +```rust,no_run +use std::{env, process::Command}; + +let out_dir = env::var("OUT_DIR").unwrap(); +let c_module = "my_module.bpf.c"; +let s = Command::new("clang") + .arg("-I") + .arg("src/") + .arg("-O2") + .arg("-emit-llvm") + .arg("-target") + .arg("bpf") + .arg("-c") + .arg("-g") + .arg(c_module) + .arg("-o") + .arg(format!("{out_dir}/my_module.bpf.o")) + .status() + .unwrap(); +assert!(s.success()); +println!("cargo:rustc-link-search=native={out_dir}"); +println!("cargo:rustc-link-lib=link-arg={out_dir}/my_module.bpf.o"); +``` + +[ebpf]: https://ebpf.io/ +[bpf-linker]: https://github.com/aya-rs/bpf-linker +[bpf-abi]: https://www.kernel.org/doc/html/v6.13-rc5/bpf/standardization/abi.html +[btf]: https://www.kernel.org/doc/html/latest/bpf/btf.html +[rbpf]: https://github.com/qmonnet/rbpf +[kprobe]: https://www.kernel.org/doc/html/latest/trace/kprobes.html +[fprobe]: https://www.kernel.org/doc/html/latest/trace/fprobe.html +[uprobe]: https://www.kernel.org/doc/html/latest/trace/uprobetracer.html +[pt-regs]: https://elixir.bootlin.com/linux/v6.12.6/source/arch/x86/include/uapi/asm/ptrace.h#L44 +[aya]: https://aya-rs.dev +[aya-ebpf-cty]: https://github.com/aya-rs/aya/tree/main/ebpf/aya-ebpf-cty +[core-ffi]: https://doc.rust-lang.org/stable/core/ffi/index.html +[clang]: https://clang.llvm.org/ From 0958d0c262d082031f2c6990a499167e4f725928 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Sat, 15 Aug 2026 17:27:22 -0400 Subject: [PATCH 41/66] Switch to c8a EC2 runner for auto merges --- src/ci/github-actions/jobs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index e7c33c13e2cf1..08121a9a693b7 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -312,7 +312,7 @@ auto: - name: dist-x86_64-illumos <<: *job-linux-4c - - <<: [*job-dist-x86_64-linux, *job-linux-36c-codebuild] + - <<: [*job-dist-x86_64-linux, *job-linux-32c-ec2] - name: dist-x86_64-linux-alt env: From d061443eaffeb978677e3b679f593ac5bc9c68fe Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 16 Aug 2026 00:03:18 +0200 Subject: [PATCH 42/66] Update `browser-ui-test` version to `0.25.1` --- yarn.lock | 68 +++++++++++++++++++++++++++---------------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/yarn.lock b/yarn.lock index c62a4c75f949b..55c862d1e75dd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -74,12 +74,12 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@puppeteer/browsers@3.0.6": - version "3.0.6" - resolved "https://registry.yarnpkg.com/@puppeteer/browsers/-/browsers-3.0.6.tgz#6b772e0fc11deb255c8a3c14219e34a16a2ab23d" - integrity sha512-B/gKoqlFkzhvzsI6jo9K1cZz9o5ypviVv/xu8CwA4grZzyVwN+XfkT+tu8T1zrauuEXv6VhS2oGX+6NL95WcKA== +"@puppeteer/browsers@3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@puppeteer/browsers/-/browsers-3.2.0.tgz#269293687a4c701a0a4701a15b50e9c0e337de93" + integrity sha512-LlBrE8oqGfU7b1Nk2d5Q1SbuPhZxTj0cJEMDPEws28OjNMELlflekmPPuf4FnK03x0ZRjKaYwJElUcKK4kyqJA== dependencies: - modern-tar "^0.7.6" + modern-tar "^0.8.0" yargs "^18.0.0" "@ungap/structured-clone@^1.2.0": @@ -113,9 +113,9 @@ ansi-regex@^5.0.1: integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-regex@^6.2.2: - version "6.2.2" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.2.2.tgz#60216eea464d864597ce2832000738a0589650c1" - integrity sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg== + version "6.3.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.3.0.tgz#247c8e7b70a1a43b10ce14c0226fcbf58e8815d5" + integrity sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ== ansi-styles@^4.1.0: version "4.3.0" @@ -155,9 +155,9 @@ braces@^3.0.3: fill-range "^7.1.1" browser-ui-test@^0.25.0: - version "0.25.0" - resolved "https://registry.yarnpkg.com/browser-ui-test/-/browser-ui-test-0.25.0.tgz#e24352d63009f07ec42583c96330a1ca57487d88" - integrity sha512-DBSpC3UFzQTKhj9cc11AiRliCUiWut1GAJ6sLPEYYPCmF6gTQGg/eynwev0NXPiDpuawiVxSpXPttHQePRK6Ug== + version "0.25.1" + resolved "https://registry.yarnpkg.com/browser-ui-test/-/browser-ui-test-0.25.1.tgz#c7f22a5e2b9e51be4ba34df3adf7bd7a9249bce6" + integrity sha512-woRwKU1dPBIwYmCI6npox8qlPO0WQ8GZH2YbL39mNkiWymByebiB4EK0PlaGMbmEja0MEqfMQD+d33LCW4S2AA== dependencies: css-unit-converter "^1.1.2" pngjs "^3.4.0" @@ -237,10 +237,10 @@ deep-is@^0.1.3: resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== -devtools-protocol@0.0.1653615: - version "0.0.1653615" - resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1653615.tgz#c600e0c619612156b2422a66d958ba188d87dbe8" - integrity sha512-pGVkY3T/qXxAp2nFPodwYqOevk6ncNMSmvL8QfRCx5ZWGd6Vor7AFNmyaA8Zs6uJyP1QAfjuLandCgvSix1BNA== +devtools-protocol@0.0.1666840: + version "0.0.1666840" + resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1666840.tgz#796cbc307f82750afc13a3b471c829bf3da19a65" + integrity sha512-gCcO42XCHKEs7Ag0S7aGYsnJ7hlgrO3qderYqeiY0Eqk+0GFfuvT13IA0hHreJTa2KCdDVyGMeOhdMNmrrTjVg== doctrine@^3.0.0: version "3.0.0" @@ -626,10 +626,10 @@ mitt@^3.0.1: resolved "https://registry.yarnpkg.com/mitt/-/mitt-3.0.1.tgz#ea36cf0cc30403601ae074c8f77b7092cdab36d1" integrity sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw== -modern-tar@^0.7.6: - version "0.7.7" - resolved "https://registry.yarnpkg.com/modern-tar/-/modern-tar-0.7.7.tgz#ca71d79603630076b10733b0751ccab284bbc1ef" - integrity sha512-t9VmxaqrmANnEOBhpSDI6HD192Ge48k8vmWqQQL7hSFEqHEYwZbbsu49+aKLWZeRvFs3j1pMhXOqqF4kPlvjkQ== +modern-tar@^0.8.0: + version "0.8.4" + resolved "https://registry.yarnpkg.com/modern-tar/-/modern-tar-0.8.4.tgz#25d2de2f522250012f33a3b366400b49741f6b8e" + integrity sha512-gN54ddmyzEg10orwZ2u4OOv+bjpMWdIl5jIkodK97bMq8QBSL5c0D7YX0lT1Ooz+99S7+PvFbnxzdjgHo1r41g== ms@^2.1.3: version "2.1.3" @@ -716,28 +716,28 @@ punycode@^2.1.0: resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== -puppeteer-core@25.4.0: - version "25.4.0" - resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-25.4.0.tgz#2fcba53a9ab94d55f196e1e42dbe794bbadf8759" - integrity sha512-K1plkLOdeoUnGeT1OvdqF3qxl33v+Ra/uH5VyPEhXdMcpvGiEskHzxxEU3fgpccJpJLIipB/rPUsvkZRWeKqOA== +puppeteer-core@25.7.0: + version "25.7.0" + resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-25.7.0.tgz#e1a31698ec4646ecf891de42636d6f6405945625" + integrity sha512-wgBBj7dU5ceGyoT2PCrJpkYOhxPY8mDOmcSKZP92Cj5GgqQ3kv/UxzawnOLxiYuupe4bDf/yiQm3bzs/1nI0rQ== dependencies: - "@puppeteer/browsers" "3.0.6" + "@puppeteer/browsers" "3.2.0" chromium-bidi "17.0.2" - devtools-protocol "0.0.1653615" + devtools-protocol "0.0.1666840" typed-query-selector "^2.12.2" webdriver-bidi-protocol "0.4.2" ws "^8.21.1" puppeteer@^25.1.0: - version "25.4.0" - resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-25.4.0.tgz#87b549a666ffc4f68fee2f195c42d67eba9b8168" - integrity sha512-xfQp8dFBcGaLc1hEMaVr7s+oW4ZkAurr8Y9H81ilKhu6QoLfSTkZjU7IavnyJ/VWpB9ni3KNJUQHUatslLWyGw== + version "25.7.0" + resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-25.7.0.tgz#4536e40b3685309b9f8444860d237d6be3484c83" + integrity sha512-zLBIYuW66SGwY7JNqGmiqeKiM92CYOs6xPibSRBPIeYGIfM1nE/8VCE8G0fGot2EfXENC4PbhktxLrQ0EK5Thg== dependencies: - "@puppeteer/browsers" "3.0.6" + "@puppeteer/browsers" "3.2.0" chromium-bidi "17.0.2" - devtools-protocol "0.0.1653615" + devtools-protocol "0.0.1666840" lilconfig "^3.1.3" - puppeteer-core "25.4.0" + puppeteer-core "25.7.0" typed-query-selector "^2.12.2" queue-microtask@^1.2.2: @@ -902,9 +902,9 @@ wrappy@1: integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== ws@^8.21.1: - version "8.21.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.21.1.tgz#045650cd4b1207809e7547146223c3814a9af586" - integrity sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw== + version "8.21.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.21.3.tgz#660b4faddb6a3e575c86e078126919961f4de4fc" + integrity sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw== y18n@^5.0.5: version "5.0.8" From 3183856eca58b52239dea80afd2d290bd71cec0e Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 16 Aug 2026 00:03:50 +0200 Subject: [PATCH 43/66] Fix typo in search.js --- src/librustdoc/html/static/js/search.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 976d7e39d4ddd..8584e0aff0538 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -5301,7 +5301,7 @@ async function showResults(docSearch, results, goToFirst, filterCrates) { } const crateSearch = document.getElementById("crate-search"); if (crateSearch) { - // #crate-search is an input element + // #crate-search is a `"). +assert-text: (".search-switcher", "Search results in all crates", STARTS_WITH) + +// Checking the display of the crate filter. +// We start with the light theme. +call-function: ("switch-theme", {"theme": "light"}) + +set-timeout: 2000 +wait-for: "#crate-search" +assert-css: ("#crate-search", { + "border": "1px solid #e0e0e0", + "color": "black", + "background-color": "white", +}) + +// We now check the dark theme. +call-function: ("switch-theme", {"theme": "dark"}) +wait-for-css: ("#crate-search", { + "border": "1px solid #e0e0e0", + "color": "#ddd", + "background-color": "#353535", +}) + +// And finally we check the ayu theme. +call-function: ("switch-theme", {"theme": "ayu"}) +wait-for-css: ("#crate-search", { + "border": "1px solid #5c6773", + "color": "#c5c5c5", + "background-color": "#0f1419", +}) From f955803a50e00e32320c03fb906c2f919c77097c Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 16 Aug 2026 14:36:39 +1000 Subject: [PATCH 45/66] Move `LLVM_TOOLS` and `LLD_FILE_NAMES` into `build_steps::dist` This seems to be the module that relies on them the most, and other modules already pull in things from `dist`. --- src/bootstrap/src/core/build_steps/compile.rs | 5 ++-- src/bootstrap/src/core/build_steps/dist.rs | 24 +++++++++++++++++-- src/bootstrap/src/core/build_steps/tool.rs | 3 ++- src/bootstrap/src/lib.rs | 20 ---------------- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 6b659206fbca5..d3e454ae22c81 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -37,8 +37,7 @@ use crate::utils::helpers::{ self, exe, get_clang_cl_resource_dir, is_debug_info, is_dylib, symlink_dir, t, up_to_date, }; use crate::{ - CLang, CodegenBackendKind, Compiler, DependencyType, FileType, GitRepo, LLVM_TOOLS, Mode, - debug, trace, + CLang, CodegenBackendKind, Compiler, DependencyType, FileType, GitRepo, Mode, debug, trace, }; /// Build a standard library for the given `target` using the given `build_compiler`. @@ -2179,7 +2178,7 @@ impl CommandLineStep for Assemble { let _llvm_tools_span = span!(tracing::Level::TRACE, "installing llvm tools to sysroot", ?libdir_bin) .entered(); - for tool in LLVM_TOOLS { + for tool in dist::LLVM_TOOLS { trace!("installing `{tool}`"); let tool_exe = exe(tool, target_compiler.host); let src_path = llvm_bin_dir.join(&tool_exe); diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 43ac42a8158ba..e2301673141c6 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -40,7 +40,27 @@ use crate::utils::helpers::{ exe, is_dylib, move_file, t, target_supports_cranelift_backend, timeit, }; use crate::utils::tarball::{GeneratedTarball, OverlayKind, Tarball}; -use crate::{CodegenBackendKind, Compiler, DependencyType, FileType, LLVM_TOOLS, Mode, trace}; +use crate::{CodegenBackendKind, Compiler, DependencyType, FileType, Mode, trace}; + +pub(crate) const LLVM_TOOLS: &[&str] = &[ + "llvm-cov", // used to generate coverage report + "llvm-nm", // used to inspect binaries; it shows symbol names, their sizes and visibility + "llvm-objcopy", // used to transform ELFs into binary format which flashing tools consume + "llvm-objdump", // used to disassemble programs + "llvm-profdata", // used to inspect and merge files generated by profiles + "llvm-readobj", // used to get information from ELFs/objects that the other tools don't provide + "llvm-size", // used to prints the size of the linker sections of a program + "llvm-strip", // used to discard symbols from binary files to reduce their size + "llvm-ar", // used for creating and modifying archive files + "llvm-as", // used to convert LLVM assembly to LLVM bitcode + "llvm-dis", // used to disassemble LLVM bitcode + "llvm-link", // Used to link LLVM bitcode + "llc", // used to compile LLVM bytecode + "opt", // used to optimize LLVM bytecode +]; + +/// LLD file names for all flavors. +pub(crate) const LLD_FILE_NAMES: &[&str] = &["ld.lld", "ld64.lld", "lld-link", "wasm-ld"]; pub fn pkgname(builder: &Builder<'_>, component: &str) -> String { format!("{}-{}", component, builder.rust_package_vers()) @@ -598,7 +618,7 @@ impl CommandLineStep for Rustc { let self_contained_lld_src_dir = src_dir.join("gcc-ld"); let self_contained_lld_dst_dir = dst_dir.join("gcc-ld"); t!(fs::create_dir(&self_contained_lld_dst_dir)); - for name in crate::LLD_FILE_NAMES { + for name in LLD_FILE_NAMES { let exe_name = exe(name, target_compiler.host); builder.copy_link( &self_contained_lld_src_dir.join(&exe_name), diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 75d5fdcdd2c33..32dd25e88ed2f 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -14,6 +14,7 @@ use std::path::{Path, PathBuf}; use std::{env, fs}; use crate::core::build_steps::compile::{CargoMessage, is_lto_stage}; +use crate::core::build_steps::dist::LLD_FILE_NAMES; use crate::core::build_steps::toolstate::ToolState; use crate::core::build_steps::{compile, llvm}; use crate::core::builder::{ @@ -978,7 +979,7 @@ pub(crate) fn copy_lld_artifacts( let self_contained_lld_dir = libdir_bin.join("gcc-ld"); t!(fs::create_dir_all(&self_contained_lld_dir)); - for name in crate::LLD_FILE_NAMES { + for name in LLD_FILE_NAMES { builder.copy_link( &lld_wrapper.tool.tool_path, &self_contained_lld_dir.join(exe(name, target)), diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 116099ee3972f..800099d6fdae6 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -50,26 +50,6 @@ pub mod cli_main; mod core; mod utils; -const LLVM_TOOLS: &[&str] = &[ - "llvm-cov", // used to generate coverage report - "llvm-nm", // used to inspect binaries; it shows symbol names, their sizes and visibility - "llvm-objcopy", // used to transform ELFs into binary format which flashing tools consume - "llvm-objdump", // used to disassemble programs - "llvm-profdata", // used to inspect and merge files generated by profiles - "llvm-readobj", // used to get information from ELFs/objects that the other tools don't provide - "llvm-size", // used to prints the size of the linker sections of a program - "llvm-strip", // used to discard symbols from binary files to reduce their size - "llvm-ar", // used for creating and modifying archive files - "llvm-as", // used to convert LLVM assembly to LLVM bitcode - "llvm-dis", // used to disassemble LLVM bitcode - "llvm-link", // Used to link LLVM bitcode - "llc", // used to compile LLVM bytecode - "opt", // used to optimize LLVM bytecode -]; - -/// LLD file names for all flavors. -const LLD_FILE_NAMES: &[&str] = &["ld.lld", "ld64.lld", "lld-link", "wasm-ld"]; - /// Extra `--check-cfg` to add when building the compiler or tools /// (Mode restriction, config name, config values (if any)) #[expect(clippy::type_complexity)] // It's fine for hard-coded list and type is explained above. From 73960bfc3c658c26cdfbb6c7e95bed56589e542f Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 16 Aug 2026 14:45:05 +1000 Subject: [PATCH 46/66] Move `EXTRA_CHECK_CFGS` into `crate::core::builder::cargo` --- src/bootstrap/src/core/builder/cargo.rs | 20 ++++++++++++++++---- src/bootstrap/src/lib.rs | 15 --------------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 1ae2f69ccfe84..e5a9f405dc1cf 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -11,10 +11,22 @@ use crate::core::config::{CompressDebuginfo, Config, DryRun, SplitDebuginfo, Tar use crate::utils::build_stamp; use crate::utils::exec::{BootstrapCommand, command}; use crate::utils::helpers::{self, LldThreads, check_cfg_arg, linker_flags, t}; -use crate::{ - CLang, Compiler, EXTRA_CHECK_CFGS, GitRepo, Mode, RemapScheme, envify, - prepare_behaviour_dump_dir, -}; +use crate::{CLang, Compiler, GitRepo, Mode, RemapScheme, envify, prepare_behaviour_dump_dir}; + +/// Extra `--check-cfg` to add when building the compiler or tools +/// (Mode restriction, config name, config values (if any)) +#[expect(clippy::type_complexity)] // It's fine for hard-coded list and type is explained above. +const EXTRA_CHECK_CFGS: &[(Option, &str, Option<&[&'static str]>)] = &[ + (Some(Mode::Rustc), "bootstrap", None), + (Some(Mode::Codegen), "bootstrap", None), + (Some(Mode::ToolRustcPrivate), "bootstrap", None), + (Some(Mode::ToolStd), "bootstrap", None), + (Some(Mode::ToolRustcPrivate), "rust_analyzer", None), + (Some(Mode::ToolStd), "rust_analyzer", None), + // Any library specific cfgs like `target_os`, `target_arch` should be put in + // priority the `[lints.rust.unexpected_cfgs.check-cfg]` table + // in the appropriate `library/{std,alloc,core}/Cargo.toml` +]; /// Represents flag values in `String` form with a `\x1f` delimiter to pass to the compiler later. /// diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 800099d6fdae6..61165cee13b40 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -50,21 +50,6 @@ pub mod cli_main; mod core; mod utils; -/// Extra `--check-cfg` to add when building the compiler or tools -/// (Mode restriction, config name, config values (if any)) -#[expect(clippy::type_complexity)] // It's fine for hard-coded list and type is explained above. -const EXTRA_CHECK_CFGS: &[(Option, &str, Option<&[&'static str]>)] = &[ - (Some(Mode::Rustc), "bootstrap", None), - (Some(Mode::Codegen), "bootstrap", None), - (Some(Mode::ToolRustcPrivate), "bootstrap", None), - (Some(Mode::ToolStd), "bootstrap", None), - (Some(Mode::ToolRustcPrivate), "rust_analyzer", None), - (Some(Mode::ToolStd), "rust_analyzer", None), - // Any library specific cfgs like `target_os`, `target_arch` should be put in - // priority the `[lints.rust.unexpected_cfgs.check-cfg]` table - // in the appropriate `library/{std,alloc,core}/Cargo.toml` -]; - /// A structure representing a Rust compiler. /// /// Each compiler has a `stage` that it is associated with and a `host` that From 43620bc5ef5e112c69f6df3bdd4c09c3dcae7abc Mon Sep 17 00:00:00 2001 From: vad Date: Sun, 16 Aug 2026 09:05:07 +0200 Subject: [PATCH 47/66] Describe Linux kernel version reqirements --- .../rustc/src/platform-support/bpf-unknown-none.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/doc/rustc/src/platform-support/bpf-unknown-none.md b/src/doc/rustc/src/platform-support/bpf-unknown-none.md index 430b5bc28646c..84242755a9b24 100644 --- a/src/doc/rustc/src/platform-support/bpf-unknown-none.md +++ b/src/doc/rustc/src/platform-support/bpf-unknown-none.md @@ -22,6 +22,14 @@ They don't support std and alloc and are meant for a `no_std` environment. Produced binaries use the ELF format. +BPF virtual machines provide a [JIT compiler][jit] that compiles the BPF +bytecode into the native host architecture. + +Running BPF programs on most host architectures requires Linux kernel 4.18, +[that introduced BTF][linux-commit-btf], or newer. On RISC-V hosts that +requirement goes up to [5.7][linux-commit-riscv], on PowerPC32 - to [5.13] +[linux-commit-ppc32], and on LoongArch - to [6.1][linux-commit-loongarch]. + ## Building the target You can build Rust with support for BPF targets by adding them to the `target` @@ -143,6 +151,11 @@ println!("cargo:rustc-link-lib=link-arg={out_dir}/my_module.bpf.o"); [ebpf]: https://ebpf.io/ [bpf-linker]: https://github.com/aya-rs/bpf-linker [bpf-abi]: https://www.kernel.org/doc/html/v6.13-rc5/bpf/standardization/abi.html +[jit]: https://www.kernel.org/doc/html/latest/networking/filter.html#jit-compiler +[linux-commit-btf]: https://github.com/torvalds/linux/commit/69b693f0a +[linux-commit-riscv]: https://github.com/torvalds/linux/commit/5f316b65e +[linux-commit-ppc32]: https://github.com/torvalds/linux/commit/51c66ad84 +[linux-commit-loongarch]: https://github.com/torvalds/linux/commit/5dc615520 [btf]: https://www.kernel.org/doc/html/latest/bpf/btf.html [rbpf]: https://github.com/qmonnet/rbpf [kprobe]: https://www.kernel.org/doc/html/latest/trace/kprobes.html From 7e2707604356187efe9a5e81fe147927d996aea4 Mon Sep 17 00:00:00 2001 From: vad Date: Sun, 16 Aug 2026 09:10:47 +0200 Subject: [PATCH 48/66] Mention the plan of using GNU flabor of linker --- src/doc/rustc/src/platform-support/bpf-unknown-none.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/doc/rustc/src/platform-support/bpf-unknown-none.md b/src/doc/rustc/src/platform-support/bpf-unknown-none.md index 84242755a9b24..be6975f6ee330 100644 --- a/src/doc/rustc/src/platform-support/bpf-unknown-none.md +++ b/src/doc/rustc/src/platform-support/bpf-unknown-none.md @@ -63,7 +63,9 @@ cargo +nightly build -Z build-std=core --target bpfel-unknown-none BPF has its own debug info format called [BTF][btf]. -BPF targets use [bpf-linker], an LLVM bitcode linker. +BPF targets use [bpf-linker], an LLVM bitcode linker. In future, they may +migrate to the GNU flavor of linker, see the details in the [following issue] +[bpf-object-linking]. ## Error handling @@ -157,6 +159,7 @@ println!("cargo:rustc-link-lib=link-arg={out_dir}/my_module.bpf.o"); [linux-commit-ppc32]: https://github.com/torvalds/linux/commit/51c66ad84 [linux-commit-loongarch]: https://github.com/torvalds/linux/commit/5dc615520 [btf]: https://www.kernel.org/doc/html/latest/bpf/btf.html +[bpf-object-linking]: https://github.com/rust-lang/rust/issues/135175 [rbpf]: https://github.com/qmonnet/rbpf [kprobe]: https://www.kernel.org/doc/html/latest/trace/kprobes.html [fprobe]: https://www.kernel.org/doc/html/latest/trace/fprobe.html From 3da3659c076bfa4b91f02329e57476fc953fd6ba Mon Sep 17 00:00:00 2001 From: vad Date: Sun, 16 Aug 2026 09:12:42 +0200 Subject: [PATCH 49/66] Fix the link to the BPF ABI convention --- src/doc/rustc/src/platform-support/bpf-unknown-none.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc/src/platform-support/bpf-unknown-none.md b/src/doc/rustc/src/platform-support/bpf-unknown-none.md index be6975f6ee330..a25520564d505 100644 --- a/src/doc/rustc/src/platform-support/bpf-unknown-none.md +++ b/src/doc/rustc/src/platform-support/bpf-unknown-none.md @@ -152,7 +152,7 @@ println!("cargo:rustc-link-lib=link-arg={out_dir}/my_module.bpf.o"); [ebpf]: https://ebpf.io/ [bpf-linker]: https://github.com/aya-rs/bpf-linker -[bpf-abi]: https://www.kernel.org/doc/html/v6.13-rc5/bpf/standardization/abi.html +[bpf-abi]: https://www.kernel.org/doc/html/latest/bpf/standardization/abi.html [jit]: https://www.kernel.org/doc/html/latest/networking/filter.html#jit-compiler [linux-commit-btf]: https://github.com/torvalds/linux/commit/69b693f0a [linux-commit-riscv]: https://github.com/torvalds/linux/commit/5f316b65e From ea31c35e770e1c4ee8aaba2ca32589b604a8bbb0 Mon Sep 17 00:00:00 2001 From: vad Date: Sun, 16 Aug 2026 09:14:50 +0200 Subject: [PATCH 50/66] Remove the "no-op" word from panic handler description It's confusing, and the next paragraph explains what's exactly happening. --- src/doc/rustc/src/platform-support/bpf-unknown-none.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc/src/platform-support/bpf-unknown-none.md b/src/doc/rustc/src/platform-support/bpf-unknown-none.md index a25520564d505..2e0e7e2f9d023 100644 --- a/src/doc/rustc/src/platform-support/bpf-unknown-none.md +++ b/src/doc/rustc/src/platform-support/bpf-unknown-none.md @@ -71,7 +71,7 @@ migrate to the GNU flavor of linker, see the details in the [following issue] There is no concept of stack unwinding in BPF, therefore BPF programs are expected to handle errors in a recoverable manner. Therefore most BPF programs -written in Rust use the following no-op panic handler implementation: +written in Rust use the following panic handler implementation: ```rust,ignore (a panic handler implementation specific to BPF targets) #[cfg(not(test))] From 5e54aa26a041edfdbfb834c6186970492b047a1a Mon Sep 17 00:00:00 2001 From: vad Date: Sun, 16 Aug 2026 09:28:30 +0200 Subject: [PATCH 51/66] Pin all kernel links to the v6.13 --- .../rustc/src/platform-support/bpf-unknown-none.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/doc/rustc/src/platform-support/bpf-unknown-none.md b/src/doc/rustc/src/platform-support/bpf-unknown-none.md index 2e0e7e2f9d023..4b1d46e0b41d7 100644 --- a/src/doc/rustc/src/platform-support/bpf-unknown-none.md +++ b/src/doc/rustc/src/platform-support/bpf-unknown-none.md @@ -152,18 +152,18 @@ println!("cargo:rustc-link-lib=link-arg={out_dir}/my_module.bpf.o"); [ebpf]: https://ebpf.io/ [bpf-linker]: https://github.com/aya-rs/bpf-linker -[bpf-abi]: https://www.kernel.org/doc/html/latest/bpf/standardization/abi.html -[jit]: https://www.kernel.org/doc/html/latest/networking/filter.html#jit-compiler +[bpf-abi]: https://www.kernel.org/doc/html/v6.13/bpf/standardization/abi.html +[jit]: https://www.kernel.org/doc/html/v6.13/networking/filter.html#jit-compiler [linux-commit-btf]: https://github.com/torvalds/linux/commit/69b693f0a [linux-commit-riscv]: https://github.com/torvalds/linux/commit/5f316b65e [linux-commit-ppc32]: https://github.com/torvalds/linux/commit/51c66ad84 [linux-commit-loongarch]: https://github.com/torvalds/linux/commit/5dc615520 -[btf]: https://www.kernel.org/doc/html/latest/bpf/btf.html +[btf]: https://www.kernel.org/doc/html/v6.13/bpf/btf.html [bpf-object-linking]: https://github.com/rust-lang/rust/issues/135175 [rbpf]: https://github.com/qmonnet/rbpf -[kprobe]: https://www.kernel.org/doc/html/latest/trace/kprobes.html -[fprobe]: https://www.kernel.org/doc/html/latest/trace/fprobe.html -[uprobe]: https://www.kernel.org/doc/html/latest/trace/uprobetracer.html +[kprobe]: https://www.kernel.org/doc/html/v6.13/trace/kprobes.html +[fprobe]: https://www.kernel.org/doc/html/v6.13/trace/fprobe.html +[uprobe]: https://www.kernel.org/doc/html/v6.13/trace/uprobetracer.html [pt-regs]: https://elixir.bootlin.com/linux/v6.12.6/source/arch/x86/include/uapi/asm/ptrace.h#L44 [aya]: https://aya-rs.dev [aya-ebpf-cty]: https://github.com/aya-rs/aya/tree/main/ebpf/aya-ebpf-cty From 8677009733a5bb0ae659f599796fcead52bd1de9 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 16 Aug 2026 10:29:41 +0200 Subject: [PATCH 52/66] Prepare for merging from rust-lang/rust This updates the rust-version file to 67854e511de21d881bb16426996cd4259d44aa2e. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index fdcf0a5aff4d4..6c163e62d963d 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -4667d75565e47ba5df36c0df598c556b543e8624 +67854e511de21d881bb16426996cd4259d44aa2e From b77b9cf2c3da9af175b4185cdcc7c0bc98c70ffc Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Fri, 14 Aug 2026 10:41:05 -0400 Subject: [PATCH 53/66] fmt, clippy --- src/tools/miri/priroda/src/debugger.rs | 10 +++------- src/tools/miri/src/bin/miri.rs | 4 ++-- .../tests/fail/validity/maybe_dangling_ref_too_big.rs | 2 +- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/tools/miri/priroda/src/debugger.rs b/src/tools/miri/priroda/src/debugger.rs index e6428b4f15ebf..646a4f2c570a5 100644 --- a/src/tools/miri/priroda/src/debugger.rs +++ b/src/tools/miri/priroda/src/debugger.rs @@ -505,17 +505,14 @@ impl<'tcx> PrirodaContext<'tcx> { // view before fields can be projected. Structs use their sole // variant directly. Keep the display name tied to the same choice. let (variant_idx, down, name) = if def.is_enum() { - let Some(variant_idx) = - self.ecx.read_discriminant(&op).discard_err() - else { + let Some(variant_idx) = self.ecx.read_discriminant(&op).discard_err() else { // FIXME: expose this as an explicit render error when // Priroda grows structured value states. Falling back to // bytes keeps today's UI usable but hides why the enum // could not be source-shaped. return self.render_op(op); }; - let Some(down) = - self.ecx.project_downcast(&op, variant_idx).discard_err() + let Some(down) = self.ecx.project_downcast(&op, variant_idx).discard_err() else { // FIXME: distinguish invalid/uninitialized discriminants // from projection bugs in the rendered output once locals @@ -541,8 +538,7 @@ impl<'tcx> PrirodaContext<'tcx> { let field_idx = FieldIdx::from_usize(i); // `project_field` avoids manual offset math and works for both // immediate and memory-backed operands through `Projectable`. - let Some(field_op) = - self.ecx.project_field(&down, field_idx).discard_err() + let Some(field_op) = self.ecx.project_field(&down, field_idx).discard_err() else { // FIXME: preserve the successfully rendered fields and // mark only this field as unavailable once the value model diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 07d64286d0c9b..ae9a64b0abcf4 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -199,11 +199,11 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { // Process interpreter result. if let Err(return_code) = res { tcx.dcx().abort_if_errors(); - exit(return_code.get()); + exit(return_code.get()) } else { // We want to continue here so rustc can do its usual shutdown and finalize the // incremental session. Our custom codegen backend ensures nothing actually happens. - return Compilation::Continue; + Compilation::Continue } } } diff --git a/src/tools/miri/tests/fail/validity/maybe_dangling_ref_too_big.rs b/src/tools/miri/tests/fail/validity/maybe_dangling_ref_too_big.rs index 350e46a31df64..37bbf955c9709 100644 --- a/src/tools/miri/tests/fail/validity/maybe_dangling_ref_too_big.rs +++ b/src/tools/miri/tests/fail/validity/maybe_dangling_ref_too_big.rs @@ -1,5 +1,5 @@ #![feature(maybe_dangling)] -use std::mem::{transmute, MaybeDangling}; +use std::mem::{MaybeDangling, transmute}; fn main() { let _x: MaybeDangling<&i8> = unsafe { transmute(usize::MAX) }; From 6d635dad073772eeb0c9bbec062ad796b38f488b Mon Sep 17 00:00:00 2001 From: joboet Date: Sun, 16 Aug 2026 11:25:43 +0200 Subject: [PATCH 54/66] std: guard against unwinds in queue-based `Once` --- library/std/src/sys/sync/once/queue.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/library/std/src/sys/sync/once/queue.rs b/library/std/src/sys/sync/once/queue.rs index f64f6523d1432..311c7b0db2d90 100644 --- a/library/std/src/sys/sync/once/queue.rs +++ b/library/std/src/sys/sync/once/queue.rs @@ -60,7 +60,7 @@ use crate::sync::atomic::Ordering::{AcqRel, Acquire, Release}; use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr}; use crate::sync::once::OnceExclusiveState; use crate::thread::{self, Thread}; -use crate::{fmt, ptr, sync as public}; +use crate::{fmt, mem, ptr, sync as public}; type StateAndQueue = *mut (); @@ -237,6 +237,15 @@ impl Once { } } +/// A type to guard against the unwinds of stacks that nodes are located on due to panics. +struct PanicGuard; + +impl Drop for PanicGuard { + fn drop(&mut self) { + rtabort!("tried to drop node in intrusive list."); + } +} + fn wait( state_and_queue: &Atomic<*mut ()>, mut current: StateAndQueue, @@ -272,6 +281,9 @@ fn wait( continue; } + // Guard against unwinds using a `PanicGuard` that aborts when dropped. + let guard = PanicGuard; + // We have enqueued ourselves, now lets wait. // It is important not to return before being signaled, otherwise we // would drop our `Waiter` node and leave a hole in the linked list @@ -288,6 +300,9 @@ fn wait( unsafe { node.thread.park() } } + // The node was removed from the queue, disarm the guard. + mem::forget(guard); + return state_and_queue.load(Acquire); } } From 148cb752a3116adb52c9dfbbce04d4aec48bc72e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 16 Aug 2026 10:55:23 +0200 Subject: [PATCH 55/66] bless and fix tests --- .../genmc/pass/atomics/cas_failure_ord_racy_key_init.stderr | 2 ++ src/tools/miri/tests/genmc/pass/atomics/cas_simple.stderr | 2 ++ src/tools/miri/tests/genmc/pass/shims/mutex_deadlock.rs | 1 - src/tools/miri/tests/pass/shims/fs.rs | 1 + 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.stderr b/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.stderr index 720879217679a..f0c3377623058 100644 --- a/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.stderr +++ b/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.stderr @@ -27,3 +27,5 @@ LL | | ) | |_________^ Verification complete with 2 executions. No errors found. +warning: 1 warning emitted + diff --git a/src/tools/miri/tests/genmc/pass/atomics/cas_simple.stderr b/src/tools/miri/tests/genmc/pass/atomics/cas_simple.stderr index 4351b312c75dc..59785a2f19d7b 100644 --- a/src/tools/miri/tests/genmc/pass/atomics/cas_simple.stderr +++ b/src/tools/miri/tests/genmc/pass/atomics/cas_simple.stderr @@ -18,3 +18,5 @@ LL | let _ = VALUE.compare_exchange_weak(99, 99, Relaxed, SeqCst); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code Verification complete with 1 executions. No errors found. +warning: 3 warnings emitted + diff --git a/src/tools/miri/tests/genmc/pass/shims/mutex_deadlock.rs b/src/tools/miri/tests/genmc/pass/shims/mutex_deadlock.rs index e2337c2ed3cd0..a11c254a29fa7 100644 --- a/src/tools/miri/tests/genmc/pass/shims/mutex_deadlock.rs +++ b/src/tools/miri/tests/genmc/pass/shims/mutex_deadlock.rs @@ -9,7 +9,6 @@ // FIXME(genmc): use `std::thread` once GenMC mode performance is better and produces fewer warnings for compare_exchange. #![no_main] -#![feature(abort_unwind)] #[path = "../../../utils/genmc.rs"] mod genmc; diff --git a/src/tools/miri/tests/pass/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs index 22bb2b4159d53..1317c08376467 100644 --- a/src/tools/miri/tests/pass/shims/fs.rs +++ b/src/tools/miri/tests/pass/shims/fs.rs @@ -4,6 +4,7 @@ #![feature(io_error_more)] #![feature(io_error_uncategorized)] #![cfg_attr(unix, feature(unix_file_vectored_at))] +#![allow(unused_features)] // feature use depends on target use std::collections::BTreeMap; use std::ffi::OsString; From 96123571d2bca493a01c3604ac5af52436a5085c Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 16 Aug 2026 13:17:00 +1000 Subject: [PATCH 56/66] Move `Compiler` to its own module `crate::core::compiler` --- src/bootstrap/src/core/build_steps/check.rs | 3 +- src/bootstrap/src/core/build_steps/clean.rs | 3 +- src/bootstrap/src/core/build_steps/clippy.rs | 3 +- src/bootstrap/src/core/build_steps/compile.rs | 5 +- src/bootstrap/src/core/build_steps/dist.rs | 3 +- src/bootstrap/src/core/build_steps/doc.rs | 3 +- src/bootstrap/src/core/build_steps/install.rs | 2 +- .../src/core/build_steps/synthetic_targets.rs | 2 +- src/bootstrap/src/core/build_steps/test.rs | 3 +- src/bootstrap/src/core/build_steps/tool.rs | 3 +- src/bootstrap/src/core/builder/cargo.rs | 3 +- src/bootstrap/src/core/builder/mod.rs | 3 +- src/bootstrap/src/core/builder/tests.rs | 2 +- src/bootstrap/src/core/compiler.rs | 51 +++++++++++++++++++ src/bootstrap/src/core/mod.rs | 1 + src/bootstrap/src/lib.rs | 49 +----------------- src/bootstrap/src/utils/build_stamp.rs | 3 +- 17 files changed, 78 insertions(+), 64 deletions(-) create mode 100644 src/bootstrap/src/core/compiler.rs diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index ff16a814c456d..e72f1d05003b0 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -16,11 +16,12 @@ use crate::core::builder::{ self, Alias, Builder, Cargo, CommandLineStep, Kind, RunConfig, ShouldRun, Step, StepMetadata, crate_description, }; +use crate::core::compiler::Compiler; use crate::core::config::TargetSelection; use crate::core::config::flags::Subcommand; use crate::utils::build_stamp::{self, BuildStamp}; use crate::utils::helpers::t; -use crate::{CodegenBackendKind, Compiler, Mode}; +use crate::{CodegenBackendKind, Mode}; /// Allows individual check-step instances to keep track of whether they /// represent `cargo check` or `cargo fix`, independently of [`Builder::kind`]. diff --git a/src/bootstrap/src/core/build_steps/clean.rs b/src/bootstrap/src/core/build_steps/clean.rs index b7dae28c42ab3..a5c7398d11302 100644 --- a/src/bootstrap/src/core/build_steps/clean.rs +++ b/src/bootstrap/src/core/build_steps/clean.rs @@ -12,10 +12,11 @@ use std::path::Path; use crate::core::builder::{ Builder, CommandLineStep, Kind, RunConfig, ShouldRun, crate_description, }; +use crate::core::compiler::Compiler; use crate::core::config::flags::Subcommand; use crate::utils::build_stamp::BuildStamp; use crate::utils::helpers::t; -use crate::{Build, Compiler, Mode}; +use crate::{Build, Mode}; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CleanAll {} diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index 0d5dd410a57c6..8dc4e1f62eede 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -14,6 +14,7 @@ //! (as usual) a massive undertaking/refactoring. use super::tool::{SourceType, prepare_tool_cargo}; +use crate::Mode; use crate::core::build_steps::check::{CompilerForCheck, prepare_compiler_for_check}; use crate::core::build_steps::compile::{ ArtifactKeepMode, run_cargo, rustc_cargo, std_cargo, std_crates_for_make_run, @@ -22,11 +23,11 @@ use crate::core::builder::{ self, Alias, Builder, CommandLineStep, Kind, RunConfig, ShouldRun, StepMetadata, crate_description, }; +use crate::core::compiler::Compiler; use crate::core::config::TargetSelection; use crate::core::config::flags::Subcommand; use crate::utils::build_stamp::{self, BuildStamp}; use crate::utils::helpers; -use crate::{Compiler, Mode}; /// Disable the most spammy clippy lints const IGNORED_RULES_FOR_STD_AND_RUSTC: &[&str] = &[ diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index d3e454ae22c81..97c834cfaf666 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -26,6 +26,7 @@ use crate::core::builder::{ self, Builder, Cargo, CommandLineStep, Kind, RunConfig, ShouldRun, Step, StepMetadata, apply_pgo, crate_description, }; +use crate::core::compiler::Compiler; use crate::core::config::toml::target::DefaultLinuxLinkerOverride; use crate::core::config::{ Allocator, CompilerBuiltins, DebuginfoLevel, LlvmLibunwind, RustcLto, TargetSelection, @@ -36,9 +37,7 @@ use crate::utils::exec::command; use crate::utils::helpers::{ self, exe, get_clang_cl_resource_dir, is_debug_info, is_dylib, symlink_dir, t, up_to_date, }; -use crate::{ - CLang, CodegenBackendKind, Compiler, DependencyType, FileType, GitRepo, Mode, debug, trace, -}; +use crate::{CLang, CodegenBackendKind, DependencyType, FileType, GitRepo, Mode, debug, trace}; /// Build a standard library for the given `target` using the given `build_compiler`. #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index e2301673141c6..d45ddc4a4a257 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -32,6 +32,7 @@ use crate::core::build_steps::{compile, llvm}; use crate::core::builder::{ Builder, CommandLineStep, Kind, RunConfig, ShouldRun, Step, StepMetadata, }; +use crate::core::compiler::Compiler; use crate::core::config::{GccCiMode, TargetSelection}; use crate::utils::build_stamp::{self, BuildStamp}; use crate::utils::channel::{self, Info}; @@ -40,7 +41,7 @@ use crate::utils::helpers::{ exe, is_dylib, move_file, t, target_supports_cranelift_backend, timeit, }; use crate::utils::tarball::{GeneratedTarball, OverlayKind, Tarball}; -use crate::{CodegenBackendKind, Compiler, DependencyType, FileType, Mode, trace}; +use crate::{CodegenBackendKind, DependencyType, FileType, Mode, trace}; pub(crate) const LLVM_TOOLS: &[&str] = &[ "llvm-cov", // used to generate coverage report diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 21f1395351a80..adff654fe88e8 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -19,9 +19,10 @@ use crate::core::builder::{ self, Builder, CommandLineStep, Kind, RunConfig, ShouldRun, Step, StepMetadata, crate_description, }; +use crate::core::compiler::Compiler; use crate::core::config::{Config, TargetSelection}; use crate::utils::helpers::{submodule_path_of, symlink_dir, t, up_to_date}; -use crate::{Compiler, FileType, Mode}; +use crate::{FileType, Mode}; macro_rules! book { ($($name:ident, $path:expr, $book_name:expr, $lang:expr ;)+) => { diff --git a/src/bootstrap/src/core/build_steps/install.rs b/src/bootstrap/src/core/build_steps/install.rs index 766c2cc0297d6..db0d18e8368e2 100644 --- a/src/bootstrap/src/core/build_steps/install.rs +++ b/src/bootstrap/src/core/build_steps/install.rs @@ -6,10 +6,10 @@ use std::path::{Component, Path, PathBuf}; use std::{env, fs}; -use crate::Compiler; use crate::core::build_steps::dist; use crate::core::build_steps::tool::RustcPrivateCompilers; use crate::core::builder::{Builder, CommandLineStep, Kind, RunConfig, ShouldRun}; +use crate::core::compiler::Compiler; use crate::core::config::{Config, TargetSelection}; use crate::utils::exec::command; use crate::utils::helpers::t; diff --git a/src/bootstrap/src/core/build_steps/synthetic_targets.rs b/src/bootstrap/src/core/build_steps/synthetic_targets.rs index 88f04dcb27c6e..2b5039214f62c 100644 --- a/src/bootstrap/src/core/build_steps/synthetic_targets.rs +++ b/src/bootstrap/src/core/build_steps/synthetic_targets.rs @@ -7,8 +7,8 @@ //! one of the target specs already defined in this module, or create new ones by adding a new step //! that calls create_synthetic_target. -use crate::Compiler; use crate::core::builder::{Builder, Step}; +use crate::core::compiler::Compiler; use crate::core::config::TargetSelection; #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 6776487b5870e..7578a90d205c4 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -34,6 +34,7 @@ use crate::core::builder::{ self, Alias, Builder, CommandLineStep, Kind, RunConfig, ShouldRun, Step, StepMetadata, crate_description, }; +use crate::core::compiler::Compiler; use crate::core::config::TargetSelection; use crate::core::config::flags::{Subcommand, get_completion, top_level_help}; use crate::core::{android, debuggers}; @@ -45,7 +46,7 @@ use crate::utils::helpers::{ up_to_date, }; use crate::utils::render_tests::{add_flags_and_try_run_tests, try_run_tests}; -use crate::{CLang, CodegenBackendKind, Compiler, GitRepo, Mode, TestTarget, envify}; +use crate::{CLang, CodegenBackendKind, GitRepo, Mode, TestTarget, envify}; mod compiletest; pub mod failed_tests; diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 32dd25e88ed2f..f74207fdd6d32 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -21,10 +21,11 @@ use crate::core::builder::{ self, Builder, Cargo as CargoCommand, CommandLineStep, Kind, RunConfig, ShouldRun, Step, StepMetadata, apply_pgo, cargo_profile_var, }; +use crate::core::compiler::Compiler; use crate::core::config::{Allocator, DebuginfoLevel, RustcLto, TargetSelection}; use crate::utils::exec::{BootstrapCommand, command}; use crate::utils::helpers::{self, add_dylib_path, exe, t}; -use crate::{Compiler, FileType, Mode}; +use crate::{FileType, Mode}; #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub enum SourceType { diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index e5a9f405dc1cf..d157610add218 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -5,13 +5,14 @@ use std::path::{Path, PathBuf}; use super::{Builder, Kind}; use crate::core::build_steps::test; use crate::core::build_steps::tool::SourceType; +use crate::core::compiler::Compiler; use crate::core::config::flags::Color; use crate::core::config::toml::pgo::PgoConfig; use crate::core::config::{CompressDebuginfo, Config, DryRun, SplitDebuginfo, TargetSelection}; use crate::utils::build_stamp; use crate::utils::exec::{BootstrapCommand, command}; use crate::utils::helpers::{self, LldThreads, check_cfg_arg, linker_flags, t}; -use crate::{CLang, Compiler, GitRepo, Mode, RemapScheme, envify, prepare_behaviour_dump_dir}; +use crate::{CLang, GitRepo, Mode, RemapScheme, envify, prepare_behaviour_dump_dir}; /// Extra `--check-cfg` to add when building the compiler or tools /// (Mode restriction, config name, config values (if any)) diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index e941e26e43efb..0c57bbff1749a 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -21,6 +21,7 @@ use crate::core::build_steps::{ }; use crate::core::builder::step_stack::StepRecord; pub use crate::core::builder::step_stack::StepStack; +use crate::core::compiler::Compiler; use crate::core::config::flags::Subcommand; use crate::core::config::{DryRun, TargetSelection}; use crate::utils::build_stamp::BuildStamp; @@ -28,7 +29,7 @@ use crate::utils::cache::Cache; use crate::utils::exec::{BootstrapCommand, ExecutionContext, command}; use crate::utils::helpers::{self, LldThreads, add_dylib_path, exe, libdir, linker_args, t}; use crate::utils::tracing::format_location; -use crate::{Build, Compiler, Crate, trace}; +use crate::{Build, Crate, trace}; mod cargo; mod cli_paths; diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index d5e2ef1579dc1..223ecdc8298dd 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -389,10 +389,10 @@ fn any_debug() { /// These tests use insta for snapshot testing. /// See bootstrap's README on how to bless the snapshots. mod snapshot { - use crate::Compiler; use crate::core::build_steps::test; use crate::core::builder::tests::{RenderConfig, TEST_TRIPLE_1, TEST_TRIPLE_2, host_target}; use crate::core::builder::{Kind, StepMetadata}; + use crate::core::compiler::Compiler; use crate::core::config::TargetSelection; use crate::core::config::toml::target::{ DefaultLinuxLinkerOverride, with_default_linux_linker_overrides, diff --git a/src/bootstrap/src/core/compiler.rs b/src/bootstrap/src/core/compiler.rs new file mode 100644 index 0000000000000..a57c60465f24c --- /dev/null +++ b/src/bootstrap/src/core/compiler.rs @@ -0,0 +1,51 @@ +use std::hash::{Hash, Hasher}; + +use crate::Build; +use crate::core::config::TargetSelection; + +/// A structure representing a Rust compiler. +/// +/// Each compiler has a `stage` that it is associated with and a `host` that +/// corresponds to the platform the compiler runs on. +#[derive(Eq, PartialOrd, Ord, Clone, Copy, Debug)] +pub struct Compiler { + pub(crate) stage: u32, + pub(crate) host: TargetSelection, + /// Indicates whether the compiler was forced to use a specific stage. + /// This field is ignored in `Hash` and `PartialEq` implementations as only the `stage` + /// and `host` fields are relevant for those. + pub(crate) forced_compiler: bool, +} + +impl Hash for Compiler { + fn hash(&self, state: &mut H) { + self.stage.hash(state); + self.host.hash(state); + } +} + +impl PartialEq for Compiler { + fn eq(&self, other: &Self) -> bool { + self.stage == other.stage && self.host == other.host + } +} + +impl Compiler { + pub(crate) fn new(stage: u32, host: TargetSelection) -> Self { + Self { stage, host, forced_compiler: false } + } + + pub(crate) fn forced_compiler(&mut self, forced_compiler: bool) { + self.forced_compiler = forced_compiler; + } + + /// Returns `true` if this is a snapshot compiler for `build`'s configuration + pub(crate) fn is_snapshot(&self, build: &Build) -> bool { + self.stage == 0 && self.host == build.host_target + } + + /// Indicates whether the compiler was forced to use a specific stage. + pub(crate) fn is_forced_compiler(&self) -> bool { + self.forced_compiler + } +} diff --git a/src/bootstrap/src/core/mod.rs b/src/bootstrap/src/core/mod.rs index 4df2ed319da68..70962e16360bb 100644 --- a/src/bootstrap/src/core/mod.rs +++ b/src/bootstrap/src/core/mod.rs @@ -1,6 +1,7 @@ pub(crate) mod android; pub(crate) mod build_steps; pub(crate) mod builder; +pub(crate) mod compiler; pub(crate) mod config; pub(crate) mod debuggers; pub(crate) mod download; diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 61165cee13b40..e4d9388a6b959 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -37,6 +37,7 @@ use tracing::{instrument, span}; use crate::core::build_steps::format::InternalRustfmt; use crate::core::build_steps::vendor::VENDOR_DIR; use crate::core::builder::{Builder, Kind}; +use crate::core::compiler::Compiler; use crate::core::config::flags::{self, Subcommand}; use crate::core::config::{BootstrapOverrideLld, Config, DryRun, LlvmLibunwind, TargetSelection}; use crate::utils::build_stamp::BuildStamp; @@ -50,34 +51,6 @@ pub mod cli_main; mod core; mod utils; -/// A structure representing a Rust compiler. -/// -/// Each compiler has a `stage` that it is associated with and a `host` that -/// corresponds to the platform the compiler runs on. This structure is used as -/// a parameter to many methods below. -#[derive(Eq, PartialOrd, Ord, Clone, Copy, Debug)] -pub struct Compiler { - stage: u32, - host: TargetSelection, - /// Indicates whether the compiler was forced to use a specific stage. - /// This field is ignored in `Hash` and `PartialEq` implementations as only the `stage` - /// and `host` fields are relevant for those. - forced_compiler: bool, -} - -impl std::hash::Hash for Compiler { - fn hash(&self, state: &mut H) { - self.stage.hash(state); - self.host.hash(state); - } -} - -impl PartialEq for Compiler { - fn eq(&self, other: &Self) -> bool { - self.stage == other.stage && self.host == other.host - } -} - /// Represents a codegen backend. #[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] pub enum CodegenBackendKind { @@ -2003,26 +1976,6 @@ fn chmod(path: &Path, perms: u32) { #[cfg(windows)] fn chmod(_path: &Path, _perms: u32) {} -impl Compiler { - pub fn new(stage: u32, host: TargetSelection) -> Self { - Self { stage, host, forced_compiler: false } - } - - pub fn forced_compiler(&mut self, forced_compiler: bool) { - self.forced_compiler = forced_compiler; - } - - /// Returns `true` if this is a snapshot compiler for `build`'s configuration - pub fn is_snapshot(&self, build: &Build) -> bool { - self.stage == 0 && self.host == build.host_target - } - - /// Indicates whether the compiler was forced to use a specific stage. - pub fn is_forced_compiler(&self) -> bool { - self.forced_compiler - } -} - fn envify(s: &str) -> String { // Converting foo-bar to FOO_BAR is a fairly idomatic mapping to an environment variable name. // We also convert '.' to '_' to fix https://github.com/rust-lang/rust/issues/158090 diff --git a/src/bootstrap/src/utils/build_stamp.rs b/src/bootstrap/src/utils/build_stamp.rs index de7f4c7343c5e..3b122064d1639 100644 --- a/src/bootstrap/src/utils/build_stamp.rs +++ b/src/bootstrap/src/utils/build_stamp.rs @@ -8,9 +8,10 @@ use std::{fs, io}; use sha2::digest::Digest; use crate::core::builder::Builder; +use crate::core::compiler::Compiler; use crate::core::config::TargetSelection; use crate::utils::helpers::{self, hex_encode, mtime, t}; -use crate::{CodegenBackendKind, Compiler, Mode}; +use crate::{CodegenBackendKind, Mode}; #[cfg(test)] mod tests; From 38c260e1f714571dcb7e1b8973069c6f7b36743e Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 16 Aug 2026 14:53:17 +1000 Subject: [PATCH 57/66] Move `CodegenBackendKind` to its own module `crate::core::backend` --- src/bootstrap/src/core/backend.rs | 48 +++++++++++++++++ src/bootstrap/src/core/build_steps/check.rs | 3 +- src/bootstrap/src/core/build_steps/compile.rs | 3 +- src/bootstrap/src/core/build_steps/dist.rs | 3 +- src/bootstrap/src/core/build_steps/test.rs | 3 +- src/bootstrap/src/core/config/config.rs | 2 +- src/bootstrap/src/core/config/flags.rs | 3 +- src/bootstrap/src/core/config/toml/rust.rs | 3 +- src/bootstrap/src/core/config/toml/target.rs | 2 +- src/bootstrap/src/core/mod.rs | 1 + src/bootstrap/src/lib.rs | 54 ------------------- src/bootstrap/src/utils/build_stamp.rs | 3 +- 12 files changed, 65 insertions(+), 63 deletions(-) create mode 100644 src/bootstrap/src/core/backend.rs diff --git a/src/bootstrap/src/core/backend.rs b/src/bootstrap/src/core/backend.rs new file mode 100644 index 0000000000000..bc8c8eb650aef --- /dev/null +++ b/src/bootstrap/src/core/backend.rs @@ -0,0 +1,48 @@ +use std::str::FromStr; + +/// Represents a codegen backend. +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] +pub enum CodegenBackendKind { + #[default] + Llvm, + Cranelift, + Gcc, + Custom(String), +} + +impl CodegenBackendKind { + /// Name of the codegen backend, as identified in the `compiler` directory + /// (`rustc_codegen_`). + pub(crate) fn name(&self) -> &str { + match self { + CodegenBackendKind::Llvm => "llvm", + CodegenBackendKind::Cranelift => "cranelift", + CodegenBackendKind::Gcc => "gcc", + CodegenBackendKind::Custom(name) => name, + } + } + + /// Name of the codegen backend's crate, e.g. `rustc_codegen_cranelift`. + pub(crate) fn crate_name(&self) -> String { + format!("rustc_codegen_{}", self.name()) + } + + pub(crate) fn is_llvm(&self) -> bool { + matches!(self, Self::Llvm) + } +} + +/// FIXME(Zalathar): This is partly redundant with the parsing code in `parse_codegen_backends`. +impl FromStr for CodegenBackendKind { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "" => Err("Invalid empty backend name"), + "gcc" => Ok(Self::Gcc), + "llvm" => Ok(Self::Llvm), + "cranelift" => Ok(Self::Cranelift), + _ => Ok(Self::Custom(s.to_string())), + } + } +} diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index e72f1d05003b0..b8918ec12fd9f 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -3,6 +3,8 @@ use std::fs; use std::path::{Path, PathBuf}; +use crate::Mode; +use crate::core::backend::CodegenBackendKind; use crate::core::build_steps::compile::{ ArtifactKeepMode, add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo, std_crates_for_make_run, @@ -21,7 +23,6 @@ use crate::core::config::TargetSelection; use crate::core::config::flags::Subcommand; use crate::utils::build_stamp::{self, BuildStamp}; use crate::utils::helpers::t; -use crate::{CodegenBackendKind, Mode}; /// Allows individual check-step instances to keep track of whether they /// represent `cargo check` or `cargo fix`, independently of [`Builder::kind`]. diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 97c834cfaf666..51ef4e8684a80 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -19,6 +19,7 @@ use serde_derive::Deserialize; #[cfg(feature = "tracing")] use tracing::span; +use crate::core::backend::CodegenBackendKind; use crate::core::build_steps::gcc::{Gcc, GccOutput, GccTargetPair}; use crate::core::build_steps::tool::{RustcPrivateCompilers, SourceType, copy_lld_artifacts}; use crate::core::build_steps::{dist, llvm}; @@ -37,7 +38,7 @@ use crate::utils::exec::command; use crate::utils::helpers::{ self, exe, get_clang_cl_resource_dir, is_debug_info, is_dylib, symlink_dir, t, up_to_date, }; -use crate::{CLang, CodegenBackendKind, DependencyType, FileType, GitRepo, Mode, debug, trace}; +use crate::{CLang, DependencyType, FileType, GitRepo, Mode, debug, trace}; /// Build a standard library for the given `target` using the given `build_compiler`. #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index d45ddc4a4a257..b9abd8e5d33fe 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -19,6 +19,7 @@ use object::read::archive::ArchiveFile; #[cfg(feature = "tracing")] use tracing::instrument; +use crate::core::backend::CodegenBackendKind; use crate::core::build_steps::compile::{ get_codegen_backend_file, libgccjit_path_relative_to_cg_dir, normalize_codegen_backend_name, }; @@ -41,7 +42,7 @@ use crate::utils::helpers::{ exe, is_dylib, move_file, t, target_supports_cranelift_backend, timeit, }; use crate::utils::tarball::{GeneratedTarball, OverlayKind, Tarball}; -use crate::{CodegenBackendKind, DependencyType, FileType, Mode, trace}; +use crate::{DependencyType, FileType, Mode, trace}; pub(crate) const LLVM_TOOLS: &[&str] = &[ "llvm-cov", // used to generate coverage report diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 7578a90d205c4..a69db6babaf8d 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -15,6 +15,7 @@ use std::{env, fs, iter}; use build_helper::git::get_closest_upstream_commit; +use crate::core::backend::CodegenBackendKind; use crate::core::build_steps::compile::{ArtifactKeepMode, Std, run_cargo}; use crate::core::build_steps::doc::{DocumentationFormat, prepare_doc_compiler}; use crate::core::build_steps::format::InternalRustfmt; @@ -46,7 +47,7 @@ use crate::utils::helpers::{ up_to_date, }; use crate::utils::render_tests::{add_flags_and_try_run_tests, try_run_tests}; -use crate::{CLang, CodegenBackendKind, GitRepo, Mode, TestTarget, envify}; +use crate::{CLang, GitRepo, Mode, TestTarget, envify}; mod compiletest; pub mod failed_tests; diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index f4d8db99aaf69..f60c34bc7059b 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -28,7 +28,7 @@ use serde::Deserialize; #[cfg(feature = "tracing")] use tracing::{instrument, span}; -use crate::CodegenBackendKind; +use crate::core::backend::CodegenBackendKind; use crate::core::build_steps::llvm; use crate::core::build_steps::llvm::LLVM_INVALIDATION_PATHS; use crate::core::build_steps::test::failed_tests::collect_previously_failed_tests; diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs index fa6225f6649e5..3bc5c9672b18c 100644 --- a/src/bootstrap/src/core/config/flags.rs +++ b/src/bootstrap/src/core/config/flags.rs @@ -10,13 +10,14 @@ use clap_complete::Generator; #[cfg(feature = "tracing")] use tracing::instrument; +use crate::core::backend::CodegenBackendKind; use crate::core::build_steps::perf::PerfArgs; use crate::core::build_steps::setup::Profile; use crate::core::builder::{Builder, Kind}; use crate::core::config::Config; use crate::core::config::target_selection::{TargetSelectionList, target_selection_list}; use crate::utils::helpers; -use crate::{Build, CodegenBackendKind, TestTarget}; +use crate::{Build, TestTarget}; #[derive(Copy, Clone, Default, Debug, ValueEnum)] pub enum Color { diff --git a/src/bootstrap/src/core/config/toml/rust.rs b/src/bootstrap/src/core/config/toml/rust.rs index 86056d489911b..0c88a3d8d0172 100644 --- a/src/bootstrap/src/core/config/toml/rust.rs +++ b/src/bootstrap/src/core/config/toml/rust.rs @@ -7,7 +7,7 @@ use std::path::PathBuf; use build_helper::ci::CiEnv; use serde::{Deserialize, Deserializer}; -use crate::CodegenBackendKind; +use crate::core::backend::CodegenBackendKind; use crate::core::config::macros::define_config; use crate::core::config::toml::TomlConfig; use crate::core::config::{CompressDebuginfo, DebuginfoLevel, StringOrBool, TargetSelection}; @@ -430,6 +430,7 @@ pub fn check_incompatible_options_for_ci_rustc( pub(crate) const BUILTIN_CODEGEN_BACKENDS: &[&str] = &["llvm", "cranelift", "gcc"]; +/// FIXME(Zalathar): This is partly redundant with the parsing code in [`CodegenBackendKind`]. pub(crate) fn parse_codegen_backends( backends: Vec, section: &str, diff --git a/src/bootstrap/src/core/config/toml/target.rs b/src/bootstrap/src/core/config/toml/target.rs index 32e0477f11853..090ccefe700ca 100644 --- a/src/bootstrap/src/core/config/toml/target.rs +++ b/src/bootstrap/src/core/config/toml/target.rs @@ -15,7 +15,7 @@ use std::path::PathBuf; use serde::de::Error; use serde::{Deserialize, Deserializer}; -use crate::CodegenBackendKind; +use crate::core::backend::CodegenBackendKind; use crate::core::config::macros::define_config; use crate::core::config::{ Allocator, CompilerBuiltins, CompressDebuginfo, LlvmLibunwind, SplitDebuginfo, StringOrBool, diff --git a/src/bootstrap/src/core/mod.rs b/src/bootstrap/src/core/mod.rs index 70962e16360bb..d6db6c701cc35 100644 --- a/src/bootstrap/src/core/mod.rs +++ b/src/bootstrap/src/core/mod.rs @@ -1,4 +1,5 @@ pub(crate) mod android; +pub(crate) mod backend; pub(crate) mod build_steps; pub(crate) mod builder; pub(crate) mod compiler; diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index e4d9388a6b959..6d810bed9b82b 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -51,60 +51,6 @@ pub mod cli_main; mod core; mod utils; -/// Represents a codegen backend. -#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] -pub enum CodegenBackendKind { - #[default] - Llvm, - Cranelift, - Gcc, - Custom(String), -} - -impl CodegenBackendKind { - /// Name of the codegen backend, as identified in the `compiler` directory - /// (`rustc_codegen_`). - pub fn name(&self) -> &str { - match self { - CodegenBackendKind::Llvm => "llvm", - CodegenBackendKind::Cranelift => "cranelift", - CodegenBackendKind::Gcc => "gcc", - CodegenBackendKind::Custom(name) => name, - } - } - - /// Name of the codegen backend's crate, e.g. `rustc_codegen_cranelift`. - pub fn crate_name(&self) -> String { - format!("rustc_codegen_{}", self.name()) - } - - pub fn is_llvm(&self) -> bool { - matches!(self, Self::Llvm) - } - - pub fn is_cranelift(&self) -> bool { - matches!(self, Self::Cranelift) - } - - pub fn is_gcc(&self) -> bool { - matches!(self, Self::Gcc) - } -} - -impl std::str::FromStr for CodegenBackendKind { - type Err = &'static str; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().as_str() { - "" => Err("Invalid empty backend name"), - "gcc" => Ok(Self::Gcc), - "llvm" => Ok(Self::Llvm), - "cranelift" => Ok(Self::Cranelift), - _ => Ok(Self::Custom(s.to_string())), - } - } -} - #[derive(PartialEq, Eq, Copy, Clone, Debug)] pub enum TestTarget { /// Run unit, integration and doc tests (default). diff --git a/src/bootstrap/src/utils/build_stamp.rs b/src/bootstrap/src/utils/build_stamp.rs index 3b122064d1639..d27d5fa2cf420 100644 --- a/src/bootstrap/src/utils/build_stamp.rs +++ b/src/bootstrap/src/utils/build_stamp.rs @@ -7,11 +7,12 @@ use std::{fs, io}; use sha2::digest::Digest; +use crate::Mode; +use crate::core::backend::CodegenBackendKind; use crate::core::builder::Builder; use crate::core::compiler::Compiler; use crate::core::config::TargetSelection; use crate::utils::helpers::{self, hex_encode, mtime, t}; -use crate::{CodegenBackendKind, Mode}; #[cfg(test)] mod tests; From 9ff48b214786950be57338f41d6e8711830479fc Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 16 Aug 2026 15:06:04 +1000 Subject: [PATCH 58/66] Move `TestTarget` into `build_steps::test` --- src/bootstrap/src/core/build_steps/test.rs | 20 +++++++++++++++++++- src/bootstrap/src/core/config/flags.rs | 3 ++- src/bootstrap/src/lib.rs | 19 +------------------ 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index a69db6babaf8d..fb7b85ee0bb85 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -47,11 +47,29 @@ use crate::utils::helpers::{ up_to_date, }; use crate::utils::render_tests::{add_flags_and_try_run_tests, try_run_tests}; -use crate::{CLang, GitRepo, Mode, TestTarget, envify}; +use crate::{CLang, GitRepo, Mode, envify}; mod compiletest; pub mod failed_tests; +#[derive(PartialEq, Eq, Copy, Clone, Debug)] +pub enum TestTarget { + /// Run unit, integration and doc tests (default). + Default, + /// Run unit, integration, doc tests, examples, bins, benchmarks (no doc tests). + AllTargets, + /// Only run doc tests. + DocOnly, + /// Only run unit and integration tests. + Tests, +} + +impl TestTarget { + pub(crate) fn runs_doctests(&self) -> bool { + matches!(self, TestTarget::DocOnly | TestTarget::Default) + } +} + /// Runs `cargo test` on various internal tools used by bootstrap. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CrateBootstrap { diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs index 3bc5c9672b18c..c8dbf8d4c4edf 100644 --- a/src/bootstrap/src/core/config/flags.rs +++ b/src/bootstrap/src/core/config/flags.rs @@ -10,14 +10,15 @@ use clap_complete::Generator; #[cfg(feature = "tracing")] use tracing::instrument; +use crate::Build; use crate::core::backend::CodegenBackendKind; use crate::core::build_steps::perf::PerfArgs; use crate::core::build_steps::setup::Profile; +use crate::core::build_steps::test::TestTarget; use crate::core::builder::{Builder, Kind}; use crate::core::config::Config; use crate::core::config::target_selection::{TargetSelectionList, target_selection_list}; use crate::utils::helpers; -use crate::{Build, TestTarget}; #[derive(Copy, Clone, Default, Debug, ValueEnum)] pub enum Color { diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 6d810bed9b82b..0bdfc3d22bb3f 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -35,6 +35,7 @@ use termcolor::{ColorChoice, StandardStream, WriteColor}; use tracing::{instrument, span}; use crate::core::build_steps::format::InternalRustfmt; +use crate::core::build_steps::test::TestTarget; use crate::core::build_steps::vendor::VENDOR_DIR; use crate::core::builder::{Builder, Kind}; use crate::core::compiler::Compiler; @@ -51,24 +52,6 @@ pub mod cli_main; mod core; mod utils; -#[derive(PartialEq, Eq, Copy, Clone, Debug)] -pub enum TestTarget { - /// Run unit, integration and doc tests (default). - Default, - /// Run unit, integration, doc tests, examples, bins, benchmarks (no doc tests). - AllTargets, - /// Only run doc tests. - DocOnly, - /// Only run unit and integration tests. - Tests, -} - -impl TestTarget { - fn runs_doctests(&self) -> bool { - matches!(self, TestTarget::DocOnly | TestTarget::Default) - } -} - pub enum GitRepo { Rustc, Llvm, From 57033a003353cb45347e1c01d3f7c7a1800c5fb4 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 16 Aug 2026 17:35:33 +1000 Subject: [PATCH 59/66] Move `Crate` into `crate::core::metadata` --- src/bootstrap/src/core/builder/mod.rs | 3 ++- src/bootstrap/src/core/metadata.rs | 19 +++++++++++++++++-- src/bootstrap/src/lib.rs | 15 +-------------- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 0c57bbff1749a..be50f1dd2ac9d 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -24,12 +24,13 @@ pub use crate::core::builder::step_stack::StepStack; use crate::core::compiler::Compiler; use crate::core::config::flags::Subcommand; use crate::core::config::{DryRun, TargetSelection}; +use crate::core::metadata::Crate; use crate::utils::build_stamp::BuildStamp; use crate::utils::cache::Cache; use crate::utils::exec::{BootstrapCommand, ExecutionContext, command}; use crate::utils::helpers::{self, LldThreads, add_dylib_path, exe, libdir, linker_args, t}; use crate::utils::tracing::format_location; -use crate::{Build, Crate, trace}; +use crate::{Build, trace}; mod cargo; mod cli_paths; diff --git a/src/bootstrap/src/core/metadata.rs b/src/bootstrap/src/core/metadata.rs index 14f33ef9bdc5d..a3b52e1071d24 100644 --- a/src/bootstrap/src/core/metadata.rs +++ b/src/bootstrap/src/core/metadata.rs @@ -5,14 +5,29 @@ //! source, dependencies, targets, and available features. The collected metadata is then //! used to update the `Build` structure, ensuring proper dependency resolution and //! compilation flow. -use std::collections::BTreeMap; + +use std::collections::{BTreeMap, HashSet}; use std::path::PathBuf; use serde_derive::Deserialize; +use crate::Build; use crate::utils::exec::command; use crate::utils::helpers::t; -use crate::{Build, Crate}; + +#[derive(Debug, Clone)] +pub(crate) struct Crate { + pub(crate) name: String, + pub(crate) deps: HashSet, + pub(crate) path: PathBuf, + pub(crate) features: Vec, +} + +impl Crate { + pub(crate) fn local_path(&self, build: &Build) -> PathBuf { + self.path.strip_prefix(&build.config.src).unwrap().into() + } +} /// For more information, see the output of /// diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 0bdfc3d22bb3f..081197bc92f6b 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -41,6 +41,7 @@ use crate::core::builder::{Builder, Kind}; use crate::core::compiler::Compiler; use crate::core::config::flags::{self, Subcommand}; use crate::core::config::{BootstrapOverrideLld, Config, DryRun, LlvmLibunwind, TargetSelection}; +use crate::core::metadata::Crate; use crate::utils::build_stamp::BuildStamp; use crate::utils::channel::GitInfo; use crate::utils::exec::{BootstrapCommand, ExecutionContext, command}; @@ -127,20 +128,6 @@ pub struct Build { step_graph: std::cell::RefCell, } -#[derive(Debug, Clone)] -struct Crate { - name: String, - deps: HashSet, - path: PathBuf, - features: Vec, -} - -impl Crate { - fn local_path(&self, build: &Build) -> PathBuf { - self.path.strip_prefix(&build.config.src).unwrap().into() - } -} - /// When building Rust various objects are handled differently. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum DependencyType { From af768f68258ddb95da012fbd6d0fdda5e1e27ef5 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 16 Aug 2026 13:43:53 +1000 Subject: [PATCH 60/66] Move `envify` into `crate::utils::helpers` and add a test --- src/bootstrap/src/core/build_steps/test.rs | 6 +++--- src/bootstrap/src/core/builder/cargo.rs | 6 +++--- src/bootstrap/src/lib.rs | 12 ------------ src/bootstrap/src/utils/helpers.rs | 19 +++++++++++++++++++ src/bootstrap/src/utils/helpers/tests.rs | 22 ++++++++++++++++++++-- 5 files changed, 45 insertions(+), 20 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index fb7b85ee0bb85..d816cb1989f9a 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -43,11 +43,11 @@ use crate::utils::build_stamp::{self, BuildStamp}; use crate::utils::exec::{BootstrapCommand, command}; use crate::utils::helpers::{ self, LldThreads, TestFilterCategory, add_dylib_path, add_rustdoc_cargo_linker_args, - dylib_path, dylib_path_var, linker_args, linker_flags, t, target_supports_cranelift_backend, - up_to_date, + dylib_path, dylib_path_var, envify, linker_args, linker_flags, t, + target_supports_cranelift_backend, up_to_date, }; use crate::utils::render_tests::{add_flags_and_try_run_tests, try_run_tests}; -use crate::{CLang, GitRepo, Mode, envify}; +use crate::{CLang, GitRepo, Mode}; mod compiletest; pub mod failed_tests; diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index d157610add218..f56fe4695d111 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -11,8 +11,8 @@ use crate::core::config::toml::pgo::PgoConfig; use crate::core::config::{CompressDebuginfo, Config, DryRun, SplitDebuginfo, TargetSelection}; use crate::utils::build_stamp; use crate::utils::exec::{BootstrapCommand, command}; -use crate::utils::helpers::{self, LldThreads, check_cfg_arg, linker_flags, t}; -use crate::{CLang, GitRepo, Mode, RemapScheme, envify, prepare_behaviour_dump_dir}; +use crate::utils::helpers::{self, LldThreads, check_cfg_arg, envify, linker_flags, t}; +use crate::{CLang, GitRepo, Mode, RemapScheme, prepare_behaviour_dump_dir}; /// Extra `--check-cfg` to add when building the compiler or tools /// (Mode restriction, config name, config values (if any)) @@ -56,7 +56,7 @@ impl Rustflags { self.env(prefix); // ... and also handle target-specific env RUSTFLAGS if they're configured. - let target_specific = format!("CARGO_TARGET_{}_{}", crate::envify(&self.1.triple), prefix); + let target_specific = format!("CARGO_TARGET_{}_{}", envify(&self.1.triple), prefix); self.env(&target_specific); } diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 081197bc92f6b..1f7cc994be938 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -1892,18 +1892,6 @@ fn chmod(path: &Path, perms: u32) { #[cfg(windows)] fn chmod(_path: &Path, _perms: u32) {} -fn envify(s: &str) -> String { - // Converting foo-bar to FOO_BAR is a fairly idomatic mapping to an environment variable name. - // We also convert '.' to '_' to fix https://github.com/rust-lang/rust/issues/158090 - s.chars() - .map(|c| match c { - '-' | '.' => '_', - c => c, - }) - .flat_map(|c| c.to_uppercase()) - .collect() -} - /// Ensures that the behavior dump directory is properly initialized. pub fn prepare_behaviour_dump_dir(build: &Build) { static INITIALIZED: OnceLock = OnceLock::new(); diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index 021763fdb371a..8e881f1bf7734 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -571,6 +571,25 @@ pub fn set_file_times>(path: P, times: fs::FileTimes) -> io::Resu f.set_times(times) } +/// Converts a target-tuple or other string into +/// [the form expected by cargo environment variable names][cargo-env]. +/// +/// For example: +/// - `x86_64-unknown-linux-gnu` => `X86_64_UNKNOWN_LINUX_GNU`. +/// +/// [cargo-env]: https://doc.rust-lang.org/cargo/reference/config.html#environment-variables +pub(crate) fn envify(s: &str) -> String { + // Converting foo-bar to FOO_BAR is a fairly idomatic mapping to an environment variable name. + // We also convert '.' to '_' to fix https://github.com/rust-lang/rust/issues/158090 + s.chars() + .map(|c| match c { + '-' | '.' => '_', + c => c, + }) + .flat_map(|c| c.to_uppercase()) + .collect() +} + /// Exits the process by calling [`std::process::exit`]. /// /// In CI, extra information will be printed to make failures easier to investigate. diff --git a/src/bootstrap/src/utils/helpers/tests.rs b/src/bootstrap/src/utils/helpers/tests.rs index cfb1dde2d6ea3..53d56169eb03e 100644 --- a/src/bootstrap/src/utils/helpers/tests.rs +++ b/src/bootstrap/src/utils/helpers/tests.rs @@ -3,8 +3,8 @@ use std::io::Write; use std::path::PathBuf; use crate::utils::helpers::{ - check_cfg_arg, extract_beta_rev, hex_encode, make, set_file_times, submodule_path_of_paths, - symlink_dir, + check_cfg_arg, envify, extract_beta_rev, hex_encode, make, set_file_times, + submodule_path_of_paths, symlink_dir, }; use crate::utils::tests::TestCtx; @@ -119,3 +119,21 @@ fn test_submodule_path_of() { // Make sure paths that only share a string prefix with a submodule are not matched. assert_eq!(submodule_path_of_paths(&submodules, "src/tools/cargo-vendor"), None); } + +#[test] +fn test_envify() { + struct Case { + input: &'static str, + expected: &'static str, + } + let cases = &[ + Case { input: "x86_64-unknown-linux-gnu", expected: "X86_64_UNKNOWN_LINUX_GNU" }, + // Arbitrary target containing `.` from the tier-3 target list. + Case { input: "thumbv8m.base-none-eabi", expected: "THUMBV8M_BASE_NONE_EABI" }, + ]; + + for &Case { input, expected } in cases { + let actual = envify(input); + assert_eq!(actual, expected, "input = {input:?}"); + } +} From d5a84642a4009b1617a4d28c4d899838315d8fc6 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 16 Aug 2026 16:38:05 +1000 Subject: [PATCH 61/66] Move/rename/adjust `prepare_behaviour_dump_dir` The destination is `crate::core::builder::cargo`, the new name is `prepare_shims_dump_dir`, and the adjustment is to take `&Builder<'_>` instead of `&Build`. --- src/bootstrap/src/core/builder/cargo.rs | 26 ++++++++++++++++++++++--- src/bootstrap/src/lib.rs | 19 ------------------ 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index f56fe4695d111..a70d36d7aafe9 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -1,6 +1,7 @@ -use std::env; use std::ffi::{OsStr, OsString}; use std::path::{Path, PathBuf}; +use std::sync::OnceLock; +use std::{env, fs}; use super::{Builder, Kind}; use crate::core::build_steps::test; @@ -12,7 +13,7 @@ use crate::core::config::{CompressDebuginfo, Config, DryRun, SplitDebuginfo, Tar use crate::utils::build_stamp; use crate::utils::exec::{BootstrapCommand, command}; use crate::utils::helpers::{self, LldThreads, check_cfg_arg, envify, linker_flags, t}; -use crate::{CLang, GitRepo, Mode, RemapScheme, prepare_behaviour_dump_dir}; +use crate::{CLang, GitRepo, Mode, RemapScheme}; /// Extra `--check-cfg` to add when building the compiler or tools /// (Mode restriction, config name, config values (if any)) @@ -1220,7 +1221,7 @@ impl Builder<'_> { } if self.config.dump_bootstrap_shims { - prepare_behaviour_dump_dir(self.build); + prepare_shims_dump_dir(self); cargo .env("DUMP_BOOTSTRAP_SHIMS", self.build.out.join("bootstrap-shims-dump")) @@ -1588,3 +1589,22 @@ pub fn apply_pgo( )); } } + +/// Ensures that the behavior dump directory is properly initialized. +fn prepare_shims_dump_dir(builder: &Builder<'_>) { + static INITIALIZED: OnceLock = OnceLock::new(); + + let dump_path = builder.out.join("bootstrap-shims-dump"); + + let initialized = INITIALIZED.get().unwrap_or(&false); + if !initialized { + // clear old dumps + if dump_path.exists() { + t!(fs::remove_dir_all(&dump_path)); + } + + t!(fs::create_dir_all(&dump_path)); + + t!(INITIALIZED.set(true)); + } +} diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 1f7cc994be938..2fc9dc0115872 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -1891,22 +1891,3 @@ fn chmod(path: &Path, perms: u32) { } #[cfg(windows)] fn chmod(_path: &Path, _perms: u32) {} - -/// Ensures that the behavior dump directory is properly initialized. -pub fn prepare_behaviour_dump_dir(build: &Build) { - static INITIALIZED: OnceLock = OnceLock::new(); - - let dump_path = build.out.join("bootstrap-shims-dump"); - - let initialized = INITIALIZED.get().unwrap_or(&false); - if !initialized { - // clear old dumps - if dump_path.exists() { - t!(fs::remove_dir_all(&dump_path)); - } - - t!(fs::create_dir_all(&dump_path)); - - t!(INITIALIZED.set(true)); - } -} From d7bd2df6bbd84aca8ffe53cd2e06cef892b64ca8 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 16 Aug 2026 12:20:03 +0200 Subject: [PATCH 62/66] Add code comment explaining why we need this binutils version --- src/ci/docker/scripts/build-gcc.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ci/docker/scripts/build-gcc.sh b/src/ci/docker/scripts/build-gcc.sh index 29502ec56a767..c1c94a89d17e7 100755 --- a/src/ci/docker/scripts/build-gcc.sh +++ b/src/ci/docker/scripts/build-gcc.sh @@ -4,6 +4,8 @@ set -eux source shared.sh +# We have to build our own binutils for the GCC build, because the default CentOS 7 binutils are +# too old, and they do not support `SHF_GNU_RETAIN`. BINUTILS="2.47" curl https://ci-mirrors.rust-lang.org/rustc/gcc/binutils-$BINUTILS.tar.xz | xzcat | tar xf - mkdir binutils-build From 59022bf58dbec65ca30823f9ebd2f331241a7789 Mon Sep 17 00:00:00 2001 From: Amirhossein Akhlaghpour Date: Sun, 16 Aug 2026 18:41:18 +0330 Subject: [PATCH 63/66] Add BPF test for Rust ABI stack arguments Co-authored-by: Folkert de Vries Signed-off-by: Amirhossein Akhlaghpour --- .../assembly-llvm/bpf-more-than-five-args.rs | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 tests/assembly-llvm/bpf-more-than-five-args.rs diff --git a/tests/assembly-llvm/bpf-more-than-five-args.rs b/tests/assembly-llvm/bpf-more-than-five-args.rs new file mode 100644 index 0000000000000..132adc5d49f80 --- /dev/null +++ b/tests/assembly-llvm/bpf-more-than-five-args.rs @@ -0,0 +1,63 @@ +//@ add-minicore +//@ revisions: el eb +//@ assembly-output: emit-asm +//@ [el] compile-flags: --target bpfel-unknown-none -Copt-level=0 +//@ [eb] compile-flags: --target bpfeb-unknown-none -Copt-level=0 +//@ needs-llvm-components: bpf +//@ min-llvm-version: 23 + +// Test that on LLVM 23 and higher BPF functions can accept more than 5 arguments. +// Earlier versions had a hard limit of at most 5 arguments. +#![feature(no_core)] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; +use minicore::*; + +// CHECK-LABEL: callee: +// CHECK: r0 = *(u64 *)(r11 + 40) +#[no_mangle] +#[inline(never)] +fn callee( + _a0: u64, + _a1: u64, + _a2: u64, + _a3: u64, + _a4: u64, + _a5: u64, + _a6: u64, + _a7: u64, + _a8: u64, + a9: u64, +) -> u64 { + a9 +} + +// CHECK-LABEL: caller: +// CHECK: [[A5:r[0-9]+]] = *(u64 *)(r11 + 8) +// CHECK: [[A6:r[0-9]+]] = *(u64 *)(r11 + 16) +// CHECK: [[A7:r[0-9]+]] = *(u64 *)(r11 + 24) +// CHECK: [[A8:r[0-9]+]] = *(u64 *)(r11 + 32) +// CHECK: [[A9:r[0-9]+]] = *(u64 *)(r11 + 40) +// CHECK: *(u64 *)(r11 - 8) = [[A5]] +// CHECK: *(u64 *)(r11 - 16) = [[A6]] +// CHECK: *(u64 *)(r11 - 24) = [[A7]] +// CHECK: *(u64 *)(r11 - 32) = [[A8]] +// CHECK: *(u64 *)(r11 - 40) = [[A9]] +// CHECK: call callee +#[no_mangle] +fn caller( + a0: u64, + a1: u64, + a2: u64, + a3: u64, + a4: u64, + a5: u64, + a6: u64, + a7: u64, + a8: u64, + a9: u64, +) -> u64 { + callee(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) +} From 9162595a5ab4f61ed295bd11e17d9becff5a285e Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 16 Aug 2026 17:46:36 +0200 Subject: [PATCH 64/66] Add back flaky gui rustdoc test `tests/rustdoc-gui/headers-color.goml` --- tests/rustdoc-gui/headers-color.goml | 74 ++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 tests/rustdoc-gui/headers-color.goml diff --git a/tests/rustdoc-gui/headers-color.goml b/tests/rustdoc-gui/headers-color.goml new file mode 100644 index 0000000000000..688c14c3ee078 --- /dev/null +++ b/tests/rustdoc-gui/headers-color.goml @@ -0,0 +1,74 @@ +// This test check for headings text and background colors for the different themes. + +include: "utils.goml" + +define-function: ( + "check-colors", + [theme, color, code_header_color, focus_background_color, headings_color], + block { + go-to: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html" + // This is needed so that the text color is computed. + show-text: true + call-function: ("switch-theme", {"theme": |theme|}) + assert-css: ( + ".impl", + {"color": |color|, "background-color": "rgba(0, 0, 0, 0)"}, + ALL, + ) + assert-css: ( + ".impl .code-header", + {"color": |code_header_color|, "background-color": "rgba(0, 0, 0, 0)"}, + ALL, + ) + // First we hover the element to make the anchor appear. + move-cursor-to: "#impl-Foo" + // Then we click on it. + click: "a.anchor[href='#impl-Foo']" + assert-css: ( + "#impl-Foo", + {"color": |color|, "background-color": |focus_background_color|}, + ) + click: "a.fn[href='#method.must_use']" + assert-css: ( + "#method\.must_use", + {"color": |color|, "background-color": |focus_background_color|}, + ALL, + ) + go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" + assert-css: (".section-header a", {"color": |color|}, ALL) + go-to: "file://" + |DOC_PATH| + "/test_docs/struct.HeavilyDocumentedStruct.html" + // We select headings (h2, h3, h...). + assert-css: (".docblock > :not(p) > a", {"color": |headings_color|}, ALL) + }, +) + +call-function: ( + "check-colors", + { + "theme": "ayu", + "color": "#c5c5c5", + "code_header_color": "#e6e1cf", + "focus_background_color": "rgba(255, 236, 164, 0.06)", + "headings_color": "#c5c5c5", + }, +) +call-function: ( + "check-colors", + { + "theme": "dark", + "color": "#ddd", + "code_header_color": "#ddd", + "focus_background_color": "#494a3d", + "headings_color": "#ddd", + }, +) +call-function: ( + "check-colors", + { + "theme": "light", + "color": "black", + "code_header_color": "black", + "focus_background_color": "#fdffd3", + "headings_color": "black", + }, +) From 916b10c99b665e9376af96d778d4d19214e8749e Mon Sep 17 00:00:00 2001 From: Joao Roberto Date: Sun, 16 Aug 2026 13:01:48 -0300 Subject: [PATCH 65/66] Replace structured for<> rewrite with a generic help The visitor-based rewrite needed extra gate bookkeeping for cfg'd-out spans and was too much complexity for feature_gate. Keep gate_all! and point people at a for<> fn type annotation instead. --- compiler/rustc_ast_passes/src/diagnostics.rs | 32 -- compiler/rustc_ast_passes/src/feature_gate.rs | 393 +----------------- compiler/rustc_parse/src/parser/expr.rs | 2 - .../const-generics-closure.stderr | 2 +- .../missing-braces-before-close-brace.stderr | 4 +- ...ture-gate-closure_lifetime_binder-macro.rs | 20 - ...-gate-closure_lifetime_binder-macro.stderr | 43 -- ...closure_lifetime_binder-maybe-incorrect.rs | 34 -- ...ure_lifetime_binder-maybe-incorrect.stderr | 119 ------ ...gate-closure_lifetime_binder-rustfix.fixed | 10 - ...re-gate-closure_lifetime_binder-rustfix.rs | 10 - ...ate-closure_lifetime_binder-rustfix.stderr | 18 - .../feature-gate-closure_lifetime_binder.rs | 119 ------ ...eature-gate-closure_lifetime_binder.stderr | 291 +------------ .../recover/recover-quantified-closure.stderr | 4 +- 15 files changed, 25 insertions(+), 1076 deletions(-) delete mode 100644 tests/ui/feature-gates/feature-gate-closure_lifetime_binder-macro.rs delete mode 100644 tests/ui/feature-gates/feature-gate-closure_lifetime_binder-macro.stderr delete mode 100644 tests/ui/feature-gates/feature-gate-closure_lifetime_binder-maybe-incorrect.rs delete mode 100644 tests/ui/feature-gates/feature-gate-closure_lifetime_binder-maybe-incorrect.stderr delete mode 100644 tests/ui/feature-gates/feature-gate-closure_lifetime_binder-rustfix.fixed delete mode 100644 tests/ui/feature-gates/feature-gate-closure_lifetime_binder-rustfix.rs delete mode 100644 tests/ui/feature-gates/feature-gate-closure_lifetime_binder-rustfix.stderr diff --git a/compiler/rustc_ast_passes/src/diagnostics.rs b/compiler/rustc_ast_passes/src/diagnostics.rs index 9ab9c1d9f40d9..0814c79d339bd 100644 --- a/compiler/rustc_ast_passes/src/diagnostics.rs +++ b/compiler/rustc_ast_passes/src/diagnostics.rs @@ -1264,35 +1264,3 @@ pub(crate) struct VarargsWithoutPattern { #[primary_span] pub span: Span, } - -#[derive(Subdiagnostic)] -pub(crate) enum ClosureLifetimeBinderBindingTypeSugg { - #[multipart_suggestion( - "consider setting the binding type instead", - applicability = "machine-applicable", - style = "verbose" - )] - MachineApplicable { - #[suggestion_part(code = ": {ty}")] - binding: Span, - ty: String, - #[suggestion_part(code = "{closure}")] - closure_header: Span, - closure: String, - }, - /// Used when the body references other simple paths: they may be captures (or free items). - /// Without name resolution we can't tell, so rustfix must not auto-apply. - #[multipart_suggestion( - "consider setting the binding type instead", - applicability = "maybe-incorrect", - style = "verbose" - )] - MaybeIncorrect { - #[suggestion_part(code = ": {ty}")] - binding: Span, - ty: String, - #[suggestion_part(code = "{closure}")] - closure_header: Span, - closure: String, - }, -} diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index c8c7a828ea281..25bacacd85036 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -1,17 +1,12 @@ use rustc_ast::visit::{self, AssocCtxt, FnKind, Visitor}; -use rustc_ast::{ - self as ast, AttrVec, BindingMode, ByRef, GenericBound, GenericParamKind, NodeId, PatKind, - attr, token, -}; -use rustc_ast_pretty::pprust; +use rustc_ast::{self as ast, AttrVec, GenericBound, NodeId, PatKind, attr, token}; use rustc_attr_ir::{Attribute, AttributeKind}; use rustc_attr_parsing::AttributeParser; -use rustc_data_structures::fx::FxHashSet; use rustc_errors::msg; use rustc_feature::Features; use rustc_session::Session; use rustc_session::diagnostics::{feature_err, feature_warn}; -use rustc_span::{Ident, Span, Spanned, Symbol, sym}; +use rustc_span::{Span, Spanned, Symbol, sym}; use crate::diagnostics; @@ -52,13 +47,7 @@ macro_rules! gate_multi { } pub fn check_attribute(attr: &ast::Attribute, sess: &Session, features: &Features) { - PostExpansionVisitor { - sess, - features, - let_binding: None, - handled_closure_lifetime_binders: FxHashSet::default(), - } - .visit_attribute(attr) + PostExpansionVisitor { sess, features }.visit_attribute(attr) } struct PostExpansionVisitor<'a> { @@ -66,14 +55,6 @@ struct PostExpansionVisitor<'a> { // `sess` contains a `Features`, but this might not be that one. features: &'a Features, - - /// Set while visiting the initializer of a `let` binding whose RHS is directly a closure. - /// Used to suggest moving `for<...>` binders onto the binding's type. - let_binding: Option<&'a ast::Local>, - - /// Binder spans for which we already emitted the `closure_lifetime_binder` gate while walking - /// the live AST. Remaining pre-expansion spans (e.g. under `#[cfg(false)]`) are gated later. - handled_closure_lifetime_binders: FxHashSet, } // ----------------------------------------------------------------------------- @@ -86,34 +67,6 @@ struct PostExpansionVisitor<'a> { // Instead, register a pre-expansion feature gate using `gate_all` in fn `check_crate`. impl<'a> PostExpansionVisitor<'a> { - /// Gate `for<...>` binders on closures, suggesting a `fn` pointer binding type when possible. - fn gate_closure_lifetime_binder(&mut self, closure: &ast::Closure, binder_span: Span) { - self.handled_closure_lifetime_binders.insert(binder_span); - - if self.features.closure_lifetime_binder() - || binder_span.allows_unstable(sym::closure_lifetime_binder) - { - return; - } - - let mut err = feature_err( - self.sess, - sym::closure_lifetime_binder, - binder_span, - "`for<...>` binders for closures are experimental", - ); - - if let Some(sugg) = - closure_lifetime_binder_binding_type_sugg(self.sess, self.let_binding, closure) - { - err.subdiagnostic(sugg); - } else { - err.help("consider removing `for<...>`"); - } - - err.emit(); - } - /// Feature gate `impl Trait` inside `type Alias = $type_expr;`. fn check_impl_trait(&self, ty: &ast::Ty, in_associated_ty: bool) { struct ImplTraitVisitor<'a> { @@ -353,32 +306,8 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { visit::walk_generic_args(self, args); } - fn visit_local(&mut self, local: &'a ast::Local) { - // Only track direct `let pat = for<'a> |...| ...` inits; parenthesized or otherwise - // wrapped closures fall back to the simpler help. - if let Some(init) = local.kind.init() - && matches!(init.kind, ast::ExprKind::Closure(_)) - { - let prev = self.let_binding.replace(local); - visit::walk_local(self, local); - self.let_binding = prev; - } else { - visit::walk_local(self, local); - } - } - fn visit_expr(&mut self, e: &'a ast::Expr) { - match &e.kind { - ast::ExprKind::Closure(closure) => { - if let ast::ClosureBinder::For { span, .. } = &closure.binder { - self.gate_closure_lifetime_binder(closure, *span); - } - // Nested expressions inside the closure are not the `let` initializer. - let prev = self.let_binding.take(); - visit::walk_expr(self, e); - self.let_binding = prev; - return; - } + match e.kind { ast::ExprKind::TryBlock(_, None) => { // `try { ... }` is old and is only gated post-expansion here. gate!(self, try_blocks, e.span, "`try` expression is experimental"); @@ -390,14 +319,14 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { kind: token::LitKind::Float | token::LitKind::Integer, suffix, .. - }) => match *suffix { + }) => match suffix { Some(sym::f16) => { gate!(self, f16, e.span, "the type `f16` is unstable") } Some(sym::f128) => { gate!(self, f128, e.span, "the type `f128` is unstable") } - _ => {} + _ => (), }, _ => {} } @@ -510,12 +439,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { check_new_solver_banned_features(sess, features); check_features_requiring_new_solver(sess, features); - let mut visitor = PostExpansionVisitor { - sess, - features, - let_binding: None, - handled_closure_lifetime_binders: FxHashSet::default(), - }; + let mut visitor = PostExpansionVisitor { sess, features }; // ----------------------------------------------------------------------------- // PRE-EXPANSION FEATURE GATES FOR UNSTABLE SYNTAX @@ -578,8 +502,12 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { "`async` trait bounds are unstable", "use the desugared name of the async trait, such as `AsyncFn`" ); - // `closure_lifetime_binder` is gated in `PostExpansionVisitor` (with a richer suggestion when - // possible). Spans not seen there — notably under `#[cfg(false)]` — are handled after the walk. + gate_all!( + closure_lifetime_binder, + "`for<...>` binders for closures are experimental", + "consider using a type annotation instead: \ + `let closure: for<...> fn(...) -> ... = /* closure */;`" + ); gate_all!( half_open_range_patterns_in_slices, "half-open range patterns in slices are unstable" @@ -701,301 +629,6 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { // ----------------------------------------------------------------------------- visit::walk_crate(&mut visitor, krate); - - // Reject `for<...>` closure binders that never reached the AST walk (e.g. `#[cfg(false)]`). - if !visitor.features.closure_lifetime_binder() { - for &span in spans.get(&sym::closure_lifetime_binder).into_flat_iter() { - if span.allows_unstable(sym::closure_lifetime_binder) - || visitor.handled_closure_lifetime_binders.contains(&span) - { - continue; - } - feature_err( - sess, - sym::closure_lifetime_binder, - span, - "`for<...>` binders for closures are experimental", - ) - .with_help("consider removing `for<...>`") - .emit(); - } - } -} - -/// Build a suggestion rewriting -/// `let cl = for<'a> |x: &'a T| -> U { ... }` into -/// `let cl: for<'a> fn(&'a T) -> U = |x| { ... }` when that is a reasonable alternative. -fn closure_lifetime_binder_binding_type_sugg( - sess: &Session, - local: Option<&ast::Local>, - closure: &ast::Closure, -) -> Option { - let local = local?; - if local.ty.is_some() { - return None; - } - // Only by-value `let ident = ...` / `let mut ident = ...` bindings. - if !matches!(&local.pat.kind, PatKind::Ident(BindingMode(ByRef::No, _), _, None)) { - return None; - } - - // Explicit `move`/`use`/`async`/`const`/`static` closures are not `fn` pointers. - if !matches!(closure.capture_clause, ast::CaptureBy::Ref) - || closure.coroutine_kind.is_some() - || matches!(closure.constness, ast::Const::Yes(_)) - || matches!(closure.movability, ast::Movability::Static) - { - return None; - } - - let ast::ClosureBinder::For { span: binder_span, generic_params } = &closure.binder else { - return None; - }; - - // `for` / `for<'a: 'static>` are not valid on `fn` pointer types. - if !generic_params - .iter() - .all(|param| matches!(param.kind, GenericParamKind::Lifetime) && param.bounds.is_empty()) - { - return None; - } - - // Need fully explicit parameter and return types to form a useful `fn` type. A top-level or - // nested `_` (e.g. `-> _`, `&'a _`) must not be copied into a MachineApplicable suggestion. - let ast::FnRetTy::Ty(ret_ty) = &closure.fn_decl.output else { - return None; - }; - if ty_contains_infer(ret_ty) - || closure.fn_decl.inputs.iter().any(|param| ty_contains_infer(¶m.ty)) - { - return None; - } - - // Only by-value binding patterns (and `_`) can be rewritten safely. - if !closure.fn_decl.inputs.iter().all(|param| { - matches!( - ¶m.pat.kind, - PatKind::Wild | PatKind::Ident(BindingMode(ByRef::No, _), _, None) - ) - }) { - return None; - } - - // `pprust::pat_to_string` drops parameter attributes; don't emit a lossy rewrite. - if closure.fn_decl.inputs.iter().any(|param| !param.attrs.is_empty()) { - return None; - } - - // Don't rewrite macro-expanded closures; hygiene makes capture analysis unreliable and the - // suggestion would point into the macro definition. - if binder_span.from_expansion() || closure.fn_decl_span.from_expansion() { - return None; - } - - let binder = sess.source_map().span_to_snippet(*binder_span).ok()?; - let inputs: String = closure - .fn_decl - .inputs - .iter() - .map(|param| pprust::ty_to_string(¶m.ty)) - .intersperse(", ".to_string()) - .collect(); - let ty = format!("{binder} fn({inputs}) -> {}", pprust::ty_to_string(ret_ty)); - - let closure_pats: String = closure - .fn_decl - .inputs - .iter() - .map(|param| pprust::pat_to_string(¶m.pat)) - .intersperse(", ".to_string()) - .collect(); - - let binding = local.pat.span.shrink_to_hi(); - let closure_header = binder_span.to(closure.fn_decl_span); - let closure_code = format!("|{closure_pats}|"); - - // `CaptureBy::Ref` only means no `move`/`use`. Without name resolution, any other simple - // path may be an env capture (including uppercase locals) or a free item. Offer the rewrite - // only as maybe-incorrect in that case so rustfix won't auto-apply a breaking change. - // Paths bound locally in the body (e.g. `let n = ...; n`) are fine for `fn` pointers. - if closure_body_has_free_simple_path(closure) { - Some(diagnostics::ClosureLifetimeBinderBindingTypeSugg::MaybeIncorrect { - binding, - ty, - closure_header, - closure: closure_code, - }) - } else { - Some(diagnostics::ClosureLifetimeBinderBindingTypeSugg::MachineApplicable { - binding, - ty, - closure_header, - closure: closure_code, - }) - } -} - -/// Returns true if `ty` contains any `_` inference placeholder, including nested forms like -/// `&'a _` or `(_, u8)`. -fn ty_contains_infer(ty: &ast::Ty) -> bool { - struct InferVisitor { - found: bool, - } - - impl<'a> Visitor<'a> for InferVisitor { - fn visit_ty(&mut self, ty: &'a ast::Ty) { - if self.found { - return; - } - if matches!(ty.kind, ast::TyKind::Infer) { - self.found = true; - return; - } - visit::walk_ty(self, ty); - } - } - - let mut visitor = InferVisitor { found: false }; - visitor.visit_ty(ty); - visitor.found -} - -/// Returns true if the closure body contains a single-segment value path that is neither a -/// parameter nor a name bound inside the body. -/// -/// Locals are tracked as hygiene-aware [`Ident`]s (name + `SyntaxContext`) so a macro parameter -/// `$x` is not confused with a closure parameter `x` that happens to share a spelling. -/// -/// This is intentionally AST-only and conservative: free functions and constructors look the same -/// as captures here. Callers should downgrade suggestion applicability when this is true. -fn closure_body_has_free_simple_path(closure: &ast::Closure) -> bool { - let mut known_locals = FxHashSet::default(); - for param in &closure.fn_decl.inputs { - if let PatKind::Ident(_, ident, _) = param.pat.kind { - known_locals.insert(ident); - } - } - - struct FreePathVisitor { - known_locals: FxHashSet, - has_free_path: bool, - } - - impl FreePathVisitor { - fn bind_pat(&mut self, pat: &ast::Pat) { - match &pat.kind { - PatKind::Ident(_, ident, sub) => { - self.known_locals.insert(*ident); - if let Some(sub) = sub { - self.bind_pat(sub); - } - } - PatKind::Tuple(pats) - | PatKind::TupleStruct(_, _, pats) - | PatKind::Slice(pats) - | PatKind::Or(pats) => { - for pat in pats { - self.bind_pat(pat); - } - } - PatKind::Struct(_, _, fields, _) => { - for field in fields { - self.bind_pat(&field.pat); - } - } - PatKind::Box(pat) - | PatKind::Deref(pat) - | PatKind::Ref(pat, ..) - | PatKind::Paren(pat) => self.bind_pat(pat), - _ => {} - } - } - } - - impl<'a> Visitor<'a> for FreePathVisitor { - fn visit_ty(&mut self, _: &'a ast::Ty) { - // Paths in types are not value captures. - } - - fn visit_block(&mut self, block: &'a ast::Block) { - let old = self.known_locals.clone(); - visit::walk_block(self, block); - self.known_locals = old; - } - - fn visit_local(&mut self, local: &'a ast::Local) { - // Visit the initializer (and `else` block) before binding names from the pattern. - // Bindings are not in scope in the `else` block. - if let Some((init, els)) = local.kind.init_else_opt() { - self.visit_expr(init); - if let Some(els) = els { - // Must go through `visit_block` so locals declared in the `else` block do not - // leak into `known_locals` for code after the `let else`. - self.visit_block(els); - } - } - self.bind_pat(&local.pat); - } - - fn visit_arm(&mut self, arm: &'a ast::Arm) { - let old = self.known_locals.clone(); - self.bind_pat(&arm.pat); - visit::walk_arm(self, arm); - self.known_locals = old; - } - - fn visit_expr(&mut self, expr: &'a ast::Expr) { - if self.has_free_path { - return; - } - if let ast::ExprKind::Path(None, path) = &expr.kind - && let [seg] = path.segments.as_slice() - && seg.args.is_none() - && !self.known_locals.contains(&seg.ident) - { - self.has_free_path = true; - return; - } - match &expr.kind { - // `let` bindings from let-chains / `if let` / `while let` conditions. The enclosing - // `If` / `While` arms restore `known_locals` so these do not escape that scope. - ast::ExprKind::Let(pat, scrutinee, _, _) => { - self.visit_expr(scrutinee); - self.bind_pat(pat); - } - // `if`/`if let`/`if` let-chains: condition bindings are in scope for the then - // branch only, not the else branch or anything after the `if`. - ast::ExprKind::If(cond, then_block, else_opt) => { - let old = self.known_locals.clone(); - self.visit_expr(cond); - self.visit_block(then_block); - self.known_locals = old; - if let Some(els) = else_opt { - self.visit_expr(els); - } - } - // `while`/`while let`: condition bindings are in scope for the loop body only. - ast::ExprKind::While(cond, body, _) => { - let old = self.known_locals.clone(); - self.visit_expr(cond); - self.visit_block(body); - self.known_locals = old; - } - ast::ExprKind::ForLoop(for_loop) => { - self.visit_expr(&for_loop.iter); - let old = self.known_locals.clone(); - self.bind_pat(&for_loop.pat); - self.visit_block(&for_loop.body); - self.known_locals = old; - } - _ => visit::walk_expr(self, expr), - } - } - } - - let mut visitor = FreePathVisitor { known_locals, has_free_path: false }; - visitor.visit_expr(&closure.body); - visitor.has_free_path } fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate) { diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 0650491ba8fbe..f81727eda4fb6 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2463,8 +2463,6 @@ impl<'a> Parser<'a> { let (bound_vars, _) = self.parse_higher_ranked_binder()?; let span = lo.to(self.prev_token.span); - // Pre-expansion gate so `#[cfg(false)]` code is still rejected. The post-expansion - // visitor may replace this with a richer diagnostic when the AST is available. self.psess.gated_spans.gate(sym::closure_lifetime_binder, span); ClosureBinder::For { span, generic_params: bound_vars } diff --git a/tests/ui/const-generics/generic_const_exprs/const-generics-closure.stderr b/tests/ui/const-generics/generic_const_exprs/const-generics-closure.stderr index 5410bbdc12536..3ab2d6676724b 100644 --- a/tests/ui/const-generics/generic_const_exprs/const-generics-closure.stderr +++ b/tests/ui/const-generics/generic_const_exprs/const-generics-closure.stderr @@ -7,7 +7,7 @@ LL | let _ = for<'a, 'b> |x: &'a &'a Vec<&'b u32>, b: bool| -> &'a Vec<& = note: see issue #97362 for more information = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = help: consider removing `for<...>` + = help: consider using a type annotation instead: `let closure: for<...> fn(...) -> ... = /* closure */;` error[E0308]: mismatched types --> $DIR/const-generics-closure.rs:4:10 diff --git a/tests/ui/expr/malformed_closure/missing-braces-before-close-brace.stderr b/tests/ui/expr/malformed_closure/missing-braces-before-close-brace.stderr index baf6a9dd9f4c9..533c1432611a3 100644 --- a/tests/ui/expr/malformed_closure/missing-braces-before-close-brace.stderr +++ b/tests/ui/expr/malformed_closure/missing-braces-before-close-brace.stderr @@ -27,7 +27,7 @@ LL | for<> || -> () {}; = note: see issue #97362 for more information = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = help: consider removing `for<...>` + = help: consider using a type annotation instead: `let closure: for<...> fn(...) -> ... = /* closure */;` error[E0658]: `for<...>` binders for closures are experimental --> $DIR/missing-braces-before-close-brace.rs:6:5 @@ -38,7 +38,7 @@ LL | for<'a> || -> () |_; = note: see issue #97362 for more information = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = help: consider removing `for<...>` + = help: consider using a type annotation instead: `let closure: for<...> fn(...) -> ... = /* closure */;` error: aborting due to 5 previous errors diff --git a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-macro.rs b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-macro.rs deleted file mode 100644 index c1cc4cc03aa18..0000000000000 --- a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-macro.rs +++ /dev/null @@ -1,20 +0,0 @@ -//@ compile-flags: --error-format=json -//@ forbid-output: MachineApplicable -//@ forbid-output: MaybeIncorrect - -// Macro-expanded closures must not get a structured fn-pointer rewrite (hygiene + spans point -// into the macro). Expect only the simple help. - -macro_rules! make { - ($x:ident) => { - for<'a> |x: &'a i32| -> i32 { *x + $x } - //~^ ERROR `for<...>` binders for closures are experimental - //~| HELP add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable - //~| HELP consider removing `for<...>` - }; -} - -fn main() { - let x = 1; - let _cl = make!(x); -} diff --git a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-macro.stderr b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-macro.stderr deleted file mode 100644 index 1f960c02be929..0000000000000 --- a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-macro.stderr +++ /dev/null @@ -1,43 +0,0 @@ -{"$message_type":"diagnostic","message":"`for<...>` binders for closures are experimental","code":{"code":"E0658","explanation":"An unstable feature was used. - -Erroneous code example: - -```compile_fail,E0658 -use std::intrinsics; // error: use of unstable library feature `core_intrinsics` -``` - -If you're using a stable or a beta version of rustc, you won't be able to use -any unstable features. In order to do so, please switch to a nightly version of -rustc (by using [rustup]). - -If you're using a nightly version of rustc, just add the corresponding feature -to be able to use it: - -``` -#![feature(core_intrinsics)] - -use std::intrinsics; // ok! -``` - -[rustup]: https://rust-lang.github.io/rustup/concepts/channels.html -"},"level":"error","spans":[{"file_name":"$DIR/feature-gate-closure_lifetime_binder-macro.rs","byte_start":304,"byte_end":311,"line_start":10,"line_end":10,"column_start":9,"column_end":16,"is_primary":true,"text":[{"text":" for<'a> |x: &'a i32| -> i32 { *x + $x }","highlight_start":9,"highlight_end":16}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":{"span":{"file_name":"$DIR/feature-gate-closure_lifetime_binder-macro.rs","byte_start":605,"byte_end":613,"line_start":19,"line_end":19,"column_start":15,"column_end":23,"is_primary":false,"text":[{"text":" let _cl = make!(x);","highlight_start":15,"highlight_end":23}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null},"macro_decl_name":"make!","def_site_span":{"file_name":"$DIR/feature-gate-closure_lifetime_binder-macro.rs","byte_start":256,"byte_end":273,"line_start":8,"line_end":8,"column_start":1,"column_end":18,"is_primary":false,"text":[{"text":"macro_rules! make {","highlight_start":1,"highlight_end":18}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}}}],"children":[{"message":"see issue #97362 for more information","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable","code":null,"level":"help","spans":[],"children":[],"rendered":null},{"message":"this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"consider removing `for<...>`","code":null,"level":"help","spans":[],"children":[],"rendered":null}],"rendered":"error[E0658]: `for<...>` binders for closures are experimental - --> $DIR/feature-gate-closure_lifetime_binder-macro.rs:10:9 - | -LL | for<'a> |x: &'a i32| -> i32 { *x + $x } - | ^^^^^^^ -... -LL | let _cl = make!(x); - | -------- in this macro invocation - | - = note: see issue #97362 for more information - = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = help: consider removing `for<...>` - = note: this error originates in the macro `make` (in Nightly builds, run with -Z macro-backtrace for more info) - -"} -{"$message_type":"diagnostic","message":"aborting due to 1 previous error","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to 1 previous error - -"} -{"$message_type":"diagnostic","message":"For more information about this error, try `rustc --explain E0658`.","code":null,"level":"failure-note","spans":[],"children":[],"rendered":"For more information about this error, try `rustc --explain E0658`. -"} diff --git a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-maybe-incorrect.rs b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-maybe-incorrect.rs deleted file mode 100644 index 8d7ab9cc2b54c..0000000000000 --- a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-maybe-incorrect.rs +++ /dev/null @@ -1,34 +0,0 @@ -//@ edition: 2024 -//@ compile-flags: --error-format=json -//@ error-pattern: "suggestion_applicability":"MaybeIncorrect" - -// Capturing closures must not get a MachineApplicable rewrite. Cover plain captures, let-else -// leakage, and let-chain shadowing — all should report MaybeIncorrect in JSON. - -fn main() { - let y = 1; - let _capture = for<'a> |x: &'a i32| -> i32 { *x + y }; - //~^ ERROR `for<...>` binders for closures are experimental - - let let_else_env = 1; - let _let_else = for<'a> |x: &'a i32| -> i32 { - //~^ ERROR `for<...>` binders for closures are experimental - let Some(_) = None:: else { - let let_else_env = 0; - return let_else_env; - }; - *x + let_else_env - }; - - let chain_env = 1; - let _let_chain = for<'a> |x: &'a i32| -> i32 { - //~^ ERROR `for<...>` binders for closures are experimental - if let Some(chain_env) = None:: - && chain_env == 0 - { - 0 - } else { - *x + chain_env - } - }; -} diff --git a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-maybe-incorrect.stderr b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-maybe-incorrect.stderr deleted file mode 100644 index 6cf49f6effdbd..0000000000000 --- a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-maybe-incorrect.stderr +++ /dev/null @@ -1,119 +0,0 @@ -{"$message_type":"diagnostic","message":"`for<...>` binders for closures are experimental","code":{"code":"E0658","explanation":"An unstable feature was used. - -Erroneous code example: - -```compile_fail,E0658 -use std::intrinsics; // error: use of unstable library feature `core_intrinsics` -``` - -If you're using a stable or a beta version of rustc, you won't be able to use -any unstable features. In order to do so, please switch to a nightly version of -rustc (by using [rustup]). - -If you're using a nightly version of rustc, just add the corresponding feature -to be able to use it: - -``` -#![feature(core_intrinsics)] - -use std::intrinsics; // ok! -``` - -[rustup]: https://rust-lang.github.io/rustup/concepts/channels.html -"},"level":"error","spans":[{"file_name":"$DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs","byte_start":345,"byte_end":352,"line_start":10,"line_end":10,"column_start":20,"column_end":27,"is_primary":true,"text":[{"text":" let _capture = for<'a> |x: &'a i32| -> i32 { *x + y };","highlight_start":20,"highlight_end":27}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"see issue #97362 for more information","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable","code":null,"level":"help","spans":[],"children":[],"rendered":null},{"message":"this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"consider setting the binding type instead","code":null,"level":"help","spans":[{"file_name":"$DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs","byte_start":342,"byte_end":342,"line_start":10,"line_end":10,"column_start":17,"column_end":17,"is_primary":true,"text":[{"text":" let _capture = for<'a> |x: &'a i32| -> i32 { *x + y };","highlight_start":17,"highlight_end":17}],"label":null,"suggested_replacement":": for<'a> fn(&'a i32) -> i32","suggestion_applicability":"MaybeIncorrect","expansion":null},{"file_name":"$DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs","byte_start":345,"byte_end":372,"line_start":10,"line_end":10,"column_start":20,"column_end":47,"is_primary":true,"text":[{"text":" let _capture = for<'a> |x: &'a i32| -> i32 { *x + y };","highlight_start":20,"highlight_end":47}],"label":null,"suggested_replacement":"|x|","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"error[E0658]: `for<...>` binders for closures are experimental - --> $DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs:10:20 - | -LL | let _capture = for<'a> |x: &'a i32| -> i32 { *x + y }; - | ^^^^^^^ - | - = note: see issue #97362 for more information - = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -help: consider setting the binding type instead - | -LL - let _capture = for<'a> |x: &'a i32| -> i32 { *x + y }; -LL + let _capture: for<'a> fn(&'a i32) -> i32 = |x| { *x + y }; - | - -"} -{"$message_type":"diagnostic","message":"`for<...>` binders for closures are experimental","code":{"code":"E0658","explanation":"An unstable feature was used. - -Erroneous code example: - -```compile_fail,E0658 -use std::intrinsics; // error: use of unstable library feature `core_intrinsics` -``` - -If you're using a stable or a beta version of rustc, you won't be able to use -any unstable features. In order to do so, please switch to a nightly version of -rustc (by using [rustup]). - -If you're using a nightly version of rustc, just add the corresponding feature -to be able to use it: - -``` -#![feature(core_intrinsics)] - -use std::intrinsics; // ok! -``` - -[rustup]: https://rust-lang.github.io/rustup/concepts/channels.html -"},"level":"error","spans":[{"file_name":"$DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs","byte_start":496,"byte_end":503,"line_start":14,"line_end":14,"column_start":21,"column_end":28,"is_primary":true,"text":[{"text":" let _let_else = for<'a> |x: &'a i32| -> i32 {","highlight_start":21,"highlight_end":28}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"see issue #97362 for more information","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable","code":null,"level":"help","spans":[],"children":[],"rendered":null},{"message":"this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"consider setting the binding type instead","code":null,"level":"help","spans":[{"file_name":"$DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs","byte_start":493,"byte_end":493,"line_start":14,"line_end":14,"column_start":18,"column_end":18,"is_primary":true,"text":[{"text":" let _let_else = for<'a> |x: &'a i32| -> i32 {","highlight_start":18,"highlight_end":18}],"label":null,"suggested_replacement":": for<'a> fn(&'a i32) -> i32","suggestion_applicability":"MaybeIncorrect","expansion":null},{"file_name":"$DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs","byte_start":496,"byte_end":523,"line_start":14,"line_end":14,"column_start":21,"column_end":48,"is_primary":true,"text":[{"text":" let _let_else = for<'a> |x: &'a i32| -> i32 {","highlight_start":21,"highlight_end":48}],"label":null,"suggested_replacement":"|x|","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"error[E0658]: `for<...>` binders for closures are experimental - --> $DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs:14:21 - | -LL | let _let_else = for<'a> |x: &'a i32| -> i32 { - | ^^^^^^^ - | - = note: see issue #97362 for more information - = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -help: consider setting the binding type instead - | -LL - let _let_else = for<'a> |x: &'a i32| -> i32 { -LL + let _let_else: for<'a> fn(&'a i32) -> i32 = |x| { - | - -"} -{"$message_type":"diagnostic","message":"`for<...>` binders for closures are experimental","code":{"code":"E0658","explanation":"An unstable feature was used. - -Erroneous code example: - -```compile_fail,E0658 -use std::intrinsics; // error: use of unstable library feature `core_intrinsics` -``` - -If you're using a stable or a beta version of rustc, you won't be able to use -any unstable features. In order to do so, please switch to a nightly version of -rustc (by using [rustup]). - -If you're using a nightly version of rustc, just add the corresponding feature -to be able to use it: - -``` -#![feature(core_intrinsics)] - -use std::intrinsics; // ok! -``` - -[rustup]: https://rust-lang.github.io/rustup/concepts/channels.html -"},"level":"error","spans":[{"file_name":"$DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs","byte_start":791,"byte_end":798,"line_start":24,"line_end":24,"column_start":22,"column_end":29,"is_primary":true,"text":[{"text":" let _let_chain = for<'a> |x: &'a i32| -> i32 {","highlight_start":22,"highlight_end":29}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"see issue #97362 for more information","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable","code":null,"level":"help","spans":[],"children":[],"rendered":null},{"message":"this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"consider setting the binding type instead","code":null,"level":"help","spans":[{"file_name":"$DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs","byte_start":788,"byte_end":788,"line_start":24,"line_end":24,"column_start":19,"column_end":19,"is_primary":true,"text":[{"text":" let _let_chain = for<'a> |x: &'a i32| -> i32 {","highlight_start":19,"highlight_end":19}],"label":null,"suggested_replacement":": for<'a> fn(&'a i32) -> i32","suggestion_applicability":"MaybeIncorrect","expansion":null},{"file_name":"$DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs","byte_start":791,"byte_end":818,"line_start":24,"line_end":24,"column_start":22,"column_end":49,"is_primary":true,"text":[{"text":" let _let_chain = for<'a> |x: &'a i32| -> i32 {","highlight_start":22,"highlight_end":49}],"label":null,"suggested_replacement":"|x|","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"error[E0658]: `for<...>` binders for closures are experimental - --> $DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs:24:22 - | -LL | let _let_chain = for<'a> |x: &'a i32| -> i32 { - | ^^^^^^^ - | - = note: see issue #97362 for more information - = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -help: consider setting the binding type instead - | -LL - let _let_chain = for<'a> |x: &'a i32| -> i32 { -LL + let _let_chain: for<'a> fn(&'a i32) -> i32 = |x| { - | - -"} -{"$message_type":"diagnostic","message":"aborting due to 3 previous errors","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to 3 previous errors - -"} -{"$message_type":"diagnostic","message":"For more information about this error, try `rustc --explain E0658`.","code":null,"level":"failure-note","spans":[],"children":[],"rendered":"For more information about this error, try `rustc --explain E0658`. -"} diff --git a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-rustfix.fixed b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-rustfix.fixed deleted file mode 100644 index 30250eca548ad..0000000000000 --- a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-rustfix.fixed +++ /dev/null @@ -1,10 +0,0 @@ -//@ run-rustfix -//@ rustfix-only-machine-applicable - -// Verify the #160431 rewrite is MachineApplicable: rustfix applies it and the result compiles -// without `#![feature(closure_lifetime_binder)]`. - -fn main() { - let _cl: for<'a> fn(&'a str) -> (&'a str, &'a str) = |x| { x.split_at(0) }; - //~^ ERROR `for<...>` binders for closures are experimental -} diff --git a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-rustfix.rs b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-rustfix.rs deleted file mode 100644 index 372a03e6e5d99..0000000000000 --- a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-rustfix.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ run-rustfix -//@ rustfix-only-machine-applicable - -// Verify the #160431 rewrite is MachineApplicable: rustfix applies it and the result compiles -// without `#![feature(closure_lifetime_binder)]`. - -fn main() { - let _cl = for<'a> |x: &'a str| -> (&'a str, &'a str) { x.split_at(0) }; - //~^ ERROR `for<...>` binders for closures are experimental -} diff --git a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-rustfix.stderr b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-rustfix.stderr deleted file mode 100644 index afdf6ece14144..0000000000000 --- a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-rustfix.stderr +++ /dev/null @@ -1,18 +0,0 @@ -error[E0658]: `for<...>` binders for closures are experimental - --> $DIR/feature-gate-closure_lifetime_binder-rustfix.rs:8:15 - | -LL | let _cl = for<'a> |x: &'a str| -> (&'a str, &'a str) { x.split_at(0) }; - | ^^^^^^^ - | - = note: see issue #97362 for more information - = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -help: consider setting the binding type instead - | -LL - let _cl = for<'a> |x: &'a str| -> (&'a str, &'a str) { x.split_at(0) }; -LL + let _cl: for<'a> fn(&'a str) -> (&'a str, &'a str) = |x| { x.split_at(0) }; - | - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder.rs b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder.rs index cb62f426083f4..b0b494fa3ff13 100644 --- a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder.rs +++ b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder.rs @@ -1,5 +1,3 @@ -//@ edition: 2024 - fn main() { for<> || -> () {}; //~^ ERROR `for<...>` binders for closures are experimental @@ -7,121 +5,4 @@ fn main() { //~^ ERROR `for<...>` binders for closures are experimental for<'a, 'b> |_: &'a ()| -> () {}; //~^ ERROR `for<...>` binders for closures are experimental - - // Issue #160431: suggest moving the binder onto a `fn` pointer binding type. - let _cl = for<'a> |x: &'a str| -> (&'a str, &'a str) { x.split_at(0) }; - //~^ ERROR `for<...>` binders for closures are experimental - - // Local temporaries in the body are fine for `fn` pointers (machine-applicable). - let _tmp = for<'a> |x: &'a str| -> usize { - //~^ ERROR `for<...>` binders for closures are experimental - let n = x.len(); - n - }; - - // Already has a type ascription — fall back to the simple help. - let _typed: _ = for<'a> |x: &'a str| -> &'a str { x }; - //~^ ERROR `for<...>` binders for closures are experimental - - // Infer placeholders must not be copied into a MachineApplicable `fn` type. - let _ret_infer = for<'a> |x: &'a str| -> _ { x }; - //~^ ERROR `for<...>` binders for closures are experimental - //~| ERROR implicit types in closure signatures are forbidden when `for<...>` is present - let _nested_infer = for<'a> |x: &'a _| -> &'a str { x }; - //~^ ERROR `for<...>` binders for closures are experimental - //~| ERROR implicit types in closure signatures are forbidden when `for<...>` is present - - // Explicit `move` closures are not `fn` pointers. - let y = 1; - let _move = for<'a> move |x: &'a i32| -> i32 { *x + y }; - //~^ ERROR `for<...>` binders for closures are experimental - - // Possible captures (any case) still get a suggestion, but only as maybe-incorrect. - let z = 1; - let _capture = for<'a> |x: &'a i32| -> i32 { *x + z }; - //~^ ERROR `for<...>` binders for closures are experimental - let Y = 1; - let _upper = for<'a> |x: &'a i32| -> i32 { *x + Y }; - //~^ ERROR `for<...>` binders for closures are experimental - - // `if let` bindings must not escape into the `else` branch (or past the `if`). - let if_let_env = 1; - let _if_let = for<'a> |x: &'a i32| -> i32 { - //~^ ERROR `for<...>` binders for closures are experimental - if let Some(if_let_env) = None:: { - if_let_env - } else { - *x + if_let_env - } - }; - - // Same for `while let`. - let while_let_env = 1; - let _while_let = for<'a> |x: &'a i32| -> i32 { - //~^ ERROR `for<...>` binders for closures are experimental - while let Some(while_let_env) = None:: { - let _ = while_let_env; - break; - } - *x + while_let_env - }; - - // Let-chain bindings are scoped to the `if` as well (same name as the outer capture). - let chain_env = 1; - let _let_chain = for<'a> |x: &'a i32| -> i32 { - //~^ ERROR `for<...>` binders for closures are experimental - if let Some(chain_env) = None:: - && chain_env == 0 - { - 0 - } else { - *x + chain_env - } - }; - - // Locals declared in a `let else` block must not leak past it. - let let_else_env = 1; - let _let_else = for<'a> |x: &'a i32| -> i32 { - //~^ ERROR `for<...>` binders for closures are experimental - let Some(_) = None:: else { - let let_else_env = 0; - return let_else_env; - }; - *x + let_else_env - }; - - // Free functions look like captures to the AST heuristic; suggestion is maybe-incorrect. - let _freefn = for<'a> |x: &'a i32| -> i32 { add(*x, 1) }; - //~^ ERROR `for<...>` binders for closures are experimental - - // `ref` bindings on the `let` are not rewritten. - let ref _ref_cl = for<'a> |x: &'a str| -> &'a str { x }; - //~^ ERROR `for<...>` binders for closures are experimental - - // `ref` closure parameters are not rewritten. - let _ref_param = for<'a> |ref x: &'a str| -> &'a str { *x }; - //~^ ERROR `for<...>` binders for closures are experimental - - // Parameter attributes would be dropped by the rewrite — fall back. - let _attrs = for<'a> |#[allow(unused)] x: &'a str| -> &'a str { x }; - //~^ ERROR `for<...>` binders for closures are experimental - - // Non-lifetime binders are not valid on `fn` pointers. - let _ty_binder = for |x: T| -> T { x }; - //~^ ERROR `for<...>` binders for closures are experimental - //~| ERROR only lifetime parameters can be used in this context - - // Bounded lifetime binders are not valid on `fn` pointers. - let _bound = for<'a: 'static> |x: &'a str| -> &'a str { x }; - //~^ ERROR `for<...>` binders for closures are experimental - //~| ERROR bounds cannot be used in this context - - // Pre-expansion gating still rejects binders under `#[cfg(false)]`. - #[cfg(false)] - let _cfg = for<'a> |x: &'a str| -> &'a str { x }; - //~^ ERROR `for<...>` binders for closures are experimental -} - -fn add(a: i32, b: i32) -> i32 { - a + b } diff --git a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder.stderr b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder.stderr index cc703db9c61e8..d5306287b58d9 100644 --- a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder.stderr +++ b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder.stderr @@ -1,5 +1,5 @@ error[E0658]: `for<...>` binders for closures are experimental - --> $DIR/feature-gate-closure_lifetime_binder.rs:4:5 + --> $DIR/feature-gate-closure_lifetime_binder.rs:2:5 | LL | for<> || -> () {}; | ^^^^^ @@ -7,10 +7,10 @@ LL | for<> || -> () {}; = note: see issue #97362 for more information = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = help: consider removing `for<...>` + = help: consider using a type annotation instead: `let closure: for<...> fn(...) -> ... = /* closure */;` error[E0658]: `for<...>` binders for closures are experimental - --> $DIR/feature-gate-closure_lifetime_binder.rs:6:5 + --> $DIR/feature-gate-closure_lifetime_binder.rs:4:5 | LL | for<'a> || -> () {}; | ^^^^^^^ @@ -18,10 +18,10 @@ LL | for<'a> || -> () {}; = note: see issue #97362 for more information = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = help: consider removing `for<...>` + = help: consider using a type annotation instead: `let closure: for<...> fn(...) -> ... = /* closure */;` error[E0658]: `for<...>` binders for closures are experimental - --> $DIR/feature-gate-closure_lifetime_binder.rs:8:5 + --> $DIR/feature-gate-closure_lifetime_binder.rs:6:5 | LL | for<'a, 'b> |_: &'a ()| -> () {}; | ^^^^^^^^^^^ @@ -29,285 +29,8 @@ LL | for<'a, 'b> |_: &'a ()| -> () {}; = note: see issue #97362 for more information = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = help: consider removing `for<...>` - -error[E0658]: `for<...>` binders for closures are experimental - --> $DIR/feature-gate-closure_lifetime_binder.rs:12:15 - | -LL | let _cl = for<'a> |x: &'a str| -> (&'a str, &'a str) { x.split_at(0) }; - | ^^^^^^^ - | - = note: see issue #97362 for more information - = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -help: consider setting the binding type instead - | -LL - let _cl = for<'a> |x: &'a str| -> (&'a str, &'a str) { x.split_at(0) }; -LL + let _cl: for<'a> fn(&'a str) -> (&'a str, &'a str) = |x| { x.split_at(0) }; - | - -error[E0658]: `for<...>` binders for closures are experimental - --> $DIR/feature-gate-closure_lifetime_binder.rs:16:16 - | -LL | let _tmp = for<'a> |x: &'a str| -> usize { - | ^^^^^^^ - | - = note: see issue #97362 for more information - = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -help: consider setting the binding type instead - | -LL - let _tmp = for<'a> |x: &'a str| -> usize { -LL + let _tmp: for<'a> fn(&'a str) -> usize = |x| { - | - -error[E0658]: `for<...>` binders for closures are experimental - --> $DIR/feature-gate-closure_lifetime_binder.rs:23:21 - | -LL | let _typed: _ = for<'a> |x: &'a str| -> &'a str { x }; - | ^^^^^^^ - | - = note: see issue #97362 for more information - = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = help: consider removing `for<...>` - -error[E0658]: `for<...>` binders for closures are experimental - --> $DIR/feature-gate-closure_lifetime_binder.rs:27:22 - | -LL | let _ret_infer = for<'a> |x: &'a str| -> _ { x }; - | ^^^^^^^ - | - = note: see issue #97362 for more information - = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = help: consider removing `for<...>` - -error[E0658]: `for<...>` binders for closures are experimental - --> $DIR/feature-gate-closure_lifetime_binder.rs:30:25 - | -LL | let _nested_infer = for<'a> |x: &'a _| -> &'a str { x }; - | ^^^^^^^ - | - = note: see issue #97362 for more information - = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = help: consider removing `for<...>` - -error[E0658]: `for<...>` binders for closures are experimental - --> $DIR/feature-gate-closure_lifetime_binder.rs:36:17 - | -LL | let _move = for<'a> move |x: &'a i32| -> i32 { *x + y }; - | ^^^^^^^ - | - = note: see issue #97362 for more information - = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = help: consider removing `for<...>` - -error[E0658]: `for<...>` binders for closures are experimental - --> $DIR/feature-gate-closure_lifetime_binder.rs:41:20 - | -LL | let _capture = for<'a> |x: &'a i32| -> i32 { *x + z }; - | ^^^^^^^ - | - = note: see issue #97362 for more information - = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -help: consider setting the binding type instead - | -LL - let _capture = for<'a> |x: &'a i32| -> i32 { *x + z }; -LL + let _capture: for<'a> fn(&'a i32) -> i32 = |x| { *x + z }; - | - -error[E0658]: `for<...>` binders for closures are experimental - --> $DIR/feature-gate-closure_lifetime_binder.rs:44:18 - | -LL | let _upper = for<'a> |x: &'a i32| -> i32 { *x + Y }; - | ^^^^^^^ - | - = note: see issue #97362 for more information - = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -help: consider setting the binding type instead - | -LL - let _upper = for<'a> |x: &'a i32| -> i32 { *x + Y }; -LL + let _upper: for<'a> fn(&'a i32) -> i32 = |x| { *x + Y }; - | - -error[E0658]: `for<...>` binders for closures are experimental - --> $DIR/feature-gate-closure_lifetime_binder.rs:49:19 - | -LL | let _if_let = for<'a> |x: &'a i32| -> i32 { - | ^^^^^^^ - | - = note: see issue #97362 for more information - = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -help: consider setting the binding type instead - | -LL - let _if_let = for<'a> |x: &'a i32| -> i32 { -LL + let _if_let: for<'a> fn(&'a i32) -> i32 = |x| { - | - -error[E0658]: `for<...>` binders for closures are experimental - --> $DIR/feature-gate-closure_lifetime_binder.rs:60:22 - | -LL | let _while_let = for<'a> |x: &'a i32| -> i32 { - | ^^^^^^^ - | - = note: see issue #97362 for more information - = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -help: consider setting the binding type instead - | -LL - let _while_let = for<'a> |x: &'a i32| -> i32 { -LL + let _while_let: for<'a> fn(&'a i32) -> i32 = |x| { - | - -error[E0658]: `for<...>` binders for closures are experimental - --> $DIR/feature-gate-closure_lifetime_binder.rs:71:22 - | -LL | let _let_chain = for<'a> |x: &'a i32| -> i32 { - | ^^^^^^^ - | - = note: see issue #97362 for more information - = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -help: consider setting the binding type instead - | -LL - let _let_chain = for<'a> |x: &'a i32| -> i32 { -LL + let _let_chain: for<'a> fn(&'a i32) -> i32 = |x| { - | - -error[E0658]: `for<...>` binders for closures are experimental - --> $DIR/feature-gate-closure_lifetime_binder.rs:84:21 - | -LL | let _let_else = for<'a> |x: &'a i32| -> i32 { - | ^^^^^^^ - | - = note: see issue #97362 for more information - = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -help: consider setting the binding type instead - | -LL - let _let_else = for<'a> |x: &'a i32| -> i32 { -LL + let _let_else: for<'a> fn(&'a i32) -> i32 = |x| { - | - -error[E0658]: `for<...>` binders for closures are experimental - --> $DIR/feature-gate-closure_lifetime_binder.rs:94:19 - | -LL | let _freefn = for<'a> |x: &'a i32| -> i32 { add(*x, 1) }; - | ^^^^^^^ - | - = note: see issue #97362 for more information - = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -help: consider setting the binding type instead - | -LL - let _freefn = for<'a> |x: &'a i32| -> i32 { add(*x, 1) }; -LL + let _freefn: for<'a> fn(&'a i32) -> i32 = |x| { add(*x, 1) }; - | - -error[E0658]: `for<...>` binders for closures are experimental - --> $DIR/feature-gate-closure_lifetime_binder.rs:98:23 - | -LL | let ref _ref_cl = for<'a> |x: &'a str| -> &'a str { x }; - | ^^^^^^^ - | - = note: see issue #97362 for more information - = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = help: consider removing `for<...>` - -error[E0658]: `for<...>` binders for closures are experimental - --> $DIR/feature-gate-closure_lifetime_binder.rs:102:22 - | -LL | let _ref_param = for<'a> |ref x: &'a str| -> &'a str { *x }; - | ^^^^^^^ - | - = note: see issue #97362 for more information - = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = help: consider removing `for<...>` - -error[E0658]: `for<...>` binders for closures are experimental - --> $DIR/feature-gate-closure_lifetime_binder.rs:106:18 - | -LL | let _attrs = for<'a> |#[allow(unused)] x: &'a str| -> &'a str { x }; - | ^^^^^^^ - | - = note: see issue #97362 for more information - = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = help: consider removing `for<...>` - -error[E0658]: `for<...>` binders for closures are experimental - --> $DIR/feature-gate-closure_lifetime_binder.rs:110:22 - | -LL | let _ty_binder = for |x: T| -> T { x }; - | ^^^^^^ - | - = note: see issue #97362 for more information - = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = help: consider removing `for<...>` - -error[E0658]: only lifetime parameters can be used in this context - --> $DIR/feature-gate-closure_lifetime_binder.rs:110:26 - | -LL | let _ty_binder = for |x: T| -> T { x }; - | ^ - | - = note: see issue #108185 for more information - = help: add `#![feature(non_lifetime_binders)]` 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[E0658]: `for<...>` binders for closures are experimental - --> $DIR/feature-gate-closure_lifetime_binder.rs:115:18 - | -LL | let _bound = for<'a: 'static> |x: &'a str| -> &'a str { x }; - | ^^^^^^^^^^^^^^^^ - | - = note: see issue #97362 for more information - = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = help: consider removing `for<...>` - -error: bounds cannot be used in this context - --> $DIR/feature-gate-closure_lifetime_binder.rs:115:26 - | -LL | let _bound = for<'a: 'static> |x: &'a str| -> &'a str { x }; - | ^^^^^^^ - -error[E0658]: `for<...>` binders for closures are experimental - --> $DIR/feature-gate-closure_lifetime_binder.rs:121:16 - | -LL | let _cfg = for<'a> |x: &'a str| -> &'a str { x }; - | ^^^^^^^ - | - = note: see issue #97362 for more information - = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = help: consider removing `for<...>` - -error: implicit types in closure signatures are forbidden when `for<...>` is present - --> $DIR/feature-gate-closure_lifetime_binder.rs:27:46 - | -LL | let _ret_infer = for<'a> |x: &'a str| -> _ { x }; - | ------- ^ - | | - | `for<...>` is here - -error: implicit types in closure signatures are forbidden when `for<...>` is present - --> $DIR/feature-gate-closure_lifetime_binder.rs:30:41 - | -LL | let _nested_infer = for<'a> |x: &'a _| -> &'a str { x }; - | ------- ^ - | | - | `for<...>` is here + = help: consider using a type annotation instead: `let closure: for<...> fn(...) -> ... = /* closure */;` -error: aborting due to 26 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/parser/recover/recover-quantified-closure.stderr b/tests/ui/parser/recover/recover-quantified-closure.stderr index 96953b7beeede..095657882c36c 100644 --- a/tests/ui/parser/recover/recover-quantified-closure.stderr +++ b/tests/ui/parser/recover/recover-quantified-closure.stderr @@ -13,7 +13,7 @@ LL | for<'a> |x: &'a u8| *x + 1; = note: see issue #97362 for more information = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = help: consider removing `for<...>` + = help: consider using a type annotation instead: `let closure: for<...> fn(...) -> ... = /* closure */;` error[E0658]: `for<...>` binders for closures are experimental --> $DIR/recover-quantified-closure.rs:10:5 @@ -24,7 +24,7 @@ LL | for ::Bar in x {} = note: see issue #97362 for more information = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = help: consider removing `for<...>` + = help: consider using a type annotation instead: `let closure: for<...> fn(...) -> ... = /* closure */;` error: implicit types in closure signatures are forbidden when `for<...>` is present --> $DIR/recover-quantified-closure.rs:3:24 From 2e791273c51e0fe68acf1f5f18dbec862311dde3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 16 Aug 2026 22:19:59 +0200 Subject: [PATCH 66/66] make memcmp test less constrained --- src/tools/miri/tests/pass-dep/libc/libc-mem.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/miri/tests/pass-dep/libc/libc-mem.rs b/src/tools/miri/tests/pass-dep/libc/libc-mem.rs index 202727b198749..a64a23aa5a38f 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-mem.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-mem.rs @@ -344,9 +344,9 @@ fn test_memset() { fn test_memcmp() { unsafe { - assert_eq!(libc::memcmp(b"123".as_ptr().cast(), b"132".as_ptr().cast(), 3), -1); - assert_eq!(libc::memcmp(b"abc".as_ptr().cast(), b"aaa".as_ptr().cast(), 3), 1); - assert_eq!(libc::memcmp(b"xyz".as_ptr().cast(), b"xyz".as_ptr().cast(), 3), 0); + assert!(libc::memcmp(b"123".as_ptr().cast(), b"132".as_ptr().cast(), 3) < 0); + assert!(libc::memcmp(b"abc".as_ptr().cast(), b"aaa".as_ptr().cast(), 3) > 0); + assert!(libc::memcmp(b"xyz".as_ptr().cast(), b"xyz".as_ptr().cast(), 3) == 0); } }