From d501f960a3950983725a94f4ec7e83f3149e13f2 Mon Sep 17 00:00:00 2001 From: Aelin Reidel Date: Tue, 24 Feb 2026 07:37:34 +0100 Subject: [PATCH 01/18] rustc_target: callconv: powerpc64: Use llvm_abiname rather than target_abi for ABI determination Currently on PowerPC64 targets, llvm_abiname and target_abi will be the same unless we're on AIX. Since llvm_abiname is what we pass on to LLVM, it is preferable to use the value of that to determine the calling convention rather than target_abi. All PowerPC64 targets set both llvm_abiname and target_abi to the respective ELF ABIs, with the exception of AIX. This is a non-functional change. --- compiler/rustc_target/src/callconv/powerpc64.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_target/src/callconv/powerpc64.rs b/compiler/rustc_target/src/callconv/powerpc64.rs index 1d83799d7dab1..683b615eec83b 100644 --- a/compiler/rustc_target/src/callconv/powerpc64.rs +++ b/compiler/rustc_target/src/callconv/powerpc64.rs @@ -5,7 +5,7 @@ use rustc_abi::{Endian, HasDataLayout, TyAbiInterface}; use crate::callconv::{Align, ArgAbi, FnAbi, Reg, RegKind, Uniform}; -use crate::spec::{Abi, HasTargetSpec, Os}; +use crate::spec::{HasTargetSpec, Os}; #[derive(Debug, Clone, Copy, PartialEq)] enum ABI { @@ -106,9 +106,9 @@ where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout + HasTargetSpec, { - let abi = if cx.target_spec().options.abi == Abi::ElfV2 { + let abi = if cx.target_spec().options.llvm_abiname == "elfv2" { ELFv2 - } else if cx.target_spec().options.abi == Abi::ElfV1 { + } else if cx.target_spec().options.llvm_abiname == "elfv1" { ELFv1 } else if cx.target_spec().os == Os::Aix { AIX From acbfd79acf662be04745dc0675558e75e0d30d87 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Mon, 26 Jan 2026 16:58:31 -0800 Subject: [PATCH 02/18] Fix: On wasm targets, call `panic_in_cleanup` if panic occurs in cleanup Previously this was not correctly implemented. Each funclet may need its own terminate block, so this changes the `terminate_block` into a `terminate_blocks` `IndexVec` which can have a terminate_block for each funclet. We key on the first basic block of the funclet -- in particular, this is the start block for the old case of the top level terminate function. Rather than using a catchswitch/catchpad pair, I used a cleanuppad. The reason for the pair is to avoid catching foreign exceptions on MSVC. On wasm, it seems that the catchswitch/catchpad pair is optimized back into a single cleanuppad and a catch_all instruction is emitted which will catch foreign exceptions. Because the new logic is only used on wasm, it seemed better to take the simpler approach seeing as they do the same thing. --- compiler/rustc_codegen_gcc/src/builder.rs | 4 + compiler/rustc_codegen_llvm/src/builder.rs | 4 + compiler/rustc_codegen_ssa/src/mir/block.rs | 96 +++++++++++++++---- compiler/rustc_codegen_ssa/src/mir/mod.rs | 9 +- .../rustc_codegen_ssa/src/traits/builder.rs | 5 +- tests/codegen-llvm/double_panic_wasm.rs | 34 +++++++ tests/codegen-llvm/terminating-catchpad.rs | 10 ++ 7 files changed, 138 insertions(+), 24 deletions(-) create mode 100644 tests/codegen-llvm/double_panic_wasm.rs diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 3def9a5c015cc..1b0a4e0b67959 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -1653,6 +1653,10 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { unimplemented!(); } + fn get_funclet_cleanuppad(&self, _funclet: &Funclet) -> RValue<'gcc> { + unimplemented!(); + } + // Atomic Operations fn atomic_cmpxchg( &mut self, diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index ca4805a93e017..c3e15eadfced1 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1309,6 +1309,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { ret } + fn get_funclet_cleanuppad(&self, funclet: &Funclet<'ll>) -> &'ll Value { + funclet.cleanuppad() + } + // Atomic Operations fn atomic_cmpxchg( &mut self, diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 35de8b5e1486b..4859380daa662 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -215,19 +215,18 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { mir::UnwindAction::Continue => None, mir::UnwindAction::Unreachable => None, mir::UnwindAction::Terminate(reason) => { - if fx.mir[self.bb].is_cleanup && base::wants_new_eh_instructions(fx.cx.tcx().sess) { + if fx.mir[self.bb].is_cleanup && base::wants_wasm_eh(fx.cx.tcx().sess) { + // For wasm, we need to generate a nested `cleanuppad within %outer_pad` + // to catch exceptions during cleanup and call `panic_in_cleanup`. + Some(fx.terminate_block(reason, Some(self.bb))) + } else if fx.mir[self.bb].is_cleanup + && base::wants_new_eh_instructions(fx.cx.tcx().sess) + { // MSVC SEH will abort automatically if an exception tries to // propagate out from cleanup. - - // FIXME(@mirkootter): For wasm, we currently do not support terminate during - // cleanup, because this requires a few more changes: The current code - // caches the `terminate_block` for each function; funclet based code - however - - // requires a different terminate_block for each funclet - // Until this is implemented, we just do not unwind inside cleanup blocks - None } else { - Some(fx.terminate_block(reason)) + Some(fx.terminate_block(reason, None)) } } }; @@ -239,7 +238,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { if let Some(unwind_block) = unwind_block { let ret_llbb = if let Some((_, target)) = destination { - fx.llbb(target) + self.llbb_with_cleanup(fx, target) } else { fx.unreachable_block() }; @@ -310,7 +309,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { ) -> MergingSucc { let unwind_target = match unwind { mir::UnwindAction::Cleanup(cleanup) => Some(self.llbb_with_cleanup(fx, cleanup)), - mir::UnwindAction::Terminate(reason) => Some(fx.terminate_block(reason)), + mir::UnwindAction::Terminate(reason) => Some(fx.terminate_block(reason, None)), mir::UnwindAction::Continue => None, mir::UnwindAction::Unreachable => None, }; @@ -318,7 +317,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { if operands.iter().any(|x| matches!(x, InlineAsmOperandRef::Label { .. })) { assert!(unwind_target.is_none()); let ret_llbb = if let Some(target) = destination { - fx.llbb(target) + self.llbb_with_cleanup(fx, target) } else { fx.unreachable_block() }; @@ -335,7 +334,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { MergingSucc::False } else if let Some(cleanup) = unwind_target { let ret_llbb = if let Some(target) = destination { - fx.llbb(target) + self.llbb_with_cleanup(fx, target) } else { fx.unreachable_block() }; @@ -1830,8 +1829,39 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }) } - fn terminate_block(&mut self, reason: UnwindTerminateReason) -> Bx::BasicBlock { - if let Some((cached_bb, cached_reason)) = self.terminate_block + fn terminate_block( + &mut self, + reason: UnwindTerminateReason, + outer_catchpad_bb: Option, + ) -> Bx::BasicBlock { + // mb_funclet_bb should be present if and only if the target is wasm and + // we're terminating because of an unwind in a cleanup block. In that + // case we have nested funclets and the inner catch_switch needs to know + // what outer catch_pad it is contained in. + debug_assert!( + outer_catchpad_bb.is_some() + == (base::wants_wasm_eh(self.cx.tcx().sess) + && reason == UnwindTerminateReason::InCleanup) + ); + + // When we aren't in a wasm InCleanup block, there's only one terminate + // block needed so we cache at START_BLOCK index. + let mut cache_bb = mir::START_BLOCK; + // In wasm eh InCleanup, use the outer funclet's cleanup BB as the cache + // key. + if let Some(outer_bb) = outer_catchpad_bb { + let cleanup_kinds = + self.cleanup_kinds.as_ref().expect("cleanup_kinds required for funclets"); + cache_bb = cleanup_kinds[outer_bb] + .funclet_bb(outer_bb) + .expect("funclet_bb should be in a funclet"); + + // Ensure the outer funclet is created first + if self.funclets[cache_bb].is_none() { + self.landing_pad_for(cache_bb); + } + } + if let Some((cached_bb, cached_reason)) = self.terminate_blocks[cache_bb] && reason == cached_reason { return cached_bb; @@ -1869,12 +1899,35 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // cp_terminate: // %cp = catchpad within %cs [null, i32 64, null] // ... + // + // By contrast, on WebAssembly targets, we specifically _do_ want to + // catch foreign exceptions. The situation with MSVC is a + // regrettable hack which we don't want to extend to other targets + // unless necessary. For WebAssembly, to generate catch(...) and + // catch only C++ exception instead of generating a catch_all, we + // need to call the intrinsics @llvm.wasm.get.exception and + // @llvm.wasm.get.ehselector in the catch pad. Since we don't do + // this, we generate a catch_all. We originally got this behavior + // by accident but it luckily matches our intention. llbb = Bx::append_block(self.cx, self.llfn, "cs_terminate"); - let cp_llbb = Bx::append_block(self.cx, self.llfn, "cp_terminate"); let mut cs_bx = Bx::build(self.cx, llbb); - let cs = cs_bx.catch_switch(None, None, &[cp_llbb]); + + // For wasm InCleanup blocks, our catch_switch is nested within the + // outer catchpad, so we need to provide it as the parent value to + // catch_switch. + let mut outer_cleanuppad = None; + if outer_catchpad_bb.is_some() { + // Get the outer funclet's catchpad + let outer_funclet = self.funclets[cache_bb] + .as_ref() + .expect("landing_pad_for didn't create funclet"); + outer_cleanuppad = Some(cs_bx.get_funclet_cleanuppad(outer_funclet)); + } + let cp_llbb = Bx::append_block(self.cx, self.llfn, "cp_terminate"); + let cs = cs_bx.catch_switch(outer_cleanuppad, None, &[cp_llbb]); + drop(cs_bx); bx = Bx::build(self.cx, cp_llbb); let null = @@ -1895,13 +1948,18 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } else { // Specifying more arguments than necessary usually doesn't // hurt, but the `WasmEHPrepare` LLVM pass does not recognize - // anything other than a single `null` as a `catch (...)` block, + // anything other than a single `null` as a `catch_all` block, // leading to problems down the line during instruction // selection. &[null] as &[_] }; funclet = Some(bx.catch_pad(cs, args)); + // On wasm, if we wanted to generate a catch(...) and only catch C++ + // exceptions, we'd call @llvm.wasm.get.exception and + // @llvm.wasm.get.ehselector selectors here. We want a catch_all so + // we leave them out. This is intentionally diverging from the MSVC + // behavior. } else { llbb = Bx::append_block(self.cx, self.llfn, "terminate"); bx = Bx::build(self.cx, llbb); @@ -1927,7 +1985,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.unreachable(); - self.terminate_block = Some((llbb, reason)); + self.terminate_blocks[cache_bb] = Some((llbb, reason)); llbb } diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 819abb9ae644d..1a0f66d31cca4 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -90,8 +90,11 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> { /// Cached unreachable block unreachable_block: Option, - /// Cached terminate upon unwinding block and its reason - terminate_block: Option<(Bx::BasicBlock, UnwindTerminateReason)>, + /// Cached terminate upon unwinding block and its reason. For non-wasm + /// targets, there is at most one such block per function, stored at index + /// `START_BLOCK`. For wasm targets, each funclet needs its own terminate + /// block, indexed by the cleanup block that is the funclet's head. + terminate_blocks: IndexVec>, /// A bool flag for each basic block indicating whether it is a cold block. /// A cold block is a block that is unlikely to be executed at runtime. @@ -227,7 +230,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( personality_slot: None, cached_llbbs, unreachable_block: None, - terminate_block: None, + terminate_blocks: IndexVec::from_elem(None, &mir.basic_blocks), cleanup_kinds, landing_pads: IndexVec::from_elem(None, &mir.basic_blocks), funclets: IndexVec::from_fn_n(|_| None, mir.basic_blocks.len()), diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 3486bd140eceb..edabd8188a3b6 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -552,12 +552,12 @@ pub trait BuilderMethods<'a, 'tcx>: fn set_personality_fn(&mut self, personality: Self::Function); - // These are used by everyone except msvc + // These are used by everyone except msvc and wasm EH fn cleanup_landing_pad(&mut self, pers_fn: Self::Function) -> (Self::Value, Self::Value); fn filter_landing_pad(&mut self, pers_fn: Self::Function); fn resume(&mut self, exn0: Self::Value, exn1: Self::Value); - // These are used only by msvc + // These are used by msvc and wasm EH fn cleanup_pad(&mut self, parent: Option, args: &[Self::Value]) -> Self::Funclet; fn cleanup_ret(&mut self, funclet: &Self::Funclet, unwind: Option); fn catch_pad(&mut self, parent: Self::Value, args: &[Self::Value]) -> Self::Funclet; @@ -567,6 +567,7 @@ pub trait BuilderMethods<'a, 'tcx>: unwind: Option, handlers: &[Self::BasicBlock], ) -> Self::Value; + fn get_funclet_cleanuppad(&self, funclet: &Self::Funclet) -> Self::Value; fn atomic_cmpxchg( &mut self, diff --git a/tests/codegen-llvm/double_panic_wasm.rs b/tests/codegen-llvm/double_panic_wasm.rs new file mode 100644 index 0000000000000..1eafe60503809 --- /dev/null +++ b/tests/codegen-llvm/double_panic_wasm.rs @@ -0,0 +1,34 @@ +//@ compile-flags: -C panic=unwind -Copt-level=0 +//@ needs-unwind +//@ only-wasm32 + +#![crate_type = "lib"] + +// Test that `panic_in_cleanup` is called on webassembly targets when a panic +// occurs in a destructor during unwinding. + +extern "Rust" { + fn may_panic(); +} + +struct PanicOnDrop; + +impl Drop for PanicOnDrop { + fn drop(&mut self) { + unsafe { may_panic() } + } +} + +// CHECK-LABEL: @double_panic +// CHECK: invoke void @may_panic() +// CHECK: invoke void @{{.+}}drop_in_place{{.+}} +// CHECK: unwind label %[[TERMINATE:.*]] +// +// CHECK: [[TERMINATE]]: +// CHECK: call void @{{.*panic_in_cleanup}} +// CHECK: unreachable +#[no_mangle] +pub fn double_panic() { + let _guard = PanicOnDrop; + unsafe { may_panic() } +} diff --git a/tests/codegen-llvm/terminating-catchpad.rs b/tests/codegen-llvm/terminating-catchpad.rs index a2ec19871d1fc..7c98ea94fdc13 100644 --- a/tests/codegen-llvm/terminating-catchpad.rs +++ b/tests/codegen-llvm/terminating-catchpad.rs @@ -9,6 +9,10 @@ // Ensure a catch-all generates: // - `catchpad ... [ptr null]` on Wasm (otherwise LLVM gets confused) // - `catchpad ... [ptr null, i32 64, ptr null]` on Windows (otherwise we catch SEH exceptions) +// +// Unlike on windows, on Wasm, we specifically do want to catch foreign +// exceptions. To catch only C++ exceptions we'd need to call +// @llvm.wasm.get.exception and @llvm.wasm.get.ehselector in the catchpad. #![feature(no_core, lang_items, rustc_attrs)] #![crate_type = "lib"] @@ -36,8 +40,14 @@ fn panic_cannot_unwind() -> ! { #[no_mangle] #[rustc_nounwind] pub fn doesnt_unwind() { + // CHECK: catchswitch within none [label %{{.*}}] unwind to caller // emscripten: %catchpad = catchpad within %catchswitch [ptr null] // wasi: %catchpad = catchpad within %catchswitch [ptr null] // seh: %catchpad = catchpad within %catchswitch [ptr null, i32 64, ptr null] + // + // We don't call these intrinsics on wasm targets so we generate a catch_all + // instruction which also picks up foreign exceptions + // NOT: @llvm.wasm.get.exception + // NOT: @llvm.wasm.get.ehselector unwinds(); } From 4450740b1b52d292dab845934c81756fb4324cdc Mon Sep 17 00:00:00 2001 From: Jynn Nelson Date: Fri, 12 Dec 2025 14:41:11 -0500 Subject: [PATCH 03/18] Split `is_msvc_link_exe` into a function --- compiler/rustc_codegen_ssa/src/back/link.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 388503c720532..e1751a9e442ea 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -670,6 +670,14 @@ struct LinkerOutput { inner: String, } +fn is_msvc_link_exe(sess: &Session) -> bool { + let (linker_path, flavor) = linker_and_flavor(sess); + sess.target.is_like_msvc + && flavor == LinkerFlavor::Msvc(Lld::No) + // Match exactly "link.exe" + && linker_path.to_str() == Some("link.exe") +} + /// Create a dynamic library or executable. /// /// This will invoke the system linker/cc to create the resulting file. This links to all upstream @@ -856,11 +864,6 @@ fn link_natively( match prog { Ok(prog) => { - let is_msvc_link_exe = sess.target.is_like_msvc - && flavor == LinkerFlavor::Msvc(Lld::No) - // Match exactly "link.exe" - && linker_path.to_str() == Some("link.exe"); - if !prog.status.success() { let mut output = prog.stderr.clone(); output.extend_from_slice(&prog.stdout); @@ -880,7 +883,7 @@ fn link_natively( if let Some(code) = prog.status.code() { // All Microsoft `link.exe` linking ror codes are // four digit numbers in the range 1000 to 9999 inclusive - if is_msvc_link_exe && (code < 1000 || code > 9999) { + if is_msvc_link_exe(sess) && (code < 1000 || code > 9999) { let is_vs_installed = find_msvc_tools::find_vs_version().is_ok(); let has_linker = find_msvc_tools::find_tool(sess.target.arch.desc(), "link.exe") @@ -919,7 +922,7 @@ fn link_natively( // Hide some progress messages from link.exe that we don't care about. // See https://github.com/chromium/chromium/blob/bfa41e41145ffc85f041384280caf2949bb7bd72/build/toolchain/win/tool_wrapper.py#L144-L146 - if is_msvc_link_exe { + if is_msvc_link_exe(sess) { if let Ok(str) = str::from_utf8(&prog.stdout) { let mut output = String::with_capacity(str.len()); for line in stdout.lines() { From 1679dfbc399ee3f5999c424ddeb2776b526376b8 Mon Sep 17 00:00:00 2001 From: Jynn Nelson Date: Fri, 12 Dec 2025 14:54:07 -0500 Subject: [PATCH 04/18] Split `report_linker_output` into its own function --- compiler/rustc_codegen_ssa/src/back/link.rs | 92 ++++++++++++--------- 1 file changed, 51 insertions(+), 41 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index e1751a9e442ea..cc11c063f9861 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -60,7 +60,7 @@ use super::rpath::{self, RPathConfig}; use super::{apple, versioned_llvm_target}; use crate::base::needs_allocator_shim_for_linking; use crate::{ - CodegenResults, CompiledModule, CrateInfo, NativeLib, errors, looks_like_rust_object_file, + CodegenLintLevels, CodegenResults, CompiledModule, CrateInfo, NativeLib, errors, looks_like_rust_object_file }; pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) { @@ -678,6 +678,55 @@ fn is_msvc_link_exe(sess: &Session) -> bool { && linker_path.to_str() == Some("link.exe") } +fn report_linker_output(sess: &Session, levels: CodegenLintLevels, stdout: &[u8], stderr: &[u8]) { + let escaped_stderr = escape_string(&stderr); + let mut escaped_stdout = escape_string(&stdout); + info!("linker stderr:\n{}", &escaped_stderr); + info!("linker stdout:\n{}", &escaped_stdout); + + // Hide some progress messages from link.exe that we don't care about. + // See https://github.com/chromium/chromium/blob/bfa41e41145ffc85f041384280caf2949bb7bd72/build/toolchain/win/tool_wrapper.py#L144-L146 + if is_msvc_link_exe(sess) { + if let Ok(str) = str::from_utf8(&stdout) { + let mut output = String::with_capacity(str.len()); + for line in str.lines() { + if line.starts_with(" Creating library") + || line.starts_with("Generating code") + || line.starts_with("Finished generating code") + { + continue; + } else { + output += line; + output += "\r\n" + } + } + escaped_stdout = escape_string(output.trim().as_bytes()) + } + } + + let lint_msg = |msg| { + diag_lint_level( + sess, + LINKER_MESSAGES, + levels.linker_messages, + None, + LinkerOutput { inner: msg }, + ); + }; + + if !escaped_stderr.is_empty() { + // We already print `warning:` at the start of the diagnostic. Remove it from the linker output if present. + let stderr = escaped_stderr + .strip_prefix("warning: ") + .unwrap_or(&escaped_stderr) + .replace(": warning: ", ": "); + lint_msg(format!("linker stderr: {stderr}")); + } + if !escaped_stdout.is_empty() { + lint_msg(format!("linker stdout: {}", escaped_stdout)) + } +} + /// Create a dynamic library or executable. /// /// This will invoke the system linker/cc to create the resulting file. This links to all upstream @@ -915,46 +964,7 @@ fn link_natively( sess.dcx().abort_if_errors(); } - let stderr = escape_string(&prog.stderr); - let mut stdout = escape_string(&prog.stdout); - info!("linker stderr:\n{}", &stderr); - info!("linker stdout:\n{}", &stdout); - - // Hide some progress messages from link.exe that we don't care about. - // See https://github.com/chromium/chromium/blob/bfa41e41145ffc85f041384280caf2949bb7bd72/build/toolchain/win/tool_wrapper.py#L144-L146 - if is_msvc_link_exe(sess) { - if let Ok(str) = str::from_utf8(&prog.stdout) { - let mut output = String::with_capacity(str.len()); - for line in stdout.lines() { - if line.starts_with(" Creating library") - || line.starts_with("Generating code") - || line.starts_with("Finished generating code") - { - continue; - } - output += line; - output += "\r\n" - } - stdout = escape_string(output.trim().as_bytes()) - } - } - - let level = codegen_results.crate_info.lint_levels.linker_messages; - let lint = |msg| { - diag_lint_level(sess, LINKER_MESSAGES, level, None, LinkerOutput { inner: msg }); - }; - - if !prog.stderr.is_empty() { - // We already print `warning:` at the start of the diagnostic. Remove it from the linker output if present. - let stderr = stderr - .strip_prefix("warning: ") - .unwrap_or(&stderr) - .replace(": warning: ", ": "); - lint(format!("linker stderr: {stderr}")); - } - if !stdout.is_empty() { - lint(format!("linker stdout: {}", stdout)) - } + report_linker_output(sess, codegen_results.crate_info.lint_levels, &prog.stdout, &prog.stderr); } Err(e) => { let linker_not_found = e.kind() == io::ErrorKind::NotFound; From 1e3bf5e37111c9075306966c2c14fcc473275bf1 Mon Sep 17 00:00:00 2001 From: Jynn Nelson Date: Fri, 12 Dec 2025 17:17:24 -0500 Subject: [PATCH 05/18] Split out linker-info from linker-messages - Hide common linker output behind `linker-info` - Add tests - Account for different capitalization on windows-gnu when removing "warning" prefix - Add some more comments - Add macOS deployment-target test - Ignore linker warnings from trying to statically link glibc I don't know what's going on in `nofile-limit.rs` but I want no part of it. - Use a fake linker so tests are platform-independent --- compiler/rustc_codegen_ssa/src/back/link.rs | 134 +++++++++++++++--- compiler/rustc_codegen_ssa/src/lib.rs | 7 +- compiler/rustc_lint_defs/src/builtin.rs | 35 +++++ compiler/rustc_passes/src/check_attr.rs | 4 +- compiler/rustc_passes/src/errors.rs | 2 +- compiler/rustc_span/src/symbol.rs | 1 + src/tools/tidy/src/ui_tests.rs | 1 + .../macos-deployment-target-warning/foo.c | 1 + .../macos-deployment-target-warning/main.rs | 8 ++ .../macos-deployment-target-warning/rmake.rs | 29 ++++ .../warnings.txt | 11 ++ tests/ui/linking/auxiliary/fake-linker.ps1 | 3 + tests/ui/linking/macos-ignoring-duplicate.rs | 6 + .../linking/macos-ignoring-duplicate.stderr | 11 ++ tests/ui/linking/macos-search-path.rs | 6 + tests/ui/linking/macos-search-path.stderr | 11 ++ .../linking/windows-gnu-corrupt-drective.rs | 6 + .../windows-gnu-corrupt-drective.stderr | 12 ++ tests/ui/lint/linker-warning.stderr | 2 +- tests/ui/process/nofile-limit.rs | 3 + 20 files changed, 268 insertions(+), 25 deletions(-) create mode 100644 tests/run-make/macos-deployment-target-warning/foo.c create mode 100644 tests/run-make/macos-deployment-target-warning/main.rs create mode 100644 tests/run-make/macos-deployment-target-warning/rmake.rs create mode 100644 tests/run-make/macos-deployment-target-warning/warnings.txt create mode 100755 tests/ui/linking/auxiliary/fake-linker.ps1 create mode 100644 tests/ui/linking/macos-ignoring-duplicate.rs create mode 100644 tests/ui/linking/macos-ignoring-duplicate.stderr create mode 100644 tests/ui/linking/macos-search-path.rs create mode 100644 tests/ui/linking/macos-search-path.stderr create mode 100644 tests/ui/linking/windows-gnu-corrupt-drective.rs create mode 100644 tests/ui/linking/windows-gnu-corrupt-drective.stderr diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index cc11c063f9861..27634cd9142ae 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -22,6 +22,7 @@ use rustc_errors::DiagCtxtHandle; use rustc_fs_util::{TempDirBuilder, fix_windows_verbatim_for_gcc, try_canonicalize}; use rustc_hir::attrs::NativeLibKind; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; +use rustc_lint_defs::builtin::LINKER_INFO; use rustc_macros::Diagnostic; use rustc_metadata::fs::{METADATA_FILENAME, copy_to_stdout, emit_wrapper_file}; use rustc_metadata::{ @@ -60,7 +61,8 @@ use super::rpath::{self, RPathConfig}; use super::{apple, versioned_llvm_target}; use crate::base::needs_allocator_shim_for_linking; use crate::{ - CodegenLintLevels, CodegenResults, CompiledModule, CrateInfo, NativeLib, errors, looks_like_rust_object_file + CodegenLintLevels, CodegenResults, CompiledModule, CrateInfo, NativeLib, errors, + looks_like_rust_object_file, }; pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) { @@ -678,30 +680,105 @@ fn is_msvc_link_exe(sess: &Session) -> bool { && linker_path.to_str() == Some("link.exe") } +fn is_macos_ld(sess: &Session) -> bool { + let (_, flavor) = linker_and_flavor(sess); + sess.target.is_like_darwin && matches!(flavor, LinkerFlavor::Darwin(_, Lld::No)) +} + +fn is_windows_gnu_ld(sess: &Session) -> bool { + let (_, flavor) = linker_and_flavor(sess); + sess.target.is_like_windows + && !sess.target.is_like_msvc + && matches!(flavor, LinkerFlavor::Gnu(_, Lld::No)) +} + fn report_linker_output(sess: &Session, levels: CodegenLintLevels, stdout: &[u8], stderr: &[u8]) { - let escaped_stderr = escape_string(&stderr); + let mut escaped_stderr = escape_string(&stderr); let mut escaped_stdout = escape_string(&stdout); + let mut linker_info = String::new(); + info!("linker stderr:\n{}", &escaped_stderr); info!("linker stdout:\n{}", &escaped_stdout); - // Hide some progress messages from link.exe that we don't care about. - // See https://github.com/chromium/chromium/blob/bfa41e41145ffc85f041384280caf2949bb7bd72/build/toolchain/win/tool_wrapper.py#L144-L146 - if is_msvc_link_exe(sess) { - if let Ok(str) = str::from_utf8(&stdout) { - let mut output = String::with_capacity(str.len()); + fn for_each(bytes: &[u8], mut f: impl FnMut(&str, &mut String)) -> String { + let mut output = String::new(); + if let Ok(str) = str::from_utf8(bytes) { + info!("line: {str}"); + output = String::with_capacity(str.len()); for line in str.lines() { - if line.starts_with(" Creating library") - || line.starts_with("Generating code") - || line.starts_with("Finished generating code") - { - continue; - } else { - output += line; - output += "\r\n" - } + f(line.trim(), &mut output); } - escaped_stdout = escape_string(output.trim().as_bytes()) } + escape_string(output.trim().as_bytes()) + } + + if is_msvc_link_exe(sess) { + info!("inferred MSVC link.exe"); + + escaped_stdout = for_each(&stdout, |line, output| { + // Hide some progress messages from link.exe that we don't care about. + // See https://github.com/chromium/chromium/blob/bfa41e41145ffc85f041384280caf2949bb7bd72/build/toolchain/win/tool_wrapper.py#L144-L146 + if line.starts_with(" Creating library") + || line.starts_with("Generating code") + || line.starts_with("Finished generating code") + { + linker_info += line; + linker_info += "\r\n"; + } else { + *output += line; + *output += "\r\n" + } + }); + } else if is_macos_ld(sess) { + info!("inferred macOS LD"); + + // FIXME: Tracked by https://github.com/rust-lang/rust/issues/136113 + let deployment_mismatch = |line: &str| { + line.starts_with("ld: warning: object file (") + && line.contains("was built for newer 'macOS' version") + && line.contains("than being linked") + }; + // FIXME: This is a real warning we would like to show, but it hits too many crates + // to want to turn it on immediately. + let search_path = |line: &str| { + line.starts_with("ld: warning: search path '") && line.ends_with("' not found") + }; + escaped_stderr = for_each(&stderr, |line, output| { + // This duplicate library warning is just not helpful at all. + if line.starts_with("ld: warning: ignoring duplicate libraries: ") + || deployment_mismatch(line) + || search_path(line) + { + linker_info += line; + linker_info += "\n"; + } else { + *output += line; + *output += "\n" + } + }); + } else if is_windows_gnu_ld(sess) { + info!("inferred Windows GNU LD"); + + let mut saw_exclude_symbol = false; + // See https://github.com/rust-lang/rust/issues/112368. + // FIXME: maybe check that binutils is older than 2.40 before downgrading this warning? + let exclude_symbols = |line: &str| { + line.starts_with("Warning: .drectve `-exclude-symbols:") + && line.ends_with("' unrecognized") + }; + escaped_stderr = for_each(&stderr, |line, output| { + if exclude_symbols(line) { + saw_exclude_symbol = true; + linker_info += line; + linker_info += "\n"; + } else if saw_exclude_symbol && line == "Warning: corrupt .drectve at end of def file" { + linker_info += line; + linker_info += "\n"; + } else { + *output += line; + *output += "\n" + } + }); } let lint_msg = |msg| { @@ -713,18 +790,27 @@ fn report_linker_output(sess: &Session, levels: CodegenLintLevels, stdout: &[u8] LinkerOutput { inner: msg }, ); }; + let lint_info = |msg| { + diag_lint_level(sess, LINKER_INFO, levels.linker_info, None, LinkerOutput { inner: msg }); + }; if !escaped_stderr.is_empty() { // We already print `warning:` at the start of the diagnostic. Remove it from the linker output if present. - let stderr = escaped_stderr - .strip_prefix("warning: ") + escaped_stderr = + escaped_stderr.strip_prefix("warning: ").unwrap_or(&escaped_stderr).to_owned(); + // Windows GNU LD prints uppercase Warning + escaped_stderr = escaped_stderr + .strip_prefix("Warning: ") .unwrap_or(&escaped_stderr) .replace(": warning: ", ": "); - lint_msg(format!("linker stderr: {stderr}")); + lint_msg(format!("linker stderr: {escaped_stderr}")); } if !escaped_stdout.is_empty() { lint_msg(format!("linker stdout: {}", escaped_stdout)) } + if !linker_info.is_empty() { + lint_info(linker_info); + } } /// Create a dynamic library or executable. @@ -964,7 +1050,13 @@ fn link_natively( sess.dcx().abort_if_errors(); } - report_linker_output(sess, codegen_results.crate_info.lint_levels, &prog.stdout, &prog.stderr); + info!("reporting linker output: flavor={flavor:?}"); + report_linker_output( + sess, + codegen_results.crate_info.lint_levels, + &prog.stdout, + &prog.stderr, + ); } Err(e) => { let linker_not_found = e.kind() == io::ErrorKind::NotFound; diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index c7e90d3b3c330..6ef2447715ffa 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -24,6 +24,7 @@ use rustc_data_structures::unord::UnordMap; use rustc_hir::CRATE_HIR_ID; use rustc_hir::attrs::{CfgEntry, NativeLibKind, WindowsSubsystemKind}; use rustc_hir::def_id::CrateNum; +use rustc_lint_defs::builtin::LINKER_INFO; use rustc_macros::{Decodable, Encodable}; use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::WorkProduct; @@ -361,10 +362,14 @@ impl CodegenResults { #[derive(Copy, Clone, Debug, Encodable, Decodable)] pub struct CodegenLintLevels { linker_messages: LevelAndSource, + linker_info: LevelAndSource, } impl CodegenLintLevels { pub fn from_tcx(tcx: TyCtxt<'_>) -> Self { - Self { linker_messages: tcx.lint_level_at_node(LINKER_MESSAGES, CRATE_HIR_ID) } + Self { + linker_messages: tcx.lint_level_at_node(LINKER_MESSAGES, CRATE_HIR_ID), + linker_info: tcx.lint_level_at_node(LINKER_INFO, CRATE_HIR_ID), + } } } diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index c20c9dca346fd..35cc16bc0fc73 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -61,6 +61,7 @@ declare_lint_pass! { LARGE_ASSIGNMENTS, LATE_BOUND_LIFETIME_ARGUMENTS, LEGACY_DERIVE_HELPERS, + LINKER_INFO, LINKER_MESSAGES, LONG_RUNNING_CONST_EVAL, LOSSY_PROVENANCE_CASTS, @@ -4067,6 +4068,40 @@ declare_lint! { "warnings emitted at runtime by the target-specific linker program" } +declare_lint! { + /// The `linker_info` lint forwards warnings from the linker that are known to be informational-only. + /// + /// ### Example + /// + /// ```rust,ignore (needs CLI args, platform-specific) + /// #[warn(linker_info)] + /// fn main () {} + /// ``` + /// + /// On MacOS, using `-C link-arg=-lc` and the default linker, this will produce + /// + /// ```text + /// warning: linker stderr: ld: ignoring duplicate libraries: '-lc' + /// | + /// note: the lint level is defined here + /// --> ex.rs:1:9 + /// | + /// 1 | #![warn(linker_info)] + /// | ^^^^^^^^^^^^^^^ + /// ``` + /// + /// ### Explanation + /// + /// Many linkers are very "chatty" and print lots of information that is not necessarily + /// indicative of an issue. This output has been ignored for many years and is often not + /// actionable by developers. It is silenced unless the developer specifically requests for it + /// to be printed. See this tracking issue for more details: + /// . + pub LINKER_INFO, + Allow, + "linker warnings known to be informational-only and not indicative of a problem" +} + declare_lint! { /// The `named_arguments_used_positionally` lint detects cases where named arguments are only /// used positionally in format strings. This usage is valid but potentially very confusing. diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index af76ea183c476..d64c6313f07dd 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1591,7 +1591,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { sym::expect, ]) && let Some(meta) = attr.meta_item_list() && meta.iter().any(|meta| { - meta.meta_item().map_or(false, |item| item.path == sym::linker_messages) + meta.meta_item().map_or(false, |item| { + item.path == sym::linker_messages || item.path == sym::linker_info + }) }) { if hir_id != CRATE_HIR_ID { diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 281279abd1e1b..ebce6e70a9e42 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -318,7 +318,7 @@ pub(crate) enum UnusedNote { #[note("`default_method_body_is_const` has been replaced with `const` on traits")] DefaultMethodBodyConst, #[note( - "the `linker_messages` lint can only be controlled at the root of a crate that needs to be linked" + "the `linker_messages` and `linker_info` lints can only be controlled at the root of a crate that needs to be linked" )] LinkerMessagesBinaryCrateOnly, } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index f87189b4de42c..32ba8e3578e32 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1150,6 +1150,7 @@ symbols! { link_section, linkage, linker, + linker_info, linker_messages, linkonce, linkonce_odr, diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index c16cfbe899b7b..c6061e78fd22a 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -215,6 +215,7 @@ fn check_unexpected_extension(check: &mut RunningCheck, file_path: &Path, ext: & "fixed", // expected source file after applying fixes "md", // test directory descriptions "ftl", // translation tests + "ps1", // powershell scripts, used for fake linkers on Windows ]; const EXTENSION_EXCEPTION_PATHS: &[&str] = &[ diff --git a/tests/run-make/macos-deployment-target-warning/foo.c b/tests/run-make/macos-deployment-target-warning/foo.c new file mode 100644 index 0000000000000..85e6cd8c3909a --- /dev/null +++ b/tests/run-make/macos-deployment-target-warning/foo.c @@ -0,0 +1 @@ +void foo() {} diff --git a/tests/run-make/macos-deployment-target-warning/main.rs b/tests/run-make/macos-deployment-target-warning/main.rs new file mode 100644 index 0000000000000..2c3be92812e12 --- /dev/null +++ b/tests/run-make/macos-deployment-target-warning/main.rs @@ -0,0 +1,8 @@ +#![warn(linker_info, linker_messages)] +unsafe extern "C" { + safe fn foo(); +} + +fn main() { + foo(); +} diff --git a/tests/run-make/macos-deployment-target-warning/rmake.rs b/tests/run-make/macos-deployment-target-warning/rmake.rs new file mode 100644 index 0000000000000..e109b2adcc17a --- /dev/null +++ b/tests/run-make/macos-deployment-target-warning/rmake.rs @@ -0,0 +1,29 @@ +//@ only-apple +//! Tests that deployment target linker warnings are shown as `linker-info`, not `linker-messages` + +use run_make_support::external_deps::c_cxx_compiler::cc; +use run_make_support::external_deps::llvm::llvm_ar; +use run_make_support::{bare_rustc, diff}; + +fn main() { + let cwd = std::env::current_dir().unwrap().to_str().unwrap().to_owned(); + + cc().arg("-c").arg("-mmacosx-version-min=15.5").output("foo.o").input("foo.c").run(); + llvm_ar().obj_to_ar().output_input("libfoo.a", "foo.o").run(); + + let warnings = bare_rustc() + .arg("-L") + .arg(format!("native={cwd}")) + .arg("-lstatic=foo") + .link_arg("-mmacosx-version-min=11.2") + .input("main.rs") + .crate_type("bin") + .run() + .stderr_utf8(); + + diff() + .expected_file("warnings.txt") + .actual_text("(rustc -W linker-info)", &warnings) + .normalize(r"\(.*/rmake_out/", "(TEST_DIR/") + .run() +} diff --git a/tests/run-make/macos-deployment-target-warning/warnings.txt b/tests/run-make/macos-deployment-target-warning/warnings.txt new file mode 100644 index 0000000000000..ab08c6d9ae5b2 --- /dev/null +++ b/tests/run-make/macos-deployment-target-warning/warnings.txt @@ -0,0 +1,11 @@ +warning: ld: warning: object file (TEST_DIR/libfoo.a[2](foo.o)) was built for newer 'macOS' version (15.5) than being linked (11.2) + + | +note: the lint level is defined here + --> main.rs:1:9 + | +1 | #![warn(linker_info, linker_messages)] + | ^^^^^^^^^^^ + +warning: 1 warning emitted + diff --git a/tests/ui/linking/auxiliary/fake-linker.ps1 b/tests/ui/linking/auxiliary/fake-linker.ps1 new file mode 100755 index 0000000000000..59322c3205dae --- /dev/null +++ b/tests/ui/linking/auxiliary/fake-linker.ps1 @@ -0,0 +1,3 @@ +#!/usr/bin/env pwsh +[Console]::Error.WriteLine('Warning: .drectve `-exclude-symbols:_ZN28windows_gnu_corrupt_drective4main17h291ed884c1aada69E '' unrecognized') +[Console]::Error.WriteLine('Warning: corrupt .drectve at end of def file') diff --git a/tests/ui/linking/macos-ignoring-duplicate.rs b/tests/ui/linking/macos-ignoring-duplicate.rs new file mode 100644 index 0000000000000..0c9241c8ee195 --- /dev/null +++ b/tests/ui/linking/macos-ignoring-duplicate.rs @@ -0,0 +1,6 @@ +//@ only-apple +//@ compile-flags: -C link-arg=-lc -C link-arg=-lc +//@ build-fail +#![deny(linker_info)] +//~? ERROR ignoring duplicate libraries +fn main() {} diff --git a/tests/ui/linking/macos-ignoring-duplicate.stderr b/tests/ui/linking/macos-ignoring-duplicate.stderr new file mode 100644 index 0000000000000..9037e8f4e391b --- /dev/null +++ b/tests/ui/linking/macos-ignoring-duplicate.stderr @@ -0,0 +1,11 @@ +error: ld: warning: ignoring duplicate libraries: '-lc' + + | +note: the lint level is defined here + --> $DIR/macos-ignoring-duplicate.rs:4:9 + | +LL | #![deny(linker_info)] + | ^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/linking/macos-search-path.rs b/tests/ui/linking/macos-search-path.rs new file mode 100644 index 0000000000000..6d3e420bcdf6d --- /dev/null +++ b/tests/ui/linking/macos-search-path.rs @@ -0,0 +1,6 @@ +//@ only-apple +//@ compile-flags: -C link-arg=-Wl,-L/no/such/file/or/directory +//@ build-fail +#![deny(linker_info)] +//~? ERROR search path +fn main() {} diff --git a/tests/ui/linking/macos-search-path.stderr b/tests/ui/linking/macos-search-path.stderr new file mode 100644 index 0000000000000..598036d0d403e --- /dev/null +++ b/tests/ui/linking/macos-search-path.stderr @@ -0,0 +1,11 @@ +error: ld: warning: search path '/no/such/file/or/directory' not found + + | +note: the lint level is defined here + --> $DIR/macos-search-path.rs:4:9 + | +LL | #![deny(linker_info)] + | ^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/linking/windows-gnu-corrupt-drective.rs b/tests/ui/linking/windows-gnu-corrupt-drective.rs new file mode 100644 index 0000000000000..983bddd2187a8 --- /dev/null +++ b/tests/ui/linking/windows-gnu-corrupt-drective.rs @@ -0,0 +1,6 @@ +//@ only-windows-gnu +//@ build-fail +//@ compile-flags: -C linker={{src-base}}/linking/auxiliary/fake-linker.ps1 +#![deny(linker_info)] +//~? ERROR Warning: .drectve +fn main() {} diff --git a/tests/ui/linking/windows-gnu-corrupt-drective.stderr b/tests/ui/linking/windows-gnu-corrupt-drective.stderr new file mode 100644 index 0000000000000..472b31bd8f810 --- /dev/null +++ b/tests/ui/linking/windows-gnu-corrupt-drective.stderr @@ -0,0 +1,12 @@ +error: Warning: .drectve `-exclude-symbols:_ZN28windows_gnu_corrupt_drective4main17h291ed884c1aada69E ' unrecognized + Warning: corrupt .drectve at end of def file + + | +note: the lint level is defined here + --> $DIR/windows-gnu-corrupt-drective.rs:4:9 + | +LL | #![deny(linker_info)] + | ^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/linker-warning.stderr b/tests/ui/lint/linker-warning.stderr index ae5f6b3adece0..54994c48ea43a 100644 --- a/tests/ui/lint/linker-warning.stderr +++ b/tests/ui/lint/linker-warning.stderr @@ -20,7 +20,7 @@ warning: unused attribute LL | #![allow(linker_messages)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | - = note: the `linker_messages` lint can only be controlled at the root of a crate that needs to be linked + = note: the `linker_messages` and `linker_info` lints can only be controlled at the root of a crate that needs to be linked warning: 2 warnings emitted diff --git a/tests/ui/process/nofile-limit.rs b/tests/ui/process/nofile-limit.rs index 64777b514256e..f5246856b80f2 100644 --- a/tests/ui/process/nofile-limit.rs +++ b/tests/ui/process/nofile-limit.rs @@ -11,6 +11,9 @@ #![feature(exit_status_error)] #![feature(rustc_private)] +// on aarch64, "Using 'getaddrinfo' in statically linked applications requires at runtime the shared +// libraries from the glibc version used for linking" +#![allow(linker_messages)] extern crate libc; use std::os::unix::process::CommandExt; From f7e19368fd838f21fcbc7acc3e33284d6a5a2862 Mon Sep 17 00:00:00 2001 From: jyn Date: Fri, 12 Dec 2025 19:48:45 -0500 Subject: [PATCH 06/18] Set linker-messages to warn-by-default --- compiler/rustc_lint_defs/src/builtin.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 35cc16bc0fc73..470e1ee5fef1f 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -4064,7 +4064,7 @@ declare_lint! { /// and actionable warning of similar quality to our other diagnostics. See this tracking /// issue for more details: . pub LINKER_MESSAGES, - Allow, + Warn, "warnings emitted at runtime by the target-specific linker program" } From 8e3cb1099fe21cb10f8ce5a79e9fda780210a218 Mon Sep 17 00:00:00 2001 From: jyn Date: Tue, 17 Feb 2026 19:56:33 -0500 Subject: [PATCH 07/18] Ignore linker messages for `raw_dylib_elf` It's an incomplete feature anyway, it's ok for it to be broken. --- tests/ui/linkage-attr/raw-dylib/elf/glibc-x86_64.rs | 2 ++ tests/ui/linkage-attr/raw-dylib/elf/multiple-libraries.rs | 2 ++ tests/ui/linkage-attr/raw-dylib/elf/single-symbol.rs | 2 ++ tests/ui/linkage-attr/raw-dylib/elf/verbatim.rs | 2 ++ 4 files changed, 8 insertions(+) diff --git a/tests/ui/linkage-attr/raw-dylib/elf/glibc-x86_64.rs b/tests/ui/linkage-attr/raw-dylib/elf/glibc-x86_64.rs index 62d352facd178..c292b8c02eca2 100644 --- a/tests/ui/linkage-attr/raw-dylib/elf/glibc-x86_64.rs +++ b/tests/ui/linkage-attr/raw-dylib/elf/glibc-x86_64.rs @@ -6,6 +6,8 @@ //@ ignore-backends: gcc #![allow(incomplete_features)] +// FIXME: "type and size of dynamic symbol are not defined" with GNU LD +#![allow(linker_messages)] #![feature(raw_dylib_elf)] #![no_std] #![no_main] diff --git a/tests/ui/linkage-attr/raw-dylib/elf/multiple-libraries.rs b/tests/ui/linkage-attr/raw-dylib/elf/multiple-libraries.rs index 030b95198f545..22e5c57b9e1d2 100644 --- a/tests/ui/linkage-attr/raw-dylib/elf/multiple-libraries.rs +++ b/tests/ui/linkage-attr/raw-dylib/elf/multiple-libraries.rs @@ -17,6 +17,8 @@ #![feature(raw_dylib_elf)] #![allow(incomplete_features)] +// FIXME: "type and size of dynamic symbol are not defined" with GNU LD +#![allow(linker_messages)] #[cfg_attr(with, link(name = "rawdylibbutforcats", kind = "raw-dylib"))] #[cfg_attr(without, link(name = "rawdylibbutforcats"))] diff --git a/tests/ui/linkage-attr/raw-dylib/elf/single-symbol.rs b/tests/ui/linkage-attr/raw-dylib/elf/single-symbol.rs index 20de4afcee0bc..327bc94d91fbb 100644 --- a/tests/ui/linkage-attr/raw-dylib/elf/single-symbol.rs +++ b/tests/ui/linkage-attr/raw-dylib/elf/single-symbol.rs @@ -16,6 +16,8 @@ #![feature(raw_dylib_elf)] #![allow(incomplete_features)] +// FIXME: "type and size of dynamic symbol are not defined" with GNU LD +#![allow(linker_messages)] #[cfg_attr(with, link(name = "rawdylibbutforcats", kind = "raw-dylib"))] #[cfg_attr(without, link(name = "rawdylibbutforcats"))] diff --git a/tests/ui/linkage-attr/raw-dylib/elf/verbatim.rs b/tests/ui/linkage-attr/raw-dylib/elf/verbatim.rs index 302ec0edeaa18..12b0ec9b88985 100644 --- a/tests/ui/linkage-attr/raw-dylib/elf/verbatim.rs +++ b/tests/ui/linkage-attr/raw-dylib/elf/verbatim.rs @@ -17,6 +17,8 @@ #![feature(raw_dylib_elf)] #![allow(incomplete_features)] +// FIXME: "type and size of dynamic symbol are not defined" with GNU LD +#![allow(linker_messages)] #[cfg_attr(with, link(name = "rawdylibbutforcats", kind = "raw-dylib", modifiers = "+verbatim"))] #[cfg_attr(without, link(name = "rawdylibbutforcats", modifiers = "+verbatim"))] From 5847b63939f452f2244e5b52f0c2450109822ea5 Mon Sep 17 00:00:00 2001 From: jyn Date: Tue, 24 Feb 2026 12:45:06 -0500 Subject: [PATCH 08/18] Revert "Set linker-messages to warn-by-default" This reverts commit 2257ffcc2fa4e930088cb0138770fdf87ed667e7. Let's land all the intermediate work before we try to make this apply everywhere. --- compiler/rustc_lint_defs/src/builtin.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 470e1ee5fef1f..35cc16bc0fc73 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -4064,7 +4064,7 @@ declare_lint! { /// and actionable warning of similar quality to our other diagnostics. See this tracking /// issue for more details: . pub LINKER_MESSAGES, - Warn, + Allow, "warnings emitted at runtime by the target-specific linker program" } From 783afe9978eefe779a7135833ec525421911ff10 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Tue, 24 Feb 2026 20:29:37 +0100 Subject: [PATCH 09/18] `is_ty_must_use`: do not require a `span` argument All callers of `is_ty_must_use()`, recursive or not, pass `span` as equal to `expr.span` alongside `expr`. The `span` parameter can be safely removed. --- compiler/rustc_lint/src/unused/must_use.rs | 31 +++++++++++----------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_lint/src/unused/must_use.rs b/compiler/rustc_lint/src/unused/must_use.rs index f2d621c2ad5ed..f37cf4c8dc8c0 100644 --- a/compiler/rustc_lint/src/unused/must_use.rs +++ b/compiler/rustc_lint/src/unused/must_use.rs @@ -151,7 +151,6 @@ pub fn is_ty_must_use<'tcx>( cx: &LateContext<'tcx>, ty: Ty<'tcx>, expr: &hir::Expr<'_>, - span: Span, simplify_uninhabited: bool, ) -> IsTyMustUse { if ty.is_unit() { @@ -165,12 +164,12 @@ pub fn is_ty_must_use<'tcx>( match *ty.kind() { _ if is_uninhabited(ty) => IsTyMustUse::Trivial, ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => { - is_ty_must_use(cx, boxed, expr, span, simplify_uninhabited) + is_ty_must_use(cx, boxed, expr, simplify_uninhabited) .map(|inner| MustUsePath::Boxed(Box::new(inner))) } ty::Adt(def, args) if cx.tcx.is_lang_item(def.did(), LangItem::Pin) => { let pinned_ty = args.type_at(0); - is_ty_must_use(cx, pinned_ty, expr, span, simplify_uninhabited) + is_ty_must_use(cx, pinned_ty, expr, simplify_uninhabited) .map(|inner| MustUsePath::Pinned(Box::new(inner))) } // Consider `Result` (e.g. `Result<(), !>`) equivalent to `T`. @@ -180,7 +179,7 @@ pub fn is_ty_must_use<'tcx>( && is_uninhabited(args.type_at(1)) => { let ok_ty = args.type_at(0); - is_ty_must_use(cx, ok_ty, expr, span, simplify_uninhabited) + is_ty_must_use(cx, ok_ty, expr, simplify_uninhabited) .map(|path| MustUsePath::Result(Box::new(path))) } // Consider `ControlFlow` (e.g. `ControlFlow`) equivalent to `T`. @@ -190,7 +189,7 @@ pub fn is_ty_must_use<'tcx>( && is_uninhabited(args.type_at(0)) => { let continue_ty = args.type_at(1); - is_ty_must_use(cx, continue_ty, expr, span, simplify_uninhabited) + is_ty_must_use(cx, continue_ty, expr, simplify_uninhabited) .map(|path| MustUsePath::ControlFlow(Box::new(path))) } // Suppress warnings on `Result<(), Uninhabited>` (e.g. `Result<(), !>`). @@ -210,7 +209,7 @@ pub fn is_ty_must_use<'tcx>( IsTyMustUse::Trivial } ty::Adt(def, _) => { - is_def_must_use(cx, def.did(), span).map_or(IsTyMustUse::No, IsTyMustUse::Yes) + is_def_must_use(cx, def.did(), expr.span).map_or(IsTyMustUse::No, IsTyMustUse::Yes) } ty::Alias(ty::Opaque | ty::Projection, ty::AliasTy { def_id: def, .. }) => { elaborate(cx.tcx, cx.tcx.explicit_item_self_bounds(def).iter_identity_copied()) @@ -223,7 +222,7 @@ pub fn is_ty_must_use<'tcx>( { let def_id = poly_trait_predicate.trait_ref.def_id; - is_def_must_use(cx, def_id, span) + is_def_must_use(cx, def_id, expr.span) } else { None } @@ -236,7 +235,7 @@ pub fn is_ty_must_use<'tcx>( .find_map(|predicate| { if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() { let def_id = trait_ref.def_id; - is_def_must_use(cx, def_id, span) + is_def_must_use(cx, def_id, expr.span) .map(|inner| MustUsePath::TraitObject(Box::new(inner))) } else { None @@ -260,9 +259,7 @@ pub fn is_ty_must_use<'tcx>( .zip(elem_exprs) .enumerate() .filter_map(|(i, (ty, expr))| { - is_ty_must_use(cx, ty, expr, expr.span, simplify_uninhabited) - .yes() - .map(|path| (i, path)) + is_ty_must_use(cx, ty, expr, simplify_uninhabited).yes().map(|path| (i, path)) }) .collect::>(); @@ -276,21 +273,23 @@ pub fn is_ty_must_use<'tcx>( // If the array is empty we don't lint, to avoid false positives Some(0) | None => IsTyMustUse::No, // If the array is definitely non-empty, we can do `#[must_use]` checking. - Some(len) => is_ty_must_use(cx, ty, expr, span, simplify_uninhabited) + Some(len) => is_ty_must_use(cx, ty, expr, simplify_uninhabited) .map(|inner| MustUsePath::Array(Box::new(inner), len)), }, - ty::Closure(..) | ty::CoroutineClosure(..) => IsTyMustUse::Yes(MustUsePath::Closure(span)), + ty::Closure(..) | ty::CoroutineClosure(..) => { + IsTyMustUse::Yes(MustUsePath::Closure(expr.span)) + } ty::Coroutine(def_id, ..) => { // async fn should be treated as "implementor of `Future`" if cx.tcx.coroutine_is_async(def_id) && let Some(def_id) = cx.tcx.lang_items().future_trait() { IsTyMustUse::Yes(MustUsePath::Opaque(Box::new( - is_def_must_use(cx, def_id, span) + is_def_must_use(cx, def_id, expr.span) .expect("future trait is marked as `#[must_use]`"), ))) } else { - IsTyMustUse::Yes(MustUsePath::Coroutine(span)) + IsTyMustUse::Yes(MustUsePath::Coroutine(expr.span)) } } _ => IsTyMustUse::No, @@ -339,7 +338,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { let ty = cx.typeck_results().expr_ty(expr); - let must_use_result = is_ty_must_use(cx, ty, expr, expr.span, false); + let must_use_result = is_ty_must_use(cx, ty, expr, false); let type_lint_emitted_or_trivial = match must_use_result { IsTyMustUse::Yes(path) => { emit_must_use_untranslated(cx, &path, "", "", 1, false, expr_is_from_block); From f4407f241d8770ba88b6c8052fff71d0201d2b6d Mon Sep 17 00:00:00 2001 From: rustbot <47979223+rustbot@users.noreply.github.com> Date: Wed, 25 Feb 2026 03:59:51 +0100 Subject: [PATCH 10/18] Update books --- src/doc/reference | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/reference b/src/doc/reference index 442cbef910566..50a1075e879be 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 442cbef9105662887d5eae2882ca551f3726bf28 +Subproject commit 50a1075e879be75aeec436252c84eef0fad489f4 From d13924caa729c14131ee4dd88f35c080095e1b05 Mon Sep 17 00:00:00 2001 From: Redddy Date: Wed, 25 Feb 2026 03:16:17 +0000 Subject: [PATCH 11/18] mGCA: Reject negated non-integer literals in const args --- compiler/rustc_ast_lowering/src/lib.rs | 21 ++++---- .../mgca/array-expr-complex.stderr | 8 ++-- .../mgca/explicit_anon_consts.stderr | 16 +++---- .../mgca/nonsensical-negated-literal.rs | 17 ++++--- .../mgca/nonsensical-negated-literal.stderr | 48 +++++++++++-------- 5 files changed, 61 insertions(+), 49 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 24a7215ddb385..ee21f0df5e217 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -2521,16 +2521,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ExprKind::Block(block, _) => { if let [stmt] = block.stmts.as_slice() && let StmtKind::Expr(expr) = &stmt.kind - && matches!( - expr.kind, - ExprKind::Block(..) - | ExprKind::Path(..) - | ExprKind::Struct(..) - | ExprKind::Call(..) - | ExprKind::Tup(..) - | ExprKind::Array(..) - | ExprKind::ConstBlock(..) - ) { return self.lower_expr_to_const_arg_direct(expr); } @@ -2553,6 +2543,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let span = expr.span; let literal = self.lower_lit(literal, span); + if !matches!(literal.node, LitKind::Int(..)) { + let err = + self.dcx().struct_span_err(expr.span, "negated literal must be an integer"); + + return ConstArg { + hir_id: self.next_id(), + kind: hir::ConstArgKind::Error(err.emit()), + span, + }; + } + ConstArg { hir_id: self.lower_node_id(expr.id), kind: hir::ConstArgKind::Literal { lit: literal.node, negated: true }, diff --git a/tests/ui/const-generics/mgca/array-expr-complex.stderr b/tests/ui/const-generics/mgca/array-expr-complex.stderr index beac1aa232f2f..544b0f05b4e44 100644 --- a/tests/ui/const-generics/mgca/array-expr-complex.stderr +++ b/tests/ui/const-generics/mgca/array-expr-complex.stderr @@ -5,16 +5,16 @@ LL | takes_array::<{ [1, 2, 1 + 2] }>(); | ^^^^^ error: complex const arguments must be placed inside of a `const` block - --> $DIR/array-expr-complex.rs:10:19 + --> $DIR/array-expr-complex.rs:10:21 | LL | takes_array::<{ [X; 3] }>(); - | ^^^^^^^^^^ + | ^^^^^^ error: complex const arguments must be placed inside of a `const` block - --> $DIR/array-expr-complex.rs:12:19 + --> $DIR/array-expr-complex.rs:12:21 | LL | takes_array::<{ [0; Y] }>(); - | ^^^^^^^^^^ + | ^^^^^^ error: aborting due to 3 previous errors diff --git a/tests/ui/const-generics/mgca/explicit_anon_consts.stderr b/tests/ui/const-generics/mgca/explicit_anon_consts.stderr index 714d7a804d111..1c72afce52c7d 100644 --- a/tests/ui/const-generics/mgca/explicit_anon_consts.stderr +++ b/tests/ui/const-generics/mgca/explicit_anon_consts.stderr @@ -1,8 +1,8 @@ error: complex const arguments must be placed inside of a `const` block - --> $DIR/explicit_anon_consts.rs:13:33 + --> $DIR/explicit_anon_consts.rs:13:35 | LL | type Adt4 = Foo<{ 1 + 1 }>; - | ^^^^^^^^^ + | ^^^^^ error: complex const arguments must be placed inside of a `const` block --> $DIR/explicit_anon_consts.rs:21:34 @@ -17,22 +17,22 @@ LL | let _4 = [(); 1 + 1]; | ^^^^^ error: complex const arguments must be placed inside of a `const` block - --> $DIR/explicit_anon_consts.rs:45:43 + --> $DIR/explicit_anon_consts.rs:45:45 | LL | type const ITEM4: usize = { 1 + 1 }; - | ^^^^^^^^^ + | ^^^^^ error: complex const arguments must be placed inside of a `const` block - --> $DIR/explicit_anon_consts.rs:62:23 + --> $DIR/explicit_anon_consts.rs:62:25 | LL | T4: Trait, - | ^^^^^^^^^ + | ^^^^^ error: complex const arguments must be placed inside of a `const` block - --> $DIR/explicit_anon_consts.rs:71:50 + --> $DIR/explicit_anon_consts.rs:71:52 | LL | struct Default4; - | ^^^^^^^^^ + | ^^^^^ error: generic parameters may not be used in const operations --> $DIR/explicit_anon_consts.rs:42:51 diff --git a/tests/ui/const-generics/mgca/nonsensical-negated-literal.rs b/tests/ui/const-generics/mgca/nonsensical-negated-literal.rs index cd68a2c0d4301..4a3efb801cb01 100644 --- a/tests/ui/const-generics/mgca/nonsensical-negated-literal.rs +++ b/tests/ui/const-generics/mgca/nonsensical-negated-literal.rs @@ -1,4 +1,4 @@ -#![feature(adt_const_params, min_generic_const_args)] +#![feature(adt_const_params, min_generic_const_args, unsized_const_params)] #![expect(incomplete_features)] use std::marker::ConstParamTy; @@ -10,17 +10,22 @@ struct Foo { fn foo() {} +fn bar() {} + fn main() { foo::<{ Foo { field: -1_usize } }>(); //~^ ERROR: type annotations needed for the literal foo::<{ Foo { field: { -1_usize } } }>(); - //~^ ERROR: complex const arguments must be placed inside of a `const` block + //~^ ERROR: type annotations needed for the literal foo::<{ Foo { field: -true } }>(); - //~^ ERROR: the constant `true` is not of type `isize` + //~^ ERROR negated literal must be an integer foo::<{ Foo { field: { -true } } }>(); - //~^ ERROR: complex const arguments must be placed inside of a `const` block + //~^ ERROR negated literal must be an integer foo::<{ Foo { field: -"<3" } }>(); - //~^ ERROR: the constant `"<3"` is not of type `isize` + //~^ ERROR negated literal must be an integer foo::<{ Foo { field: { -"<3" } } }>(); - //~^ ERROR: complex const arguments must be placed inside of a `const` block + //~^ ERROR negated literal must be an integer + + bar::<{ -"hi" }>(); + //~^ ERROR: negated literal must be an integer } diff --git a/tests/ui/const-generics/mgca/nonsensical-negated-literal.stderr b/tests/ui/const-generics/mgca/nonsensical-negated-literal.stderr index 43ed4b71e33e7..60c559d1d5ba9 100644 --- a/tests/ui/const-generics/mgca/nonsensical-negated-literal.stderr +++ b/tests/ui/const-generics/mgca/nonsensical-negated-literal.stderr @@ -1,38 +1,44 @@ -error: complex const arguments must be placed inside of a `const` block - --> $DIR/nonsensical-negated-literal.rs:16:26 +error: negated literal must be an integer + --> $DIR/nonsensical-negated-literal.rs:20:26 | -LL | foo::<{ Foo { field: { -1_usize } } }>(); - | ^^^^^^^^^^^^ +LL | foo::<{ Foo { field: -true } }>(); + | ^^^^^ -error: complex const arguments must be placed inside of a `const` block - --> $DIR/nonsensical-negated-literal.rs:20:26 +error: negated literal must be an integer + --> $DIR/nonsensical-negated-literal.rs:22:28 | LL | foo::<{ Foo { field: { -true } } }>(); - | ^^^^^^^^^ + | ^^^^^ -error: complex const arguments must be placed inside of a `const` block +error: negated literal must be an integer --> $DIR/nonsensical-negated-literal.rs:24:26 | +LL | foo::<{ Foo { field: -"<3" } }>(); + | ^^^^^ + +error: negated literal must be an integer + --> $DIR/nonsensical-negated-literal.rs:26:28 + | LL | foo::<{ Foo { field: { -"<3" } } }>(); - | ^^^^^^^^^ + | ^^^^^ + +error: negated literal must be an integer + --> $DIR/nonsensical-negated-literal.rs:29:13 + | +LL | bar::<{ -"hi" }>(); + | ^^^^^ error: type annotations needed for the literal - --> $DIR/nonsensical-negated-literal.rs:14:26 + --> $DIR/nonsensical-negated-literal.rs:16:26 | LL | foo::<{ Foo { field: -1_usize } }>(); | ^^^^^^^^ -error: the constant `true` is not of type `isize` - --> $DIR/nonsensical-negated-literal.rs:18:13 - | -LL | foo::<{ Foo { field: -true } }>(); - | ^^^^^^^^^^^^^^^^^^^^ expected `isize`, found `bool` - -error: the constant `"<3"` is not of type `isize` - --> $DIR/nonsensical-negated-literal.rs:22:13 +error: type annotations needed for the literal + --> $DIR/nonsensical-negated-literal.rs:18:28 | -LL | foo::<{ Foo { field: -"<3" } }>(); - | ^^^^^^^^^^^^^^^^^^^^ expected `isize`, found `&'static str` +LL | foo::<{ Foo { field: { -1_usize } } }>(); + | ^^^^^^^^ -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors From 51271084000fc9bcf1dc39be1c528767da6aa95c Mon Sep 17 00:00:00 2001 From: Redddy Date: Wed, 25 Feb 2026 04:05:12 +0000 Subject: [PATCH 12/18] mGCA: drop literal anon-const special case in parser --- compiler/rustc_parse/src/parser/path.rs | 22 ++++-------------- ...const-projections-in-assoc-const-ty.stderr | 21 ++++------------- ...array-expr-type-mismatch-in-where-bound.rs | 2 +- ...y-expr-type-mismatch-in-where-bound.stderr | 5 ++-- .../type_const-inherent-const-omitted-type.rs | 1 + ...e_const-inherent-const-omitted-type.stderr | 8 ++++++- .../mgca/type_const-mismatched-types.rs | 3 +-- .../mgca/type_const-mismatched-types.stderr | 23 ++++--------------- .../type_const-only-in-impl-omitted-type.rs | 1 + ...ype_const-only-in-impl-omitted-type.stderr | 10 ++++++-- 10 files changed, 34 insertions(+), 62 deletions(-) diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 9196d8d156d8e..f9a882c371d59 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -919,24 +919,10 @@ impl<'a> Parser<'a> { Ok((expr, mgca_disambiguation)) } - /// Under `min_generic_const_args` we still allow *some* anon consts to be written without - /// a `const` block as it makes things quite a lot nicer. This function is useful for contexts - /// where we would like to use `MgcaDisambiguation::Direct` but need to fudge it to be `AnonConst` - /// in the presence of literals. - // - /// FIXME(min_generic_const_args): In the long term it would be nice to have a way to directly - /// represent literals in `hir::ConstArgKind` so that we can remove this special case by not - /// needing an anon const. - pub fn mgca_direct_lit_hack(&self, expr: &Expr) -> MgcaDisambiguation { - match &expr.kind { - ast::ExprKind::Lit(_) => MgcaDisambiguation::AnonConst, - ast::ExprKind::Unary(ast::UnOp::Neg, expr) - if matches!(expr.kind, ast::ExprKind::Lit(_)) => - { - MgcaDisambiguation::AnonConst - } - _ => MgcaDisambiguation::Direct, - } + /// Under `min_generic_const_args`, prefer direct const arguments rather than + /// wrapping literals in anon consts. + pub fn mgca_direct_lit_hack(&self, _expr: &Expr) -> MgcaDisambiguation { + MgcaDisambiguation::Direct } /// Parse a generic argument in a path segment. diff --git a/tests/ui/const-generics/associated-const-bindings/dyn-compat-self-const-projections-in-assoc-const-ty.stderr b/tests/ui/const-generics/associated-const-bindings/dyn-compat-self-const-projections-in-assoc-const-ty.stderr index cc08c25906b66..7443c2977d54d 100644 --- a/tests/ui/const-generics/associated-const-bindings/dyn-compat-self-const-projections-in-assoc-const-ty.stderr +++ b/tests/ui/const-generics/associated-const-bindings/dyn-compat-self-const-projections-in-assoc-const-ty.stderr @@ -1,27 +1,14 @@ -error[E0277]: the trait bound `FreshTy(0): A` is not satisfied +error: type annotations needed for the literal --> $DIR/dyn-compat-self-const-projections-in-assoc-const-ty.rs:32:33 | LL | let _: dyn A; - | ^ the trait `A` is not implemented for `FreshTy(0)` - | -help: the trait `A` is implemented for `()` - --> $DIR/dyn-compat-self-const-projections-in-assoc-const-ty.rs:20:1 - | -LL | impl A for () { - | ^^^^^^^^^^^^^ + | ^ -error[E0277]: the trait bound `FreshTy(0): A` is not satisfied +error: type annotations needed for the literal --> $DIR/dyn-compat-self-const-projections-in-assoc-const-ty.rs:34:34 | LL | let _: &dyn A = &(); - | ^ the trait `A` is not implemented for `FreshTy(0)` - | -help: the trait `A` is implemented for `()` - --> $DIR/dyn-compat-self-const-projections-in-assoc-const-ty.rs:20:1 - | -LL | impl A for () { - | ^^^^^^^^^^^^^ + | ^ error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/const-generics/mgca/array-expr-type-mismatch-in-where-bound.rs b/tests/ui/const-generics/mgca/array-expr-type-mismatch-in-where-bound.rs index cda519b96d4d8..98feccfc99c74 100644 --- a/tests/ui/const-generics/mgca/array-expr-type-mismatch-in-where-bound.rs +++ b/tests/ui/const-generics/mgca/array-expr-type-mismatch-in-where-bound.rs @@ -14,7 +14,7 @@ where fn bar() where - T: Trait2<3>, //~ ERROR: mismatched types + T: Trait2<3>, //~ ERROR: type annotations needed for the literal { } diff --git a/tests/ui/const-generics/mgca/array-expr-type-mismatch-in-where-bound.stderr b/tests/ui/const-generics/mgca/array-expr-type-mismatch-in-where-bound.stderr index be40e44742267..169282547fc1d 100644 --- a/tests/ui/const-generics/mgca/array-expr-type-mismatch-in-where-bound.stderr +++ b/tests/ui/const-generics/mgca/array-expr-type-mismatch-in-where-bound.stderr @@ -4,12 +4,11 @@ error: expected `usize`, found const array LL | T: Trait1<{ [] }>, | ^^ -error[E0308]: mismatched types +error: type annotations needed for the literal --> $DIR/array-expr-type-mismatch-in-where-bound.rs:17:15 | LL | T: Trait2<3>, - | ^ expected `[u8; 3]`, found integer + | ^ error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/const-generics/mgca/type_const-inherent-const-omitted-type.rs b/tests/ui/const-generics/mgca/type_const-inherent-const-omitted-type.rs index 3262e79478bdb..c57121a4a26a0 100644 --- a/tests/ui/const-generics/mgca/type_const-inherent-const-omitted-type.rs +++ b/tests/ui/const-generics/mgca/type_const-inherent-const-omitted-type.rs @@ -6,6 +6,7 @@ struct A; impl A { type const B = 4; //~^ ERROR: missing type for `const` item + //~| ERROR: type annotations needed for the literal } fn main() {} diff --git a/tests/ui/const-generics/mgca/type_const-inherent-const-omitted-type.stderr b/tests/ui/const-generics/mgca/type_const-inherent-const-omitted-type.stderr index 77e54ab2f2e93..7fbb7461a4911 100644 --- a/tests/ui/const-generics/mgca/type_const-inherent-const-omitted-type.stderr +++ b/tests/ui/const-generics/mgca/type_const-inherent-const-omitted-type.stderr @@ -9,5 +9,11 @@ help: provide a type for the item LL | type const B: = 4; | ++++++++ -error: aborting due to 1 previous error +error: type annotations needed for the literal + --> $DIR/type_const-inherent-const-omitted-type.rs:7:20 + | +LL | type const B = 4; + | ^ + +error: aborting due to 2 previous errors diff --git a/tests/ui/const-generics/mgca/type_const-mismatched-types.rs b/tests/ui/const-generics/mgca/type_const-mismatched-types.rs index c73785f9a3e38..b17a5b2b978a6 100644 --- a/tests/ui/const-generics/mgca/type_const-mismatched-types.rs +++ b/tests/ui/const-generics/mgca/type_const-mismatched-types.rs @@ -3,7 +3,6 @@ type const FREE: u32 = 5_usize; //~^ ERROR the constant `5` is not of type `u32` -//~| ERROR mismatched types type const FREE2: isize = FREE; //~^ ERROR the constant `5` is not of type `isize` @@ -14,7 +13,7 @@ trait Tr { impl Tr for () { type const N: usize = false; - //~^ ERROR mismatched types + //~^ ERROR the constant `false` is not of type `usize` } fn main() {} diff --git a/tests/ui/const-generics/mgca/type_const-mismatched-types.stderr b/tests/ui/const-generics/mgca/type_const-mismatched-types.stderr index f7f64c535f602..e2c916cf6d05a 100644 --- a/tests/ui/const-generics/mgca/type_const-mismatched-types.stderr +++ b/tests/ui/const-generics/mgca/type_const-mismatched-types.stderr @@ -5,29 +5,16 @@ LL | type const FREE: u32 = 5_usize; | ^^^^^^^^^^^^^^^^^^^^ expected `u32`, found `usize` error: the constant `5` is not of type `isize` - --> $DIR/type_const-mismatched-types.rs:8:1 + --> $DIR/type_const-mismatched-types.rs:7:1 | LL | type const FREE2: isize = FREE; | ^^^^^^^^^^^^^^^^^^^^^^^ expected `isize`, found `usize` -error[E0308]: mismatched types - --> $DIR/type_const-mismatched-types.rs:16:27 +error: the constant `false` is not of type `usize` + --> $DIR/type_const-mismatched-types.rs:15:5 | LL | type const N: usize = false; - | ^^^^^ expected `usize`, found `bool` + | ^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool` -error[E0308]: mismatched types - --> $DIR/type_const-mismatched-types.rs:4:24 - | -LL | type const FREE: u32 = 5_usize; - | ^^^^^^^ expected `u32`, found `usize` - | -help: change the type of the numeric literal from `usize` to `u32` - | -LL - type const FREE: u32 = 5_usize; -LL + type const FREE: u32 = 5_u32; - | - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/const-generics/mgca/type_const-only-in-impl-omitted-type.rs b/tests/ui/const-generics/mgca/type_const-only-in-impl-omitted-type.rs index a7ff9f0ce03f7..c8c7788eb135a 100644 --- a/tests/ui/const-generics/mgca/type_const-only-in-impl-omitted-type.rs +++ b/tests/ui/const-generics/mgca/type_const-only-in-impl-omitted-type.rs @@ -10,6 +10,7 @@ struct GoodS; impl BadTr for GoodS { type const NUM: = 84; //~^ ERROR: missing type for `const` item + //~| ERROR: type annotations needed for the literal } diff --git a/tests/ui/const-generics/mgca/type_const-only-in-impl-omitted-type.stderr b/tests/ui/const-generics/mgca/type_const-only-in-impl-omitted-type.stderr index 11a60246f6a68..99dc6398170db 100644 --- a/tests/ui/const-generics/mgca/type_const-only-in-impl-omitted-type.stderr +++ b/tests/ui/const-generics/mgca/type_const-only-in-impl-omitted-type.stderr @@ -5,12 +5,18 @@ LL | type const NUM: = 84; | ^ help: provide a type for the associated constant: `usize` error: use of trait associated const not defined as `type const` - --> $DIR/type_const-only-in-impl-omitted-type.rs:16:43 + --> $DIR/type_const-only-in-impl-omitted-type.rs:17:43 | LL | fn accept_bad_tr>(_x: &T) {} | ^^^^^^^^^^^ | = note: the declaration in the trait must begin with `type const` not just `const` alone -error: aborting due to 2 previous errors +error: type annotations needed for the literal + --> $DIR/type_const-only-in-impl-omitted-type.rs:11:23 + | +LL | type const NUM: = 84; + | ^^ + +error: aborting due to 3 previous errors From 8eed8bd7bbf80ad06fc3262824aa39dd335eaa36 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 24 Feb 2026 08:42:04 +1100 Subject: [PATCH 13/18] Rename `pass_by_value` lint as `disallowed_pass_by_ref`. The name `pass_by_value` is completely wrong. The lint actually checks for the use of pass by reference for types marked with `rustc_pass_by_value`. The hardest part of this was choosing the new name. The `disallowed_` part of the name closely matches the following clippy lints: - `disallowed_macros` - `disallowed_methods` - `disallowed_names` - `disallowed_script_idents` - `disallowed_types` The `pass_by_value` part of the name aligns with the following clippy lints: - `needless_pass_by_value` - `needless_pass_by_ref_mut` - `trivially_copy_pass_by_ref` - `large_types_passed_by_value` (less so) --- compiler/rustc_ast/src/visit.rs | 6 +++-- ..._by_value.rs => disallowed_pass_by_ref.rs} | 24 +++++++++---------- compiler/rustc_lint/src/lib.rs | 10 ++++---- compiler/rustc_lint/src/lints.rs | 4 ++-- compiler/rustc_macros/src/query.rs | 8 +++---- compiler/rustc_mir_transform/src/gvn.rs | 3 ++- .../internal-lints/rustc_pass_by_value.rs | 4 ++-- .../internal-lints/rustc_pass_by_value.stderr | 4 ++-- .../rustc_pass_by_value_self.rs | 2 +- .../rustc_pass_by_value_self.stderr | 4 ++-- 10 files changed, 35 insertions(+), 34 deletions(-) rename compiler/rustc_lint/src/{pass_by_value.rs => disallowed_pass_by_ref.rs} (75%) diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 8556e8288670f..ff389607457d4 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -757,9 +757,11 @@ macro_rules! common_visitor_and_walkers { ) -> V::Result; } - // this is only used by the MutVisitor. We include this symmetry here to make writing other functions easier + // This is only used by the MutVisitor. We include this symmetry here to make writing other + // functions easier. $(${ignore($lt)} - #[expect(unused, rustc::pass_by_value)] + #[cfg_attr(not(bootstrap), expect(unused, rustc::disallowed_pass_by_ref))] + #[cfg_attr(bootstrap, expect(unused, rustc::pass_by_value))] #[inline] )? fn visit_span<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, span: &$($lt)? $($mut)? Span) -> V::Result { diff --git a/compiler/rustc_lint/src/pass_by_value.rs b/compiler/rustc_lint/src/disallowed_pass_by_ref.rs similarity index 75% rename from compiler/rustc_lint/src/pass_by_value.rs rename to compiler/rustc_lint/src/disallowed_pass_by_ref.rs index 5a4eb29433815..2a572a5a76bdb 100644 --- a/compiler/rustc_lint/src/pass_by_value.rs +++ b/compiler/rustc_lint/src/disallowed_pass_by_ref.rs @@ -3,34 +3,34 @@ use rustc_hir::{self as hir, AmbigArg, GenericArg, PathSegment, QPath, TyKind, f use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::lints::PassByValueDiag; +use crate::lints::DisallowedPassByRefDiag; use crate::{LateContext, LateLintPass, LintContext}; declare_tool_lint! { - /// The `rustc_pass_by_value` lint marks a type with `#[rustc_pass_by_value]` requiring it to - /// always be passed by value. This is usually used for types that are thin wrappers around - /// references, so there is no benefit to an extra layer of indirection. (Example: `Ty` which - /// is a reference to an `Interned`) - pub rustc::PASS_BY_VALUE, + /// The `disallowed_pass_by_ref` lint detects if types marked with `#[rustc_pass_by_value]` are + /// passed by reference. Types with this marker are usually thin wrappers around references, so + /// there is no benefit to an extra layer of indirection. (Example: `Ty` which is a reference + /// to an `Interned`) + pub rustc::DISALLOWED_PASS_BY_REF, Warn, "pass by reference of a type flagged as `#[rustc_pass_by_value]`", report_in_external_macro: true } -declare_lint_pass!(PassByValue => [PASS_BY_VALUE]); +declare_lint_pass!(DisallowedPassByRef => [DISALLOWED_PASS_BY_REF]); -impl<'tcx> LateLintPass<'tcx> for PassByValue { +impl<'tcx> LateLintPass<'tcx> for DisallowedPassByRef { fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx, AmbigArg>) { match &ty.kind { TyKind::Ref(_, hir::MutTy { ty: inner_ty, mutbl: hir::Mutability::Not }) => { if cx.tcx.trait_impl_of_assoc(ty.hir_id.owner.to_def_id()).is_some() { return; } - if let Some(t) = path_for_pass_by_value(cx, inner_ty) { + if let Some(t) = path_for_rustc_pass_by_value(cx, inner_ty) { cx.emit_span_lint( - PASS_BY_VALUE, + DISALLOWED_PASS_BY_REF, ty.span, - PassByValueDiag { ty: t, suggestion: ty.span }, + DisallowedPassByRefDiag { ty: t, suggestion: ty.span }, ); } } @@ -39,7 +39,7 @@ impl<'tcx> LateLintPass<'tcx> for PassByValue { } } -fn path_for_pass_by_value(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> Option { +fn path_for_rustc_pass_by_value(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> Option { if let TyKind::Path(QPath::Resolved(_, path)) = &ty.kind { match path.res { Res::Def(_, def_id) if find_attr!(cx.tcx, def_id, RustcPassByValue(_)) => { diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index a5c3a889826c7..4c847671a070d 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -37,6 +37,7 @@ mod context; mod dangling; mod default_could_be_derived; mod deref_into_dyn_supertrait; +mod disallowed_pass_by_ref; mod drop_forget_useless; mod early; mod enum_intrinsics_non_enums; @@ -65,7 +66,6 @@ mod non_local_def; mod nonstandard_style; mod noop_method_call; mod opaque_hidden_inferred_bound; -mod pass_by_value; mod passes; mod precedence; mod ptr_nulls; @@ -88,6 +88,7 @@ use builtin::*; use dangling::*; use default_could_be_derived::DefaultCouldBeDerived; use deref_into_dyn_supertrait::*; +use disallowed_pass_by_ref::*; use drop_forget_useless::*; use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums; use for_loops_over_fallibles::*; @@ -109,7 +110,6 @@ use non_local_def::*; use nonstandard_style::*; use noop_method_call::*; use opaque_hidden_inferred_bound::*; -use pass_by_value::*; use precedence::*; use ptr_nulls::*; use redundant_semicolon::*; @@ -657,8 +657,8 @@ fn register_internals(store: &mut LintStore) { store.register_late_mod_pass(|_| Box::new(TypeIr)); store.register_lints(&BadOptAccess::lint_vec()); store.register_late_mod_pass(|_| Box::new(BadOptAccess)); - store.register_lints(&PassByValue::lint_vec()); - store.register_late_mod_pass(|_| Box::new(PassByValue)); + store.register_lints(&DisallowedPassByRef::lint_vec()); + store.register_late_mod_pass(|_| Box::new(DisallowedPassByRef)); store.register_lints(&SpanUseEqCtxt::lint_vec()); store.register_late_mod_pass(|_| Box::new(SpanUseEqCtxt)); store.register_lints(&SymbolInternStringLiteral::lint_vec()); @@ -676,7 +676,7 @@ fn register_internals(store: &mut LintStore) { LintId::of(POTENTIAL_QUERY_INSTABILITY), LintId::of(UNTRACKED_QUERY_INFORMATION), LintId::of(USAGE_OF_TY_TYKIND), - LintId::of(PASS_BY_VALUE), + LintId::of(DISALLOWED_PASS_BY_REF), LintId::of(LINT_PASS_IMPL_WITHOUT_MACRO), LintId::of(USAGE_OF_QUALIFIED_TY), LintId::of(NON_GLOB_IMPORT_OF_TYPE_IR_INHERENT), diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 5627f34f82e97..b2ee01a9a0ee6 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -1806,10 +1806,10 @@ pub(crate) struct AmbiguousNegativeLiteralsCurrentBehaviorSuggestion { pub end_span: Span, } -// pass_by_value.rs +// disallowed_pass_by_ref.rs #[derive(LintDiagnostic)] #[diag("passing `{$ty}` by reference")] -pub(crate) struct PassByValueDiag { +pub(crate) struct DisallowedPassByRefDiag { pub ty: String, #[suggestion("try passing by value", code = "{ty}", applicability = "maybe-incorrect")] pub suggestion: Span, diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index 346604a46ef7d..9799d5b430868 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -293,12 +293,10 @@ fn make_helpers_for_query(query: &Query, streams: &mut HelperTokenStreams) { // Generate a function to check whether we should cache the query to disk, for some key. if let Some(CacheOnDiskIf { block, .. }) = modifiers.cache_on_disk_if.as_ref() { - // `pass_by_value`: some keys are marked with `rustc_pass_by_value`, but we take keys by - // reference here. - // FIXME: `pass_by_value` is badly named; `allow(rustc::pass_by_value)` actually means - // "allow pass by reference of `rustc_pass_by_value` types". + // `disallowed_pass_by_ref` is needed because some keys are `rustc_pass_by_value`. streams.cache_on_disk_if_fns_stream.extend(quote! { - #[allow(unused_variables, rustc::pass_by_value)] + #[cfg_attr(not(bootstrap), allow(unused_variables, rustc::disallowed_pass_by_ref))] + #[cfg_attr(bootstrap, allow(unused_variables, rustc::pass_by_value))] #[inline] pub fn #erased_name<'tcx>(tcx: TyCtxt<'tcx>, #key_pat: &#key_ty) -> bool #block diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 4e38b9dd6534b..0ef9a1348527d 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -300,7 +300,8 @@ impl<'a, 'tcx> ValueSet<'a, 'tcx> { /// Insert a `(Value, Ty)` pair to be deduplicated. /// Returns `true` as second tuple field if this value did not exist previously. - #[allow(rustc::pass_by_value)] // closures take `&VnIndex` + #[cfg_attr(not(bootstrap), allow(rustc::disallowed_pass_by_ref))] // closures take `&VnIndex` + #[cfg_attr(bootstrap, allow(rustc::pass_by_value))] fn insert(&mut self, ty: Ty<'tcx>, value: Value<'a, 'tcx>) -> (VnIndex, bool) { debug_assert!(match value { Value::Opaque(_) | Value::Address { .. } => false, diff --git a/tests/ui-fulldeps/internal-lints/rustc_pass_by_value.rs b/tests/ui-fulldeps/internal-lints/rustc_pass_by_value.rs index 06d2232be5179..42d75be966840 100644 --- a/tests/ui-fulldeps/internal-lints/rustc_pass_by_value.rs +++ b/tests/ui-fulldeps/internal-lints/rustc_pass_by_value.rs @@ -1,8 +1,8 @@ //@ compile-flags: -Z unstable-options - +//@ ignore-stage1 (this can be removed when nightly goes to 1.96) #![feature(rustc_attrs)] #![feature(rustc_private)] -#![deny(rustc::pass_by_value)] +#![deny(rustc::disallowed_pass_by_ref)] #![allow(unused)] extern crate rustc_middle; diff --git a/tests/ui-fulldeps/internal-lints/rustc_pass_by_value.stderr b/tests/ui-fulldeps/internal-lints/rustc_pass_by_value.stderr index 69cf20656d7b1..b2906ea1e1195 100644 --- a/tests/ui-fulldeps/internal-lints/rustc_pass_by_value.stderr +++ b/tests/ui-fulldeps/internal-lints/rustc_pass_by_value.stderr @@ -7,8 +7,8 @@ LL | ty_ref: &Ty<'_>, note: the lint level is defined here --> $DIR/rustc_pass_by_value.rs:5:9 | -LL | #![deny(rustc::pass_by_value)] - | ^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(rustc::disallowed_pass_by_ref)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: passing `TyCtxt<'_>` by reference --> $DIR/rustc_pass_by_value.rs:16:18 diff --git a/tests/ui/internal-lints/rustc_pass_by_value_self.rs b/tests/ui/internal-lints/rustc_pass_by_value_self.rs index d2e0e272025f6..695c617d32f82 100644 --- a/tests/ui/internal-lints/rustc_pass_by_value_self.rs +++ b/tests/ui/internal-lints/rustc_pass_by_value_self.rs @@ -5,7 +5,7 @@ // Considering that all other `internal-lints` are tested here // this seems like the cleaner solution though. #![feature(rustc_attrs)] -#![deny(rustc::pass_by_value)] +#![deny(rustc::disallowed_pass_by_ref)] #![allow(unused)] #[rustc_pass_by_value] diff --git a/tests/ui/internal-lints/rustc_pass_by_value_self.stderr b/tests/ui/internal-lints/rustc_pass_by_value_self.stderr index fb39ed60b8235..d9e9f7e48506b 100644 --- a/tests/ui/internal-lints/rustc_pass_by_value_self.stderr +++ b/tests/ui/internal-lints/rustc_pass_by_value_self.stderr @@ -7,8 +7,8 @@ LL | fn by_ref(&self) {} note: the lint level is defined here --> $DIR/rustc_pass_by_value_self.rs:8:9 | -LL | #![deny(rustc::pass_by_value)] - | ^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(rustc::disallowed_pass_by_ref)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: passing `Ty<'tcx>` by reference --> $DIR/rustc_pass_by_value_self.rs:30:21 From d2619b5dcfdcef4ad55d73cbb21ed2d031c128cd Mon Sep 17 00:00:00 2001 From: Redddy Date: Wed, 25 Feb 2026 04:19:38 +0000 Subject: [PATCH 14/18] Remove mgca_direct_lit_hack indirection --- compiler/rustc_parse/src/parser/expr.rs | 3 +-- compiler/rustc_parse/src/parser/item.rs | 4 +--- compiler/rustc_parse/src/parser/path.rs | 9 +-------- compiler/rustc_parse/src/parser/ty.rs | 3 +-- 4 files changed, 4 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 52dcade91aeaf..44e8f13dd2364 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1628,8 +1628,7 @@ impl<'a> Parser<'a> { let first_expr = self.parse_expr()?; if self.eat(exp!(Semi)) { // Repeating array syntax: `[ 0; 512 ]` - let count = - self.parse_expr_anon_const(|this, expr| this.mgca_direct_lit_hack(expr))?; + let count = self.parse_expr_anon_const(|_, _| MgcaDisambiguation::Direct)?; self.expect(close)?; ExprKind::Repeat(first_expr, count) } else if self.eat(exp!(Comma)) { diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 69610d062919d..2ca4fac7fc46b 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1587,9 +1587,7 @@ impl<'a> Parser<'a> { let rhs = match (self.eat(exp!(Eq)), const_arg) { (true, true) => ConstItemRhsKind::TypeConst { - rhs: Some( - self.parse_expr_anon_const(|this, expr| this.mgca_direct_lit_hack(expr))?, - ), + rhs: Some(self.parse_expr_anon_const(|_, _| MgcaDisambiguation::Direct)?), }, (true, false) => ConstItemRhsKind::Body { rhs: Some(self.parse_expr()?) }, (false, true) => ConstItemRhsKind::TypeConst { rhs: None }, diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index f9a882c371d59..e514a08c8fb3a 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -915,14 +915,7 @@ impl<'a> Parser<'a> { }); } - let mgca_disambiguation = self.mgca_direct_lit_hack(&expr); - Ok((expr, mgca_disambiguation)) - } - - /// Under `min_generic_const_args`, prefer direct const arguments rather than - /// wrapping literals in anon consts. - pub fn mgca_direct_lit_hack(&self, _expr: &Expr) -> MgcaDisambiguation { - MgcaDisambiguation::Direct + Ok((expr, MgcaDisambiguation::Direct)) } /// Parse a generic argument in a path segment. diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 6ff165eb22b71..40335ef1b1bc7 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -658,8 +658,7 @@ impl<'a> Parser<'a> { }; let ty = if self.eat(exp!(Semi)) { - let mut length = - self.parse_expr_anon_const(|this, expr| this.mgca_direct_lit_hack(expr))?; + let mut length = self.parse_expr_anon_const(|_, _| MgcaDisambiguation::Direct)?; if let Err(e) = self.expect(exp!(CloseBracket)) { // Try to recover from `X` when `X::` works From b76b26db3b1388564044339909bfc153b95abf38 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 25 Feb 2026 14:03:17 +1100 Subject: [PATCH 15/18] Remove `QuerySystemFns`. It has a single use and doesn't provide any real value. Removing it allows the removal of two `for<'tcx>` qualifiers. --- .../rustc_middle/src/query/on_disk_cache.rs | 2 +- compiler/rustc_middle/src/query/plumbing.rs | 22 ++++++++----------- compiler/rustc_query_impl/src/lib.rs | 12 +++++----- compiler/rustc_query_impl/src/plumbing.rs | 6 ++--- 4 files changed, 18 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index e874e7e22b5c9..a9f4aee4049a7 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -261,7 +261,7 @@ impl OnDiskCache { tcx.sess.time("encode_query_results", || { let enc = &mut encoder; let qri = &mut query_result_index; - (tcx.query_system.fns.encode_query_results)(tcx, enc, qri); + (tcx.query_system.encode_query_results)(tcx, enc, qri); }); // Encode side effects. diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 9141f49bd45de..6d5be3213f2df 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -216,17 +216,6 @@ impl<'tcx, C: QueryCache> QueryVTable<'tcx, C> { } } -pub struct QuerySystemFns { - pub local_providers: Providers, - pub extern_providers: ExternProviders, - pub encode_query_results: for<'tcx> fn( - tcx: TyCtxt<'tcx>, - encoder: &mut CacheEncoder<'_, 'tcx>, - query_result_index: &mut EncodedDepNodeIndex, - ), - pub try_mark_green: for<'tcx> fn(tcx: TyCtxt<'tcx>, dep_node: &dep_graph::DepNode) -> bool, -} - pub struct QuerySystem<'tcx> { pub arenas: WorkerLocal>, pub query_vtables: QueryVTables<'tcx>, @@ -237,7 +226,14 @@ pub struct QuerySystem<'tcx> { /// This is `None` if we are not incremental compilation mode pub on_disk_cache: Option, - pub fns: QuerySystemFns, + pub local_providers: Providers, + pub extern_providers: ExternProviders, + pub encode_query_results: fn( + tcx: TyCtxt<'tcx>, + encoder: &mut CacheEncoder<'_, 'tcx>, + query_result_index: &mut EncodedDepNodeIndex, + ), + pub try_mark_green: fn(tcx: TyCtxt<'tcx>, dep_node: &dep_graph::DepNode) -> bool, pub jobs: AtomicU64, } @@ -329,7 +325,7 @@ impl<'tcx> TyCtxt<'tcx> { } pub fn try_mark_green(self, dep_node: &dep_graph::DepNode) -> bool { - (self.query_system.fns.try_mark_green)(self, dep_node) + (self.query_system.try_mark_green)(self, dep_node) } } diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index 890c0b2ef92c7..dc7dca4208b52 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -12,7 +12,7 @@ use rustc_data_structures::sync::AtomicU64; use rustc_middle::dep_graph; use rustc_middle::queries::{self, ExternProviders, Providers}; use rustc_middle::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache}; -use rustc_middle::query::plumbing::{QuerySystem, QuerySystemFns, QueryVTable}; +use rustc_middle::query::plumbing::{QuerySystem, QueryVTable}; use rustc_middle::query::{AsLocalKey, QueryCache, QueryMode}; use rustc_middle::ty::TyCtxt; use rustc_span::Span; @@ -59,12 +59,10 @@ pub fn query_system<'tcx>( arenas: Default::default(), query_vtables: make_query_vtables(incremental), on_disk_cache, - fns: QuerySystemFns { - local_providers, - extern_providers, - encode_query_results: encode_all_query_results, - try_mark_green, - }, + local_providers, + extern_providers, + encode_query_results: encode_all_query_results, + try_mark_green, jobs: AtomicU64::new(1), } } diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 7b5fd76c31958..275fe5b63d8b0 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -226,13 +226,13 @@ macro_rules! hash_result { macro_rules! call_provider { ([][$tcx:expr, $name:ident, $key:expr]) => {{ - ($tcx.query_system.fns.local_providers.$name)($tcx, $key) + ($tcx.query_system.local_providers.$name)($tcx, $key) }}; ([(separate_provide_extern) $($rest:tt)*][$tcx:expr, $name:ident, $key:expr]) => {{ if let Some(key) = $key.as_local_key() { - ($tcx.query_system.fns.local_providers.$name)($tcx, key) + ($tcx.query_system.local_providers.$name)($tcx, key) } else { - ($tcx.query_system.fns.extern_providers.$name)($tcx, $key) + ($tcx.query_system.extern_providers.$name)($tcx, $key) } }}; ([$other:tt $($modifiers:tt)*][$($args:tt)*]) => { From 608d85f989a35a10b9730a3ac50417dfad37419c Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 25 Feb 2026 14:20:11 +1100 Subject: [PATCH 16/18] Move two functions into `hooks`. `QuerySystem` has two function pointers: `encode_query_results` and `try_mark_green`. These exist so that `rustc_middle` can call functions from upstream crates. But we have a more general mechanism for that: hooks. So this commit converts these two cases into hooks. --- compiler/rustc_middle/src/hooks/mod.rs | 8 ++++++++ compiler/rustc_middle/src/query/on_disk_cache.rs | 13 ++++++++++--- compiler/rustc_middle/src/query/plumbing.rs | 13 +------------ compiler/rustc_query_impl/src/lib.rs | 4 ++-- 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_middle/src/hooks/mod.rs b/compiler/rustc_middle/src/hooks/mod.rs index c926604a41952..0ddcdac817b80 100644 --- a/compiler/rustc_middle/src/hooks/mod.rs +++ b/compiler/rustc_middle/src/hooks/mod.rs @@ -9,6 +9,7 @@ use rustc_span::def_id::{CrateNum, LocalDefId}; use rustc_span::{ExpnHash, ExpnId}; use crate::mir; +use crate::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex}; use crate::ty::{Ty, TyCtxt}; macro_rules! declare_hooks { @@ -107,6 +108,13 @@ declare_hooks! { /// /// Creates the MIR for a given `DefId`, including unreachable code. hook build_mir_inner_impl(def: LocalDefId) -> mir::Body<'tcx>; + + hook try_mark_green(dep_node: &crate::dep_graph::DepNode) -> bool; + + hook encode_all_query_results( + encoder: &mut CacheEncoder<'_, 'tcx>, + query_result_index: &mut EncodedDepNodeIndex + ) -> (); } #[cold] diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index a9f4aee4049a7..8db48fc2f9956 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -1,6 +1,6 @@ use std::collections::hash_map::Entry; -use std::mem; use std::sync::Arc; +use std::{fmt, mem}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_data_structures::memmap::Mmap; @@ -261,7 +261,7 @@ impl OnDiskCache { tcx.sess.time("encode_query_results", || { let enc = &mut encoder; let qri = &mut query_result_index; - (tcx.query_system.encode_query_results)(tcx, enc, qri); + tcx.encode_all_query_results(enc, qri); }); // Encode side effects. @@ -508,7 +508,7 @@ impl<'a, 'tcx> CacheDecoder<'a, 'tcx> { // tag matches and the correct amount of bytes was read. fn decode_tagged(decoder: &mut D, expected_tag: T) -> V where - T: Decodable + Eq + std::fmt::Debug, + T: Decodable + Eq + fmt::Debug, V: Decodable, D: Decoder, { @@ -829,6 +829,13 @@ pub struct CacheEncoder<'a, 'tcx> { symbol_index_table: FxHashMap, } +impl<'a, 'tcx> fmt::Debug for CacheEncoder<'a, 'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Add more details here if/when necessary. + f.write_str("CacheEncoder") + } +} + impl<'a, 'tcx> CacheEncoder<'a, 'tcx> { #[inline] fn source_file_index(&mut self, source_file: Arc) -> SourceFileIndex { diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 6d5be3213f2df..c2d524d200dee 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -11,11 +11,10 @@ use rustc_macros::HashStable; use rustc_span::{ErrorGuaranteed, Span}; pub use sealed::IntoQueryParam; -use crate::dep_graph; use crate::dep_graph::{DepKind, DepNode, DepNodeIndex, SerializedDepNodeIndex}; use crate::ich::StableHashingContext; use crate::queries::{ExternProviders, Providers, QueryArenas, QueryVTables}; -use crate::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache}; +use crate::query::on_disk_cache::OnDiskCache; use crate::query::stack::{QueryStackDeferred, QueryStackFrame, QueryStackFrameExtra}; use crate::query::{QueryCache, QueryInfo, QueryJob}; use crate::ty::TyCtxt; @@ -228,12 +227,6 @@ pub struct QuerySystem<'tcx> { pub local_providers: Providers, pub extern_providers: ExternProviders, - pub encode_query_results: fn( - tcx: TyCtxt<'tcx>, - encoder: &mut CacheEncoder<'_, 'tcx>, - query_result_index: &mut EncodedDepNodeIndex, - ), - pub try_mark_green: fn(tcx: TyCtxt<'tcx>, dep_node: &dep_graph::DepNode) -> bool, pub jobs: AtomicU64, } @@ -323,10 +316,6 @@ impl<'tcx> TyCtxt<'tcx> { pub fn at(self, span: Span) -> TyCtxtAt<'tcx> { TyCtxtAt { tcx: self, span } } - - pub fn try_mark_green(self, dep_node: &dep_graph::DepNode) -> bool { - (self.query_system.try_mark_green)(self, dep_node) - } } macro_rules! query_helper_param_ty { diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index dc7dca4208b52..afb41f69b5ebb 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -61,8 +61,6 @@ pub fn query_system<'tcx>( on_disk_cache, local_providers, extern_providers, - encode_query_results: encode_all_query_results, - try_mark_green, jobs: AtomicU64::new(1), } } @@ -72,4 +70,6 @@ rustc_middle::rustc_with_all_queries! { define_queries! } pub fn provide(providers: &mut rustc_middle::util::Providers) { providers.hooks.alloc_self_profile_query_strings = alloc_self_profile_query_strings; providers.hooks.query_key_hash_verify_all = query_key_hash_verify_all; + providers.hooks.encode_all_query_results = encode_all_query_results; + providers.hooks.try_mark_green = try_mark_green; } From f1ec10ecbd8628f315bee531ddee87f57cdb8096 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Thu, 8 Jan 2026 12:17:55 +0000 Subject: [PATCH 17/18] deprecate `Eq::assert_receiver_is_total_eq` and emit a FCW on manual impls --- .../src/deriving/cmp/eq.rs | 5 +- compiler/rustc_lint/src/builtin.rs | 64 ++++++++++++++++++- compiler/rustc_lint/src/lib.rs | 1 + compiler/rustc_lint/src/lints.rs | 5 ++ compiler/rustc_span/src/symbol.rs | 2 + library/core/src/cmp.rs | 14 +++- tests/ui/deriving/deriving-all-codegen.stdout | 48 +++++--------- .../internal_eq_trait_method_impls.rs | 48 ++++++++++++++ .../internal_eq_trait_method_impls.stderr | 41 ++++++++++++ tests/ui/stats/macro-stats.stderr | 2 +- 10 files changed, 188 insertions(+), 42 deletions(-) create mode 100644 tests/ui/deriving/internal_eq_trait_method_impls.rs create mode 100644 tests/ui/deriving/internal_eq_trait_method_impls.stderr diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs index d9b82e97cb462..650e6262b35e8 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs @@ -26,15 +26,14 @@ pub(crate) fn expand_deriving_eq( additional_bounds: Vec::new(), supports_unions: true, methods: vec![MethodDef { - name: sym::assert_receiver_is_total_eq, + name: sym::assert_fields_are_eq, generics: Bounds::empty(), explicit_self: true, nonself_args: vec![], ret_ty: Unit, attributes: thin_vec![ - cx.attr_word(sym::inline, span), cx.attr_nested_word(sym::doc, sym::hidden, span), - cx.attr_nested_word(sym::coverage, sym::off, span) + cx.attr_nested_word(sym::coverage, sym::off, span), ], fieldless_variants_strategy: FieldlessVariantsStrategy::Unify, combine_substructure: combine_substructure(Box::new(|a, b, c| { diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 22636959f0db5..e0590263737cb 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -24,12 +24,11 @@ use rustc_ast_pretty::pprust::expr_to_string; use rustc_attr_parsing::AttributeParser; use rustc_errors::{Applicability, LintDiagnostic, msg}; use rustc_feature::GateIssue; -use rustc_hir as hir; use rustc_hir::attrs::{AttributeKind, DocAttribute}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId}; use rustc_hir::intravisit::FnKind as HirFnKind; -use rustc_hir::{Body, FnDecl, ImplItemImplKind, PatKind, PredicateOrigin, find_attr}; +use rustc_hir::{self as hir, Body, FnDecl, ImplItemImplKind, PatKind, PredicateOrigin, find_attr}; use rustc_middle::bug; use rustc_middle::lint::LevelAndSource; use rustc_middle::ty::layout::LayoutOf; @@ -59,7 +58,7 @@ use crate::lints::{ BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasBounds, BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment, - BuiltinUnusedDocCommentSub, BuiltinWhileTrue, InvalidAsmLabel, + BuiltinUnusedDocCommentSub, BuiltinWhileTrue, EqInternalMethodImplemented, InvalidAsmLabel, }; use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext}; declare_lint! { @@ -3184,3 +3183,62 @@ impl EarlyLintPass for SpecialModuleName { } } } + +declare_lint! { + /// The `internal_eq_trait_method_impls` lint detects manual + /// implementations of `Eq::assert_receiver_is_total_eq`. + /// + /// ### Example + /// + /// ```rust + /// #[derive(PartialEq)] + /// pub struct Foo; + /// + /// impl Eq for Foo { + /// fn assert_receiver_is_total_eq(&self) {} + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This method existed so that `#[derive(Eq)]` could check that all + /// fields of a type implement `Eq`. Other users were never supposed + /// to implement it and it was hidden from documentation. + /// + /// Unfortunately, it was not explicitly marked as unstable and some + /// people have now mistakenly assumed they had to implement this method. + /// + /// As the method is never called by the standard library, you can safely + /// remove any implementations of the method and just write `impl Eq for Foo {}`. + /// + /// This is a [future-incompatible] lint to transition this to a hard + /// error in the future. See [issue #152336] for more details. + /// + /// [issue #152336]: https://github.com/rust-lang/rust/issues/152336 + pub INTERNAL_EQ_TRAIT_METHOD_IMPLS, + Warn, + "manual implementation of the internal `Eq::assert_receiver_is_total_eq` method", + @future_incompatible = FutureIncompatibleInfo { + reason: fcw!(FutureReleaseError #152336), + report_in_deps: false, + }; +} + +declare_lint_pass!(InternalEqTraitMethodImpls => [INTERNAL_EQ_TRAIT_METHOD_IMPLS]); + +impl<'tcx> LateLintPass<'tcx> for InternalEqTraitMethodImpls { + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx rustc_hir::ImplItem<'tcx>) { + if let ImplItemImplKind::Trait { defaultness: _, trait_item_def_id: Ok(trait_item_def_id) } = + item.impl_kind + && cx.tcx.is_diagnostic_item(sym::assert_receiver_is_total_eq, trait_item_def_id) + { + cx.emit_span_lint( + INTERNAL_EQ_TRAIT_METHOD_IMPLS, + item.span, + EqInternalMethodImplemented, + ); + } + } +} diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index cd0d8765dd933..4fcc2051fb393 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -250,6 +250,7 @@ late_lint_methods!( FunctionCastsAsInteger: FunctionCastsAsInteger, CheckTransmutes: CheckTransmutes, LifetimeSyntax: LifetimeSyntax, + InternalEqTraitMethodImpls: InternalEqTraitMethodImpls, ] ] ); diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 285a7b7f52935..3c57382e6b173 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3926,3 +3926,8 @@ pub(crate) struct MalformedOnConstAttrLint { #[label("invalid option found here")] pub span: Span, } + +#[derive(LintDiagnostic)] +#[diag("`Eq::assert_receiver_is_total_eq` should never be implemented by hand")] +#[note("this method was used to add checks to the `Eq` derive macro")] +pub(crate) struct EqInternalMethodImplemented; diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 3d40b7317459b..422a15b060cc0 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -432,6 +432,7 @@ symbols! { assert, assert_eq, assert_eq_macro, + assert_fields_are_eq, assert_inhabited, assert_macro, assert_mem_uninitialized_valid, @@ -1089,6 +1090,7 @@ symbols! { integer_: "integer", // underscore to avoid clashing with the function `sym::integer` below integral, internal, + internal_eq_trait_method_impls, internal_features, into_async_iter_into_iter, into_future, diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 78ea1f1113258..b3dc435dda176 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -336,16 +336,24 @@ pub macro PartialEq($item:item) { #[rustc_diagnostic_item = "Eq"] #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] pub const trait Eq: [const] PartialEq + PointeeSized { - // this method is used solely by `impl Eq or #[derive(Eq)]` to assert that every component of a - // type implements `Eq` itself. The current deriving infrastructure means doing this assertion - // without using a method on this trait is nearly impossible. + // This method was used solely by `#[derive(Eq)]` to assert that every component of a + // type implements `Eq` itself. // // This should never be implemented by hand. #[doc(hidden)] #[coverage(off)] #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_diagnostic_item = "assert_receiver_is_total_eq"] + #[deprecated(since = "1.95.0", note = "implementation detail of `#[derive(Eq)]`")] fn assert_receiver_is_total_eq(&self) {} + + // FIXME (#152504): this method is used solely by `#[derive(Eq)]` to assert that + // every component of a type implements `Eq` itself. It will be removed again soon. + #[doc(hidden)] + #[coverage(off)] + #[unstable(feature = "derive_eq_internals", issue = "none")] + fn assert_fields_are_eq(&self) {} } /// Derive macro generating an impl of the trait [`Eq`]. diff --git a/tests/ui/deriving/deriving-all-codegen.stdout b/tests/ui/deriving/deriving-all-codegen.stdout index 2a05d77f8f6da..19c8090e8de8b 100644 --- a/tests/ui/deriving/deriving-all-codegen.stdout +++ b/tests/ui/deriving/deriving-all-codegen.stdout @@ -62,10 +62,9 @@ impl ::core::cmp::PartialEq for Empty { } #[automatically_derived] impl ::core::cmp::Eq for Empty { - #[inline] #[doc(hidden)] #[coverage(off)] - fn assert_receiver_is_total_eq(&self) {} + fn assert_fields_are_eq(&self) {} } #[automatically_derived] impl ::core::cmp::PartialOrd for Empty { @@ -139,10 +138,9 @@ impl ::core::cmp::PartialEq for Point { } #[automatically_derived] impl ::core::cmp::Eq for Point { - #[inline] #[doc(hidden)] #[coverage(off)] - fn assert_receiver_is_total_eq(&self) { + fn assert_fields_are_eq(&self) { let _: ::core::cmp::AssertParamIsEq; } } @@ -227,10 +225,9 @@ impl ::core::cmp::PartialEq for PackedPoint { } #[automatically_derived] impl ::core::cmp::Eq for PackedPoint { - #[inline] #[doc(hidden)] #[coverage(off)] - fn assert_receiver_is_total_eq(&self) { + fn assert_fields_are_eq(&self) { let _: ::core::cmp::AssertParamIsEq; } } @@ -310,10 +307,9 @@ impl ::core::cmp::PartialEq for TupleSingleField { } #[automatically_derived] impl ::core::cmp::Eq for TupleSingleField { - #[inline] #[doc(hidden)] #[coverage(off)] - fn assert_receiver_is_total_eq(&self) { + fn assert_fields_are_eq(&self) { let _: ::core::cmp::AssertParamIsEq; } } @@ -385,10 +381,9 @@ impl ::core::cmp::PartialEq for SingleField { } #[automatically_derived] impl ::core::cmp::Eq for SingleField { - #[inline] #[doc(hidden)] #[coverage(off)] - fn assert_receiver_is_total_eq(&self) { + fn assert_fields_are_eq(&self) { let _: ::core::cmp::AssertParamIsEq; } } @@ -490,10 +485,9 @@ impl ::core::cmp::PartialEq for Big { } #[automatically_derived] impl ::core::cmp::Eq for Big { - #[inline] #[doc(hidden)] #[coverage(off)] - fn assert_receiver_is_total_eq(&self) { + fn assert_fields_are_eq(&self) { let _: ::core::cmp::AssertParamIsEq; } } @@ -754,10 +748,9 @@ impl ::core::cmp::PartialEq for Unsized { } #[automatically_derived] impl ::core::cmp::Eq for Unsized { - #[inline] #[doc(hidden)] #[coverage(off)] - fn assert_receiver_is_total_eq(&self) { + fn assert_fields_are_eq(&self) { let _: ::core::cmp::AssertParamIsEq<[u32]>; } } @@ -849,10 +842,9 @@ impl #[automatically_derived] impl ::core::cmp::Eq for Generic where T::A: ::core::cmp::Eq { - #[inline] #[doc(hidden)] #[coverage(off)] - fn assert_receiver_is_total_eq(&self) { + fn assert_fields_are_eq(&self) { let _: ::core::cmp::AssertParamIsEq; let _: ::core::cmp::AssertParamIsEq; let _: ::core::cmp::AssertParamIsEq; @@ -971,10 +963,9 @@ impl ::core::cmp::Eq for PackedGeneric where T::A: ::core::cmp::Eq + ::core::marker::Copy { - #[inline] #[doc(hidden)] #[coverage(off)] - fn assert_receiver_is_total_eq(&self) { + fn assert_fields_are_eq(&self) { let _: ::core::cmp::AssertParamIsEq; let _: ::core::cmp::AssertParamIsEq; let _: ::core::cmp::AssertParamIsEq; @@ -1056,10 +1047,9 @@ impl ::core::cmp::PartialEq for Enum0 { } #[automatically_derived] impl ::core::cmp::Eq for Enum0 { - #[inline] #[doc(hidden)] #[coverage(off)] - fn assert_receiver_is_total_eq(&self) {} + fn assert_fields_are_eq(&self) {} } #[automatically_derived] impl ::core::cmp::PartialOrd for Enum0 { @@ -1126,10 +1116,9 @@ impl ::core::cmp::PartialEq for Enum1 { } #[automatically_derived] impl ::core::cmp::Eq for Enum1 { - #[inline] #[doc(hidden)] #[coverage(off)] - fn assert_receiver_is_total_eq(&self) { + fn assert_fields_are_eq(&self) { let _: ::core::cmp::AssertParamIsEq; } } @@ -1192,10 +1181,9 @@ impl ::core::cmp::PartialEq for Fieldless1 { } #[automatically_derived] impl ::core::cmp::Eq for Fieldless1 { - #[inline] #[doc(hidden)] #[coverage(off)] - fn assert_receiver_is_total_eq(&self) {} + fn assert_fields_are_eq(&self) {} } #[automatically_derived] impl ::core::cmp::PartialOrd for Fieldless1 { @@ -1269,10 +1257,9 @@ impl ::core::cmp::PartialEq for Fieldless { } #[automatically_derived] impl ::core::cmp::Eq for Fieldless { - #[inline] #[doc(hidden)] #[coverage(off)] - fn assert_receiver_is_total_eq(&self) {} + fn assert_fields_are_eq(&self) {} } #[automatically_derived] impl ::core::cmp::PartialOrd for Fieldless { @@ -1379,10 +1366,9 @@ impl ::core::cmp::PartialEq for Mixed { } #[automatically_derived] impl ::core::cmp::Eq for Mixed { - #[inline] #[doc(hidden)] #[coverage(off)] - fn assert_receiver_is_total_eq(&self) { + fn assert_fields_are_eq(&self) { let _: ::core::cmp::AssertParamIsEq; let _: ::core::cmp::AssertParamIsEq>; let _: ::core::cmp::AssertParamIsEq>; @@ -1577,10 +1563,9 @@ impl ::core::cmp::PartialEq for Fielded { } #[automatically_derived] impl ::core::cmp::Eq for Fielded { - #[inline] #[doc(hidden)] #[coverage(off)] - fn assert_receiver_is_total_eq(&self) { + fn assert_fields_are_eq(&self) { let _: ::core::cmp::AssertParamIsEq; let _: ::core::cmp::AssertParamIsEq; let _: ::core::cmp::AssertParamIsEq>; @@ -1699,10 +1684,9 @@ impl #[automatically_derived] impl ::core::cmp::Eq for EnumGeneric { - #[inline] #[doc(hidden)] #[coverage(off)] - fn assert_receiver_is_total_eq(&self) { + fn assert_fields_are_eq(&self) { let _: ::core::cmp::AssertParamIsEq; let _: ::core::cmp::AssertParamIsEq; } diff --git a/tests/ui/deriving/internal_eq_trait_method_impls.rs b/tests/ui/deriving/internal_eq_trait_method_impls.rs new file mode 100644 index 0000000000000..b2605fe3c8668 --- /dev/null +++ b/tests/ui/deriving/internal_eq_trait_method_impls.rs @@ -0,0 +1,48 @@ +#![deny(deprecated, internal_eq_trait_method_impls)] +pub struct Bad; + +impl PartialEq for Bad { + fn eq(&self, _: &Self) -> bool { + true + } +} + +impl Eq for Bad { + fn assert_receiver_is_total_eq(&self) {} + //~^ ERROR: `Eq::assert_receiver_is_total_eq` should never be implemented by hand [internal_eq_trait_method_impls] + //~| WARN: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +} + +#[derive(PartialEq, Eq)] +pub struct Good; + +#[derive(PartialEq)] +pub struct Good2; + +impl Eq for Good2 {} + +pub struct Foo; + +pub trait SameName { + fn assert_receiver_is_total_eq(&self) {} +} + +impl SameName for Foo { + fn assert_receiver_is_total_eq(&self) {} +} + +pub fn main() { + Foo.assert_receiver_is_total_eq(); + Good2.assert_receiver_is_total_eq(); + //~^ ERROR: use of deprecated method `std::cmp::Eq::assert_receiver_is_total_eq`: implementation detail of `#[derive(Eq)]` [deprecated] + Good.assert_receiver_is_total_eq(); + //~^ ERROR: use of deprecated method `std::cmp::Eq::assert_receiver_is_total_eq`: implementation detail of `#[derive(Eq)]` [deprecated] + Bad.assert_receiver_is_total_eq(); + //~^ ERROR: use of deprecated method `std::cmp::Eq::assert_receiver_is_total_eq`: implementation detail of `#[derive(Eq)]` [deprecated] +} + +#[forbid(internal_eq_trait_method_impls)] +mod forbid { + #[derive(PartialEq, Eq)] + pub struct Foo; +} diff --git a/tests/ui/deriving/internal_eq_trait_method_impls.stderr b/tests/ui/deriving/internal_eq_trait_method_impls.stderr new file mode 100644 index 0000000000000..8ff8fe337a78c --- /dev/null +++ b/tests/ui/deriving/internal_eq_trait_method_impls.stderr @@ -0,0 +1,41 @@ +error: use of deprecated method `std::cmp::Eq::assert_receiver_is_total_eq`: implementation detail of `#[derive(Eq)]` + --> $DIR/internal_eq_trait_method_impls.rs:36:11 + | +LL | Good2.assert_receiver_is_total_eq(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/internal_eq_trait_method_impls.rs:1:9 + | +LL | #![deny(deprecated, internal_eq_trait_method_impls)] + | ^^^^^^^^^^ + +error: use of deprecated method `std::cmp::Eq::assert_receiver_is_total_eq`: implementation detail of `#[derive(Eq)]` + --> $DIR/internal_eq_trait_method_impls.rs:38:10 + | +LL | Good.assert_receiver_is_total_eq(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: use of deprecated method `std::cmp::Eq::assert_receiver_is_total_eq`: implementation detail of `#[derive(Eq)]` + --> $DIR/internal_eq_trait_method_impls.rs:40:9 + | +LL | Bad.assert_receiver_is_total_eq(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `Eq::assert_receiver_is_total_eq` should never be implemented by hand + --> $DIR/internal_eq_trait_method_impls.rs:11:5 + | +LL | fn assert_receiver_is_total_eq(&self) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #152336 + = note: this method was used to add checks to the `Eq` derive macro +note: the lint level is defined here + --> $DIR/internal_eq_trait_method_impls.rs:1:21 + | +LL | #![deny(deprecated, internal_eq_trait_method_impls)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/stats/macro-stats.stderr b/tests/ui/stats/macro-stats.stderr index a48940460f91e..4a94b81452a90 100644 --- a/tests/ui/stats/macro-stats.stderr +++ b/tests/ui/stats/macro-stats.stderr @@ -8,7 +8,7 @@ macro-stats #[derive(Hash)] 2 17 8.5 macro-stats q! 1 26 26.0 519 519.0 macro-stats #[derive(Ord)] 1 15 15.0 503 503.0 macro-stats #[derive(Default)] 2 16 8.0 403 201.5 -macro-stats #[derive(Eq)] 1 11 11.0 319 319.0 +macro-stats #[derive(Eq)] 1 10 10.0 298 298.0 macro-stats #[derive(Debug)] 1 8 8.0 277 277.0 macro-stats #[derive(PartialEq)] 1 9 9.0 267 267.0 macro-stats #[derive(Copy)] 1 2 2.0 61 61.0 From 5b99fcd043fb177c757391b4a25adc3831208d86 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 25 Feb 2026 13:49:49 +0100 Subject: [PATCH 18/18] Revert "Move aarch64-apple dist builder to dynamic llvm linking and enable autodiff in CI for it" This reverts commit c033de932ec6f46ccb1fd14bf848aec0bc88eece. --- src/ci/github-actions/jobs.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index c06c85513856b..0687d142a3504 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -504,7 +504,7 @@ auto: - name: dist-aarch64-apple env: SCRIPT: >- - ./x.py dist bootstrap enzyme + ./x.py dist bootstrap --include-default-paths --host=aarch64-apple-darwin --target=aarch64-apple-darwin @@ -513,7 +513,6 @@ auto: --enable-sanitizers --enable-profiler --set rust.jemalloc - --set llvm.link-shared=true --set rust.lto=thin --set rust.codegen-units=1 # Aarch64 tooling only needs to support macOS 11.0 and up as nothing else