From 62435f922a0b0b100f217f13047d4741c14ec1c5 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Mon, 26 May 2025 15:20:59 +0800 Subject: [PATCH 01/18] impl `Default` for `array::IntoIter` --- library/core/src/array/iter.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/library/core/src/array/iter.rs b/library/core/src/array/iter.rs index 90f76d6d4c7be..a59b2f05305d2 100644 --- a/library/core/src/array/iter.rs +++ b/library/core/src/array/iter.rs @@ -224,6 +224,13 @@ impl IntoIter { } } +#[stable(feature = "array_value_iter_default", since = "CURRENT_RUSTC_VERSION")] +impl Default for IntoIter { + fn default() -> Self { + IntoIter::empty() + } +} + #[stable(feature = "array_value_iter_impls", since = "1.40.0")] impl Iterator for IntoIter { type Item = T; From 5f0dd44b3b1b142582d3dc8264cb2b8dd8f7c7f0 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Thu, 12 Jun 2025 00:02:12 +0800 Subject: [PATCH 02/18] avoid `&mut P` in `visit_expr` etc methods --- compiler/rustc_ast/src/ast.rs | 26 +++++++++++--- compiler/rustc_ast/src/mut_visit.rs | 6 ++-- compiler/rustc_builtin_macros/src/cfg_eval.rs | 2 +- .../src/deriving/coerce_pointee.rs | 5 ++- compiler/rustc_expand/src/expand.rs | 35 ++++++++++--------- compiler/rustc_expand/src/placeholders.rs | 12 +++---- compiler/rustc_parse/src/parser/expr.rs | 2 +- compiler/rustc_parse/src/parser/pat.rs | 2 +- .../clippy_lints/src/unnested_or_patterns.rs | 6 ++-- tests/ui-fulldeps/pprust-expr-roundtrip.rs | 8 ++--- .../pprust-parenthesis-insertion.rs | 3 +- 11 files changed, 62 insertions(+), 45 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index cf40c3f7f6f8e..760e43ef10fc9 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -693,6 +693,12 @@ impl Pat { } } +impl From> for Pat { + fn from(value: P) -> Self { + *value + } +} + /// A single field in a struct pattern. /// /// Patterns like the fields of `Foo { x, ref y, ref mut z }` @@ -1522,17 +1528,23 @@ impl Expr { ) } - /// Creates a dummy `P`. + /// Creates a dummy `Expr`. /// /// Should only be used when it will be replaced afterwards or as a return value when an error was encountered. - pub fn dummy() -> P { - P(Expr { + pub fn dummy() -> Expr { + Expr { id: DUMMY_NODE_ID, kind: ExprKind::Dummy, span: DUMMY_SP, attrs: ThinVec::new(), tokens: None, - }) + } + } +} + +impl From> for Expr { + fn from(value: P) -> Self { + *value } } @@ -2343,6 +2355,12 @@ impl Clone for Ty { } } +impl From> for Ty { + fn from(value: P) -> Self { + *value + } +} + impl Ty { pub fn peel_refs(&self) -> &Self { let mut final_ty = self; diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 71a47dcfcba2b..07fbe8045fc2f 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -168,7 +168,7 @@ pub trait MutVisitor: Sized + MutVisitorResult { walk_flat_map_arm(self, arm) } - fn visit_pat(&mut self, p: &mut P) { + fn visit_pat(&mut self, p: &mut Pat) { walk_pat(self, p); } @@ -176,7 +176,7 @@ pub trait MutVisitor: Sized + MutVisitorResult { walk_anon_const(self, c); } - fn visit_expr(&mut self, e: &mut P) { + fn visit_expr(&mut self, e: &mut Expr) { walk_expr(self, e); } @@ -194,7 +194,7 @@ pub trait MutVisitor: Sized + MutVisitorResult { walk_generic_arg(self, arg); } - fn visit_ty(&mut self, t: &mut P) { + fn visit_ty(&mut self, t: &mut Ty) { walk_ty(self, t); } diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index da01e3e9607bb..fe44350863c9a 100644 --- a/compiler/rustc_builtin_macros/src/cfg_eval.rs +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -155,7 +155,7 @@ impl CfgEval<'_> { impl MutVisitor for CfgEval<'_> { #[instrument(level = "trace", skip(self))] - fn visit_expr(&mut self, expr: &mut P) { + fn visit_expr(&mut self, expr: &mut ast::Expr) { self.0.configure_expr(expr, false); mut_visit::walk_expr(self, expr); } diff --git a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs index 0794192621a93..3a20b39798d7b 100644 --- a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs +++ b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs @@ -1,5 +1,4 @@ use ast::HasAttrs; -use ast::ptr::P; use rustc_ast::mut_visit::MutVisitor; use rustc_ast::visit::BoundKind; use rustc_ast::{ @@ -378,11 +377,11 @@ struct TypeSubstitution<'a> { } impl<'a> ast::mut_visit::MutVisitor for TypeSubstitution<'a> { - fn visit_ty(&mut self, ty: &mut P) { + fn visit_ty(&mut self, ty: &mut ast::Ty) { if let Some(name) = ty.kind.is_simple_path() && name == self.from_name { - **ty = self.to_ty.clone(); + *ty = self.to_ty.clone(); self.rewritten = true; } else { ast::mut_visit::walk_ty(self, ty); diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 569165a64e520..301da8e604b8b 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1668,7 +1668,7 @@ impl InvocationCollectorNode for ast::Crate { } } -impl InvocationCollectorNode for P { +impl InvocationCollectorNode for ast::Ty { type OutputTy = P; const KIND: AstFragmentKind = AstFragmentKind::Ty; fn to_annotatable(self) -> Annotatable { @@ -1691,7 +1691,7 @@ impl InvocationCollectorNode for P { } } -impl InvocationCollectorNode for P { +impl InvocationCollectorNode for ast::Pat { type OutputTy = P; const KIND: AstFragmentKind = AstFragmentKind::Pat; fn to_annotatable(self) -> Annotatable { @@ -1714,11 +1714,11 @@ impl InvocationCollectorNode for P { } } -impl InvocationCollectorNode for P { +impl InvocationCollectorNode for ast::Expr { type OutputTy = P; const KIND: AstFragmentKind = AstFragmentKind::Expr; fn to_annotatable(self) -> Annotatable { - Annotatable::Expr(self) + Annotatable::Expr(P(self)) } fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { fragment.make_expr() @@ -1855,29 +1855,29 @@ impl DummyAstNode for ast::Crate { } } -impl DummyAstNode for P { +impl DummyAstNode for ast::Ty { fn dummy() -> Self { - P(ast::Ty { + ast::Ty { id: DUMMY_NODE_ID, kind: TyKind::Dummy, span: Default::default(), tokens: Default::default(), - }) + } } } -impl DummyAstNode for P { +impl DummyAstNode for ast::Pat { fn dummy() -> Self { - P(ast::Pat { + ast::Pat { id: DUMMY_NODE_ID, kind: PatKind::Wild, span: Default::default(), tokens: Default::default(), - }) + } } } -impl DummyAstNode for P { +impl DummyAstNode for ast::Expr { fn dummy() -> Self { ast::Expr::dummy() } @@ -1885,7 +1885,7 @@ impl DummyAstNode for P { impl DummyAstNode for AstNodeWrapper, MethodReceiverTag> { fn dummy() -> Self { - AstNodeWrapper::new(ast::Expr::dummy(), MethodReceiverTag) + AstNodeWrapper::new(P(ast::Expr::dummy()), MethodReceiverTag) } } @@ -2172,7 +2172,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { } } - fn visit_node + DummyAstNode>( + fn visit_node> + DummyAstNode>( &mut self, node: &mut Node, ) { @@ -2197,6 +2197,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { *node = self .collect_attr((attr, pos, derives), n.to_annotatable(), Node::KIND) .make_ast::() + .into() } }, None if node.is_mac_call() => { @@ -2204,7 +2205,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { let (mac, attrs, _) = n.take_mac_call(); self.check_attributes(&attrs, &mac); - *node = self.collect_bang(mac, Node::KIND).make_ast::() + *node = self.collect_bang(mac, Node::KIND).make_ast::().into() } None if node.delegation().is_some() => unreachable!(), None => { @@ -2314,15 +2315,15 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { self.visit_node(node) } - fn visit_ty(&mut self, node: &mut P) { + fn visit_ty(&mut self, node: &mut ast::Ty) { self.visit_node(node) } - fn visit_pat(&mut self, node: &mut P) { + fn visit_pat(&mut self, node: &mut ast::Pat) { self.visit_node(node) } - fn visit_expr(&mut self, node: &mut P) { + fn visit_expr(&mut self, node: &mut ast::Expr) { // FIXME: Feature gating is performed inconsistently between `Expr` and `OptExpr`. if let Some(attr) = node.attrs.first() { self.cfg().maybe_emit_expr_attr_err(attr); diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index 3dcb20c8c7682..2c486a02bdf18 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -332,9 +332,9 @@ impl MutVisitor for PlaceholderExpander { } } - fn visit_expr(&mut self, expr: &mut P) { + fn visit_expr(&mut self, expr: &mut ast::Expr) { match expr.kind { - ast::ExprKind::MacCall(_) => *expr = self.remove(expr.id).make_expr(), + ast::ExprKind::MacCall(_) => *expr = *self.remove(expr.id).make_expr(), _ => walk_expr(self, expr), } } @@ -399,16 +399,16 @@ impl MutVisitor for PlaceholderExpander { stmts } - fn visit_pat(&mut self, pat: &mut P) { + fn visit_pat(&mut self, pat: &mut ast::Pat) { match pat.kind { - ast::PatKind::MacCall(_) => *pat = self.remove(pat.id).make_pat(), + ast::PatKind::MacCall(_) => *pat = *self.remove(pat.id).make_pat(), _ => walk_pat(self, pat), } } - fn visit_ty(&mut self, ty: &mut P) { + fn visit_ty(&mut self, ty: &mut ast::Ty) { match ty.kind { - ast::TyKind::MacCall(_) => *ty = self.remove(ty.id).make_ty(), + ast::TyKind::MacCall(_) => *ty = *self.remove(ty.id).make_ty(), _ => walk_ty(self, ty), } } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 93489aa8ee948..93c76c47f060b 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -4087,7 +4087,7 @@ impl<'a> CondChecker<'a> { } impl MutVisitor for CondChecker<'_> { - fn visit_expr(&mut self, e: &mut P) { + fn visit_expr(&mut self, e: &mut Expr) { self.depth += 1; use ForbiddenLetReason::*; diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 7a226136e2353..64653ee2a04c9 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -1094,7 +1094,7 @@ impl<'a> Parser<'a> { fn make_all_value_bindings_mutable(pat: &mut P) -> bool { struct AddMut(bool); impl MutVisitor for AddMut { - fn visit_pat(&mut self, pat: &mut P) { + fn visit_pat(&mut self, pat: &mut Pat) { if let PatKind::Ident(BindingMode(ByRef::No, m @ Mutability::Not), ..) = &mut pat.kind { diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs index b839b6f56728c..bd8420917f5e6 100644 --- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs @@ -128,7 +128,7 @@ fn remove_all_parens(pat: &mut P) { } impl MutVisitor for Visitor { - fn visit_pat(&mut self, pat: &mut P) { + fn visit_pat(&mut self, pat: &mut Pat) { let is_inner = mem::replace(&mut self.is_inner, true); walk_pat(self, pat); let inner = match &mut pat.kind { @@ -145,7 +145,7 @@ fn remove_all_parens(pat: &mut P) { fn insert_necessary_parens(pat: &mut P) { struct Visitor; impl MutVisitor for Visitor { - fn visit_pat(&mut self, pat: &mut P) { + fn visit_pat(&mut self, pat: &mut Pat) { use ast::BindingMode; walk_pat(self, pat); let target = match &mut pat.kind { @@ -167,7 +167,7 @@ fn unnest_or_patterns(pat: &mut P) -> bool { changed: bool, } impl MutVisitor for Visitor { - fn visit_pat(&mut self, p: &mut P) { + fn visit_pat(&mut self, p: &mut Pat) { // This is a bottom up transformation, so recurse first. walk_pat(self, p); diff --git a/tests/ui-fulldeps/pprust-expr-roundtrip.rs b/tests/ui-fulldeps/pprust-expr-roundtrip.rs index f5cfa9e0bccff..8bca20852add4 100644 --- a/tests/ui-fulldeps/pprust-expr-roundtrip.rs +++ b/tests/ui-fulldeps/pprust-expr-roundtrip.rs @@ -187,9 +187,9 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P)) { struct RemoveParens; impl MutVisitor for RemoveParens { - fn visit_expr(&mut self, e: &mut P) { + fn visit_expr(&mut self, e: &mut Expr) { match e.kind.clone() { - ExprKind::Paren(inner) => *e = inner, + ExprKind::Paren(inner) => *e = *inner, _ => {} }; mut_visit::walk_expr(self, e); @@ -200,11 +200,11 @@ impl MutVisitor for RemoveParens { struct AddParens; impl MutVisitor for AddParens { - fn visit_expr(&mut self, e: &mut P) { + fn visit_expr(&mut self, e: &mut Expr) { mut_visit::walk_expr(self, e); let expr = std::mem::replace(e, Expr::dummy()); - e.kind = ExprKind::Paren(expr); + e.kind = ExprKind::Paren(P(expr)); } } diff --git a/tests/ui-fulldeps/pprust-parenthesis-insertion.rs b/tests/ui-fulldeps/pprust-parenthesis-insertion.rs index c566ac459e0d2..2047f8dc76546 100644 --- a/tests/ui-fulldeps/pprust-parenthesis-insertion.rs +++ b/tests/ui-fulldeps/pprust-parenthesis-insertion.rs @@ -43,7 +43,6 @@ use std::process::ExitCode; use parser::parse_expr; use rustc_ast::ast::{Expr, ExprKind}; use rustc_ast::mut_visit::{self, MutVisitor}; -use rustc_ast::ptr::P; use rustc_ast_pretty::pprust; use rustc_session::parse::ParseSess; @@ -152,7 +151,7 @@ static EXPRS: &[&str] = &[ struct Unparenthesize; impl MutVisitor for Unparenthesize { - fn visit_expr(&mut self, e: &mut P) { + fn visit_expr(&mut self, e: &mut Expr) { while let ExprKind::Paren(paren) = &mut e.kind { *e = mem::replace(paren, Expr::dummy()); } From 0e1db54b7eba50b79ff318eb31bf44e0c7b6a4c2 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Sat, 14 Jun 2025 17:38:10 +0000 Subject: [PATCH 03/18] Windows: Use anonymous pipes in Command --- library/std/src/sys/pal/windows/c.rs | 17 ++ .../std/src/sys/pal/windows/c/bindings.txt | 17 ++ .../std/src/sys/pal/windows/c/windows_sys.rs | 19 +- library/std/src/sys/pal/windows/pipe.rs | 175 +++++++++--------- 4 files changed, 142 insertions(+), 86 deletions(-) diff --git a/library/std/src/sys/pal/windows/c.rs b/library/std/src/sys/pal/windows/c.rs index ac1c5e9932e1c..53dec105d0c85 100644 --- a/library/std/src/sys/pal/windows/c.rs +++ b/library/std/src/sys/pal/windows/c.rs @@ -119,6 +119,23 @@ unsafe extern "system" { pub fn ProcessPrng(pbdata: *mut u8, cbdata: usize) -> BOOL; } +windows_targets::link!("ntdll.dll" "system" fn NtCreateNamedPipeFile( + filehandle: *mut HANDLE, + desiredaccess: FILE_ACCESS_RIGHTS, + objectattributes: *const OBJECT_ATTRIBUTES, + iostatusblock: *mut IO_STATUS_BLOCK, + shareaccess: FILE_SHARE_MODE, + createdisposition: NTCREATEFILE_CREATE_DISPOSITION, + createoptions: NTCREATEFILE_CREATE_OPTIONS, + namedpipetype: u32, + readmode: u32, + completionmode: u32, + maximuminstances: u32, + inboundquota: u32, + outboundquota: u32, + defaulttimeout: *const u64, +) -> NTSTATUS); + // Functions that aren't available on every version of Windows that we support, // but we still use them and just provide some form of a fallback implementation. compat_fn_with_fallback! { diff --git a/library/std/src/sys/pal/windows/c/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt index a99c474c763c5..827d96e73db41 100644 --- a/library/std/src/sys/pal/windows/c/bindings.txt +++ b/library/std/src/sys/pal/windows/c/bindings.txt @@ -2060,6 +2060,14 @@ FILE_OPEN_REPARSE_POINT FILE_OPEN_REQUIRING_OPLOCK FILE_OVERWRITE FILE_OVERWRITE_IF +FILE_PIPE_ACCEPT_REMOTE_CLIENTS +FILE_PIPE_BYTE_STREAM_MODE +FILE_PIPE_BYTE_STREAM_TYPE +FILE_PIPE_COMPLETE_OPERATION +FILE_PIPE_MESSAGE_MODE +FILE_PIPE_MESSAGE_TYPE +FILE_PIPE_QUEUE_OPERATION +FILE_PIPE_REJECT_REMOTE_CLIENTS FILE_RANDOM_ACCESS FILE_READ_ATTRIBUTES FILE_READ_DATA @@ -2294,7 +2302,16 @@ NtOpenFile NtReadFile NTSTATUS NtWriteFile +OBJ_CASE_INSENSITIVE OBJ_DONT_REPARSE +OBJ_EXCLUSIVE +OBJ_FORCE_ACCESS_CHECK +OBJ_IGNORE_IMPERSONATED_DEVICEMAP +OBJ_INHERIT +OBJ_KERNEL_HANDLE +OBJ_OPENIF +OBJ_OPENLINK +OBJ_PERMANENT OPEN_ALWAYS OPEN_EXISTING OpenProcessToken diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs index 95bf8040229d0..b2e3aabc63353 100644 --- a/library/std/src/sys/pal/windows/c/windows_sys.rs +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -1,4 +1,4 @@ -// Bindings generated by `windows-bindgen` 0.61.0 +// Bindings generated by `windows-bindgen` 0.61.1 #![allow(non_snake_case, non_upper_case_globals, non_camel_case_types, dead_code, clippy::all)] @@ -2552,6 +2552,14 @@ pub const FILE_OPEN_REPARSE_POINT: NTCREATEFILE_CREATE_OPTIONS = 2097152u32; pub const FILE_OPEN_REQUIRING_OPLOCK: NTCREATEFILE_CREATE_OPTIONS = 65536u32; pub const FILE_OVERWRITE: NTCREATEFILE_CREATE_DISPOSITION = 4u32; pub const FILE_OVERWRITE_IF: NTCREATEFILE_CREATE_DISPOSITION = 5u32; +pub const FILE_PIPE_ACCEPT_REMOTE_CLIENTS: u32 = 0u32; +pub const FILE_PIPE_BYTE_STREAM_MODE: u32 = 0u32; +pub const FILE_PIPE_BYTE_STREAM_TYPE: u32 = 0u32; +pub const FILE_PIPE_COMPLETE_OPERATION: u32 = 1u32; +pub const FILE_PIPE_MESSAGE_MODE: u32 = 1u32; +pub const FILE_PIPE_MESSAGE_TYPE: u32 = 1u32; +pub const FILE_PIPE_QUEUE_OPERATION: u32 = 0u32; +pub const FILE_PIPE_REJECT_REMOTE_CLIENTS: u32 = 2u32; pub const FILE_RANDOM_ACCESS: NTCREATEFILE_CREATE_OPTIONS = 2048u32; pub const FILE_READ_ATTRIBUTES: FILE_ACCESS_RIGHTS = 128u32; pub const FILE_READ_DATA: FILE_ACCESS_RIGHTS = 1u32; @@ -2983,7 +2991,16 @@ impl Default for OBJECT_ATTRIBUTES { } } pub type OBJECT_ATTRIBUTE_FLAGS = u32; +pub const OBJ_CASE_INSENSITIVE: OBJECT_ATTRIBUTE_FLAGS = 64u32; pub const OBJ_DONT_REPARSE: OBJECT_ATTRIBUTE_FLAGS = 4096u32; +pub const OBJ_EXCLUSIVE: OBJECT_ATTRIBUTE_FLAGS = 32u32; +pub const OBJ_FORCE_ACCESS_CHECK: OBJECT_ATTRIBUTE_FLAGS = 1024u32; +pub const OBJ_IGNORE_IMPERSONATED_DEVICEMAP: OBJECT_ATTRIBUTE_FLAGS = 2048u32; +pub const OBJ_INHERIT: OBJECT_ATTRIBUTE_FLAGS = 2u32; +pub const OBJ_KERNEL_HANDLE: OBJECT_ATTRIBUTE_FLAGS = 512u32; +pub const OBJ_OPENIF: OBJECT_ATTRIBUTE_FLAGS = 128u32; +pub const OBJ_OPENLINK: OBJECT_ATTRIBUTE_FLAGS = 256u32; +pub const OBJ_PERMANENT: OBJECT_ATTRIBUTE_FLAGS = 16u32; pub const OPEN_ALWAYS: FILE_CREATION_DISPOSITION = 4u32; pub const OPEN_EXISTING: FILE_CREATION_DISPOSITION = 3u32; #[repr(C)] diff --git a/library/std/src/sys/pal/windows/pipe.rs b/library/std/src/sys/pal/windows/pipe.rs index 00d469fbaf8c7..bc5d05c45052a 100644 --- a/library/std/src/sys/pal/windows/pipe.rs +++ b/library/std/src/sys/pal/windows/pipe.rs @@ -1,14 +1,9 @@ -use crate::ffi::OsStr; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::ops::Neg; use crate::os::windows::prelude::*; -use crate::path::Path; -use crate::random::{DefaultRandomSource, Random}; -use crate::sync::atomic::Ordering::Relaxed; -use crate::sync::atomic::{Atomic, AtomicUsize}; +use crate::sys::api::utf16; use crate::sys::c; -use crate::sys::fs::{File, OpenOptions}; use crate::sys::handle::Handle; -use crate::sys::pal::windows::api::{self, WinError}; use crate::sys_common::{FromInner, IntoInner}; use crate::{mem, ptr}; @@ -62,92 +57,113 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res // Note that we specifically do *not* use `CreatePipe` here because // unfortunately the anonymous pipes returned do not support overlapped - // operations. Instead, we create a "hopefully unique" name and create a - // named pipe which has overlapped operations enabled. + // operations. Instead, we use `NtCreateNamedPipeFile` to create the + // anonymous pipe with overlapped support. // - // Once we do this, we connect do it as usual via `CreateFileW`, and then + // Once we do this, we connect to it via `NtOpenFile`, and then // we return those reader/writer halves. Note that the `ours` pipe return // value is always the named pipe, whereas `theirs` is just the normal file. // This should hopefully shield us from child processes which assume their // stdout is a named pipe, which would indeed be odd! unsafe { - let ours; - let mut name; - let mut tries = 0; - loop { - tries += 1; - name = format!( - r"\\.\pipe\__rust_anonymous_pipe1__.{}.{}", - c::GetCurrentProcessId(), - random_number(), + let mut io_status = c::IO_STATUS_BLOCK::default(); + let mut object_attributes = c::OBJECT_ATTRIBUTES::default(); + object_attributes.Length = size_of::() as u32; + + // Open a handle to the pipe filesystem (`\??\PIPE\`). + // This will be used when creating a new annon pipe. + let pipe_fs = { + let path = c::UNICODE_STRING::from_ref(utf16!(r"\??\PIPE\")); + object_attributes.ObjectName = &path; + let mut pipe_fs = ptr::null_mut(); + let status = c::NtOpenFile( + &mut pipe_fs, + c::SYNCHRONIZE | c::GENERIC_READ, + &object_attributes, + &mut io_status, + c::FILE_SHARE_READ | c::FILE_SHARE_WRITE, + c::FILE_SYNCHRONOUS_IO_NONALERT, // synchronous access ); - let wide_name = OsStr::new(&name).encode_wide().chain(Some(0)).collect::>(); - let mut flags = c::FILE_FLAG_FIRST_PIPE_INSTANCE | c::FILE_FLAG_OVERLAPPED; - if ours_readable { - flags |= c::PIPE_ACCESS_INBOUND; + if c::nt_success(status) { + Handle::from_raw_handle(pipe_fs) } else { - flags |= c::PIPE_ACCESS_OUTBOUND; + return Err(io::Error::from_raw_os_error(c::RtlNtStatusToDosError(status) as i32)); } + }; - let handle = c::CreateNamedPipeW( - wide_name.as_ptr(), - flags, - c::PIPE_TYPE_BYTE - | c::PIPE_READMODE_BYTE - | c::PIPE_WAIT - | c::PIPE_REJECT_REMOTE_CLIENTS, + // From now on we're using handles instead of paths to create and open pipes. + // So set the `ObjectName` to a zero length string. + let empty = c::UNICODE_STRING::default(); + object_attributes.ObjectName = ∅ + + // Create our side of the pipe for async access. + let ours = { + // Use the pipe filesystem as the root directory. + // With no name provided, an anonymous pipe will be created. + object_attributes.RootDirectory = pipe_fs.as_raw_handle(); + + // A negative timeout value is a relative time (rather than an absolute time). + // The time is given in 100's of nanoseconds so this is 50 milliseconds. + // This value was chosen to be consistent with the default timeout set by `CreateNamedPipeW` + // See: https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createnamedpipew + let timeout = (50_i64 * 10000).neg() as u64; + + let mut ours = ptr::null_mut(); + let status = c::NtCreateNamedPipeFile( + &mut ours, + c::SYNCHRONIZE | if ours_readable { c::GENERIC_READ } else { c::GENERIC_WRITE }, + &object_attributes, + &mut io_status, + if ours_readable { c::FILE_SHARE_WRITE } else { c::FILE_SHARE_READ }, + c::FILE_CREATE, + 0, + c::FILE_PIPE_BYTE_STREAM_TYPE, + c::FILE_PIPE_BYTE_STREAM_MODE, + c::FILE_PIPE_QUEUE_OPERATION, + // only allow one client pipe 1, PIPE_BUFFER_CAPACITY, PIPE_BUFFER_CAPACITY, - 0, - ptr::null_mut(), + &timeout, ); - - // We pass the `FILE_FLAG_FIRST_PIPE_INSTANCE` flag above, and we're - // also just doing a best effort at selecting a unique name. If - // `ERROR_ACCESS_DENIED` is returned then it could mean that we - // accidentally conflicted with an already existing pipe, so we try - // again. - // - // Don't try again too much though as this could also perhaps be a - // legit error. - if handle == c::INVALID_HANDLE_VALUE { - let error = api::get_last_error(); - if tries < 10 && error == WinError::ACCESS_DENIED { - continue; - } else { - return Err(io::Error::from_raw_os_error(error.code as i32)); - } + if c::nt_success(status) { + Handle::from_raw_handle(ours) + } else { + return Err(io::Error::from_raw_os_error(c::RtlNtStatusToDosError(status) as i32)); } + }; - ours = Handle::from_raw_handle(handle); - break; - } + // Open their side of the pipe for synchronous access. + let theirs = { + // We can reopen the anonymous pipe without a name by setting + // RootDirectory to the pipe handle and not setting a path name, + object_attributes.RootDirectory = ours.as_raw_handle(); - // Connect to the named pipe we just created. This handle is going to be - // returned in `theirs`, so if `ours` is readable we want this to be - // writable, otherwise if `ours` is writable we want this to be - // readable. - // - // Additionally we don't enable overlapped mode on this because most - // client processes aren't enabled to work with that. - let mut opts = OpenOptions::new(); - opts.write(ours_readable); - opts.read(!ours_readable); - opts.share_mode(0); - let size = size_of::(); - let mut sa = c::SECURITY_ATTRIBUTES { - nLength: size as u32, - lpSecurityDescriptor: ptr::null_mut(), - bInheritHandle: their_handle_inheritable as i32, + if their_handle_inheritable { + object_attributes.Attributes |= c::OBJ_INHERIT; + } + let mut theirs = ptr::null_mut(); + let status = c::NtOpenFile( + &mut theirs, + c::SYNCHRONIZE + | if ours_readable { + c::GENERIC_WRITE | c::FILE_READ_ATTRIBUTES + } else { + c::GENERIC_READ + }, + &object_attributes, + &mut io_status, + 0, + c::FILE_NON_DIRECTORY_FILE | c::FILE_SYNCHRONOUS_IO_NONALERT, + ); + if c::nt_success(status) { + Handle::from_raw_handle(theirs) + } else { + return Err(io::Error::from_raw_os_error(c::RtlNtStatusToDosError(status) as i32)); + } }; - opts.security_attributes(&mut sa); - let theirs = File::open(Path::new(&name), &opts)?; - Ok(Pipes { - ours: AnonPipe { inner: ours }, - theirs: AnonPipe { inner: theirs.into_inner() }, - }) + Ok(Pipes { ours: AnonPipe { inner: ours }, theirs: AnonPipe { inner: theirs } }) } } @@ -191,17 +207,6 @@ pub fn spawn_pipe_relay( Ok(theirs) } -fn random_number() -> usize { - static N: Atomic = AtomicUsize::new(0); - loop { - if N.load(Relaxed) != 0 { - return N.fetch_add(1, Relaxed); - } - - N.store(usize::random(&mut DefaultRandomSource), Relaxed); - } -} - impl AnonPipe { pub fn handle(&self) -> &Handle { &self.inner From e8dfd812dd888cc738035c4d9edd3222138583a7 Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Mon, 16 Jun 2025 04:46:06 +0000 Subject: [PATCH 04/18] linked_list tests: less static mut --- .../src/collections/linked_list/tests.rs | 96 ++++++------------- 1 file changed, 30 insertions(+), 66 deletions(-) diff --git a/library/alloc/src/collections/linked_list/tests.rs b/library/alloc/src/collections/linked_list/tests.rs index 812fe229a0fc5..493352447c43c 100644 --- a/library/alloc/src/collections/linked_list/tests.rs +++ b/library/alloc/src/collections/linked_list/tests.rs @@ -1,6 +1,4 @@ -// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint -#![allow(static_mut_refs)] - +use std::cell::Cell; use std::panic::{AssertUnwindSafe, catch_unwind}; use std::thread; @@ -1027,21 +1025,26 @@ fn extract_if_drop_panic_leak() { assert_eq!(d7.dropped(), 1); } -#[test] -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -fn extract_if_pred_panic_leak() { - static mut DROPS: i32 = 0; +macro_rules! struct_with_counted_drop { + ($struct_name:ident$(($elt_ty:ty))?, $drop_counter:ident $(=> $drop_stmt:expr)?) => { + thread_local! {static $drop_counter: Cell = Cell::new(0);} - #[derive(Debug)] - struct D(u32); + struct $struct_name$(($elt_ty))?; - impl Drop for D { - fn drop(&mut self) { - unsafe { - DROPS += 1; + impl Drop for $struct_name { + fn drop(&mut self) { + $drop_counter.set($drop_counter.get() + 1); + + $($drop_stmt(self))? } } - } + }; +} + +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn extract_if_pred_panic_leak() { + struct_with_counted_drop!(D(u32), DROPS); let mut q = LinkedList::new(); q.push_back(D(3)); @@ -1053,26 +1056,17 @@ fn extract_if_pred_panic_leak() { q.push_front(D(1)); q.push_front(D(0)); - catch_unwind(AssertUnwindSafe(|| { + _ = catch_unwind(AssertUnwindSafe(|| { q.extract_if(|item| if item.0 >= 2 { panic!() } else { true }).for_each(drop) - })) - .ok(); + })); - assert_eq!(unsafe { DROPS }, 2); // 0 and 1 + assert_eq!(DROPS.get(), 2); // 0 and 1 assert_eq!(q.len(), 6); } #[test] fn test_drop() { - static mut DROPS: i32 = 0; - struct Elem; - impl Drop for Elem { - fn drop(&mut self) { - unsafe { - DROPS += 1; - } - } - } + struct_with_counted_drop!(Elem, DROPS); let mut ring = LinkedList::new(); ring.push_back(Elem); @@ -1081,20 +1075,12 @@ fn test_drop() { ring.push_front(Elem); drop(ring); - assert_eq!(unsafe { DROPS }, 4); + assert_eq!(DROPS.get(), 4); } #[test] fn test_drop_with_pop() { - static mut DROPS: i32 = 0; - struct Elem; - impl Drop for Elem { - fn drop(&mut self) { - unsafe { - DROPS += 1; - } - } - } + struct_with_counted_drop!(Elem, DROPS); let mut ring = LinkedList::new(); ring.push_back(Elem); @@ -1104,23 +1090,15 @@ fn test_drop_with_pop() { drop(ring.pop_back()); drop(ring.pop_front()); - assert_eq!(unsafe { DROPS }, 2); + assert_eq!(DROPS.get(), 2); drop(ring); - assert_eq!(unsafe { DROPS }, 4); + assert_eq!(DROPS.get(), 4); } #[test] fn test_drop_clear() { - static mut DROPS: i32 = 0; - struct Elem; - impl Drop for Elem { - fn drop(&mut self) { - unsafe { - DROPS += 1; - } - } - } + struct_with_counted_drop!(Elem, DROPS); let mut ring = LinkedList::new(); ring.push_back(Elem); @@ -1128,30 +1106,16 @@ fn test_drop_clear() { ring.push_back(Elem); ring.push_front(Elem); ring.clear(); - assert_eq!(unsafe { DROPS }, 4); + assert_eq!(DROPS.get(), 4); drop(ring); - assert_eq!(unsafe { DROPS }, 4); + assert_eq!(DROPS.get(), 4); } #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_drop_panic() { - static mut DROPS: i32 = 0; - - struct D(bool); - - impl Drop for D { - fn drop(&mut self) { - unsafe { - DROPS += 1; - } - - if self.0 { - panic!("panic in `drop`"); - } - } - } + struct_with_counted_drop!(D(bool), DROPS => |this: &D| if this.0 { panic!("panic in `drop`"); } ); let mut q = LinkedList::new(); q.push_back(D(false)); @@ -1165,7 +1129,7 @@ fn test_drop_panic() { catch_unwind(move || drop(q)).ok(); - assert_eq!(unsafe { DROPS }, 8); + assert_eq!(DROPS.get(), 8); } #[test] From 23e35c6bd371485eff7c5f58cb59ee28da59ba62 Mon Sep 17 00:00:00 2001 From: Tomoaki Kobayashi Date: Tue, 27 May 2025 02:30:53 +0900 Subject: [PATCH 05/18] Add support for repetition to `proc_macro::quote` --- library/proc_macro/src/lib.rs | 3 +- library/proc_macro/src/quote.rs | 328 +++++++++++++++++- tests/ui/proc-macro/quote/auxiliary/basic.rs | 152 +++++++- .../does-not-have-iter-interpolated-dup.rs | 6 +- ...does-not-have-iter-interpolated-dup.stderr | 11 +- .../quote/does-not-have-iter-interpolated.rs | 6 +- .../does-not-have-iter-interpolated.stderr | 11 +- .../quote/does-not-have-iter-separated.rs | 6 +- .../quote/does-not-have-iter-separated.stderr | 10 +- .../ui/proc-macro/quote/does-not-have-iter.rs | 6 +- .../quote/does-not-have-iter.stderr | 10 +- tests/ui/proc-macro/quote/not-quotable.stderr | 4 +- tests/ui/proc-macro/quote/not-repeatable.rs | 6 +- .../ui/proc-macro/quote/not-repeatable.stderr | 23 +- 14 files changed, 519 insertions(+), 63 deletions(-) diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 32c306be94ecd..b0084edf2f85c 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -45,6 +45,7 @@ mod diagnostic; mod escape; mod to_tokens; +use core::ops::BitOr; use std::ffi::CStr; use std::ops::{Range, RangeBounds}; use std::path::PathBuf; @@ -237,7 +238,7 @@ impl Default for TokenStream { } #[unstable(feature = "proc_macro_quote", issue = "54722")] -pub use quote::{quote, quote_span}; +pub use quote::{HasIterator, RepInterp, ThereIsNoIteratorInRepetition, ext, quote, quote_span}; fn tree_to_bridge_tree( tree: TokenTree, diff --git a/library/proc_macro/src/quote.rs b/library/proc_macro/src/quote.rs index bcb15912bb65e..dbb55cd9fb300 100644 --- a/library/proc_macro/src/quote.rs +++ b/library/proc_macro/src/quote.rs @@ -5,9 +5,183 @@ //! items from `proc_macro`, to build a `proc_macro::TokenStream`. use crate::{ - Delimiter, Group, Ident, Literal, Punct, Spacing, Span, ToTokens, TokenStream, TokenTree, + BitOr, Delimiter, Group, Ident, Literal, Punct, Spacing, Span, ToTokens, TokenStream, TokenTree, }; +#[doc(hidden)] +pub struct HasIterator; // True +#[doc(hidden)] +pub struct ThereIsNoIteratorInRepetition; // False + +impl BitOr for ThereIsNoIteratorInRepetition { + type Output = ThereIsNoIteratorInRepetition; + fn bitor(self, _rhs: ThereIsNoIteratorInRepetition) -> ThereIsNoIteratorInRepetition { + ThereIsNoIteratorInRepetition + } +} + +impl BitOr for HasIterator { + type Output = HasIterator; + fn bitor(self, _rhs: ThereIsNoIteratorInRepetition) -> HasIterator { + HasIterator + } +} + +impl BitOr for ThereIsNoIteratorInRepetition { + type Output = HasIterator; + fn bitor(self, _rhs: HasIterator) -> HasIterator { + HasIterator + } +} + +impl BitOr for HasIterator { + type Output = HasIterator; + fn bitor(self, _rhs: HasIterator) -> HasIterator { + HasIterator + } +} + +/// Extension traits used by the implementation of `quote!`. These are defined +/// in separate traits, rather than as a single trait due to ambiguity issues. +/// +/// These traits expose a `quote_into_iter` method which should allow calling +/// whichever impl happens to be applicable. Calling that method repeatedly on +/// the returned value should be idempotent. +#[doc(hidden)] +pub mod ext { + use core::slice; + use std::collections::btree_set::{self, BTreeSet}; + + use super::{ + HasIterator as HasIter, RepInterp, ThereIsNoIteratorInRepetition as DoesNotHaveIter, + }; + use crate::ToTokens; + + /// Extension trait providing the `quote_into_iter` method on iterators. + #[doc(hidden)] + pub trait RepIteratorExt: Iterator + Sized { + fn quote_into_iter(self) -> (Self, HasIter) { + (self, HasIter) + } + } + + impl RepIteratorExt for T {} + + /// Extension trait providing the `quote_into_iter` method for + /// non-iterable types. These types interpolate the same value in each + /// iteration of the repetition. + #[doc(hidden)] + pub trait RepToTokensExt { + /// Pretend to be an iterator for the purposes of `quote_into_iter`. + /// This allows repeated calls to `quote_into_iter` to continue + /// correctly returning DoesNotHaveIter. + fn next(&self) -> Option<&Self> { + Some(self) + } + + fn quote_into_iter(&self) -> (&Self, DoesNotHaveIter) { + (self, DoesNotHaveIter) + } + } + + impl RepToTokensExt for T {} + + /// Extension trait providing the `quote_into_iter` method for types that + /// can be referenced as an iterator. + #[doc(hidden)] + pub trait RepAsIteratorExt<'q> { + type Iter: Iterator; + + fn quote_into_iter(&'q self) -> (Self::Iter, HasIter); + } + + impl<'q, T: RepAsIteratorExt<'q> + ?Sized> RepAsIteratorExt<'q> for &T { + type Iter = T::Iter; + + fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { + ::quote_into_iter(*self) + } + } + + impl<'q, T: RepAsIteratorExt<'q> + ?Sized> RepAsIteratorExt<'q> for &mut T { + type Iter = T::Iter; + + fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { + ::quote_into_iter(*self) + } + } + + impl<'q, T: 'q> RepAsIteratorExt<'q> for [T] { + type Iter = slice::Iter<'q, T>; + + fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { + (self.iter(), HasIter) + } + } + + impl<'q, T: 'q, const N: usize> RepAsIteratorExt<'q> for [T; N] { + type Iter = slice::Iter<'q, T>; + + fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { + (self.iter(), HasIter) + } + } + + impl<'q, T: 'q> RepAsIteratorExt<'q> for Vec { + type Iter = slice::Iter<'q, T>; + + fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { + (self.iter(), HasIter) + } + } + + impl<'q, T: 'q> RepAsIteratorExt<'q> for BTreeSet { + type Iter = btree_set::Iter<'q, T>; + + fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { + (self.iter(), HasIter) + } + } + + impl<'q, T: RepAsIteratorExt<'q>> RepAsIteratorExt<'q> for RepInterp { + type Iter = T::Iter; + + fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { + self.0.quote_into_iter() + } + } +} + +// Helper type used within interpolations to allow for repeated binding names. +// Implements the relevant traits, and exports a dummy `next()` method. +#[derive(Copy, Clone)] +#[doc(hidden)] +pub struct RepInterp(pub T); + +impl RepInterp { + // This method is intended to look like `Iterator::next`, and is called when + // a name is bound multiple times, as the previous binding will shadow the + // original `Iterator` object. This allows us to avoid advancing the + // iterator multiple times per iteration. + pub fn next(self) -> Option { + Some(self.0) + } +} + +impl Iterator for RepInterp { + type Item = T::Item; + + fn next(&mut self) -> Option { + self.0.next() + } +} + +impl ToTokens for RepInterp { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.0.to_tokens(tokens); + } +} + macro_rules! minimal_quote_tt { (($($t:tt)*)) => { Group::new(Delimiter::Parenthesis, minimal_quote!($($t)*)) }; ([$($t:tt)*]) => { Group::new(Delimiter::Bracket, minimal_quote!($($t)*)) }; @@ -20,7 +194,13 @@ macro_rules! minimal_quote_tt { (>) => { Punct::new('>', Spacing::Alone) }; (&) => { Punct::new('&', Spacing::Alone) }; (=) => { Punct::new('=', Spacing::Alone) }; + (#) => { Punct::new('#', Spacing::Alone) }; + (|) => { Punct::new('|', Spacing::Alone) }; + (:) => { Punct::new(':', Spacing::Alone) }; + (*) => { Punct::new('*', Spacing::Alone) }; + (_) => { Ident::new("_", Span::def_site()) }; ($i:ident) => { Ident::new(stringify!($i), Span::def_site()) }; + ($lit:literal) => { stringify!($lit).parse::().unwrap() }; } macro_rules! minimal_quote_ts { @@ -36,6 +216,39 @@ macro_rules! minimal_quote_ts { [c.0, c.1].into_iter().collect::() } }; + (=>) => { + { + let mut c = ( + TokenTree::from(Punct::new('=', Spacing::Joint)), + TokenTree::from(Punct::new('>', Spacing::Alone)) + ); + c.0.set_span(Span::def_site()); + c.1.set_span(Span::def_site()); + [c.0, c.1].into_iter().collect::() + } + }; + (+=) => { + { + let mut c = ( + TokenTree::from(Punct::new('+', Spacing::Joint)), + TokenTree::from(Punct::new('=', Spacing::Alone)) + ); + c.0.set_span(Span::def_site()); + c.1.set_span(Span::def_site()); + [c.0, c.1].into_iter().collect::() + } + }; + (!=) => { + { + let mut c = ( + TokenTree::from(Punct::new('!', Spacing::Joint)), + TokenTree::from(Punct::new('=', Spacing::Alone)) + ); + c.0.set_span(Span::def_site()); + c.1.set_span(Span::def_site()); + [c.0, c.1].into_iter().collect::() + } + }; ($t:tt) => { TokenTree::from(minimal_quote_tt!($t)) }; } @@ -71,17 +284,99 @@ pub fn quote(stream: TokenStream) -> TokenStream { let mut after_dollar = false; let mut tokens = crate::TokenStream::new(); - for tree in stream { + let mut iter = stream.into_iter().peekable(); + while let Some(tree) = iter.next() { if after_dollar { after_dollar = false; match tree { + TokenTree::Group(tt) => { + // Handles repetition by expanding `$( CONTENTS ) SEP_OPT *` to `{ REP_EXPANDED }`. + let contents = tt.stream(); + + // The `*` token is also consumed here. + let sep_opt: Option = match (iter.next(), iter.peek()) { + (Some(TokenTree::Punct(sep)), Some(TokenTree::Punct(star))) + if sep.spacing() == Spacing::Joint && star.as_char() == '*' => + { + iter.next(); + Some(sep) + } + (Some(TokenTree::Punct(star)), _) if star.as_char() == '*' => None, + _ => panic!("`$(...)` must be followed by `*` in `quote!`"), + }; + + let mut rep_expanded = TokenStream::new(); + + // Append setup code for a `while`, where recursively quoted `CONTENTS` + // and `SEP_OPT` are repeatedly processed, to `REP_EXPANDED`. + let meta_vars = collect_meta_vars(contents.clone()); + minimal_quote!( + use crate::ext::*; + (@ if sep_opt.is_some() { + minimal_quote!(let mut _i = 0usize;) + } else { + minimal_quote!(();) + }) + let has_iter = crate::ThereIsNoIteratorInRepetition; + ) + .to_tokens(&mut rep_expanded); + for meta_var in &meta_vars { + minimal_quote!( + #[allow(unused_mut)] + let (mut (@ meta_var), i) = (@ meta_var).quote_into_iter(); + let has_iter = has_iter | i; + ) + .to_tokens(&mut rep_expanded); + } + minimal_quote!(let _: crate::HasIterator = has_iter;) + .to_tokens(&mut rep_expanded); + + // Append the `while` to `REP_EXPANDED`. + let mut while_body = TokenStream::new(); + for meta_var in &meta_vars { + minimal_quote!( + let (@ meta_var) = match (@ meta_var).next() { + Some(_x) => crate::RepInterp(_x), + None => break, + }; + ) + .to_tokens(&mut while_body); + } + minimal_quote!( + (@ if let Some(sep) = sep_opt { + minimal_quote!( + if _i > 0 { + (@ minimal_quote!(crate::ToTokens::to_tokens(&crate::TokenTree::Punct(crate::Punct::new( + (@ TokenTree::from(Literal::character(sep.as_char()))), + (@ minimal_quote!(crate::Spacing::Alone)), + )), &mut ts);)) + } + _i += 1; + ) + } else { + minimal_quote!(();) + }) + (@ quote(contents.clone())).to_tokens(&mut ts); + ) + .to_tokens(&mut while_body); + rep_expanded.extend(vec![ + TokenTree::Ident(Ident::new("while", Span::call_site())), + TokenTree::Ident(Ident::new("true", Span::call_site())), + TokenTree::Group(Group::new(Delimiter::Brace, while_body)), + ]); + + minimal_quote!((@ TokenTree::Group(Group::new(Delimiter::Brace, rep_expanded)))).to_tokens(&mut tokens); + continue; + } TokenTree::Ident(_) => { minimal_quote!(crate::ToTokens::to_tokens(&(@ tree), &mut ts);) .to_tokens(&mut tokens); continue; } TokenTree::Punct(ref tt) if tt.as_char() == '$' => {} - _ => panic!("`$` must be followed by an ident or `$` in `quote!`"), + _ => panic!( + "`$` must be followed by an ident or `$` or a repetition group in `quote!`" + ), } } else if let TokenTree::Punct(ref tt) = tree { if tt.as_char() == '$' { @@ -155,6 +450,33 @@ pub fn quote(stream: TokenStream) -> TokenStream { } } +/// Helper function to support macro repetitions like `$( CONTENTS ) SEP_OPT *` in `quote!`. +/// Recursively collects all `Ident`s (meta-variables) that follow a `$` +/// from the given `CONTENTS` stream, preserving their order of appearance. +fn collect_meta_vars(content_stream: TokenStream) -> Vec { + fn helper(stream: TokenStream, out: &mut Vec) { + let mut iter = stream.into_iter().peekable(); + while let Some(tree) = iter.next() { + match &tree { + TokenTree::Punct(tt) if tt.as_char() == '$' => { + if let Some(TokenTree::Ident(id)) = iter.peek() { + out.push(id.clone()); + iter.next(); + } + } + TokenTree::Group(tt) => { + helper(tt.stream(), out); + } + _ => {} + } + } + } + + let mut vars = Vec::new(); + helper(content_stream, &mut vars); + vars +} + /// Quote a `Span` into a `TokenStream`. /// This is needed to implement a custom quoter. #[unstable(feature = "proc_macro_quote", issue = "54722")] diff --git a/tests/ui/proc-macro/quote/auxiliary/basic.rs b/tests/ui/proc-macro/quote/auxiliary/basic.rs index ef726bbfbe3a0..c50bb964eab06 100644 --- a/tests/ui/proc-macro/quote/auxiliary/basic.rs +++ b/tests/ui/proc-macro/quote/auxiliary/basic.rs @@ -4,6 +4,7 @@ extern crate proc_macro; use std::borrow::Cow; +use std::collections::BTreeSet; use std::ffi::{CStr, CString}; use proc_macro::*; @@ -12,6 +13,8 @@ use proc_macro::*; pub fn run_tests(_: TokenStream) -> TokenStream { test_quote_impl(); test_substitution(); + test_iter(); + test_array(); test_advanced(); test_integer(); test_floating(); @@ -24,6 +27,13 @@ pub fn run_tests(_: TokenStream) -> TokenStream { test_ident(); test_underscore(); test_duplicate(); + test_fancy_repetition(); + test_nested_fancy_repetition(); + test_duplicate_name_repetition(); + test_duplicate_name_repetition_no_copy(); + test_btreeset_repetition(); + test_variable_name_conflict(); + test_nonrep_in_repetition(); test_empty_quote(); test_box_str(); test_cow(); @@ -34,6 +44,7 @@ pub fn run_tests(_: TokenStream) -> TokenStream { test_inner_block_comment(); test_outer_attr(); test_inner_attr(); + test_star_after_repetition(); test_quote_raw_id(); TokenStream::new() @@ -49,20 +60,9 @@ pub fn run_tests(_: TokenStream) -> TokenStream { // - fn test_type_inference_for_span // - wrong-type-span.rs // - format_ident: +// - fn test_closure // - fn test_format_ident // - fn test_format_ident_strip_raw -// - repetition: -// - fn test_iter -// - fn test_array -// - fn test_fancy_repetition -// - fn test_nested_fancy_repetition -// - fn test_duplicate_name_repetition -// - fn test_duplicate_name_repetition_no_copy -// - fn test_btreeset_repetition -// - fn test_variable_name_conflict -// - fn test_nonrep_in_repetition -// - fn test_closure -// - fn test_star_after_repetition struct X; @@ -99,6 +99,39 @@ fn test_substitution() { assert_eq!(expected, tokens.to_string()); } +fn test_iter() { + let primes = &[X, X, X, X]; + + assert_eq!("X X X X", quote!($($primes)*).to_string()); + + assert_eq!("X, X, X, X,", quote!($($primes,)*).to_string()); + + assert_eq!("X, X, X, X", quote!($($primes),*).to_string()); +} + +fn test_array() { + let array: [u8; 40] = [0; 40]; + let _ = quote!($($array $array)*); + + let ref_array: &[u8; 40] = &[0; 40]; + let _ = quote!($($ref_array $ref_array)*); + + let ref_slice: &[u8] = &[0; 40]; + let _ = quote!($($ref_slice $ref_slice)*); + + let array: [X; 2] = [X, X]; // !Copy + let _ = quote!($($array $array)*); + + let ref_array: &[X; 2] = &[X, X]; + let _ = quote!($($ref_array $ref_array)*); + + let ref_slice: &[X] = &[X, X]; + let _ = quote!($($ref_slice $ref_slice)*); + + let array_of_array: [[u8; 2]; 2] = [[0; 2]; 2]; + let _ = quote!($($($array_of_array)*)*); +} + fn test_advanced() { let generics = quote!( <'a, T> ); @@ -279,6 +312,88 @@ fn test_duplicate() { assert_eq!(expected, tokens.to_string()); } +fn test_fancy_repetition() { + let foo = vec!["a", "b"]; + let bar = vec![true, false]; + + let tokens = quote! { + $($foo: $bar),* + }; + + let expected = r#""a" : true, "b" : false"#; + assert_eq!(expected, tokens.to_string()); +} + +fn test_nested_fancy_repetition() { + let nested = vec![vec!['a', 'b', 'c'], vec!['x', 'y', 'z']]; + + let tokens = quote! { + $( + $($nested)* + ),* + }; + + let expected = "'a' 'b' 'c', 'x' 'y' 'z'"; + assert_eq!(expected, tokens.to_string()); +} + +fn test_duplicate_name_repetition() { + let foo = &["a", "b"]; + + let tokens = quote! { + $($foo: $foo),* + $($foo: $foo),* + }; + + let expected = r#""a" : "a", "b" : "b" "a" : "a", "b" : "b""#; + assert_eq!(expected, tokens.to_string()); +} + +fn test_duplicate_name_repetition_no_copy() { + let foo = vec!["a".to_owned(), "b".to_owned()]; + + let tokens = quote! { + $($foo: $foo),* + }; + + let expected = r#""a" : "a", "b" : "b""#; + assert_eq!(expected, tokens.to_string()); +} + +fn test_btreeset_repetition() { + let mut set = BTreeSet::new(); + set.insert("a".to_owned()); + set.insert("b".to_owned()); + + let tokens = quote! { + $($set: $set),* + }; + + let expected = r#""a" : "a", "b" : "b""#; + assert_eq!(expected, tokens.to_string()); +} + +fn test_variable_name_conflict() { + // The implementation of `#(...),*` uses the variable `_i` but it should be + // fine, if a little confusing when debugging. + let _i = vec!['a', 'b']; + let tokens = quote! { $($_i),* }; + let expected = "'a', 'b'"; + assert_eq!(expected, tokens.to_string()); +} + +fn test_nonrep_in_repetition() { + let rep = vec!["a", "b"]; + let nonrep = "c"; + + let tokens = quote! { + $($rep $rep : $nonrep $nonrep),* + }; + + let expected = r#""a" "a" : "c" "c", "b" "b" : "c" "c""#; + assert_eq!(expected, tokens.to_string()); +} + fn test_empty_quote() { let tokens = quote!(); assert_eq!("", tokens.to_string()); @@ -355,6 +470,19 @@ fn test_inner_attr() { assert_eq!(expected, tokens.to_string()); } +// https://github.com/dtolnay/quote/issues/130 +fn test_star_after_repetition() { + let c = vec!['0', '1']; + let tokens = quote! { + $( + f($c); + )* + *out = None; + }; + let expected = "f('0'); f('1'); * out = None;"; + assert_eq!(expected, tokens.to_string()); +} + fn test_quote_raw_id() { let id = quote!(r#raw_id); assert_eq!(id.to_string(), "r#raw_id"); diff --git a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.rs b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.rs index 2f67ae1bc6edc..418e3dd444dda 100644 --- a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.rs +++ b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.rs @@ -1,7 +1,3 @@ -// FIXME(quote): `proc_macro::quote!` doesn't support repetition at the moment, so the stderr is -// expected to be incorrect. -//@ known-bug: #54722 - #![feature(proc_macro_quote)] extern crate proc_macro; @@ -13,5 +9,5 @@ fn main() { // Without some protection against repetitions with no iterator somewhere // inside, this would loop infinitely. - quote!($($nonrep $nonrep)*); + quote!($($nonrep $nonrep)*); //~ ERROR mismatched types } diff --git a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.stderr b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.stderr index 5f28a46f3181d..ecb12c1df3b6a 100644 --- a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.stderr +++ b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.stderr @@ -1,10 +1,13 @@ -error: proc macro panicked - --> $DIR/does-not-have-iter-interpolated-dup.rs:16:5 +error[E0308]: mismatched types + --> $DIR/does-not-have-iter-interpolated-dup.rs:12:5 | LL | quote!($($nonrep $nonrep)*); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: message: `$` must be followed by an ident or `$` in `quote!` + | | + | expected `HasIterator`, found `ThereIsNoIteratorInRepetition` + | expected due to this + | here the type of `has_iter` is inferred to be `ThereIsNoIteratorInRepetition` error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.rs b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.rs index 1efb3eac64247..507936770aa4f 100644 --- a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.rs +++ b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.rs @@ -1,7 +1,3 @@ -// FIXME(quote): `proc_macro::quote!` doesn't support repetition at the moment, so the stderr is -// expected to be incorrect. -//@ known-bug: #54722 - #![feature(proc_macro_quote)] extern crate proc_macro; @@ -13,5 +9,5 @@ fn main() { // Without some protection against repetitions with no iterator somewhere // inside, this would loop infinitely. - quote!($($nonrep)*); + quote!($($nonrep)*); //~ ERROR mismatched types } diff --git a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.stderr b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.stderr index 595aa8587634a..093e2ebc09858 100644 --- a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.stderr +++ b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.stderr @@ -1,10 +1,13 @@ -error: proc macro panicked - --> $DIR/does-not-have-iter-interpolated.rs:16:5 +error[E0308]: mismatched types + --> $DIR/does-not-have-iter-interpolated.rs:12:5 | LL | quote!($($nonrep)*); | ^^^^^^^^^^^^^^^^^^^ - | - = help: message: `$` must be followed by an ident or `$` in `quote!` + | | + | expected `HasIterator`, found `ThereIsNoIteratorInRepetition` + | expected due to this + | here the type of `has_iter` is inferred to be `ThereIsNoIteratorInRepetition` error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/proc-macro/quote/does-not-have-iter-separated.rs b/tests/ui/proc-macro/quote/does-not-have-iter-separated.rs index 5f2ddabc390da..7e41b08f2636d 100644 --- a/tests/ui/proc-macro/quote/does-not-have-iter-separated.rs +++ b/tests/ui/proc-macro/quote/does-not-have-iter-separated.rs @@ -1,7 +1,3 @@ -// FIXME(quote): `proc_macro::quote!` doesn't support repetition at the moment, so the stderr is -// expected to be incorrect. -//@ known-bug: #54722 - #![feature(proc_macro_quote)] extern crate proc_macro; @@ -9,5 +5,5 @@ extern crate proc_macro; use proc_macro::quote; fn main() { - quote!($(a b),*); + quote!($(a b),*); //~ ERROR mismatched types } diff --git a/tests/ui/proc-macro/quote/does-not-have-iter-separated.stderr b/tests/ui/proc-macro/quote/does-not-have-iter-separated.stderr index f6f5d7e007d0f..937209e675ec8 100644 --- a/tests/ui/proc-macro/quote/does-not-have-iter-separated.stderr +++ b/tests/ui/proc-macro/quote/does-not-have-iter-separated.stderr @@ -1,10 +1,12 @@ -error: proc macro panicked - --> $DIR/does-not-have-iter-separated.rs:12:5 +error[E0308]: mismatched types + --> $DIR/does-not-have-iter-separated.rs:8:5 | LL | quote!($(a b),*); | ^^^^^^^^^^^^^^^^ - | - = help: message: `$` must be followed by an ident or `$` in `quote!` + | | + | expected `HasIterator`, found `ThereIsNoIteratorInRepetition` + | expected due to this error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/proc-macro/quote/does-not-have-iter.rs b/tests/ui/proc-macro/quote/does-not-have-iter.rs index 25ffd786cc614..038851ff76ed6 100644 --- a/tests/ui/proc-macro/quote/does-not-have-iter.rs +++ b/tests/ui/proc-macro/quote/does-not-have-iter.rs @@ -1,7 +1,3 @@ -// FIXME(quote): `proc_macro::quote!` doesn't support repetition at the moment, so the stderr is -// expected to be incorrect. -//@ known-bug: #54722 - #![feature(proc_macro_quote)] extern crate proc_macro; @@ -9,5 +5,5 @@ extern crate proc_macro; use proc_macro::quote; fn main() { - quote!($(a b)*); + quote!($(a b)*); //~ ERROR mismatched types } diff --git a/tests/ui/proc-macro/quote/does-not-have-iter.stderr b/tests/ui/proc-macro/quote/does-not-have-iter.stderr index 0ed1daffc8cdf..e74ea3348992f 100644 --- a/tests/ui/proc-macro/quote/does-not-have-iter.stderr +++ b/tests/ui/proc-macro/quote/does-not-have-iter.stderr @@ -1,10 +1,12 @@ -error: proc macro panicked - --> $DIR/does-not-have-iter.rs:12:5 +error[E0308]: mismatched types + --> $DIR/does-not-have-iter.rs:8:5 | LL | quote!($(a b)*); | ^^^^^^^^^^^^^^^ - | - = help: message: `$` must be followed by an ident or `$` in `quote!` + | | + | expected `HasIterator`, found `ThereIsNoIteratorInRepetition` + | expected due to this error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/proc-macro/quote/not-quotable.stderr b/tests/ui/proc-macro/quote/not-quotable.stderr index 62a02638e548b..d1c3d06f2b661 100644 --- a/tests/ui/proc-macro/quote/not-quotable.stderr +++ b/tests/ui/proc-macro/quote/not-quotable.stderr @@ -15,8 +15,8 @@ LL | let _ = quote! { $ip }; Cow<'_, T> Option Rc - bool - and 24 others + RepInterp + and 25 others error: aborting due to 1 previous error diff --git a/tests/ui/proc-macro/quote/not-repeatable.rs b/tests/ui/proc-macro/quote/not-repeatable.rs index d115da7318156..0291e4ddf88d6 100644 --- a/tests/ui/proc-macro/quote/not-repeatable.rs +++ b/tests/ui/proc-macro/quote/not-repeatable.rs @@ -1,7 +1,3 @@ -// FIXME(quote): `proc_macro::quote!` doesn't support repetition at the moment, so the stderr is -// expected to be incorrect. -//@ known-bug: #54722 - #![feature(proc_macro_quote)] extern crate proc_macro; @@ -12,5 +8,5 @@ struct Ipv4Addr; fn main() { let ip = Ipv4Addr; - let _ = quote! { $($ip)* }; + let _ = quote! { $($ip)* }; //~ ERROR the method `quote_into_iter` exists for struct `Ipv4Addr`, but its trait bounds were not satisfied } diff --git a/tests/ui/proc-macro/quote/not-repeatable.stderr b/tests/ui/proc-macro/quote/not-repeatable.stderr index 18fbcd7379858..aeda08d7de68b 100644 --- a/tests/ui/proc-macro/quote/not-repeatable.stderr +++ b/tests/ui/proc-macro/quote/not-repeatable.stderr @@ -1,10 +1,25 @@ -error: proc macro panicked - --> $DIR/not-repeatable.rs:15:13 +error[E0599]: the method `quote_into_iter` exists for struct `Ipv4Addr`, but its trait bounds were not satisfied + --> $DIR/not-repeatable.rs:11:13 | +LL | struct Ipv4Addr; + | --------------- method `quote_into_iter` not found for this struct because it doesn't satisfy `Ipv4Addr: Iterator`, `Ipv4Addr: ToTokens`, `Ipv4Addr: proc_macro::ext::RepIteratorExt` or `Ipv4Addr: proc_macro::ext::RepToTokensExt` +... LL | let _ = quote! { $($ip)* }; - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ method cannot be called on `Ipv4Addr` due to unsatisfied trait bounds | - = help: message: `$` must be followed by an ident or `$` in `quote!` + = note: the following trait bounds were not satisfied: + `Ipv4Addr: Iterator` + which is required by `Ipv4Addr: proc_macro::ext::RepIteratorExt` + `&Ipv4Addr: Iterator` + which is required by `&Ipv4Addr: proc_macro::ext::RepIteratorExt` + `Ipv4Addr: ToTokens` + which is required by `Ipv4Addr: proc_macro::ext::RepToTokensExt` + `&mut Ipv4Addr: Iterator` + which is required by `&mut Ipv4Addr: proc_macro::ext::RepIteratorExt` +note: the traits `Iterator` and `ToTokens` must be implemented + --> $SRC_DIR/proc_macro/src/to_tokens.rs:LL:COL + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0599`. From 7d3a1d49ab6bba49eae6536c77fc52152c9e3768 Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Mon, 16 Jun 2025 10:36:15 -0700 Subject: [PATCH 06/18] make more CodegenCx function generic --- compiler/rustc_codegen_llvm/src/common.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 3cfa96393e920..ae5add59322fe 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -99,14 +99,14 @@ impl<'ll, CX: Borrow>> BackendTypes for GenericCx<'ll, CX> { type DIVariable = &'ll llvm::debuginfo::DIVariable; } -impl<'ll> CodegenCx<'ll, '_> { +impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { pub(crate) fn const_array(&self, ty: &'ll Type, elts: &[&'ll Value]) -> &'ll Value { let len = u64::try_from(elts.len()).expect("LLVMConstArray2 elements len overflow"); unsafe { llvm::LLVMConstArray2(ty, elts.as_ptr(), len) } } pub(crate) fn const_bytes(&self, bytes: &[u8]) -> &'ll Value { - bytes_in_context(self.llcx, bytes) + bytes_in_context(self.llcx(), bytes) } pub(crate) fn const_get_elt(&self, v: &'ll Value, idx: u64) -> &'ll Value { From 6359123d25e8ff38c6fbf280559d5f829bec691f Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Mon, 16 Jun 2025 14:23:06 -0700 Subject: [PATCH 07/18] add and use generic get_const_int function --- compiler/rustc_codegen_llvm/src/builder/autodiff.rs | 4 ++-- compiler/rustc_codegen_llvm/src/context.rs | 7 ++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index c5c13ac097a27..b07d9a5cfca8c 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -114,7 +114,7 @@ fn match_args_from_caller_to_enzyme<'ll>( let mul = unsafe { llvm::LLVMBuildMul( builder.llbuilder, - cx.get_const_i64(elem_bytes_size), + cx.get_const_int(cx.type_i64(), elem_bytes_size), next_outer_arg, UNNAMED, ) @@ -385,7 +385,7 @@ fn generate_enzyme_call<'ll>( if attrs.width > 1 { let enzyme_width = cx.create_metadata("enzyme_width".to_string()).unwrap(); args.push(cx.get_metadata_value(enzyme_width)); - args.push(cx.get_const_i64(attrs.width as u64)); + args.push(cx.get_const_int(cx.type_i64(), attrs.width as u64)); } let has_sret = has_sret(outer_fn); diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 8d6e1d8941b72..0684c69ec00a1 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -678,11 +678,8 @@ impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { llvm::LLVMMetadataAsValue(self.llcx(), metadata) } - // FIXME(autodiff): We should split `ConstCodegenMethods` to pull the reusable parts - // onto a trait that is also implemented for GenericCx. - pub(crate) fn get_const_i64(&self, n: u64) -> &'ll Value { - let ty = unsafe { llvm::LLVMInt64TypeInContext(self.llcx()) }; - unsafe { llvm::LLVMConstInt(ty, n, llvm::False) } + pub(crate) fn get_const_int(&self, ty: &'ll Type, val: u64) -> &'ll Value { + unsafe { llvm::LLVMConstInt(ty, val, llvm::False) } } pub(crate) fn get_function(&self, name: &str) -> Option<&'ll Value> { From 00c104225fad5d3eb27dc37719d9b6c26844c389 Mon Sep 17 00:00:00 2001 From: binarycat Date: Thu, 5 Jun 2025 18:42:54 -0500 Subject: [PATCH 08/18] rustdoc: make srcIndex no longer a global variable this is one-time initialization data, it can just be a function parameter. we also move the json parsing into createSrcSidebar to save a few bytes. --- src/librustdoc/html/render/write_shared.rs | 6 +----- .../html/render/write_shared/tests.rs | 18 +++--------------- src/librustdoc/html/static/js/rustdoc.d.ts | 6 ++---- src/librustdoc/html/static/js/src-script.js | 9 +++++---- tests/rustdoc-gui/globals.goml | 3 --- 5 files changed, 11 insertions(+), 31 deletions(-) diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index 33738f7a242f6..fb2b45802a671 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -477,11 +477,7 @@ impl SourcesPart { // This needs to be `var`, not `const`. // This variable needs declared in the current global scope so that if // src-script.js loads first, it can pick it up. - SortedTemplate::from_before_after( - r"var srcIndex = new Map(JSON.parse('[", - r"]')); -createSrcSidebar();", - ) + SortedTemplate::from_before_after(r"createSrcSidebar('[", r"]');") } fn get(cx: &Context<'_>, crate_name: &OrderedJson) -> Result, Error> { diff --git a/src/librustdoc/html/render/write_shared/tests.rs b/src/librustdoc/html/render/write_shared/tests.rs index a235f1d37243a..6f185e85345bc 100644 --- a/src/librustdoc/html/render/write_shared/tests.rs +++ b/src/librustdoc/html/render/write_shared/tests.rs @@ -22,23 +22,11 @@ fn but_last_line(s: &str) -> &str { #[test] fn sources_template() { let mut template = SourcesPart::blank(); - assert_eq!( - but_last_line(&template.to_string()), - r"var srcIndex = new Map(JSON.parse('[]')); -createSrcSidebar();" - ); + assert_eq!(but_last_line(&template.to_string()), r"createSrcSidebar('[]');"); template.append(EscapedJson::from(OrderedJson::serialize("u").unwrap()).to_string()); - assert_eq!( - but_last_line(&template.to_string()), - r#"var srcIndex = new Map(JSON.parse('["u"]')); -createSrcSidebar();"# - ); + assert_eq!(but_last_line(&template.to_string()), r#"createSrcSidebar('["u"]');"#); template.append(EscapedJson::from(OrderedJson::serialize("v").unwrap()).to_string()); - assert_eq!( - but_last_line(&template.to_string()), - r#"var srcIndex = new Map(JSON.parse('["u","v"]')); -createSrcSidebar();"# - ); + assert_eq!(but_last_line(&template.to_string()), r#"createSrcSidebar('["u","v"]');"#); } #[test] diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts index 0d2e19e019f34..6af16441de88b 100644 --- a/src/librustdoc/html/static/js/rustdoc.d.ts +++ b/src/librustdoc/html/static/js/rustdoc.d.ts @@ -4,8 +4,6 @@ /* eslint-disable */ declare global { - /** Map from crate name to directory structure, for source view */ - declare var srcIndex: Map; /** Defined and documented in `storage.js` */ declare function nonnull(x: T|null, msg: string|undefined); /** Defined and documented in `storage.js` */ @@ -64,7 +62,7 @@ declare global { * create's the sidebar in source code view. * called in generated `src-files.js`. */ - createSrcSidebar?: function(), + createSrcSidebar?: function(string), /** * Set up event listeners for a scraped source example. */ @@ -129,7 +127,7 @@ declare namespace rustdoc { /** * A single parsed "atom" in a search query. For example, - * + * * std::fmt::Formatter, Write -> Result<()> * ┏━━━━━━━━━━━━━━━━━━ ┌──── ┏━━━━━┅┅┅┅┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐ * ┃ │ ┗ QueryElement { ┊ diff --git a/src/librustdoc/html/static/js/src-script.js b/src/librustdoc/html/static/js/src-script.js index b9ab6e85603bc..0c6afbeed22e2 100644 --- a/src/librustdoc/html/static/js/src-script.js +++ b/src/librustdoc/html/static/js/src-script.js @@ -1,6 +1,3 @@ -// From rust: -/* global srcIndex */ - // Local js definitions: /* global addClass, onEachLazy, removeClass, browserSupportsHistoryApi */ /* global updateLocalStorage, getVar, nonnull */ @@ -100,11 +97,15 @@ window.rustdocToggleSrcSidebar = () => { // This function is called from "src-files.js", generated in `html/render/write_shared.rs`. // eslint-disable-next-line no-unused-vars -function createSrcSidebar() { +/** + * @param {string} srcIndexStr - strinified json map from crate name to dir structure + */ +function createSrcSidebar(srcIndexStr) { const container = nonnull(document.querySelector("nav.sidebar")); const sidebar = document.createElement("div"); sidebar.id = "src-sidebar"; + const srcIndex = new Map(JSON.parse(srcIndexStr)); let hasFoundFile = false; diff --git a/tests/rustdoc-gui/globals.goml b/tests/rustdoc-gui/globals.goml index f8c495ec18a69..7a0e2b9eb7462 100644 --- a/tests/rustdoc-gui/globals.goml +++ b/tests/rustdoc-gui/globals.goml @@ -6,7 +6,6 @@ go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=sa'%3Bda'%3Bds" wait-for: "#search-tabs" assert-window-property-false: {"searchIndex": null} -assert-window-property: {"srcIndex": null} // Form input go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" @@ -14,11 +13,9 @@ write-into: (".search-input", "Foo") press-key: 'Enter' wait-for: "#search-tabs" assert-window-property-false: {"searchIndex": null} -assert-window-property: {"srcIndex": null} // source sidebar go-to: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html" click: "#sidebar-button" wait-for: "#src-sidebar details" -assert-window-property-false: {"srcIndex": null} assert-window-property: {"searchIndex": null} From f1fea6c950cc2680e06221096f361bb9092a815c Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Mon, 16 Jun 2025 20:04:57 -0700 Subject: [PATCH 09/18] don't unwrap in enzyme builds in case of missing llvm-config --- src/bootstrap/src/core/build_steps/compile.rs | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 560925abba6a9..52f421b478235 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -2105,19 +2105,20 @@ impl Step for Assemble { if builder.config.llvm_enzyme && !builder.config.dry_run() { debug!("`llvm_enzyme` requested"); let enzyme_install = builder.ensure(llvm::Enzyme { target: build_compiler.host }); - let llvm_config = builder.llvm_config(builder.config.host_target).unwrap(); - let llvm_version_major = llvm::get_llvm_version_major(builder, &llvm_config); - let lib_ext = std::env::consts::DLL_EXTENSION; - let libenzyme = format!("libEnzyme-{llvm_version_major}"); - let src_lib = - enzyme_install.join("build/Enzyme").join(&libenzyme).with_extension(lib_ext); - let libdir = builder.sysroot_target_libdir(build_compiler, build_compiler.host); - let target_libdir = - builder.sysroot_target_libdir(target_compiler, target_compiler.host); - let dst_lib = libdir.join(&libenzyme).with_extension(lib_ext); - let target_dst_lib = target_libdir.join(&libenzyme).with_extension(lib_ext); - builder.copy_link(&src_lib, &dst_lib, FileType::NativeLibrary); - builder.copy_link(&src_lib, &target_dst_lib, FileType::NativeLibrary); + if let Some(llvm_config) = builder.llvm_config(builder.config.host_target) { + let llvm_version_major = llvm::get_llvm_version_major(builder, &llvm_config); + let lib_ext = std::env::consts::DLL_EXTENSION; + let libenzyme = format!("libEnzyme-{llvm_version_major}"); + let src_lib = + enzyme_install.join("build/Enzyme").join(&libenzyme).with_extension(lib_ext); + let libdir = builder.sysroot_target_libdir(build_compiler, build_compiler.host); + let target_libdir = + builder.sysroot_target_libdir(target_compiler, target_compiler.host); + let dst_lib = libdir.join(&libenzyme).with_extension(lib_ext); + let target_dst_lib = target_libdir.join(&libenzyme).with_extension(lib_ext); + builder.copy_link(&src_lib, &dst_lib, FileType::NativeLibrary); + builder.copy_link(&src_lib, &target_dst_lib, FileType::NativeLibrary); + } } // Build the libraries for this compiler to link to (i.e., the libraries From de792eb0303de6ed2fbe84926b2b0772765b825c Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Mon, 16 Jun 2025 21:00:21 -0700 Subject: [PATCH 10/18] compiler: Redescribe rustc_target::spec more accurately --- compiler/rustc_target/src/spec/mod.rs | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 010355abd7817..8166c9ef5d689 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -25,14 +25,24 @@ //! //! # Defining a new target //! -//! Targets are defined using [JSON](https://json.org/). The `Target` struct in -//! this module defines the format the JSON file should take, though each -//! underscore in the field names should be replaced with a hyphen (`-`) in the -//! JSON file. Some fields are required in every target specification, such as -//! `llvm-target`, `target-endian`, `target-pointer-width`, `data-layout`, -//! `arch`, and `os`. In general, options passed to rustc with `-C` override -//! the target's settings, though `target-feature` and `link-args` will *add* -//! to the list specified by the target, rather than replace. +//! Targets are defined using a struct which additionally has serialization to and from [JSON]. +//! The `Target` struct in this module loosely corresponds with the format the JSON takes. +//! We usually try to make the fields equivalent but we have given up on a 1:1 correspondence +//! between the JSON and the actual structure itself. +//! +//! Some fields are required in every target spec, and they should be embedded in Target directly. +//! Optional keys are in TargetOptions, but Target derefs to it, for no practical difference. +//! Most notable is the "data-layout" field which specifies Rust's notion of sizes and alignments +//! for several key types, such as f64, pointers, and so on. +//! +//! At one point we felt `-C` options should override the target's settings, like in C compilers, +//! but that was an essentially-unmarked route for making code incorrect and Rust unsound. +//! Confronted with programmers who prefer a compiler with a good UX instead of a lethal weapon, +//! we have almost-entirely recanted that notion, though we hope "target modifiers" will offer +//! a way to have a decent UX yet still extend the necessary compiler controls, without +//! requiring a new target spec for each and every single possible target micro-variant. +//! +//! [JSON]: https://json.org use std::borrow::Cow; use std::collections::BTreeMap; From 679a2e3a5b8e57264170403ac86e7eba00035c2d Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Mon, 16 Jun 2025 21:24:53 -0700 Subject: [PATCH 11/18] compiler: Redescribe rustc_target search algo more accurately --- compiler/rustc_target/src/spec/mod.rs | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 8166c9ef5d689..7a49f0040722c 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -6,22 +6,15 @@ //! to a new platform, and allows for an unprecedented level of control over how //! the compiler works. //! -//! # Using custom targets +//! # Using targets and target.json //! -//! A target tuple, as passed via `rustc --target=TUPLE`, will first be -//! compared against the list of built-in targets. This is to ease distributing -//! rustc (no need for configuration files) and also to hold these built-in -//! targets as immutable and sacred. If `TUPLE` is not one of the built-in -//! targets, rustc will check if a file named `TUPLE` exists. If it does, it -//! will be loaded as the target configuration. If the file does not exist, -//! rustc will search each directory in the environment variable -//! `RUST_TARGET_PATH` for a file named `TUPLE.json`. The first one found will -//! be loaded. If no file is found in any of those directories, a fatal error -//! will be given. +//! Invoking "rustc --target=${TUPLE}" will result in rustc initiating the [`Target::search`] by +//! - checking if "$TUPLE" is a complete path to a json (ending with ".json") and loading if so +//! - checking builtin targets for "${TUPLE}" +//! - checking directories in "${RUST_TARGET_PATH}" for "${TUPLE}.json" +//! - checking for "${RUSTC_SYSROOT}/lib/rustlib/${TUPLE}/target.json" //! -//! Projects defining their own targets should use -//! `--target=path/to/my-awesome-platform.json` instead of adding to -//! `RUST_TARGET_PATH`. +//! Code will then be compiled using the first discovered target spec. //! //! # Defining a new target //! From 2d3a37d1e50e61daa1db8d1ee7198ded15bb370e Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Mon, 16 Jun 2025 09:42:55 +0000 Subject: [PATCH 12/18] linked_list tests: buff check_links --- .../src/collections/linked_list/tests.rs | 65 +++++++------------ 1 file changed, 25 insertions(+), 40 deletions(-) diff --git a/library/alloc/src/collections/linked_list/tests.rs b/library/alloc/src/collections/linked_list/tests.rs index 493352447c43c..410e67d3fdb08 100644 --- a/library/alloc/src/collections/linked_list/tests.rs +++ b/library/alloc/src/collections/linked_list/tests.rs @@ -56,48 +56,33 @@ fn list_from(v: &[T]) -> LinkedList { v.iter().cloned().collect() } +/// Starting from the head of the LinkedList, +/// follow the next links, while checking the prev links, +/// and check that length equals the count of visited nodes. fn check_links(list: &LinkedList) { - unsafe { - let mut len = 0; - let mut last_ptr: Option<&Node> = None; - let mut node_ptr: &Node; - match list.head { - None => { - // tail node should also be None. - assert!(list.tail.is_none()); - assert_eq!(0, list.len); - return; - } - Some(node) => node_ptr = &*node.as_ptr(), - } - loop { - match (last_ptr, node_ptr.prev) { - (None, None) => {} - (None, _) => panic!("prev link for head"), - (Some(p), Some(pptr)) => { - assert_eq!(p as *const Node, pptr.as_ptr() as *const Node); - } - _ => panic!("prev link is none, not good"), - } - match node_ptr.next { - Some(next) => { - last_ptr = Some(node_ptr); - node_ptr = &*next.as_ptr(); - len += 1; - } - None => { - len += 1; - break; - } - } - } + let mut node: &Node = if let Some(node) = list.head { + // SAFETY: depends on correctness of LinkedList + unsafe { &*node.as_ptr() } + } else { + assert!(list.tail.is_none(), "empty list should have no tail node"); + assert_eq!(list.len, 0, "empty list should have length 0"); + return; + }; - // verify that the tail node points to the last node. - let tail = list.tail.as_ref().expect("some tail node").as_ref(); - assert_eq!(tail as *const Node, node_ptr as *const Node); - // check that len matches interior links. - assert_eq!(len, list.len); + assert!(node.prev.is_none(), "head node should not have a prev link"); + let mut prev; + let mut len = 1; + while let Some(next) = node.next { + prev = node; + // SAFETY: depends on correctness of LinkedList + node = unsafe { &*next.as_ptr() }; + len += 1; + assert_eq!(node.prev.expect("missing prev link"), prev.into(), "bad prev link"); } + + let tail = list.tail.expect("list is non-empty, so there should be a tail node"); + assert_eq!(tail, node.into(), "tail node points to the last node"); + assert_eq!(len, list.len, "len matches interior links"); } #[test] @@ -1027,7 +1012,7 @@ fn extract_if_drop_panic_leak() { macro_rules! struct_with_counted_drop { ($struct_name:ident$(($elt_ty:ty))?, $drop_counter:ident $(=> $drop_stmt:expr)?) => { - thread_local! {static $drop_counter: Cell = Cell::new(0);} + thread_local! {static $drop_counter: Cell = Cell::new(0);} struct $struct_name$(($elt_ty))?; From 2bbc974bede2f23953aa5fc81d6399811ab47400 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 17 Jun 2025 11:50:03 +0200 Subject: [PATCH 13/18] Lint about `console` calls in rustdoc JS --- src/librustdoc/html/static/.eslintrc.js | 1 + src/librustdoc/html/static/js/search.js | 1 + 2 files changed, 2 insertions(+) diff --git a/src/librustdoc/html/static/.eslintrc.js b/src/librustdoc/html/static/.eslintrc.js index fbb096fe9c753..303c5667140cf 100644 --- a/src/librustdoc/html/static/.eslintrc.js +++ b/src/librustdoc/html/static/.eslintrc.js @@ -91,5 +91,6 @@ module.exports = { "no-script-url": "error", "no-sequences": "error", "no-div-regex": "error", + "no-console": "error", } }; diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index dce5fddb3177e..b611a3e501dc1 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1133,6 +1133,7 @@ class RoaringBitmap { } for (let j = 0; j < size; ++j) { if (offsets && offsets[j] !== i) { + // eslint-disable-next-line no-console console.log(this.containers); throw new Error(`corrupt bitmap ${j}: ${i} / ${offsets[j]}`); } From e3c21dd88b760aefa39fb72bcc34299ca913967d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20du=20Garreau?= Date: Tue, 17 Jun 2025 13:08:36 +0200 Subject: [PATCH 14/18] Remove a panicking branch in `BorrowedCursor::advance` --- library/core/src/io/borrowed_buf.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/core/src/io/borrowed_buf.rs b/library/core/src/io/borrowed_buf.rs index f86abf7f1e91c..6bd9c18e00bdf 100644 --- a/library/core/src/io/borrowed_buf.rs +++ b/library/core/src/io/borrowed_buf.rs @@ -281,10 +281,10 @@ impl<'a> BorrowedCursor<'a> { /// Panics if there are less than `n` bytes initialized. #[inline] pub fn advance(&mut self, n: usize) -> &mut Self { - let filled = self.buf.filled.strict_add(n); - assert!(filled <= self.buf.init); + // The substraction cannot underflow by invariant of this type. + assert!(n <= self.buf.init - self.buf.filled); - self.buf.filled = filled; + self.buf.filled += n; self } From 1ab8ff57d6fe41af9f22c75cbf41d07130bb3d60 Mon Sep 17 00:00:00 2001 From: xizheyin Date: Tue, 17 Jun 2025 22:37:24 +0800 Subject: [PATCH 15/18] Add test suggest-remove-semi-in-macro-expansion-issue-142143.rs Signed-off-by: xizheyin --- ...ove-semi-in-macro-expansion-issue-142143.rs | 11 +++++++++++ ...semi-in-macro-expansion-issue-142143.stderr | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 tests/ui/lint/redundant-semicolon/suggest-remove-semi-in-macro-expansion-issue-142143.rs create mode 100644 tests/ui/lint/redundant-semicolon/suggest-remove-semi-in-macro-expansion-issue-142143.stderr diff --git a/tests/ui/lint/redundant-semicolon/suggest-remove-semi-in-macro-expansion-issue-142143.rs b/tests/ui/lint/redundant-semicolon/suggest-remove-semi-in-macro-expansion-issue-142143.rs new file mode 100644 index 0000000000000..4360eb964a4af --- /dev/null +++ b/tests/ui/lint/redundant-semicolon/suggest-remove-semi-in-macro-expansion-issue-142143.rs @@ -0,0 +1,11 @@ +// Make sure we don't suggest remove redundant semicolon inside macro expansion.(issue #142143) + +#![deny(redundant_semicolons)] + +macro_rules! m { + ($stmt:stmt) => { #[allow(bad_style)] $stmt } //~ ERROR unnecessary trailing semicolon [redundant_semicolons] +} + +fn main() { + m!(;); +} diff --git a/tests/ui/lint/redundant-semicolon/suggest-remove-semi-in-macro-expansion-issue-142143.stderr b/tests/ui/lint/redundant-semicolon/suggest-remove-semi-in-macro-expansion-issue-142143.stderr new file mode 100644 index 0000000000000..909c9f84d0b54 --- /dev/null +++ b/tests/ui/lint/redundant-semicolon/suggest-remove-semi-in-macro-expansion-issue-142143.stderr @@ -0,0 +1,18 @@ +error: unnecessary trailing semicolon + --> $DIR/suggest-remove-semi-in-macro-expansion-issue-142143.rs:6:43 + | +LL | ($stmt:stmt) => { #[allow(bad_style)] $stmt } + | ^^^^^ help: remove this semicolon +... +LL | m!(;); + | ----- in this macro invocation + | +note: the lint level is defined here + --> $DIR/suggest-remove-semi-in-macro-expansion-issue-142143.rs:3:9 + | +LL | #![deny(redundant_semicolons)] + | ^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + From 3f1de7b2e8101cefba1e983af8637d9581c37f68 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 17 Jun 2025 09:13:12 -0700 Subject: [PATCH 16/18] Update cargo --- src/tools/cargo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/cargo b/src/tools/cargo index fc1518ef02b77..2251525ae503f 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit fc1518ef02b77327d70d4026b95ea719dd9b8c51 +Subproject commit 2251525ae503fa196f6d7f9ce6d32eccb2d5f044 From 72fbf3ea6163d739dfa6c15312e3a7433b1fcb67 Mon Sep 17 00:00:00 2001 From: xizheyin Date: Tue, 17 Jun 2025 22:48:58 +0800 Subject: [PATCH 17/18] Dont suggest remove semi inside macro expansion for redundant semi lint Signed-off-by: xizheyin --- compiler/rustc_lint/messages.ftl | 3 ++- compiler/rustc_lint/src/lints.rs | 12 ++++++++++-- compiler/rustc_lint/src/redundant_semicolon.rs | 13 +++++++++---- ...move-semi-in-macro-expansion-issue-142143.stderr | 2 +- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 46ae414652870..f92f830780801 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -738,7 +738,8 @@ lint_redundant_semicolons = [true] semicolons *[false] semicolon } - .suggestion = remove {$multiple -> + +lint_redundant_semicolons_suggestion = remove {$multiple_semicolons -> [true] these semicolons *[false] this semicolon } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 3d17dfbc45198..0b8c68404f16c 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -1538,8 +1538,16 @@ pub(crate) struct PassByValueDiag { #[diag(lint_redundant_semicolons)] pub(crate) struct RedundantSemicolonsDiag { pub multiple: bool, - #[suggestion(code = "", applicability = "maybe-incorrect")] - pub suggestion: Span, + #[subdiagnostic] + pub suggestion: Option, +} + +#[derive(Subdiagnostic)] +#[suggestion(lint_redundant_semicolons_suggestion, code = "", applicability = "maybe-incorrect")] +pub(crate) struct RedundantSemicolonsSuggestion { + pub multiple_semicolons: bool, + #[primary_span] + pub span: Span, } // traits.rs diff --git a/compiler/rustc_lint/src/redundant_semicolon.rs b/compiler/rustc_lint/src/redundant_semicolon.rs index b43e4938b736c..f6d2fbe42618e 100644 --- a/compiler/rustc_lint/src/redundant_semicolon.rs +++ b/compiler/rustc_lint/src/redundant_semicolon.rs @@ -2,7 +2,7 @@ use rustc_ast::{Block, StmtKind}; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::Span; -use crate::lints::RedundantSemicolonsDiag; +use crate::lints::{RedundantSemicolonsDiag, RedundantSemicolonsSuggestion}; use crate::{EarlyContext, EarlyLintPass, LintContext}; declare_lint! { @@ -44,16 +44,21 @@ impl EarlyLintPass for RedundantSemicolons { fn maybe_lint_redundant_semis(cx: &EarlyContext<'_>, seq: &mut Option<(Span, bool)>) { if let Some((span, multiple)) = seq.take() { - // FIXME: Find a better way of ignoring the trailing - // semicolon from macro expansion if span == rustc_span::DUMMY_SP { return; } + // Ignore redundant semicolons inside macro expansion.(issue #142143) + let suggestion = if span.from_expansion() { + None + } else { + Some(RedundantSemicolonsSuggestion { multiple_semicolons: multiple, span }) + }; + cx.emit_span_lint( REDUNDANT_SEMICOLONS, span, - RedundantSemicolonsDiag { multiple, suggestion: span }, + RedundantSemicolonsDiag { multiple, suggestion }, ); } } diff --git a/tests/ui/lint/redundant-semicolon/suggest-remove-semi-in-macro-expansion-issue-142143.stderr b/tests/ui/lint/redundant-semicolon/suggest-remove-semi-in-macro-expansion-issue-142143.stderr index 909c9f84d0b54..7a38ec318ab6a 100644 --- a/tests/ui/lint/redundant-semicolon/suggest-remove-semi-in-macro-expansion-issue-142143.stderr +++ b/tests/ui/lint/redundant-semicolon/suggest-remove-semi-in-macro-expansion-issue-142143.stderr @@ -2,7 +2,7 @@ error: unnecessary trailing semicolon --> $DIR/suggest-remove-semi-in-macro-expansion-issue-142143.rs:6:43 | LL | ($stmt:stmt) => { #[allow(bad_style)] $stmt } - | ^^^^^ help: remove this semicolon + | ^^^^^ ... LL | m!(;); | ----- in this macro invocation From a88a32d7467f6c279989b24a7f0ed35071989246 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Tue, 17 Jun 2025 17:15:01 +0000 Subject: [PATCH 18/18] Temporarily add back -Zwasm-c-abi=spec This allows a more gradual transition path for projects that need to use use the spec-complaint C ABI both with older and newer rustc versions. --- compiler/rustc_session/src/config.rs | 1 + compiler/rustc_session/src/options.rs | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 406a6bd335a74..04ca0b75c3113 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -3118,6 +3118,7 @@ pub(crate) mod dep_tracking { } impl_dep_tracking_hash_via_hash!( + (), AutoDiff, bool, usize, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index f76f258d00de9..9ca405333f43d 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -802,6 +802,7 @@ mod desc { "either a boolean (`yes`, `no`, `on`, `off`, etc), or a non-negative number"; pub(crate) const parse_llvm_module_flag: &str = ":::. Type must currently be `u32`. Behavior should be one of (`error`, `warning`, `require`, `override`, `append`, `appendunique`, `max`, `min`)"; pub(crate) const parse_function_return: &str = "`keep` or `thunk-extern`"; + pub(crate) const parse_wasm_c_abi: &str = "`spec`"; pub(crate) const parse_mir_include_spans: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or `nll` (default: `nll`)"; pub(crate) const parse_align: &str = "a number that is a power of 2 between 1 and 2^29"; @@ -1897,6 +1898,10 @@ pub mod parse { true } + pub(crate) fn parse_wasm_c_abi(_slot: &mut (), v: Option<&str>) -> bool { + v == Some("spec") + } + pub(crate) fn parse_mir_include_spans(slot: &mut MirIncludeSpans, v: Option<&str>) -> bool { *slot = match v { Some("on" | "yes" | "y" | "true") | None => MirIncludeSpans::On, @@ -2631,6 +2636,11 @@ written to standard error output)"), Requires `-Clto[=[fat,yes]]`"), wasi_exec_model: Option = (None, parse_wasi_exec_model, [TRACKED], "whether to build a wasi command or reactor"), + // This option only still exists to provide a more gradual transition path for people who need + // the spec-complaint C ABI to be used. + // FIXME remove this after a couple releases + wasm_c_abi: () = ((), parse_wasm_c_abi, [TRACKED], + "use spec-compliant C ABI for `wasm32-unknown-unknown` (deprecated, always enabled)"), write_long_types_to_disk: bool = (true, parse_bool, [UNTRACKED], "whether long type names should be written to files instead of being printed in errors"), // tidy-alphabetical-end