From 12f41b519435e3952f2bf8e2c829e19c982ace04 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 17 Oct 2025 12:52:31 +0000 Subject: [PATCH 01/69] More explicit handling of the allocator shim around LTO --- compiler/rustc_codegen_ssa/src/back/write.rs | 56 +++++++++----------- compiler/rustc_codegen_ssa/src/base.rs | 11 +--- 2 files changed, 27 insertions(+), 40 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 3e36bd8552b1..885af95b1f2c 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -334,7 +334,6 @@ pub struct CodegenContext { pub output_filenames: Arc, pub invocation_temp: Option, pub module_config: Arc, - pub allocator_config: Arc, pub tm_factory: TargetMachineFactoryFn, pub msvc_imps_needed: bool, pub is_pe_coff: bool, @@ -799,19 +798,12 @@ pub(crate) fn compute_per_cgu_lto_type( sess_lto: &Lto, opts: &config::Options, sess_crate_types: &[CrateType], - module_kind: ModuleKind, ) -> ComputedLtoType { // If the linker does LTO, we don't have to do it. Note that we // keep doing full LTO, if it is requested, as not to break the // assumption that the output will be a single module. let linker_does_lto = opts.cg.linker_plugin_lto.enabled(); - // When we're automatically doing ThinLTO for multi-codegen-unit - // builds we don't actually want to LTO the allocator module if - // it shows up. This is due to various linker shenanigans that - // we'll encounter later. - let is_allocator = module_kind == ModuleKind::Allocator; - // We ignore a request for full crate graph LTO if the crate type // is only an rlib, as there is no full crate graph to process, // that'll happen later. @@ -823,7 +815,7 @@ pub(crate) fn compute_per_cgu_lto_type( let is_rlib = matches!(sess_crate_types, [CrateType::Rlib]); match sess_lto { - Lto::ThinLocal if !linker_does_lto && !is_allocator => ComputedLtoType::Thin, + Lto::ThinLocal if !linker_does_lto => ComputedLtoType::Thin, Lto::Thin if !linker_does_lto && !is_rlib => ComputedLtoType::Thin, Lto::Fat if !is_rlib => ComputedLtoType::Fat, _ => ComputedLtoType::No, @@ -839,23 +831,18 @@ fn execute_optimize_work_item( let dcx = cgcx.create_dcx(); let dcx = dcx.handle(); - let module_config = match module.kind { - ModuleKind::Regular => &cgcx.module_config, - ModuleKind::Allocator => &cgcx.allocator_config, - }; - - B::optimize(cgcx, dcx, &mut module, module_config); + B::optimize(cgcx, dcx, &mut module, &cgcx.module_config); // After we've done the initial round of optimizations we need to // decide whether to synchronously codegen this module or ship it // back to the coordinator thread for further LTO processing (which // has to wait for all the initial modules to be optimized). - let lto_type = compute_per_cgu_lto_type(&cgcx.lto, &cgcx.opts, &cgcx.crate_types, module.kind); + let lto_type = compute_per_cgu_lto_type(&cgcx.lto, &cgcx.opts, &cgcx.crate_types); // If we're doing some form of incremental LTO then we need to be sure to // save our module to disk first. - let bitcode = if module_config.emit_pre_lto_bc { + let bitcode = if cgcx.module_config.emit_pre_lto_bc { let filename = pre_lto_bitcode_filename(&module.name); cgcx.incr_comp_session_dir.as_ref().map(|path| path.join(&filename)) } else { @@ -864,7 +851,7 @@ fn execute_optimize_work_item( match lto_type { ComputedLtoType::No => { - let module = B::codegen(cgcx, module, module_config); + let module = B::codegen(cgcx, module, &cgcx.module_config); WorkItemResult::Finished(module) } ComputedLtoType::Thin => { @@ -1245,7 +1232,7 @@ fn start_executing_work( coordinator_receive: Receiver>, regular_config: Arc, allocator_config: Arc, - allocator_module: Option>, + mut allocator_module: Option>, coordinator_send: Sender>, ) -> thread::JoinHandle> { let sess = tcx.sess; @@ -1303,7 +1290,6 @@ fn start_executing_work( diag_emitter: shared_emitter.clone(), output_filenames: Arc::clone(tcx.output_filenames(())), module_config: regular_config, - allocator_config, tm_factory: backend.target_machine_factory(tcx.sess, ol, backend_features), msvc_imps_needed: msvc_imps_needed(tcx), is_pe_coff: tcx.sess.target.is_like_windows, @@ -1497,16 +1483,12 @@ fn start_executing_work( let mut llvm_start_time: Option> = None; - let compiled_allocator_module = allocator_module.and_then(|allocator_module| { - match execute_optimize_work_item(&cgcx, allocator_module) { - WorkItemResult::Finished(compiled_module) => return Some(compiled_module), - WorkItemResult::NeedsFatLto(fat_lto_input) => needs_fat_lto.push(fat_lto_input), - WorkItemResult::NeedsThinLto(name, thin_buffer) => { - needs_thin_lto.push((name, thin_buffer)) - } - } - None - }); + if let Some(allocator_module) = &mut allocator_module { + let dcx = cgcx.create_dcx(); + let dcx = dcx.handle(); + + B::optimize(&cgcx, dcx, allocator_module, &allocator_config); + } // Run the message loop while there's still anything that needs message // processing. Note that as soon as codegen is aborted we simply want to @@ -1733,6 +1715,10 @@ fn start_executing_work( assert!(compiled_modules.is_empty()); assert!(needs_thin_lto.is_empty()); + if let Some(allocator_module) = allocator_module.take() { + needs_fat_lto.push(FatLtoInput::InMemory(allocator_module)); + } + // This uses the implicit token let module = do_fat_lto( &cgcx, @@ -1746,6 +1732,13 @@ fn start_executing_work( assert!(compiled_modules.is_empty()); assert!(needs_fat_lto.is_empty()); + if cgcx.lto != Lto::ThinLocal { + if let Some(allocator_module) = allocator_module.take() { + let (name, thin_buffer) = B::prepare_thin(allocator_module); + needs_thin_lto.push((name, thin_buffer)); + } + } + compiled_modules.extend(do_thin_lto( &cgcx, exported_symbols_for_lto, @@ -1762,7 +1755,8 @@ fn start_executing_work( Ok(CompiledModules { modules: compiled_modules, - allocator_module: compiled_allocator_module, + allocator_module: allocator_module + .map(|allocator_module| B::codegen(&cgcx, allocator_module, &allocator_config)), }) }) .expect("failed to spawn coordinator thread"); diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 414e9ce1c821..815a3ee706f8 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -49,9 +49,7 @@ use crate::meth::load_vtable; use crate::mir::operand::OperandValue; use crate::mir::place::PlaceRef; use crate::traits::*; -use crate::{ - CachedModuleCodegen, CodegenLintLevels, CrateInfo, ModuleCodegen, ModuleKind, errors, meth, mir, -}; +use crate::{CachedModuleCodegen, CodegenLintLevels, CrateInfo, ModuleCodegen, errors, meth, mir}; pub(crate) fn bin_op_to_icmp_predicate(op: BinOp, signed: bool) -> IntPredicate { match (op, signed) { @@ -1132,12 +1130,7 @@ pub fn determine_cgu_reuse<'tcx>(tcx: TyCtxt<'tcx>, cgu: &CodegenUnit<'tcx>) -> // We can re-use either the pre- or the post-thinlto state. If no LTO is // being performed then we can use post-LTO artifacts, otherwise we must // reuse pre-LTO artifacts - match compute_per_cgu_lto_type( - &tcx.sess.lto(), - &tcx.sess.opts, - tcx.crate_types(), - ModuleKind::Regular, - ) { + match compute_per_cgu_lto_type(&tcx.sess.lto(), &tcx.sess.opts, tcx.crate_types()) { ComputedLtoType::No => CguReuse::PostLto, _ => CguReuse::PreLto, } From b93b4b003eb1b1dea681d65a5ac9c222b854046d Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 23 Oct 2025 09:39:13 +0000 Subject: [PATCH 02/69] Remove opts field from CodegenContext --- compiler/rustc_codegen_gcc/src/back/lto.rs | 2 +- compiler/rustc_codegen_llvm/src/back/lto.rs | 2 +- compiler/rustc_codegen_llvm/src/back/write.rs | 6 +++--- compiler/rustc_codegen_ssa/src/back/lto.rs | 6 +++--- compiler/rustc_codegen_ssa/src/back/write.rs | 14 +++++++++----- compiler/rustc_codegen_ssa/src/base.rs | 6 +++++- 6 files changed, 22 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/back/lto.rs b/compiler/rustc_codegen_gcc/src/back/lto.rs index 404064fb7a06..08c36d2b7318 100644 --- a/compiler/rustc_codegen_gcc/src/back/lto.rs +++ b/compiler/rustc_codegen_gcc/src/back/lto.rs @@ -290,7 +290,7 @@ pub(crate) fn run_thin( let dcx = cgcx.create_dcx(); let dcx = dcx.handle(); let lto_data = prepare_lto(cgcx, each_linked_rlib_for_lto, dcx); - if cgcx.opts.cg.linker_plugin_lto.enabled() { + if cgcx.use_linker_plugin_lto { unreachable!( "We should never reach this case if the LTO step \ is deferred to the linker" diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index b820b992105f..3137ed288fa8 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -179,7 +179,7 @@ pub(crate) fn run_thin( prepare_lto(cgcx, exported_symbols_for_lto, each_linked_rlib_for_lto, dcx); let symbols_below_threshold = symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::>(); - if cgcx.opts.cg.linker_plugin_lto.enabled() { + if cgcx.use_linker_plugin_lto { unreachable!( "We should never reach this case if the LTO step \ is deferred to the linker" diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 95539059653b..a49d4c010d67 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -398,7 +398,7 @@ impl<'a> DiagnosticHandlers<'a> { }) .and_then(|dir| dir.to_str().and_then(|p| CString::new(p).ok())); - let pgo_available = cgcx.opts.cg.profile_use.is_some(); + let pgo_available = cgcx.module_config.pgo_use.is_some(); let data = Box::into_raw(Box::new((cgcx, dcx))); unsafe { let old_handler = llvm::LLVMRustContextGetDiagnosticHandler(llcx); @@ -738,7 +738,7 @@ pub(crate) unsafe fn llvm_optimize( &*module.module_llvm.tm.raw(), to_pass_builder_opt_level(opt_level), opt_stage, - cgcx.opts.cg.linker_plugin_lto.enabled(), + cgcx.use_linker_plugin_lto, config.no_prepopulate_passes, config.verify_llvm_ir, config.lint_llvm_ir, @@ -801,7 +801,7 @@ pub(crate) fn optimize( let opt_stage = match cgcx.lto { Lto::Fat => llvm::OptStage::PreLinkFatLTO, Lto::Thin | Lto::ThinLocal => llvm::OptStage::PreLinkThinLTO, - _ if cgcx.opts.cg.linker_plugin_lto.enabled() => llvm::OptStage::PreLinkThinLTO, + _ if cgcx.use_linker_plugin_lto => llvm::OptStage::PreLinkThinLTO, _ => llvm::OptStage::PreLinkNoLTO, }; diff --git a/compiler/rustc_codegen_ssa/src/back/lto.rs b/compiler/rustc_codegen_ssa/src/back/lto.rs index e6df6a2469f3..e3dc985bf782 100644 --- a/compiler/rustc_codegen_ssa/src/back/lto.rs +++ b/compiler/rustc_codegen_ssa/src/back/lto.rs @@ -137,15 +137,15 @@ pub(super) fn check_lto_allowed(cgcx: &CodegenContext if !crate_type_allows_lto(*crate_type) { dcx.handle().emit_fatal(LtoDisallowed); } else if *crate_type == CrateType::Dylib { - if !cgcx.opts.unstable_opts.dylib_lto { + if !cgcx.dylib_lto { dcx.handle().emit_fatal(LtoDylib); } - } else if *crate_type == CrateType::ProcMacro && !cgcx.opts.unstable_opts.dylib_lto { + } else if *crate_type == CrateType::ProcMacro && !cgcx.dylib_lto { dcx.handle().emit_fatal(LtoProcMacro); } } - if cgcx.opts.cg.prefer_dynamic && !cgcx.opts.unstable_opts.dylib_lto { + if cgcx.prefer_dynamic && !cgcx.dylib_lto { dcx.handle().emit_fatal(DynamicLinkingWithLTO); } } diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 885af95b1f2c..bc7c32931613 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -326,10 +326,12 @@ pub struct CodegenContext { // Resources needed when running LTO pub prof: SelfProfilerRef, pub lto: Lto, + pub use_linker_plugin_lto: bool, + pub dylib_lto: bool, + pub prefer_dynamic: bool, pub save_temps: bool, pub fewer_names: bool, pub time_trace: bool, - pub opts: Arc, pub crate_types: Vec, pub output_filenames: Arc, pub invocation_temp: Option, @@ -796,13 +798,12 @@ pub(crate) enum ComputedLtoType { pub(crate) fn compute_per_cgu_lto_type( sess_lto: &Lto, - opts: &config::Options, + linker_does_lto: bool, sess_crate_types: &[CrateType], ) -> ComputedLtoType { // If the linker does LTO, we don't have to do it. Note that we // keep doing full LTO, if it is requested, as not to break the // assumption that the output will be a single module. - let linker_does_lto = opts.cg.linker_plugin_lto.enabled(); // We ignore a request for full crate graph LTO if the crate type // is only an rlib, as there is no full crate graph to process, @@ -838,7 +839,8 @@ fn execute_optimize_work_item( // back to the coordinator thread for further LTO processing (which // has to wait for all the initial modules to be optimized). - let lto_type = compute_per_cgu_lto_type(&cgcx.lto, &cgcx.opts, &cgcx.crate_types); + let lto_type = + compute_per_cgu_lto_type(&cgcx.lto, cgcx.use_linker_plugin_lto, &cgcx.crate_types); // If we're doing some form of incremental LTO then we need to be sure to // save our module to disk first. @@ -1279,10 +1281,12 @@ fn start_executing_work( let cgcx = CodegenContext:: { crate_types: tcx.crate_types().to_vec(), lto: sess.lto(), + use_linker_plugin_lto: sess.opts.cg.linker_plugin_lto.enabled(), + dylib_lto: sess.opts.unstable_opts.dylib_lto, + prefer_dynamic: sess.opts.cg.prefer_dynamic, fewer_names: sess.fewer_names(), save_temps: sess.opts.cg.save_temps, time_trace: sess.opts.unstable_opts.llvm_time_trace, - opts: Arc::new(sess.opts.clone()), prof: sess.prof.clone(), remark: sess.opts.cg.remark.clone(), remark_dir, diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 815a3ee706f8..2943df8f02aa 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -1130,7 +1130,11 @@ pub fn determine_cgu_reuse<'tcx>(tcx: TyCtxt<'tcx>, cgu: &CodegenUnit<'tcx>) -> // We can re-use either the pre- or the post-thinlto state. If no LTO is // being performed then we can use post-LTO artifacts, otherwise we must // reuse pre-LTO artifacts - match compute_per_cgu_lto_type(&tcx.sess.lto(), &tcx.sess.opts, tcx.crate_types()) { + match compute_per_cgu_lto_type( + &tcx.sess.lto(), + tcx.sess.opts.cg.linker_plugin_lto.enabled(), + tcx.crate_types(), + ) { ComputedLtoType::No => CguReuse::PostLto, _ => CguReuse::PreLto, } From 2d7c571391e558a55767dc9aaeca6989a63c77c4 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 21 Nov 2025 15:08:09 +0000 Subject: [PATCH 03/69] Remove SharedEmitter from CodegenContext --- compiler/rustc_codegen_gcc/src/back/lto.rs | 12 +- compiler/rustc_codegen_gcc/src/back/write.rs | 8 +- compiler/rustc_codegen_gcc/src/lib.rs | 14 ++- compiler/rustc_codegen_llvm/src/back/lto.rs | 27 +++-- compiler/rustc_codegen_llvm/src/back/write.rs | 43 ++++--- compiler/rustc_codegen_llvm/src/lib.rs | 29 +++-- compiler/rustc_codegen_ssa/src/back/lto.rs | 8 +- compiler/rustc_codegen_ssa/src/back/write.rs | 112 +++++++++++------- .../rustc_codegen_ssa/src/traits/write.rs | 8 +- 9 files changed, 165 insertions(+), 96 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/back/lto.rs b/compiler/rustc_codegen_gcc/src/back/lto.rs index 08c36d2b7318..24be3ee4c34d 100644 --- a/compiler/rustc_codegen_gcc/src/back/lto.rs +++ b/compiler/rustc_codegen_gcc/src/back/lto.rs @@ -26,11 +26,11 @@ use std::sync::atomic::Ordering; use gccjit::{Context, OutputKind}; use object::read::archive::ArchiveFile; use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule, ThinShared}; -use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput}; +use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput, SharedEmitter}; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{ModuleCodegen, ModuleKind, looks_like_rust_object_file}; use rustc_data_structures::memmap::Mmap; -use rustc_errors::DiagCtxtHandle; +use rustc_errors::{DiagCtxt, DiagCtxtHandle}; use rustc_log::tracing::info; use rustc_middle::bug; use rustc_middle::dep_graph::WorkProduct; @@ -112,10 +112,11 @@ fn save_as_file(obj: &[u8], path: &Path) -> Result<(), LtoBitcodeFromRlib> { /// for further optimization. pub(crate) fn run_fat( cgcx: &CodegenContext, + shared_emitter: &SharedEmitter, each_linked_rlib_for_lto: &[PathBuf], modules: Vec>, ) -> ModuleCodegen { - let dcx = cgcx.create_dcx(); + let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); let dcx = dcx.handle(); let lto_data = prepare_lto(cgcx, each_linked_rlib_for_lto, dcx); /*let symbols_below_threshold = @@ -283,12 +284,11 @@ impl ModuleBufferMethods for ModuleBuffer { /// can simply be copied over from the incr. comp. cache. pub(crate) fn run_thin( cgcx: &CodegenContext, + dcx: DiagCtxtHandle<'_>, each_linked_rlib_for_lto: &[PathBuf], modules: Vec<(String, ThinBuffer)>, cached_modules: Vec<(SerializedModule, WorkProduct)>, ) -> (Vec>, Vec) { - let dcx = cgcx.create_dcx(); - let dcx = dcx.handle(); let lto_data = prepare_lto(cgcx, each_linked_rlib_for_lto, dcx); if cgcx.use_linker_plugin_lto { unreachable!( @@ -522,8 +522,6 @@ pub fn optimize_thin_module( thin_module: ThinModule, _cgcx: &CodegenContext, ) -> ModuleCodegen { - //let dcx = cgcx.create_dcx(); - //let module_name = &thin_module.shared.module_names[thin_module.idx]; /*let tm_factory_config = TargetMachineFactoryConfig::new(cgcx, module_name.to_str().unwrap()); let tm = (cgcx.tm_factory)(tm_factory_config).map_err(|e| write::llvm_err(&dcx, e))?;*/ diff --git a/compiler/rustc_codegen_gcc/src/back/write.rs b/compiler/rustc_codegen_gcc/src/back/write.rs index eae0f2aa00f6..b6223c5be370 100644 --- a/compiler/rustc_codegen_gcc/src/back/write.rs +++ b/compiler/rustc_codegen_gcc/src/back/write.rs @@ -2,8 +2,11 @@ use std::{env, fs}; use gccjit::{Context, OutputKind}; use rustc_codegen_ssa::back::link::ensure_removed; -use rustc_codegen_ssa::back::write::{BitcodeSection, CodegenContext, EmitObj, ModuleConfig}; +use rustc_codegen_ssa::back::write::{ + BitcodeSection, CodegenContext, EmitObj, ModuleConfig, SharedEmitter, +}; use rustc_codegen_ssa::{CompiledModule, ModuleCodegen}; +use rustc_errors::DiagCtxt; use rustc_fs_util::link_or_copy; use rustc_log::tracing::debug; use rustc_session::config::OutputType; @@ -15,10 +18,11 @@ use crate::{GccCodegenBackend, GccContext, LtoMode}; pub(crate) fn codegen( cgcx: &CodegenContext, + shared_emitter: &SharedEmitter, module: ModuleCodegen, config: &ModuleConfig, ) -> CompiledModule { - let dcx = cgcx.create_dcx(); + let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); let dcx = dcx.handle(); let _timer = cgcx.prof.generic_activity_with_arg("GCC_module_codegen", &*module.name); diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 409b7886740a..5a2ec0a2fc68 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -82,7 +82,7 @@ use gccjit::{TargetInfo, Version}; use rustc_ast::expand::allocator::AllocatorMethod; use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule}; use rustc_codegen_ssa::back::write::{ - CodegenContext, FatLtoInput, ModuleConfig, TargetMachineFactoryFn, + CodegenContext, FatLtoInput, ModuleConfig, SharedEmitter, TargetMachineFactoryFn, }; use rustc_codegen_ssa::base::codegen_crate; use rustc_codegen_ssa::target_features::cfg_target_feature; @@ -371,23 +371,25 @@ impl WriteBackendMethods for GccCodegenBackend { fn run_and_optimize_fat_lto( cgcx: &CodegenContext, + shared_emitter: &SharedEmitter, // FIXME(bjorn3): Limit LTO exports to these symbols _exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], modules: Vec>, ) -> ModuleCodegen { - back::lto::run_fat(cgcx, each_linked_rlib_for_lto, modules) + back::lto::run_fat(cgcx, shared_emitter, each_linked_rlib_for_lto, modules) } fn run_thin_lto( cgcx: &CodegenContext, + dcx: DiagCtxtHandle<'_>, // FIXME(bjorn3): Limit LTO exports to these symbols _exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], modules: Vec<(String, Self::ThinBuffer)>, cached_modules: Vec<(SerializedModule, WorkProduct)>, ) -> (Vec>, Vec) { - back::lto::run_thin(cgcx, each_linked_rlib_for_lto, modules, cached_modules) + back::lto::run_thin(cgcx, dcx, each_linked_rlib_for_lto, modules, cached_modules) } fn print_pass_timings(&self) { @@ -400,7 +402,7 @@ impl WriteBackendMethods for GccCodegenBackend { fn optimize( _cgcx: &CodegenContext, - _dcx: DiagCtxtHandle<'_>, + _shared_emitter: &SharedEmitter, module: &mut ModuleCodegen, config: &ModuleConfig, ) { @@ -409,6 +411,7 @@ impl WriteBackendMethods for GccCodegenBackend { fn optimize_thin( cgcx: &CodegenContext, + _shared_emitter: &SharedEmitter, thin: ThinModule, ) -> ModuleCodegen { back::lto::optimize_thin_module(thin, cgcx) @@ -416,10 +419,11 @@ impl WriteBackendMethods for GccCodegenBackend { fn codegen( cgcx: &CodegenContext, + shared_emitter: &SharedEmitter, module: ModuleCodegen, config: &ModuleConfig, ) -> CompiledModule { - back::write::codegen(cgcx, module, config) + back::write::codegen(cgcx, shared_emitter, module, config) } fn prepare_thin(module: ModuleCodegen) -> (String, Self::ThinBuffer) { diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 3137ed288fa8..3210afc063a9 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -9,12 +9,12 @@ use std::{io, iter, slice}; use object::read::archive::ArchiveFile; use object::{Object, ObjectSection}; use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule, ThinShared}; -use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput}; +use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput, SharedEmitter}; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{ModuleCodegen, ModuleKind, looks_like_rust_object_file}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::memmap::Mmap; -use rustc_errors::DiagCtxtHandle; +use rustc_errors::{DiagCtxt, DiagCtxtHandle}; use rustc_hir::attrs::SanitizerSet; use rustc_middle::bug; use rustc_middle::dep_graph::WorkProduct; @@ -150,17 +150,18 @@ fn get_bitcode_slice_from_object_data<'a>( /// for further optimization. pub(crate) fn run_fat( cgcx: &CodegenContext, + shared_emitter: &SharedEmitter, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], modules: Vec>, ) -> ModuleCodegen { - let dcx = cgcx.create_dcx(); + let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); let dcx = dcx.handle(); let (symbols_below_threshold, upstream_modules) = prepare_lto(cgcx, exported_symbols_for_lto, each_linked_rlib_for_lto, dcx); let symbols_below_threshold = symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::>(); - fat_lto(cgcx, dcx, modules, upstream_modules, &symbols_below_threshold) + fat_lto(cgcx, dcx, shared_emitter, modules, upstream_modules, &symbols_below_threshold) } /// Performs thin LTO by performing necessary global analysis and returning two @@ -168,13 +169,12 @@ pub(crate) fn run_fat( /// can simply be copied over from the incr. comp. cache. pub(crate) fn run_thin( cgcx: &CodegenContext, + dcx: DiagCtxtHandle<'_>, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], modules: Vec<(String, ThinBuffer)>, cached_modules: Vec<(SerializedModule, WorkProduct)>, ) -> (Vec>, Vec) { - let dcx = cgcx.create_dcx(); - let dcx = dcx.handle(); let (symbols_below_threshold, upstream_modules) = prepare_lto(cgcx, exported_symbols_for_lto, each_linked_rlib_for_lto, dcx); let symbols_below_threshold = @@ -197,6 +197,7 @@ pub(crate) fn prepare_thin(module: ModuleCodegen) -> (String, ThinBu fn fat_lto( cgcx: &CodegenContext, dcx: DiagCtxtHandle<'_>, + shared_emitter: &SharedEmitter, modules: Vec>, mut serialized_modules: Vec<(SerializedModule, CString)>, symbols_below_threshold: &[*const libc::c_char], @@ -265,8 +266,13 @@ fn fat_lto( // The linking steps below may produce errors and diagnostics within LLVM // which we'd like to handle and print, so set up our diagnostic handlers // (which get unregistered when they go out of scope below). - let _handler = - DiagnosticHandlers::new(cgcx, dcx, llcx, &module, CodegenDiagnosticsStage::LTO); + let _handler = DiagnosticHandlers::new( + cgcx, + shared_emitter, + llcx, + &module, + CodegenDiagnosticsStage::LTO, + ); // For all other modules we codegened we'll need to link them into our own // bitcode. All modules were codegened in their own LLVM context, however, @@ -730,10 +736,11 @@ impl Drop for ThinBuffer { } pub(crate) fn optimize_thin_module( - thin_module: ThinModule, cgcx: &CodegenContext, + shared_emitter: &SharedEmitter, + thin_module: ThinModule, ) -> ModuleCodegen { - let dcx = cgcx.create_dcx(); + let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); let dcx = dcx.handle(); let module_name = &thin_module.shared.module_names[thin_module.idx]; diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index a49d4c010d67..a5b6ea08a66d 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -9,7 +9,7 @@ use libc::{c_char, c_int, c_void, size_t}; use rustc_codegen_ssa::back::link::ensure_removed; use rustc_codegen_ssa::back::versioned_llvm_target; use rustc_codegen_ssa::back::write::{ - BitcodeSection, CodegenContext, EmitObj, InlineAsmError, ModuleConfig, + BitcodeSection, CodegenContext, EmitObj, InlineAsmError, ModuleConfig, SharedEmitter, TargetMachineFactoryConfig, TargetMachineFactoryFn, }; use rustc_codegen_ssa::base::wants_wasm_eh; @@ -17,7 +17,7 @@ use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{CompiledModule, ModuleCodegen, ModuleKind}; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::small_c_str::SmallCStr; -use rustc_errors::{DiagCtxtHandle, Level}; +use rustc_errors::{DiagCtxt, DiagCtxtHandle, Level}; use rustc_fs_util::{link_or_copy, path_to_c_string}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; @@ -356,7 +356,7 @@ pub(crate) enum CodegenDiagnosticsStage { } pub(crate) struct DiagnosticHandlers<'a> { - data: *mut (&'a CodegenContext, DiagCtxtHandle<'a>), + data: *mut (&'a CodegenContext, &'a SharedEmitter), llcx: &'a llvm::Context, old_handler: Option<&'a llvm::DiagnosticHandler>, } @@ -364,7 +364,7 @@ pub(crate) struct DiagnosticHandlers<'a> { impl<'a> DiagnosticHandlers<'a> { pub(crate) fn new( cgcx: &'a CodegenContext, - dcx: DiagCtxtHandle<'a>, + shared_emitter: &'a SharedEmitter, llcx: &'a llvm::Context, module: &ModuleCodegen, stage: CodegenDiagnosticsStage, @@ -399,7 +399,7 @@ impl<'a> DiagnosticHandlers<'a> { .and_then(|dir| dir.to_str().and_then(|p| CString::new(p).ok())); let pgo_available = cgcx.module_config.pgo_use.is_some(); - let data = Box::into_raw(Box::new((cgcx, dcx))); + let data = Box::into_raw(Box::new((cgcx, shared_emitter))); unsafe { let old_handler = llvm::LLVMRustContextGetDiagnosticHandler(llcx); llvm::LLVMRustContextConfigureDiagnosticHandler( @@ -461,12 +461,16 @@ unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void if user.is_null() { return; } - let (cgcx, dcx) = - unsafe { *(user as *const (&CodegenContext, DiagCtxtHandle<'_>)) }; + let (cgcx, shared_emitter) = + unsafe { *(user as *const (&CodegenContext, &SharedEmitter)) }; + + let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); + let dcx = dcx.handle(); match unsafe { llvm::diagnostic::Diagnostic::unpack(info) } { llvm::diagnostic::InlineAsm(inline) => { - cgcx.diag_emitter.inline_asm_error(report_inline_asm( + // FIXME use dcx + shared_emitter.inline_asm_error(report_inline_asm( cgcx, inline.message, inline.level, @@ -777,14 +781,18 @@ pub(crate) unsafe fn llvm_optimize( // Unsafe due to LLVM calls. pub(crate) fn optimize( cgcx: &CodegenContext, - dcx: DiagCtxtHandle<'_>, + shared_emitter: &SharedEmitter, module: &mut ModuleCodegen, config: &ModuleConfig, ) { let _timer = cgcx.prof.generic_activity_with_arg("LLVM_module_optimize", &*module.name); + let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); + let dcx = dcx.handle(); + let llcx = &*module.module_llvm.llcx; - let _handlers = DiagnosticHandlers::new(cgcx, dcx, llcx, module, CodegenDiagnosticsStage::Opt); + let _handlers = + DiagnosticHandlers::new(cgcx, shared_emitter, llcx, module, CodegenDiagnosticsStage::Opt); if config.emit_no_opt_bc { let out = cgcx.output_filenames.temp_path_ext_for_cgu( @@ -865,19 +873,26 @@ pub(crate) fn optimize( pub(crate) fn codegen( cgcx: &CodegenContext, + shared_emitter: &SharedEmitter, module: ModuleCodegen, config: &ModuleConfig, ) -> CompiledModule { - let dcx = cgcx.create_dcx(); + let _timer = cgcx.prof.generic_activity_with_arg("LLVM_module_codegen", &*module.name); + + let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); let dcx = dcx.handle(); - let _timer = cgcx.prof.generic_activity_with_arg("LLVM_module_codegen", &*module.name); { let llmod = module.module_llvm.llmod(); let llcx = &*module.module_llvm.llcx; let tm = &*module.module_llvm.tm; - let _handlers = - DiagnosticHandlers::new(cgcx, dcx, llcx, &module, CodegenDiagnosticsStage::Codegen); + let _handlers = DiagnosticHandlers::new( + cgcx, + shared_emitter, + llcx, + &module, + CodegenDiagnosticsStage::Codegen, + ); if cgcx.msvc_imps_needed { create_msvc_imps(cgcx, llcx, llmod); diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 1b65a133d58c..3901662442e9 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -30,12 +30,13 @@ use llvm_util::target_config; use rustc_ast::expand::allocator::AllocatorMethod; use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule}; use rustc_codegen_ssa::back::write::{ - CodegenContext, FatLtoInput, ModuleConfig, TargetMachineFactoryConfig, TargetMachineFactoryFn, + CodegenContext, FatLtoInput, ModuleConfig, SharedEmitter, TargetMachineFactoryConfig, + TargetMachineFactoryFn, }; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen, TargetConfig}; use rustc_data_structures::fx::FxIndexMap; -use rustc_errors::DiagCtxtHandle; +use rustc_errors::{DiagCtxt, DiagCtxtHandle}; use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::ty::TyCtxt; @@ -166,14 +167,20 @@ impl WriteBackendMethods for LlvmCodegenBackend { } fn run_and_optimize_fat_lto( cgcx: &CodegenContext, + shared_emitter: &SharedEmitter, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], modules: Vec>, ) -> ModuleCodegen { - let mut module = - back::lto::run_fat(cgcx, exported_symbols_for_lto, each_linked_rlib_for_lto, modules); + let mut module = back::lto::run_fat( + cgcx, + shared_emitter, + exported_symbols_for_lto, + each_linked_rlib_for_lto, + modules, + ); - let dcx = cgcx.create_dcx(); + let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); let dcx = dcx.handle(); back::lto::run_pass_manager(cgcx, dcx, &mut module, false); @@ -181,6 +188,7 @@ impl WriteBackendMethods for LlvmCodegenBackend { } fn run_thin_lto( cgcx: &CodegenContext, + dcx: DiagCtxtHandle<'_>, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], modules: Vec<(String, Self::ThinBuffer)>, @@ -188,6 +196,7 @@ impl WriteBackendMethods for LlvmCodegenBackend { ) -> (Vec>, Vec) { back::lto::run_thin( cgcx, + dcx, exported_symbols_for_lto, each_linked_rlib_for_lto, modules, @@ -196,24 +205,26 @@ impl WriteBackendMethods for LlvmCodegenBackend { } fn optimize( cgcx: &CodegenContext, - dcx: DiagCtxtHandle<'_>, + shared_emitter: &SharedEmitter, module: &mut ModuleCodegen, config: &ModuleConfig, ) { - back::write::optimize(cgcx, dcx, module, config) + back::write::optimize(cgcx, shared_emitter, module, config) } fn optimize_thin( cgcx: &CodegenContext, + shared_emitter: &SharedEmitter, thin: ThinModule, ) -> ModuleCodegen { - back::lto::optimize_thin_module(thin, cgcx) + back::lto::optimize_thin_module(cgcx, shared_emitter, thin) } fn codegen( cgcx: &CodegenContext, + shared_emitter: &SharedEmitter, module: ModuleCodegen, config: &ModuleConfig, ) -> CompiledModule { - back::write::codegen(cgcx, module, config) + back::write::codegen(cgcx, shared_emitter, module, config) } fn prepare_thin(module: ModuleCodegen) -> (String, Self::ThinBuffer) { back::lto::prepare_thin(module) diff --git a/compiler/rustc_codegen_ssa/src/back/lto.rs b/compiler/rustc_codegen_ssa/src/back/lto.rs index e3dc985bf782..ef4c193c4c2a 100644 --- a/compiler/rustc_codegen_ssa/src/back/lto.rs +++ b/compiler/rustc_codegen_ssa/src/back/lto.rs @@ -2,6 +2,7 @@ use std::ffi::CString; use std::sync::Arc; use rustc_data_structures::memmap::Mmap; +use rustc_errors::DiagCtxtHandle; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportLevel}; use rustc_middle::ty::TyCtxt; @@ -124,14 +125,15 @@ pub(super) fn exported_symbols_for_lto( symbols_below_threshold } -pub(super) fn check_lto_allowed(cgcx: &CodegenContext) { +pub(super) fn check_lto_allowed( + cgcx: &CodegenContext, + dcx: DiagCtxtHandle<'_>, +) { if cgcx.lto == Lto::ThinLocal { // Crate local LTO is always allowed return; } - let dcx = cgcx.create_dcx(); - // Make sure we actually can run LTO for crate_type in cgcx.crate_types.iter() { if !crate_type_allows_lto(*crate_type) { diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index bc7c32931613..76f76c84dbdc 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -15,8 +15,8 @@ use rustc_data_structures::profiling::{SelfProfilerRef, VerboseTimingGuard}; use rustc_errors::emitter::Emitter; use rustc_errors::translation::Translator; use rustc_errors::{ - Diag, DiagArgMap, DiagCtxt, DiagMessage, ErrCode, FatalError, FatalErrorMarker, Level, - MultiSpan, Style, Suggestions, + Diag, DiagArgMap, DiagCtxt, DiagCtxtHandle, DiagMessage, ErrCode, FatalError, FatalErrorMarker, + Level, MultiSpan, Style, Suggestions, }; use rustc_fs_util::link_or_copy; use rustc_incremental::{ @@ -348,8 +348,6 @@ pub struct CodegenContext { pub split_dwarf_kind: rustc_session::config::SplitDwarfKind, pub pointer_size: Size, - /// Emitter to use for diagnostics produced during codegen. - pub diag_emitter: SharedEmitter, /// LLVM optimizations for which we want to print remarks. pub remark: Passes, /// Directory into which should the LLVM optimization remarks be written. @@ -364,14 +362,9 @@ pub struct CodegenContext { pub parallel: bool, } -impl CodegenContext { - pub fn create_dcx(&self) -> DiagCtxt { - DiagCtxt::new(Box::new(self.diag_emitter.clone())) - } -} - fn generate_thin_lto_work( cgcx: &CodegenContext, + dcx: DiagCtxtHandle<'_>, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], needs_thin_lto: Vec<(String, B::ThinBuffer)>, @@ -381,6 +374,7 @@ fn generate_thin_lto_work( let (lto_modules, copy_jobs) = B::run_thin_lto( cgcx, + dcx, exported_symbols_for_lto, each_linked_rlib_for_lto, needs_thin_lto, @@ -825,14 +819,12 @@ pub(crate) fn compute_per_cgu_lto_type( fn execute_optimize_work_item( cgcx: &CodegenContext, + shared_emitter: SharedEmitter, mut module: ModuleCodegen, ) -> WorkItemResult { let _timer = cgcx.prof.generic_activity_with_arg("codegen_module_optimize", &*module.name); - let dcx = cgcx.create_dcx(); - let dcx = dcx.handle(); - - B::optimize(cgcx, dcx, &mut module, &cgcx.module_config); + B::optimize(cgcx, &shared_emitter, &mut module, &cgcx.module_config); // After we've done the initial round of optimizations we need to // decide whether to synchronously codegen this module or ship it @@ -853,7 +845,7 @@ fn execute_optimize_work_item( match lto_type { ComputedLtoType::No => { - let module = B::codegen(cgcx, module, &cgcx.module_config); + let module = B::codegen(cgcx, &shared_emitter, module, &cgcx.module_config); WorkItemResult::Finished(module) } ComputedLtoType::Thin => { @@ -883,12 +875,16 @@ fn execute_optimize_work_item( fn execute_copy_from_cache_work_item( cgcx: &CodegenContext, + shared_emitter: SharedEmitter, module: CachedModuleCodegen, ) -> CompiledModule { let _timer = cgcx .prof .generic_activity_with_arg("codegen_copy_artifacts_from_incr_cache", &*module.name); + let dcx = DiagCtxt::new(Box::new(shared_emitter)); + let dcx = dcx.handle(); + let incr_comp_session_dir = cgcx.incr_comp_session_dir.as_ref().unwrap(); let mut links_from_incr_cache = Vec::new(); @@ -907,11 +903,7 @@ fn execute_copy_from_cache_work_item( Some(output_path) } Err(error) => { - cgcx.create_dcx().handle().emit_err(errors::CopyPathBuf { - source_file, - output_path, - error, - }); + dcx.emit_err(errors::CopyPathBuf { source_file, output_path, error }); None } } @@ -954,7 +946,7 @@ fn execute_copy_from_cache_work_item( let bytecode = load_from_incr_cache(module_config.emit_bc, OutputType::Bitcode); let object = load_from_incr_cache(should_emit_obj, OutputType::Object); if should_emit_obj && object.is_none() { - cgcx.create_dcx().handle().emit_fatal(errors::NoSavedObjectFile { cgu_name: &module.name }) + dcx.emit_fatal(errors::NoSavedObjectFile { cgu_name: &module.name }) } CompiledModule { @@ -971,6 +963,7 @@ fn execute_copy_from_cache_work_item( fn do_fat_lto( cgcx: &CodegenContext, + shared_emitter: SharedEmitter, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], mut needs_fat_lto: Vec>, @@ -978,7 +971,10 @@ fn do_fat_lto( ) -> CompiledModule { let _timer = cgcx.prof.verbose_generic_activity("LLVM_fatlto"); - check_lto_allowed(&cgcx); + let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); + let dcx = dcx.handle(); + + check_lto_allowed(&cgcx, dcx); for (module, wp) in import_only_modules { needs_fat_lto.push(FatLtoInput::Serialized { name: wp.cgu_name, buffer: module }) @@ -986,15 +982,17 @@ fn do_fat_lto( let module = B::run_and_optimize_fat_lto( cgcx, + &shared_emitter, exported_symbols_for_lto, each_linked_rlib_for_lto, needs_fat_lto, ); - B::codegen(cgcx, module, &cgcx.module_config) + B::codegen(cgcx, &shared_emitter, module, &cgcx.module_config) } fn do_thin_lto<'a, B: ExtraBackendMethods>( cgcx: &'a CodegenContext, + shared_emitter: SharedEmitter, exported_symbols_for_lto: Arc>, each_linked_rlib_for_lto: Vec, needs_thin_lto: Vec<(String, ::ThinBuffer)>, @@ -1005,7 +1003,10 @@ fn do_thin_lto<'a, B: ExtraBackendMethods>( ) -> Vec { let _timer = cgcx.prof.verbose_generic_activity("LLVM_thinlto"); - check_lto_allowed(&cgcx); + let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); + let dcx = dcx.handle(); + + check_lto_allowed(&cgcx, dcx); let (coordinator_send, coordinator_receive) = channel(); @@ -1030,6 +1031,7 @@ fn do_thin_lto<'a, B: ExtraBackendMethods>( // we don't worry about tokens. for (work, cost) in generate_thin_lto_work( cgcx, + dcx, &exported_symbols_for_lto, &each_linked_rlib_for_lto, needs_thin_lto, @@ -1071,7 +1073,7 @@ fn do_thin_lto<'a, B: ExtraBackendMethods>( while used_token_count < tokens.len() + 1 && let Some((item, _)) = work_items.pop() { - spawn_thin_lto_work(&cgcx, coordinator_send.clone(), item); + spawn_thin_lto_work(&cgcx, shared_emitter.clone(), coordinator_send.clone(), item); used_token_count += 1; } } else { @@ -1095,7 +1097,7 @@ fn do_thin_lto<'a, B: ExtraBackendMethods>( } Err(e) => { let msg = &format!("failed to acquire jobserver token: {e}"); - cgcx.diag_emitter.fatal(msg); + shared_emitter.fatal(msg); codegen_aborted = Some(FatalError); } }, @@ -1133,12 +1135,13 @@ fn do_thin_lto<'a, B: ExtraBackendMethods>( fn execute_thin_lto_work_item( cgcx: &CodegenContext, + shared_emitter: SharedEmitter, module: lto::ThinModule, ) -> CompiledModule { let _timer = cgcx.prof.generic_activity_with_arg("codegen_module_perform_lto", module.name()); - let module = B::optimize_thin(cgcx, module); - B::codegen(cgcx, module, &cgcx.module_config) + let module = B::optimize_thin(cgcx, &shared_emitter, module); + B::codegen(cgcx, &shared_emitter, module, &cgcx.module_config) } /// Messages sent to the coordinator. @@ -1291,7 +1294,6 @@ fn start_executing_work( remark: sess.opts.cg.remark.clone(), remark_dir, incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()), - diag_emitter: shared_emitter.clone(), output_filenames: Arc::clone(tcx.output_filenames(())), module_config: regular_config, tm_factory: backend.target_machine_factory(tcx.sess, ol, backend_features), @@ -1488,10 +1490,7 @@ fn start_executing_work( let mut llvm_start_time: Option> = None; if let Some(allocator_module) = &mut allocator_module { - let dcx = cgcx.create_dcx(); - let dcx = dcx.handle(); - - B::optimize(&cgcx, dcx, allocator_module, &allocator_config); + B::optimize(&cgcx, &shared_emitter, allocator_module, &allocator_config); } // Run the message loop while there's still anything that needs message @@ -1529,7 +1528,13 @@ fn start_executing_work( let (item, _) = work_items.pop().expect("queue empty - queue_full_enough() broken?"); main_thread_state = MainThreadState::Lending; - spawn_work(&cgcx, coordinator_send.clone(), &mut llvm_start_time, item); + spawn_work( + &cgcx, + shared_emitter.clone(), + coordinator_send.clone(), + &mut llvm_start_time, + item, + ); } } } else if codegen_state == Completed { @@ -1547,7 +1552,13 @@ fn start_executing_work( MainThreadState::Idle => { if let Some((item, _)) = work_items.pop() { main_thread_state = MainThreadState::Lending; - spawn_work(&cgcx, coordinator_send.clone(), &mut llvm_start_time, item); + spawn_work( + &cgcx, + shared_emitter.clone(), + coordinator_send.clone(), + &mut llvm_start_time, + item, + ); } else { // There is no unstarted work, so let the main thread // take over for a running worker. Otherwise the @@ -1583,7 +1594,13 @@ fn start_executing_work( while running_with_own_token < tokens.len() && let Some((item, _)) = work_items.pop() { - spawn_work(&cgcx, coordinator_send.clone(), &mut llvm_start_time, item); + spawn_work( + &cgcx, + shared_emitter.clone(), + coordinator_send.clone(), + &mut llvm_start_time, + item, + ); running_with_own_token += 1; } } @@ -1726,6 +1743,7 @@ fn start_executing_work( // This uses the implicit token let module = do_fat_lto( &cgcx, + shared_emitter.clone(), &exported_symbols_for_lto, &each_linked_rlib_file_for_lto, needs_fat_lto, @@ -1745,6 +1763,7 @@ fn start_executing_work( compiled_modules.extend(do_thin_lto( &cgcx, + shared_emitter.clone(), exported_symbols_for_lto, each_linked_rlib_file_for_lto, needs_thin_lto, @@ -1759,8 +1778,9 @@ fn start_executing_work( Ok(CompiledModules { modules: compiled_modules, - allocator_module: allocator_module - .map(|allocator_module| B::codegen(&cgcx, allocator_module, &allocator_config)), + allocator_module: allocator_module.map(|allocator_module| { + B::codegen(&cgcx, &shared_emitter, allocator_module, &allocator_config) + }), }) }) .expect("failed to spawn coordinator thread"); @@ -1829,6 +1849,7 @@ pub(crate) struct WorkerFatalError; fn spawn_work<'a, B: ExtraBackendMethods>( cgcx: &'a CodegenContext, + shared_emitter: SharedEmitter, coordinator_send: Sender>, llvm_start_time: &mut Option>, work: WorkItem, @@ -1841,10 +1862,10 @@ fn spawn_work<'a, B: ExtraBackendMethods>( B::spawn_named_thread(cgcx.time_trace, work.short_description(), move || { let result = std::panic::catch_unwind(AssertUnwindSafe(|| match work { - WorkItem::Optimize(m) => execute_optimize_work_item(&cgcx, m), - WorkItem::CopyPostLtoArtifacts(m) => { - WorkItemResult::Finished(execute_copy_from_cache_work_item(&cgcx, m)) - } + WorkItem::Optimize(m) => execute_optimize_work_item(&cgcx, shared_emitter, m), + WorkItem::CopyPostLtoArtifacts(m) => WorkItemResult::Finished( + execute_copy_from_cache_work_item(&cgcx, shared_emitter, m), + ), })); let msg = match result { @@ -1866,6 +1887,7 @@ fn spawn_work<'a, B: ExtraBackendMethods>( fn spawn_thin_lto_work<'a, B: ExtraBackendMethods>( cgcx: &'a CodegenContext, + shared_emitter: SharedEmitter, coordinator_send: Sender, work: ThinLtoWorkItem, ) { @@ -1873,8 +1895,10 @@ fn spawn_thin_lto_work<'a, B: ExtraBackendMethods>( B::spawn_named_thread(cgcx.time_trace, work.short_description(), move || { let result = std::panic::catch_unwind(AssertUnwindSafe(|| match work { - ThinLtoWorkItem::CopyPostLtoArtifacts(m) => execute_copy_from_cache_work_item(&cgcx, m), - ThinLtoWorkItem::ThinLto(m) => execute_thin_lto_work_item(&cgcx, m), + ThinLtoWorkItem::CopyPostLtoArtifacts(m) => { + execute_copy_from_cache_work_item(&cgcx, shared_emitter, m) + } + ThinLtoWorkItem::ThinLto(m) => execute_thin_lto_work_item(&cgcx, shared_emitter, m), })); let msg = match result { diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index 1ac1d7ef2e2e..e1d23841118c 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -4,7 +4,7 @@ use rustc_errors::DiagCtxtHandle; use rustc_middle::dep_graph::WorkProduct; use crate::back::lto::{SerializedModule, ThinModule}; -use crate::back::write::{CodegenContext, FatLtoInput, ModuleConfig}; +use crate::back::write::{CodegenContext, FatLtoInput, ModuleConfig, SharedEmitter}; use crate::{CompiledModule, ModuleCodegen}; pub trait WriteBackendMethods: Clone + 'static { @@ -19,6 +19,7 @@ pub trait WriteBackendMethods: Clone + 'static { /// if necessary and running any further optimizations fn run_and_optimize_fat_lto( cgcx: &CodegenContext, + shared_emitter: &SharedEmitter, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], modules: Vec>, @@ -28,6 +29,7 @@ pub trait WriteBackendMethods: Clone + 'static { /// can simply be copied over from the incr. comp. cache. fn run_thin_lto( cgcx: &CodegenContext, + dcx: DiagCtxtHandle<'_>, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], modules: Vec<(String, Self::ThinBuffer)>, @@ -37,16 +39,18 @@ pub trait WriteBackendMethods: Clone + 'static { fn print_statistics(&self); fn optimize( cgcx: &CodegenContext, - dcx: DiagCtxtHandle<'_>, + shared_emitter: &SharedEmitter, module: &mut ModuleCodegen, config: &ModuleConfig, ); fn optimize_thin( cgcx: &CodegenContext, + shared_emitter: &SharedEmitter, thin: ThinModule, ) -> ModuleCodegen; fn codegen( cgcx: &CodegenContext, + shared_emitter: &SharedEmitter, module: ModuleCodegen, config: &ModuleConfig, ) -> CompiledModule; From b13bb4b2da208c2c26d000354816400243df1b4a Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 21 Nov 2025 15:26:47 +0000 Subject: [PATCH 04/69] Move LTO to OngoingCodegen::join --- compiler/rustc_codegen_ssa/src/back/write.rs | 140 +++++++++++++++---- compiler/rustc_driver_impl/src/lib.rs | 20 +-- compiler/rustc_errors/src/lib.rs | 2 +- compiler/rustc_span/src/fatal_error.rs | 17 +++ 4 files changed, 131 insertions(+), 48 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 76f76c84dbdc..ddcd8decf303 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -16,7 +16,7 @@ use rustc_errors::emitter::Emitter; use rustc_errors::translation::Translator; use rustc_errors::{ Diag, DiagArgMap, DiagCtxt, DiagCtxtHandle, DiagMessage, ErrCode, FatalError, FatalErrorMarker, - Level, MultiSpan, Style, Suggestions, + Level, MultiSpan, Style, Suggestions, catch_fatal_errors, }; use rustc_fs_util::link_or_copy; use rustc_incremental::{ @@ -403,6 +403,29 @@ struct CompiledModules { allocator_module: Option, } +enum MaybeLtoModules { + NoLto { + modules: Vec, + allocator_module: Option, + }, + FatLto { + cgcx: CodegenContext, + exported_symbols_for_lto: Arc>, + each_linked_rlib_file_for_lto: Vec, + needs_fat_lto: Vec>, + lto_import_only_modules: + Vec<(SerializedModule<::ModuleBuffer>, WorkProduct)>, + }, + ThinLto { + cgcx: CodegenContext, + exported_symbols_for_lto: Arc>, + each_linked_rlib_file_for_lto: Vec, + needs_thin_lto: Vec<(String, ::ThinBuffer)>, + lto_import_only_modules: + Vec<(SerializedModule<::ModuleBuffer>, WorkProduct)>, + }, +} + fn need_bitcode_in_object(tcx: TyCtxt<'_>) -> bool { let sess = tcx.sess; sess.opts.cg.embed_bitcode @@ -1239,7 +1262,7 @@ fn start_executing_work( allocator_config: Arc, mut allocator_module: Option>, coordinator_send: Sender>, -) -> thread::JoinHandle> { +) -> thread::JoinHandle, ()>> { let sess = tcx.sess; let mut each_linked_rlib_for_lto = Vec::new(); @@ -1740,43 +1763,43 @@ fn start_executing_work( needs_fat_lto.push(FatLtoInput::InMemory(allocator_module)); } - // This uses the implicit token - let module = do_fat_lto( - &cgcx, - shared_emitter.clone(), - &exported_symbols_for_lto, - &each_linked_rlib_file_for_lto, + return Ok(MaybeLtoModules::FatLto { + cgcx, + exported_symbols_for_lto, + each_linked_rlib_file_for_lto, needs_fat_lto, lto_import_only_modules, - ); - compiled_modules.push(module); + }); } else if !needs_thin_lto.is_empty() || !lto_import_only_modules.is_empty() { assert!(compiled_modules.is_empty()); assert!(needs_fat_lto.is_empty()); - if cgcx.lto != Lto::ThinLocal { + if cgcx.lto == Lto::ThinLocal { + compiled_modules.extend(do_thin_lto( + &cgcx, + shared_emitter.clone(), + exported_symbols_for_lto, + each_linked_rlib_file_for_lto, + needs_thin_lto, + lto_import_only_modules, + )); + } else { if let Some(allocator_module) = allocator_module.take() { let (name, thin_buffer) = B::prepare_thin(allocator_module); needs_thin_lto.push((name, thin_buffer)); } - } - compiled_modules.extend(do_thin_lto( - &cgcx, - shared_emitter.clone(), - exported_symbols_for_lto, - each_linked_rlib_file_for_lto, - needs_thin_lto, - lto_import_only_modules, - )); + return Ok(MaybeLtoModules::ThinLto { + cgcx, + exported_symbols_for_lto, + each_linked_rlib_file_for_lto, + needs_thin_lto, + lto_import_only_modules, + }); + } } - // Regardless of what order these modules completed in, report them to - // the backend in the same order every time to ensure that we're handing - // out deterministic results. - compiled_modules.sort_by(|a, b| a.name.cmp(&b.name)); - - Ok(CompiledModules { + Ok(MaybeLtoModules::NoLto { modules: compiled_modules, allocator_module: allocator_module.map(|allocator_module| { B::codegen(&cgcx, &shared_emitter, allocator_module, &allocator_config) @@ -2074,13 +2097,13 @@ impl SharedEmitterMain { pub struct Coordinator { sender: Sender>, - future: Option>>, + future: Option, ()>>>, // Only used for the Message type. phantom: PhantomData, } impl Coordinator { - fn join(mut self) -> std::thread::Result> { + fn join(mut self) -> std::thread::Result, ()>> { self.future.take().unwrap().join() } } @@ -2111,8 +2134,9 @@ pub struct OngoingCodegen { impl OngoingCodegen { pub fn join(self, sess: &Session) -> (CodegenResults, FxIndexMap) { self.shared_emitter_main.check(sess, true); - let compiled_modules = sess.time("join_worker_thread", || match self.coordinator.join() { - Ok(Ok(compiled_modules)) => compiled_modules, + + let maybe_lto_modules = sess.time("join_worker_thread", || match self.coordinator.join() { + Ok(Ok(maybe_lto_modules)) => maybe_lto_modules, Ok(Err(())) => { sess.dcx().abort_if_errors(); panic!("expected abort due to worker thread errors") @@ -2124,6 +2148,62 @@ impl OngoingCodegen { sess.dcx().abort_if_errors(); + let (shared_emitter, shared_emitter_main) = SharedEmitter::new(); + + // Catch fatal errors to ensure shared_emitter_main.check() can emit the actual diagnostics + let compiled_modules = catch_fatal_errors(|| match maybe_lto_modules { + MaybeLtoModules::NoLto { modules, allocator_module } => { + drop(shared_emitter); + CompiledModules { modules, allocator_module } + } + MaybeLtoModules::FatLto { + cgcx, + exported_symbols_for_lto, + each_linked_rlib_file_for_lto, + needs_fat_lto, + lto_import_only_modules, + } => CompiledModules { + modules: vec![do_fat_lto( + &cgcx, + shared_emitter, + &exported_symbols_for_lto, + &each_linked_rlib_file_for_lto, + needs_fat_lto, + lto_import_only_modules, + )], + allocator_module: None, + }, + MaybeLtoModules::ThinLto { + cgcx, + exported_symbols_for_lto, + each_linked_rlib_file_for_lto, + needs_thin_lto, + lto_import_only_modules, + } => CompiledModules { + modules: do_thin_lto( + &cgcx, + shared_emitter, + exported_symbols_for_lto, + each_linked_rlib_file_for_lto, + needs_thin_lto, + lto_import_only_modules, + ), + allocator_module: None, + }, + }); + + shared_emitter_main.check(sess, true); + + sess.dcx().abort_if_errors(); + + let mut compiled_modules = + compiled_modules.expect("fatal error emitted but not sent to SharedEmitter"); + + // Regardless of what order these modules completed in, report them to + // the backend in the same order every time to ensure that we're handing + // out deterministic results. + compiled_modules.modules.sort_by(|a, b| a.name.cmp(&b.name)); + let work_products = copy_all_cgu_workproducts_to_incr_comp_cache_dir(sess, &compiled_modules); produce_final_output_artifacts(sess, &compiled_modules, &self.output_filenames); diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 9a3d7cc506cf..cf5a5fdd234c 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -19,7 +19,7 @@ use std::ffi::OsString; use std::fmt::Write as _; use std::fs::{self, File}; use std::io::{self, IsTerminal, Read, Write}; -use std::panic::{self, PanicHookInfo, catch_unwind}; +use std::panic::{self, PanicHookInfo}; use std::path::{Path, PathBuf}; use std::process::{self, Command, Stdio}; use std::sync::OnceLock; @@ -33,10 +33,11 @@ use rustc_codegen_ssa::{CodegenErrors, CodegenResults}; use rustc_data_structures::profiling::{ TimePassesFormat, get_resident_set_size, print_time_passes_entry, }; +pub use rustc_errors::catch_fatal_errors; use rustc_errors::emitter::stderr_destination; use rustc_errors::registry::Registry; use rustc_errors::translation::Translator; -use rustc_errors::{ColorConfig, DiagCtxt, ErrCode, FatalError, PResult, markdown}; +use rustc_errors::{ColorConfig, DiagCtxt, ErrCode, PResult, markdown}; use rustc_feature::find_gated_cfg; // This avoids a false positive with `-Wunused_crate_dependencies`. // `rust_index` isn't used in this crate's code, but it must be named in the @@ -1306,21 +1307,6 @@ fn parse_crate_attrs<'a>(sess: &'a Session) -> PResult<'a, ast::AttrVec> { parser.parse_inner_attributes() } -/// Runs a closure and catches unwinds triggered by fatal errors. -/// -/// The compiler currently unwinds with a special sentinel value to abort -/// compilation on fatal errors. This function catches that sentinel and turns -/// the panic into a `Result` instead. -pub fn catch_fatal_errors R, R>(f: F) -> Result { - catch_unwind(panic::AssertUnwindSafe(f)).map_err(|value| { - if value.is::() { - FatalError - } else { - panic::resume_unwind(value); - } - }) -} - /// Variant of `catch_fatal_errors` for the `interface::Result` return type /// that also computes the exit code. pub fn catch_with_exit_code(f: impl FnOnce()) -> i32 { diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index bbdda155496f..ada32bf0c283 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -68,7 +68,7 @@ pub use rustc_lint_defs::{Applicability, listify, pluralize}; use rustc_lint_defs::{Lint, LintExpectationId}; use rustc_macros::{Decodable, Encodable}; pub use rustc_span::ErrorGuaranteed; -pub use rustc_span::fatal_error::{FatalError, FatalErrorMarker}; +pub use rustc_span::fatal_error::{FatalError, FatalErrorMarker, catch_fatal_errors}; use rustc_span::source_map::SourceMap; use rustc_span::{BytePos, DUMMY_SP, Loc, Span}; pub use snippet::Style; diff --git a/compiler/rustc_span/src/fatal_error.rs b/compiler/rustc_span/src/fatal_error.rs index 26c5711099c6..5e2d82681a11 100644 --- a/compiler/rustc_span/src/fatal_error.rs +++ b/compiler/rustc_span/src/fatal_error.rs @@ -3,6 +3,8 @@ #[must_use] pub struct FatalError; +use std::panic; + pub use rustc_data_structures::FatalErrorMarker; // Don't implement Send on FatalError. This makes it impossible to `panic_any!(FatalError)`. @@ -22,3 +24,18 @@ impl std::fmt::Display for FatalError { } impl std::error::Error for FatalError {} + +/// Runs a closure and catches unwinds triggered by fatal errors. +/// +/// The compiler currently unwinds with a special sentinel value to abort +/// compilation on fatal errors. This function catches that sentinel and turns +/// the panic into a `Result` instead. +pub fn catch_fatal_errors R, R>(f: F) -> Result { + panic::catch_unwind(panic::AssertUnwindSafe(f)).map_err(|value| { + if value.is::() { + FatalError + } else { + panic::resume_unwind(value); + } + }) +} From d0d8258886dfb8c09d337b094ca1e6f26f85cd8b Mon Sep 17 00:00:00 2001 From: Urgau Date: Sun, 12 Oct 2025 17:19:17 +0200 Subject: [PATCH 05/69] Stabilize `-Zremap-path-scope` as `--remap-path-scope` In the process also document that new `--remap-path-scope` scopes may be added in the future, and that the `all` scope always represent all the scopes. Co-authored-by: David Wood --- compiler/rustc_session/src/config.rs | 48 ++++++++++++++++--- compiler/rustc_session/src/options.rs | 28 +---------- src/doc/rustc/src/command-line-arguments.md | 8 ++++ src/doc/rustc/src/remap-source-paths.md | 27 ++++++++++- .../src/compiler-flags/remap-path-scope.md | 23 --------- tests/coverage/remap-path-prefix.rs | 6 +-- ...emap-path-prefix.with_macro_scope.coverage | 6 +-- .../remap-path-prefix-consts/rmake.rs | 4 +- .../run-make/remap-path-prefix-dwarf/rmake.rs | 8 ++-- tests/run-make/remap-path-prefix/rmake.rs | 6 +-- tests/run-make/rustc-help/help-v.diff | 5 +- tests/run-make/rustc-help/help-v.stdout | 3 ++ tests/run-make/split-debuginfo/rmake.rs | 13 +++-- tests/ui/errors/auxiliary/file-debuginfo.rs | 2 +- tests/ui/errors/auxiliary/file-diag.rs | 2 +- tests/ui/errors/auxiliary/file-macro.rs | 2 +- tests/ui/errors/auxiliary/trait-debuginfo.rs | 2 +- tests/ui/errors/auxiliary/trait-diag.rs | 2 +- tests/ui/errors/auxiliary/trait-macro.rs | 2 +- .../errors/remap-path-prefix-diagnostics.rs | 10 ++-- tests/ui/errors/remap-path-prefix-macro.rs | 10 ++-- tests/ui/errors/remap-path-prefix.rs | 4 +- 22 files changed, 124 insertions(+), 97 deletions(-) delete mode 100644 src/doc/unstable-book/src/compiler-flags/remap-path-scope.md diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index be4b36e3b926..4d2ca8987df4 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -24,7 +24,9 @@ use rustc_hashes::Hash64; use rustc_macros::{BlobDecodable, Decodable, Encodable, HashStable_Generic}; use rustc_span::edition::{DEFAULT_EDITION, EDITION_NAME_LIST, Edition, LATEST_STABLE_EDITION}; use rustc_span::source_map::FilePathMapping; -use rustc_span::{FileName, RealFileName, SourceFileHashAlgorithm, Symbol, sym}; +use rustc_span::{ + FileName, RealFileName, RemapPathScopeComponents, SourceFileHashAlgorithm, Symbol, sym, +}; use rustc_target::spec::{ FramePointer, LinkSelfContainedComponents, LinkerFeatures, PanicStrategy, SplitDebuginfo, Target, TargetTuple, @@ -1315,6 +1317,29 @@ impl OutputFilenames { } } +pub(crate) fn parse_remap_path_scope( + early_dcx: &EarlyDiagCtxt, + matches: &getopts::Matches, +) -> RemapPathScopeComponents { + if let Some(v) = matches.opt_str("remap-path-scope") { + let mut slot = RemapPathScopeComponents::empty(); + for s in v.split(',') { + slot |= match s { + "macro" => RemapPathScopeComponents::MACRO, + "diagnostics" => RemapPathScopeComponents::DIAGNOSTICS, + "debuginfo" => RemapPathScopeComponents::DEBUGINFO, + "coverage" => RemapPathScopeComponents::COVERAGE, + "object" => RemapPathScopeComponents::OBJECT, + "all" => RemapPathScopeComponents::all(), + _ => early_dcx.early_fatal("argument for `--remap-path-scope` must be a comma separated list of scopes: `macro`, `diagnostics`, `debuginfo`, `coverage`, `object`, `all`"), + } + } + slot + } else { + RemapPathScopeComponents::all() + } +} + #[derive(Clone, Debug)] pub struct Sysroot { pub explicit: Option, @@ -1351,9 +1376,9 @@ pub fn host_tuple() -> &'static str { fn file_path_mapping( remap_path_prefix: Vec<(PathBuf, PathBuf)>, - unstable_opts: &UnstableOptions, + remap_path_scope: RemapPathScopeComponents, ) -> FilePathMapping { - FilePathMapping::new(remap_path_prefix.clone(), unstable_opts.remap_path_scope) + FilePathMapping::new(remap_path_prefix.clone(), remap_path_scope) } impl Default for Options { @@ -1365,7 +1390,7 @@ impl Default for Options { // to create a default working directory. let working_dir = { let working_dir = std::env::current_dir().unwrap(); - let file_mapping = file_path_mapping(Vec::new(), &unstable_opts); + let file_mapping = file_path_mapping(Vec::new(), RemapPathScopeComponents::empty()); file_mapping.to_real_filename(&RealFileName::empty(), &working_dir) }; @@ -1401,6 +1426,7 @@ impl Default for Options { cli_forced_codegen_units: None, cli_forced_local_thinlto_off: false, remap_path_prefix: Vec::new(), + remap_path_scope: RemapPathScopeComponents::all(), real_rust_source_base_dir: None, real_rustc_dev_source_base_dir: None, edition: DEFAULT_EDITION, @@ -1427,7 +1453,7 @@ impl Options { } pub fn file_path_mapping(&self) -> FilePathMapping { - file_path_mapping(self.remap_path_prefix.clone(), &self.unstable_opts) + file_path_mapping(self.remap_path_prefix.clone(), self.remap_path_scope) } /// Returns `true` if there will be an output file generated. @@ -1864,6 +1890,14 @@ pub fn rustc_optgroups() -> Vec { "Remap source names in all output (compiler messages and output files)", "=", ), + opt( + Stable, + Opt, + "", + "remap-path-scope", + "Defines which scopes of paths should be remapped by `--remap-path-prefix`", + "", + ), opt(Unstable, Multi, "", "env-set", "Inject an environment variable", "="), ]; options.extend(verbose_only.into_iter().map(|mut opt| { @@ -2704,6 +2738,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M let externs = parse_externs(early_dcx, matches, &unstable_opts); let remap_path_prefix = parse_remap_path_prefix(early_dcx, matches, &unstable_opts); + let remap_path_scope = parse_remap_path_scope(early_dcx, matches); let pretty = parse_pretty(early_dcx, &unstable_opts); @@ -2770,7 +2805,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M early_dcx.early_fatal(format!("Current directory is invalid: {e}")); }); - let file_mapping = file_path_mapping(remap_path_prefix.clone(), &unstable_opts); + let file_mapping = file_path_mapping(remap_path_prefix.clone(), remap_path_scope); file_mapping.to_real_filename(&RealFileName::empty(), &working_dir) }; @@ -2808,6 +2843,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M cli_forced_codegen_units: codegen_units, cli_forced_local_thinlto_off: disable_local_thinlto, remap_path_prefix, + remap_path_scope, real_rust_source_base_dir, real_rustc_dev_source_base_dir, edition, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index aea0b73ee927..42d62a7f8437 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -454,6 +454,8 @@ top_level_options!( /// Remap source path prefixes in all output (messages, object files, debug, etc.). remap_path_prefix: Vec<(PathBuf, PathBuf)> [TRACKED_NO_CRATE_HASH], + /// Defines which scopes of paths should be remapped by `--remap-path-prefix`. + remap_path_scope: RemapPathScopeComponents [TRACKED_NO_CRATE_HASH], /// Base directory containing the `library/` directory for the Rust standard library. /// Right now it's always `$sysroot/lib/rustlib/src/rust` @@ -872,7 +874,6 @@ mod desc { pub(crate) const parse_branch_protection: &str = "a `,` separated combination of `bti`, `gcs`, `pac-ret`, (optionally with `pc`, `b-key`, `leaf` if `pac-ret` is set)"; pub(crate) const parse_proc_macro_execution_strategy: &str = "one of supported execution strategies (`same-thread`, or `cross-thread`)"; - pub(crate) const parse_remap_path_scope: &str = "comma separated list of scopes: `macro`, `diagnostics`, `debuginfo`, `coverage`, `object`, `all`"; pub(crate) const parse_inlining_threshold: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or a non-negative number"; pub(crate) const parse_llvm_module_flag: &str = ":::. Type must currently be `u32`. Behavior should be one of (`error`, `warning`, `require`, `override`, `append`, `appendunique`, `max`, `min`)"; @@ -1711,29 +1712,6 @@ pub mod parse { true } - pub(crate) fn parse_remap_path_scope( - slot: &mut RemapPathScopeComponents, - v: Option<&str>, - ) -> bool { - if let Some(v) = v { - *slot = RemapPathScopeComponents::empty(); - for s in v.split(',') { - *slot |= match s { - "macro" => RemapPathScopeComponents::MACRO, - "diagnostics" => RemapPathScopeComponents::DIAGNOSTICS, - "debuginfo" => RemapPathScopeComponents::DEBUGINFO, - "coverage" => RemapPathScopeComponents::COVERAGE, - "object" => RemapPathScopeComponents::OBJECT, - "all" => RemapPathScopeComponents::all(), - _ => return false, - } - } - true - } else { - false - } - } - pub(crate) fn parse_relocation_model(slot: &mut Option, v: Option<&str>) -> bool { match v.and_then(|s| RelocModel::from_str(s).ok()) { Some(relocation_model) => *slot = Some(relocation_model), @@ -2584,8 +2562,6 @@ options! { "whether ELF relocations can be relaxed"), remap_cwd_prefix: Option = (None, parse_opt_pathbuf, [TRACKED], "remap paths under the current working directory to this path prefix"), - remap_path_scope: RemapPathScopeComponents = (RemapPathScopeComponents::all(), parse_remap_path_scope, [TRACKED], - "remap path scope (default: all)"), remark_dir: Option = (None, parse_opt_pathbuf, [UNTRACKED], "directory into which to write optimization remarks (if not specified, they will be \ written to standard error output)"), diff --git a/src/doc/rustc/src/command-line-arguments.md b/src/doc/rustc/src/command-line-arguments.md index 0b15fbc24dfc..1309ecd73679 100644 --- a/src/doc/rustc/src/command-line-arguments.md +++ b/src/doc/rustc/src/command-line-arguments.md @@ -428,6 +428,14 @@ specified multiple times. Refer to the [Remap source paths](remap-source-paths.md) section of this book for further details and explanation. + +## `--remap-path-scope`: remap source paths in output + +Defines which scopes of paths should be remapped by `--remap-path-prefix`. + +Refer to the [Remap source paths](remap-source-paths.md) section of this book for +further details and explanation. + ## `--json`: configure json messages printed by the compiler diff --git a/src/doc/rustc/src/remap-source-paths.md b/src/doc/rustc/src/remap-source-paths.md index 03f5d98091cc..d27359fb550f 100644 --- a/src/doc/rustc/src/remap-source-paths.md +++ b/src/doc/rustc/src/remap-source-paths.md @@ -6,7 +6,7 @@ output, including compiler diagnostics, debugging information, macro expansions, This is useful for normalizing build products, for example by removing the current directory out of the paths emitted into object files. -The remapping is done via the `--remap-path-prefix` option. +The remapping is done via the `--remap-path-prefix` flag and can be customized via the `--remap-path-scope` flag. ## `--remap-path-prefix` @@ -25,6 +25,31 @@ rustc --remap-path-prefix "/home/user/project=/redacted" This example replaces all occurrences of `/home/user/project` in emitted paths with `/redacted`. +## `--remap-path-scope` + +Defines which scopes of paths should be remapped by `--remap-path-prefix`. + +This flag accepts a comma-separated list of values and may be specified multiple times, in which case the scopes are aggregated together. + +The valid scopes are: + +- `macro` - apply remappings to the expansion of `std::file!()` macro. This is where paths in embedded panic messages come from +- `diagnostics` - apply remappings to printed compiler diagnostics +- `debuginfo` - apply remappings to debug information +- `coverage` - apply remappings to coverage information +- `object` - apply remappings to all paths in compiled executables or libraries, but not elsewhere. Currently an alias for `macro,coverage,debuginfo`. +- `all` (default) - an alias for all of the above, also equivalent to supplying only `--remap-path-prefix` without `--remap-path-scope`. + +The scopes accepted by `--remap-path-scope` are not exhaustive - new scopes may be added in future releases for eventual stabilisation. +This implies that the `all` scope can correspond to different scopes between releases. + +### Example + +```sh +# With `object` scope only the build outputs will be remapped, the diagnostics won't be remapped. +rustc --remap-path-prefix=$(PWD)=/remapped --remap-path-scope=object main.rs +``` + ## Caveats and Limitations ### Linkers generated paths diff --git a/src/doc/unstable-book/src/compiler-flags/remap-path-scope.md b/src/doc/unstable-book/src/compiler-flags/remap-path-scope.md deleted file mode 100644 index fb1c7d7a6878..000000000000 --- a/src/doc/unstable-book/src/compiler-flags/remap-path-scope.md +++ /dev/null @@ -1,23 +0,0 @@ -# `remap-path-scope` - -The tracking issue for this feature is: [#111540](https://github.com/rust-lang/rust/issues/111540). - ------------------------- - -When the `--remap-path-prefix` option is passed to rustc, source path prefixes in all output will be affected by default. -The `--remap-path-scope` argument can be used in conjunction with `--remap-path-prefix` to determine paths in which output context should be affected. -This flag accepts a comma-separated list of values and may be specified multiple times, in which case the scopes are aggregated together. The valid scopes are: - -- `macro` - apply remappings to the expansion of `std::file!()` macro. This is where paths in embedded panic messages come from -- `diagnostics` - apply remappings to printed compiler diagnostics -- `debuginfo` - apply remappings to debug information -- `coverage` - apply remappings to coverage information -- `object` - apply remappings to all paths in compiled executables or libraries, but not elsewhere. Currently an alias for `macro,debuginfo`. -- `all` - an alias for all of the above, also equivalent to supplying only `--remap-path-prefix` without `--remap-path-scope`. - -## Example -```sh -# This would produce an absolute path to main.rs in build outputs of -# "./main.rs". -rustc --remap-path-prefix=$(PWD)=/remapped -Zremap-path-scope=object main.rs -``` diff --git a/tests/coverage/remap-path-prefix.rs b/tests/coverage/remap-path-prefix.rs index 29c5826989c4..031b87b8f771 100644 --- a/tests/coverage/remap-path-prefix.rs +++ b/tests/coverage/remap-path-prefix.rs @@ -10,8 +10,8 @@ //@ revisions: with_remap with_coverage_scope with_object_scope with_macro_scope //@ compile-flags: --remap-path-prefix={{src-base}}=remapped // -//@[with_coverage_scope] compile-flags: -Zremap-path-scope=coverage -//@[with_object_scope] compile-flags: -Zremap-path-scope=object -//@[with_macro_scope] compile-flags: -Zremap-path-scope=macro +//@[with_coverage_scope] compile-flags: --remap-path-scope=coverage +//@[with_object_scope] compile-flags: --remap-path-scope=object +//@[with_macro_scope] compile-flags: --remap-path-scope=macro fn main() {} diff --git a/tests/coverage/remap-path-prefix.with_macro_scope.coverage b/tests/coverage/remap-path-prefix.with_macro_scope.coverage index 63979d8fe15a..9e8317672f90 100644 --- a/tests/coverage/remap-path-prefix.with_macro_scope.coverage +++ b/tests/coverage/remap-path-prefix.with_macro_scope.coverage @@ -10,9 +10,9 @@ LL| |//@ revisions: with_remap with_coverage_scope with_object_scope with_macro_scope LL| |//@ compile-flags: --remap-path-prefix={{src-base}}=remapped LL| |// - LL| |//@[with_coverage_scope] compile-flags: -Zremap-path-scope=coverage - LL| |//@[with_object_scope] compile-flags: -Zremap-path-scope=object - LL| |//@[with_macro_scope] compile-flags: -Zremap-path-scope=macro + LL| |//@[with_coverage_scope] compile-flags: --remap-path-scope=coverage + LL| |//@[with_object_scope] compile-flags: --remap-path-scope=object + LL| |//@[with_macro_scope] compile-flags: --remap-path-scope=macro LL| | LL| 1|fn main() {} diff --git a/tests/run-make/remap-path-prefix-consts/rmake.rs b/tests/run-make/remap-path-prefix-consts/rmake.rs index d07a5e00768a..07b5e2f97414 100644 --- a/tests/run-make/remap-path-prefix-consts/rmake.rs +++ b/tests/run-make/remap-path-prefix-consts/rmake.rs @@ -97,7 +97,7 @@ fn main() { location_caller .crate_type("lib") .remap_path_prefix(cwd(), "/remapped") - .arg("-Zremap-path-scope=object") + .arg("--remap-path-scope=object") .input(cwd().join("location-caller.rs")); location_caller.run(); @@ -105,7 +105,7 @@ fn main() { runner .crate_type("bin") .remap_path_prefix(cwd(), "/remapped") - .arg("-Zremap-path-scope=diagnostics") + .arg("--remap-path-scope=diagnostics") .input(cwd().join("runner.rs")) .output(&runner_bin); runner.run(); diff --git a/tests/run-make/remap-path-prefix-dwarf/rmake.rs b/tests/run-make/remap-path-prefix-dwarf/rmake.rs index 3b88fca0bb7f..ab6c1fb70d68 100644 --- a/tests/run-make/remap-path-prefix-dwarf/rmake.rs +++ b/tests/run-make/remap-path-prefix-dwarf/rmake.rs @@ -105,7 +105,7 @@ fn check_dwarf_deps(scope: &str, dwarf_test: DwarfDump) { let mut rustc_sm = rustc(); rustc_sm.input(cwd().join("src/some_value.rs")); rustc_sm.arg("-Cdebuginfo=2"); - rustc_sm.arg(format!("-Zremap-path-scope={}", scope)); + rustc_sm.arg(format!("--remap-path-scope={}", scope)); rustc_sm.arg("--remap-path-prefix"); rustc_sm.arg(format!("{}=/REMAPPED", cwd().display())); rustc_sm.arg("-Csplit-debuginfo=off"); @@ -117,7 +117,7 @@ fn check_dwarf_deps(scope: &str, dwarf_test: DwarfDump) { rustc_pv.input(cwd().join("src/print_value.rs")); rustc_pv.output(&print_value_rlib); rustc_pv.arg("-Cdebuginfo=2"); - rustc_pv.arg(format!("-Zremap-path-scope={}", scope)); + rustc_pv.arg(format!("--remap-path-scope={}", scope)); rustc_pv.arg("--remap-path-prefix"); rustc_pv.arg(format!("{}=/REMAPPED", cwd().display())); rustc_pv.arg("-Csplit-debuginfo=off"); @@ -158,8 +158,8 @@ fn check_dwarf(test: DwarfTest) { rustc.arg("-Cdebuginfo=2"); if let Some(scope) = test.scope { match scope { - ScopeType::Object => rustc.arg("-Zremap-path-scope=object"), - ScopeType::Diagnostics => rustc.arg("-Zremap-path-scope=diagnostics"), + ScopeType::Object => rustc.arg("--remap-path-scope=object"), + ScopeType::Diagnostics => rustc.arg("--remap-path-scope=diagnostics"), }; if is_darwin() { rustc.arg("-Csplit-debuginfo=off"); diff --git a/tests/run-make/remap-path-prefix/rmake.rs b/tests/run-make/remap-path-prefix/rmake.rs index b75ca9e796ac..22ffd4f1f0d1 100644 --- a/tests/run-make/remap-path-prefix/rmake.rs +++ b/tests/run-make/remap-path-prefix/rmake.rs @@ -38,9 +38,9 @@ fn main() { rmeta_contains("/the/aux/lib.rs"); rmeta_not_contains("auxiliary"); - out_object.arg("-Zremap-path-scope=object"); - out_macro.arg("-Zremap-path-scope=macro"); - out_diagobj.arg("-Zremap-path-scope=diagnostics,object"); + out_object.arg("--remap-path-scope=object"); + out_macro.arg("--remap-path-scope=macro"); + out_diagobj.arg("--remap-path-scope=diagnostics,object"); if is_darwin() { out_object.arg("-Csplit-debuginfo=off"); out_macro.arg("-Csplit-debuginfo=off"); diff --git a/tests/run-make/rustc-help/help-v.diff b/tests/run-make/rustc-help/help-v.diff index 60a9dfbe201d..94ed6a0ed027 100644 --- a/tests/run-make/rustc-help/help-v.diff +++ b/tests/run-make/rustc-help/help-v.diff @@ -1,4 +1,4 @@ -@@ -65,10 +65,28 @@ +@@ -65,10 +65,31 @@ Set a codegen option -V, --version Print version info and exit -v, --verbose Use verbose output @@ -20,6 +20,9 @@ + --remap-path-prefix = + Remap source names in all output (compiler messages + and output files) ++ --remap-path-scope ++ Defines which scopes of paths should be remapped by ++ `--remap-path-prefix` + @path Read newline separated options from `path` Additional help: diff --git a/tests/run-make/rustc-help/help-v.stdout b/tests/run-make/rustc-help/help-v.stdout index c41cb5e3bde8..fb08d114ad1a 100644 --- a/tests/run-make/rustc-help/help-v.stdout +++ b/tests/run-make/rustc-help/help-v.stdout @@ -83,6 +83,9 @@ Options: --remap-path-prefix = Remap source names in all output (compiler messages and output files) + --remap-path-scope + Defines which scopes of paths should be remapped by + `--remap-path-prefix` @path Read newline separated options from `path` Additional help: diff --git a/tests/run-make/split-debuginfo/rmake.rs b/tests/run-make/split-debuginfo/rmake.rs index e53b71010781..0d311607a11a 100644 --- a/tests/run-make/split-debuginfo/rmake.rs +++ b/tests/run-make/split-debuginfo/rmake.rs @@ -171,8 +171,7 @@ enum RemapPathPrefix { Unspecified, } -/// `-Zremap-path-scope`. See -/// . +/// `--remap-path-scope` #[derive(Debug, Clone)] enum RemapPathScope { /// Comma-separated list of remap scopes: `macro`, `diagnostics`, `debuginfo`, `object`, `all`. @@ -921,7 +920,7 @@ mod shared_linux_other_tests { .debuginfo(level.cli_value()) .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) .remap_path_prefix(cwd(), remapped_prefix) - .arg(format!("-Zremap-path-scope={scope}")) + .arg(format!("--remap-path-scope={scope}")) .run(); let found_files = cwd_filenames(); FileAssertions { expected_files: BTreeSet::from(["foo", "foo.dwp"]) } @@ -950,7 +949,7 @@ mod shared_linux_other_tests { .debuginfo(level.cli_value()) .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) .remap_path_prefix(cwd(), remapped_prefix) - .arg(format!("-Zremap-path-scope={scope}")) + .arg(format!("--remap-path-scope={scope}")) .run(); let found_files = cwd_filenames(); FileAssertions { expected_files: BTreeSet::from(["foo", "foo.dwp"]) } @@ -1202,7 +1201,7 @@ mod shared_linux_other_tests { .debuginfo(level.cli_value()) .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) .remap_path_prefix(cwd(), remapped_prefix) - .arg(format!("-Zremap-path-scope={scope}")) + .arg(format!("--remap-path-scope={scope}")) .run(); let found_files = cwd_filenames(); @@ -1242,7 +1241,7 @@ mod shared_linux_other_tests { .debuginfo(level.cli_value()) .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) .remap_path_prefix(cwd(), remapped_prefix) - .arg(format!("-Zremap-path-scope={scope}")) + .arg(format!("--remap-path-scope={scope}")) .run(); let found_files = cwd_filenames(); @@ -1356,7 +1355,7 @@ fn main() { // NOTE: these combinations are not exhaustive, because while porting to rmake.rs initially I // tried to preserve the existing test behavior closely. Notably, no attempt was made to // exhaustively cover all cases in the 6-fold Cartesian product of `{,-Csplit=debuginfo=...}` x - // `{,-Cdebuginfo=...}` x `{,--remap-path-prefix}` x `{,-Zremap-path-scope=...}` x + // `{,-Cdebuginfo=...}` x `{,--remap-path-prefix}` x `{,--remap-path-scope=...}` x // `{,-Zsplit-dwarf-kind=...}` x `{,-Clinker-plugin-lto}`. If you really want to, you can // identify which combination isn't exercised with a 6-layers nested for loop iterating through // each of the cli flag enum variants. diff --git a/tests/ui/errors/auxiliary/file-debuginfo.rs b/tests/ui/errors/auxiliary/file-debuginfo.rs index 08113ec26bfd..3c95512d8cc2 100644 --- a/tests/ui/errors/auxiliary/file-debuginfo.rs +++ b/tests/ui/errors/auxiliary/file-debuginfo.rs @@ -1,5 +1,5 @@ //@ compile-flags: --remap-path-prefix={{src-base}}=remapped -//@ compile-flags: -Zremap-path-scope=debuginfo +//@ compile-flags: --remap-path-scope=debuginfo #[macro_export] macro_rules! my_file { diff --git a/tests/ui/errors/auxiliary/file-diag.rs b/tests/ui/errors/auxiliary/file-diag.rs index f29c349f703b..61fc9d2b4829 100644 --- a/tests/ui/errors/auxiliary/file-diag.rs +++ b/tests/ui/errors/auxiliary/file-diag.rs @@ -1,5 +1,5 @@ //@ compile-flags: --remap-path-prefix={{src-base}}=remapped -//@ compile-flags: -Zremap-path-scope=diagnostics +//@ compile-flags: --remap-path-scope=diagnostics #[macro_export] macro_rules! my_file { diff --git a/tests/ui/errors/auxiliary/file-macro.rs b/tests/ui/errors/auxiliary/file-macro.rs index 11abc0549a7b..11e5de1e3274 100644 --- a/tests/ui/errors/auxiliary/file-macro.rs +++ b/tests/ui/errors/auxiliary/file-macro.rs @@ -1,5 +1,5 @@ //@ compile-flags: --remap-path-prefix={{src-base}}=remapped -//@ compile-flags: -Zremap-path-scope=macro +//@ compile-flags: --remap-path-scope=macro #[macro_export] macro_rules! my_file { diff --git a/tests/ui/errors/auxiliary/trait-debuginfo.rs b/tests/ui/errors/auxiliary/trait-debuginfo.rs index d5a0825fe6d1..dbe3d09d317d 100644 --- a/tests/ui/errors/auxiliary/trait-debuginfo.rs +++ b/tests/ui/errors/auxiliary/trait-debuginfo.rs @@ -1,4 +1,4 @@ //@ compile-flags: --remap-path-prefix={{src-base}}=remapped -//@ compile-flags: -Zremap-path-scope=debuginfo +//@ compile-flags: --remap-path-scope=debuginfo pub trait Trait: std::fmt::Display {} diff --git a/tests/ui/errors/auxiliary/trait-diag.rs b/tests/ui/errors/auxiliary/trait-diag.rs index e07961a276a9..3a30366683c4 100644 --- a/tests/ui/errors/auxiliary/trait-diag.rs +++ b/tests/ui/errors/auxiliary/trait-diag.rs @@ -1,4 +1,4 @@ //@ compile-flags: --remap-path-prefix={{src-base}}=remapped -//@ compile-flags: -Zremap-path-scope=diagnostics +//@ compile-flags: --remap-path-scope=diagnostics pub trait Trait: std::fmt::Display {} diff --git a/tests/ui/errors/auxiliary/trait-macro.rs b/tests/ui/errors/auxiliary/trait-macro.rs index 48673d04ee16..334b1c9bba2f 100644 --- a/tests/ui/errors/auxiliary/trait-macro.rs +++ b/tests/ui/errors/auxiliary/trait-macro.rs @@ -1,4 +1,4 @@ //@ compile-flags: --remap-path-prefix={{src-base}}=remapped -//@ compile-flags: -Zremap-path-scope=macro +//@ compile-flags: --remap-path-scope=macro pub trait Trait: std::fmt::Display {} diff --git a/tests/ui/errors/remap-path-prefix-diagnostics.rs b/tests/ui/errors/remap-path-prefix-diagnostics.rs index fac7e937cb0b..54dbcfd64711 100644 --- a/tests/ui/errors/remap-path-prefix-diagnostics.rs +++ b/tests/ui/errors/remap-path-prefix-diagnostics.rs @@ -1,4 +1,4 @@ -// This test exercises `-Zremap-path-scope`, diagnostics printing paths and dependency. +// This test exercises `--remap-path-scope`, diagnostics printing paths and dependency. // // We test different combinations with/without remap in deps, with/without remap in this // crate but always in deps and always here but never in deps. @@ -12,10 +12,10 @@ //@[with-debuginfo-in-deps] compile-flags: --remap-path-prefix={{src-base}}=remapped //@[not-diag-in-deps] compile-flags: --remap-path-prefix={{src-base}}=remapped -//@[with-diag-in-deps] compile-flags: -Zremap-path-scope=diagnostics -//@[with-macro-in-deps] compile-flags: -Zremap-path-scope=macro -//@[with-debuginfo-in-deps] compile-flags: -Zremap-path-scope=debuginfo -//@[not-diag-in-deps] compile-flags: -Zremap-path-scope=diagnostics +//@[with-diag-in-deps] compile-flags: --remap-path-scope=diagnostics +//@[with-macro-in-deps] compile-flags: --remap-path-scope=macro +//@[with-debuginfo-in-deps] compile-flags: --remap-path-scope=debuginfo +//@[not-diag-in-deps] compile-flags: --remap-path-scope=diagnostics //@[with-diag-in-deps] aux-build:trait-diag.rs //@[with-macro-in-deps] aux-build:trait-macro.rs diff --git a/tests/ui/errors/remap-path-prefix-macro.rs b/tests/ui/errors/remap-path-prefix-macro.rs index 3e93843f9164..1f895aeeb6b4 100644 --- a/tests/ui/errors/remap-path-prefix-macro.rs +++ b/tests/ui/errors/remap-path-prefix-macro.rs @@ -1,4 +1,4 @@ -// This test exercises `-Zremap-path-scope`, macros (like file!()) and dependency. +// This test exercises `--remap-path-scope`, macros (like file!()) and dependency. // // We test different combinations with/without remap in deps, with/without remap in // this crate but always in deps and always here but never in deps. @@ -15,10 +15,10 @@ //@[with-debuginfo-in-deps] compile-flags: --remap-path-prefix={{src-base}}=remapped //@[not-macro-in-deps] compile-flags: --remap-path-prefix={{src-base}}=remapped -//@[with-diag-in-deps] compile-flags: -Zremap-path-scope=diagnostics -//@[with-macro-in-deps] compile-flags: -Zremap-path-scope=macro -//@[with-debuginfo-in-deps] compile-flags: -Zremap-path-scope=debuginfo -//@[not-macro-in-deps] compile-flags: -Zremap-path-scope=macro +//@[with-diag-in-deps] compile-flags: --remap-path-scope=diagnostics +//@[with-macro-in-deps] compile-flags: --remap-path-scope=macro +//@[with-debuginfo-in-deps] compile-flags: --remap-path-scope=debuginfo +//@[not-macro-in-deps] compile-flags: --remap-path-scope=macro //@[with-diag-in-deps] aux-build:file-diag.rs //@[with-macro-in-deps] aux-build:file-macro.rs diff --git a/tests/ui/errors/remap-path-prefix.rs b/tests/ui/errors/remap-path-prefix.rs index de18aa8cc204..b49514711355 100644 --- a/tests/ui/errors/remap-path-prefix.rs +++ b/tests/ui/errors/remap-path-prefix.rs @@ -1,7 +1,7 @@ //@ revisions: normal with-diagnostic-scope without-diagnostic-scope //@ compile-flags: --remap-path-prefix={{src-base}}=remapped -//@ [with-diagnostic-scope]compile-flags: -Zremap-path-scope=diagnostics -//@ [without-diagnostic-scope]compile-flags: -Zremap-path-scope=object +//@ [with-diagnostic-scope]compile-flags: --remap-path-scope=diagnostics +//@ [without-diagnostic-scope]compile-flags: --remap-path-scope=object // Manually remap, so the remapped path remains in .stderr file. // The remapped paths are not normalized by compiletest. From 97a06feab0869c9a57a3a63c840d9e3e6664aea8 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 12 Jan 2026 22:33:08 +0200 Subject: [PATCH 06/69] add range-diff triagebot option --- src/doc/rustc-dev-guide/triagebot.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/doc/rustc-dev-guide/triagebot.toml b/src/doc/rustc-dev-guide/triagebot.toml index 974f4cd3dd96..f894456d0de3 100644 --- a/src/doc/rustc-dev-guide/triagebot.toml +++ b/src/doc/rustc-dev-guide/triagebot.toml @@ -86,3 +86,6 @@ rustc-dev-guide = [ "@jyn514", "@tshepang", ] + +# Make rebases more easy to read: https://forge.rust-lang.org/triagebot/range-diff.html +[range-diff] From 513b6ad231859f0b3c7953c454f9b209d58c25a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 13 Jan 2026 17:41:55 +0100 Subject: [PATCH 07/69] Remove references to homu and fix bors delegation command --- src/doc/rustc-dev-guide/src/about-this-guide.md | 4 ++-- src/doc/rustc-dev-guide/src/compiler-team.md | 4 ++-- src/doc/rustc-dev-guide/src/contributing.md | 2 +- src/doc/rustc-dev-guide/src/tests/ci.md | 11 +++-------- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/about-this-guide.md b/src/doc/rustc-dev-guide/src/about-this-guide.md index 2082481a200e..9d493e0cb065 100644 --- a/src/doc/rustc-dev-guide/src/about-this-guide.md +++ b/src/doc/rustc-dev-guide/src/about-this-guide.md @@ -103,9 +103,9 @@ You might also find the following sites useful: [tlgba]: https://tomlee.co/2014/04/a-more-detailed-tour-of-the-rust-compiler/ [ro]: https://www.rustaceans.org/ [rctd]: tests/intro.md -[cheatsheet]: https://bors.rust-lang.org/ +[cheatsheet]: https://bors.rust-lang.org/help [Miri]: https://github.com/rust-lang/miri -[@bors]: https://github.com/bors +[@bors]: https://github.com/rust-lang/bors [a GitHub repository]: https://github.com/rust-lang/rustc-dev-guide/ [rustc API docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle [Forge]: https://forge.rust-lang.org/ diff --git a/src/doc/rustc-dev-guide/src/compiler-team.md b/src/doc/rustc-dev-guide/src/compiler-team.md index dfc1e7eedf62..495bd22da4d8 100644 --- a/src/doc/rustc-dev-guide/src/compiler-team.md +++ b/src/doc/rustc-dev-guide/src/compiler-team.md @@ -96,9 +96,9 @@ Once you have made a number of individual PRs to rustc, we will often offer r+ privileges. This means that you have the right to instruct "bors" (the robot that manages which PRs get landed into rustc) to merge a PR -([here are some instructions for how to talk to bors][homu-guide]). +([here are some instructions for how to talk to bors][bors-guide]). -[homu-guide]: https://bors.rust-lang.org/ +[bors-guide]: https://bors.rust-lang.org/ The guidelines for reviewers are as follows: diff --git a/src/doc/rustc-dev-guide/src/contributing.md b/src/doc/rustc-dev-guide/src/contributing.md index 40ad58e025c1..46d0dc23394a 100644 --- a/src/doc/rustc-dev-guide/src/contributing.md +++ b/src/doc/rustc-dev-guide/src/contributing.md @@ -283,7 +283,7 @@ this can take a while and the queue can sometimes be long. Also, note that PRs are never merged by hand. [@rustbot]: https://github.com/rustbot -[@bors]: https://github.com/bors +[@bors]: https://github.com/rust-lang/bors ### Opening a PR diff --git a/src/doc/rustc-dev-guide/src/tests/ci.md b/src/doc/rustc-dev-guide/src/tests/ci.md index e132946ae83e..ce80b07fe08d 100644 --- a/src/doc/rustc-dev-guide/src/tests/ci.md +++ b/src/doc/rustc-dev-guide/src/tests/ci.md @@ -198,7 +198,7 @@ to help make the perf comparison as fair as possible. > > 3. Run the prescribed try jobs with `@bors try`. As aforementioned, this > requires the user to either (1) have `try` permissions or (2) be delegated -> with `try` permissions by `@bors delegate` by someone who has `try` +> with `try` permissions by `@bors delegate=try` by someone who has `try` > permissions. > > Note that this is usually easier to do than manually edit [`jobs.yml`]. @@ -213,10 +213,7 @@ the corresponding PR. Multiple try builds can execute concurrently across different PRs, but there can be at most a single try build running on a single PR at any given time. -Note that try builds are handled using the [new bors] implementation. - [rustc-perf]: https://github.com/rust-lang/rustc-perf -[new bors]: https://github.com/rust-lang/bors ### Modifying CI jobs @@ -281,8 +278,7 @@ Breakages like these usually happen when another, incompatible PR is merged after the build happened. To ensure a `main` branch that works all the time, we forbid manual merges. -Instead, all PRs have to be approved through our bot, [bors] (the software -behind it is called [homu]). +Instead, all PRs have to be approved through our bot, [bors]. All the approved PRs are put in a [merge queue] (sorted by priority and creation date) and are automatically tested one at the time. If all the builders are green, the PR is merged, otherwise the failure is @@ -465,8 +461,7 @@ To do this: [`jobs.yml`]: https://github.com/rust-lang/rust/blob/HEAD/src/ci/github-actions/jobs.yml [`.github/workflows/ci.yml`]: https://github.com/rust-lang/rust/blob/HEAD/.github/workflows/ci.yml [`src/ci/citool`]: https://github.com/rust-lang/rust/blob/HEAD/src/ci/citool -[bors]: https://github.com/bors -[homu]: https://github.com/rust-lang/homu +[bors]: https://github.com/rust-lang/bors [merge queue]: https://bors.rust-lang.org/queue/rust [dist-x86_64-linux]: https://github.com/rust-lang/rust/blob/HEAD/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile [the GitHub Actions workflows page]: https://github.com/rust-lang/rust/actions From 348bfe3e355a4479287d3940ca6b87f9be967040 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 13 Jan 2026 10:54:33 -0800 Subject: [PATCH 08/69] compiler: upgrade to hashbrown 0.16.1 See also #135634, #149159, and rust-lang/hashbrown#662. This includes an in-tree upgrade of `indexmap` as well, which uses the new `HashTable` buckets API internally, hopefully impacting performance for the better! --- Cargo.lock | 19 +++++++++++-------- compiler/rustc_data_structures/Cargo.toml | 4 ++-- compiler/rustc_mir_transform/Cargo.toml | 2 +- compiler/rustc_query_system/Cargo.toml | 2 +- src/bootstrap/src/utils/proc_macro_deps.rs | 1 + 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bf28939ac87b..45d01c486faf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1670,9 +1670,12 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "foldhash 0.2.0", +] [[package]] name = "heck" @@ -1950,12 +1953,12 @@ checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" [[package]] name = "indexmap" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "serde", "serde_core", ] @@ -3730,7 +3733,7 @@ dependencies = [ "either", "elsa", "ena", - "hashbrown 0.15.5", + "hashbrown 0.16.1", "indexmap", "jobserver", "libc", @@ -4346,7 +4349,7 @@ name = "rustc_mir_transform" version = "0.0.0" dependencies = [ "either", - "hashbrown 0.15.5", + "hashbrown 0.16.1", "itertools", "rustc_abi", "rustc_arena", @@ -4557,7 +4560,7 @@ dependencies = [ name = "rustc_query_system" version = "0.0.0" dependencies = [ - "hashbrown 0.15.5", + "hashbrown 0.16.1", "parking_lot", "rustc_abi", "rustc_ast", diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index c8296e05f6bd..f358ffffb47d 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -10,7 +10,7 @@ bitflags = "2.4.1" either = "1.0" elsa = "1.11.0" ena = "0.14.3" -indexmap = "2.4.0" +indexmap = "2.12.1" jobserver_crate = { version = "0.1.28", package = "jobserver" } measureme = "12.0.1" parking_lot = "0.12" @@ -31,7 +31,7 @@ tracing = "0.1" # tidy-alphabetical-end [dependencies.hashbrown] -version = "0.15.2" +version = "0.16.1" default-features = false features = ["nightly"] # for may_dangle diff --git a/compiler/rustc_mir_transform/Cargo.toml b/compiler/rustc_mir_transform/Cargo.toml index 6df7a869ca28..22de197d374a 100644 --- a/compiler/rustc_mir_transform/Cargo.toml +++ b/compiler/rustc_mir_transform/Cargo.toml @@ -6,7 +6,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start either = "1" -hashbrown = "0.15" +hashbrown = { version = "0.16.1", default-features = false } itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } diff --git a/compiler/rustc_query_system/Cargo.toml b/compiler/rustc_query_system/Cargo.toml index 7480ba03474f..73ab03576dec 100644 --- a/compiler/rustc_query_system/Cargo.toml +++ b/compiler/rustc_query_system/Cargo.toml @@ -25,6 +25,6 @@ tracing = "0.1" # tidy-alphabetical-end [dependencies.hashbrown] -version = "0.15.2" +version = "0.16.1" default-features = false features = ["nightly"] # for may_dangle diff --git a/src/bootstrap/src/utils/proc_macro_deps.rs b/src/bootstrap/src/utils/proc_macro_deps.rs index 71b6aacdebed..f1bf6e399fb1 100644 --- a/src/bootstrap/src/utils/proc_macro_deps.rs +++ b/src/bootstrap/src/utils/proc_macro_deps.rs @@ -22,6 +22,7 @@ pub static CRATES: &[&str] = &[ "fluent-langneg", "fluent-syntax", "fnv", + "foldhash", "generic-array", "hashbrown", "heck", From afa2260a9cea4592606353895e0f8eee88356198 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 13 Jan 2026 10:54:39 -0800 Subject: [PATCH 09/69] library: finish unspecializing `Copy` --- library/core/src/marker.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 68f5649210de..da7ba167ef74 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -455,9 +455,6 @@ marker_impls! { /// [impls]: #implementors #[stable(feature = "rust1", since = "1.0.0")] #[lang = "copy"] -// This is unsound, but required by `hashbrown` -// FIXME(joboet): change `hashbrown` to use `TrivialClone` -#[rustc_unsafe_specialization_marker] #[rustc_diagnostic_item = "Copy"] pub trait Copy: Clone { // Empty. From 03cc50fc57424001285b72c02af2e19f36aabfd5 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 13 Jan 2026 15:23:59 -0800 Subject: [PATCH 10/69] update to indexmap v2.13.0 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 45d01c486faf..4297c1b32e35 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1953,9 +1953,9 @@ checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" [[package]] name = "indexmap" -version = "2.12.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", "hashbrown 0.16.1", From bc751adcdb7cd5cce59a6aec36e74b959132e0af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelo=20Dom=C3=ADnguez?= Date: Sun, 4 Jan 2026 17:26:21 +0100 Subject: [PATCH 11/69] Minor doc and ty fixes --- compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs index b8eb4f038216..7817755dafe4 100644 --- a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs +++ b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs @@ -445,9 +445,8 @@ fn declare_offload_fn<'ll>( // the gpu. For now, we only handle the data transfer part of it. // If two consecutive kernels use the same memory, we still move it to the host and back to the gpu. // Since in our frontend users (by default) don't have to specify data transfer, this is something -// we should optimize in the future! We also assume that everything should be copied back and forth, -// but sometimes we can directly zero-allocate on the device and only move back, or if something is -// immutable, we might only copy it to the device, but not back. +// we should optimize in the future! In some cases we can directly zero-allocate ont he device and +// only move data back, or if something is immutable, we might only copy it to the device. // // Current steps: // 0. Alloca some variables for the following steps From 2c9c5d14a210767f83358c6cb17c7a81ec7ae41c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelo=20Dom=C3=ADnguez?= Date: Tue, 6 Jan 2026 19:14:14 +0100 Subject: [PATCH 12/69] Allow bounded types --- compiler/rustc_codegen_llvm/src/intrinsic.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 481f75f337d6..59cbcd78dd0f 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -1388,7 +1388,8 @@ fn codegen_offload<'ll, 'tcx>( let args = get_args_from_tuple(bx, args[3], fn_target); let target_symbol = symbol_name_for_instance_in_crate(tcx, fn_target, LOCAL_CRATE); - let sig = tcx.fn_sig(fn_target.def_id()).skip_binder().skip_binder(); + let sig = tcx.fn_sig(fn_target.def_id()).instantiate_identity(); + let sig = tcx.instantiate_bound_regions_with_erased(sig); let inputs = sig.inputs(); let metadata = inputs.iter().map(|ty| OffloadMetadata::from_ty(tcx, *ty)).collect::>(); From 4ded6397b376b0a005c26381ed92c944e3702019 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 14 Jan 2026 00:45:16 +0100 Subject: [PATCH 13/69] Add new "hide deprecated items" setting in rustdoc --- src/librustdoc/clean/auto_trait.rs | 1 + src/librustdoc/clean/blanket_impl.rs | 3 ++ src/librustdoc/clean/inline.rs | 3 ++ src/librustdoc/clean/mod.rs | 4 ++ src/librustdoc/clean/types.rs | 8 ++++ src/librustdoc/html/render/mod.rs | 33 +++++++++++---- src/librustdoc/html/render/print_item.rs | 49 +++++++++++++++------- src/librustdoc/html/static/css/rustdoc.css | 9 ++++ src/librustdoc/html/static/js/search.js | 3 ++ src/librustdoc/html/static/js/settings.js | 11 +++++ src/librustdoc/html/static/js/storage.js | 3 ++ src/librustdoc/json/conversions.rs | 3 +- 12 files changed, 107 insertions(+), 23 deletions(-) diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index 6c67916571a4..847e688d03d0 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -126,6 +126,7 @@ fn synthesize_auto_trait_impl<'tcx>( items: Vec::new(), polarity, kind: clean::ImplKind::Auto, + is_deprecated: false, })), item_id: clean::ItemId::Auto { trait_: trait_def_id, for_: item_def_id }, cfg: None, diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index ddfce7aeb92d..de45922e856f 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -117,6 +117,9 @@ pub(crate) fn synthesize_blanket_impls( None, None, ))), + is_deprecated: tcx + .lookup_deprecation(impl_def_id) + .is_some_and(|deprecation| deprecation.is_in_effect()), })), cfg: None, inline_stmt_id: None, diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index c86a655c083a..19974f4847d4 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -645,6 +645,9 @@ pub(crate) fn build_impl( } else { ImplKind::Normal }, + is_deprecated: tcx + .lookup_deprecation(did) + .is_some_and(|deprecation| deprecation.is_in_effect()), })), merged_attrs, cfg, diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 817eda4c52ec..0e970a042ff0 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2882,6 +2882,9 @@ fn clean_impl<'tcx>( )), _ => None, }); + let is_deprecated = tcx + .lookup_deprecation(def_id.to_def_id()) + .is_some_and(|deprecation| deprecation.is_in_effect()); let mut make_item = |trait_: Option, for_: Type, items: Vec| { let kind = ImplItem(Box::new(Impl { safety: match impl_.of_trait { @@ -2902,6 +2905,7 @@ fn clean_impl<'tcx>( } else { ImplKind::Normal }, + is_deprecated, })); Item::from_def_id_and_parts(def_id.to_def_id(), None, kind, cx) }; diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index c3bafd3db13a..93863ea41396 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -427,6 +427,10 @@ impl Item { }) } + pub(crate) fn is_deprecated(&self, tcx: TyCtxt<'_>) -> bool { + self.deprecation(tcx).is_some_and(|deprecation| deprecation.is_in_effect()) + } + pub(crate) fn inner_docs(&self, tcx: TyCtxt<'_>) -> bool { self.item_id.as_def_id().map(|did| inner_docs(tcx.get_all_attrs(did))).unwrap_or(false) } @@ -1270,6 +1274,9 @@ impl Trait { pub(crate) fn is_dyn_compatible(&self, tcx: TyCtxt<'_>) -> bool { tcx.is_dyn_compatible(self.def_id) } + pub(crate) fn is_deprecated(&self, tcx: TyCtxt<'_>) -> bool { + tcx.lookup_deprecation(self.def_id).is_some_and(|deprecation| deprecation.is_in_effect()) + } } #[derive(Clone, Debug)] @@ -2254,6 +2261,7 @@ pub(crate) struct Impl { pub(crate) items: Vec, pub(crate) polarity: ty::ImplPolarity, pub(crate) kind: ImplKind, + pub(crate) is_deprecated: bool, } impl Impl { diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 63de870f07f4..106aefa4626b 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1794,12 +1794,14 @@ fn render_impl( let mut info_buffer = String::new(); let mut short_documented = true; + let mut trait_item_deprecated = false; if render_method_item { if !is_default_item { if let Some(t) = trait_ { // The trait item may have been stripped so we might not // find any documentation or stability for it. if let Some(it) = t.items.iter().find(|i| i.name == item.name) { + trait_item_deprecated = it.is_deprecated(cx.tcx()); // We need the stability of the item from the trait // because impls can't have a stability. if !item.doc_value().is_empty() { @@ -1839,10 +1841,20 @@ fn render_impl( Either::Right(boring) }; + let mut deprecation_class = if trait_item_deprecated || item.is_deprecated(cx.tcx()) { + " deprecated" + } else { + "" + }; + let toggled = !doc_buffer.is_empty(); if toggled { let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" }; - write!(w, "
")?; + write!( + w, + "
" + )?; + deprecation_class = ""; } match &item.kind { clean::MethodItem(..) | clean::RequiredMethodItem(_) => { @@ -1859,7 +1871,7 @@ fn render_impl( .map(|item| format!("{}.{name}", item.type_())); write!( w, - "
\ + "
\ {}", render_rightside(cx, item, render_mode) )?; @@ -1885,7 +1897,7 @@ fn render_impl( let id = cx.derive_id(&source_id); write!( w, - "
\ + "
\ {}", render_rightside(cx, item, render_mode) )?; @@ -1912,7 +1924,7 @@ fn render_impl( let id = cx.derive_id(&source_id); write!( w, - "
\ + "
\ {}", render_rightside(cx, item, render_mode), )?; @@ -1944,7 +1956,7 @@ fn render_impl( let id = cx.derive_id(&source_id); write!( w, - "
\ + "
\ {}", render_rightside(cx, item, render_mode), )?; @@ -1971,7 +1983,7 @@ fn render_impl( let id = cx.derive_id(&source_id); write!( w, - "
\ + "
\ {}", render_rightside(cx, item, render_mode), )?; @@ -2143,11 +2155,18 @@ fn render_impl( } if render_mode == RenderMode::Normal { let toggled = !(impl_items.is_empty() && default_impl_items.is_empty()); + let deprecation_attr = if impl_.is_deprecated + || trait_.is_some_and(|trait_| trait_.is_deprecated(cx.tcx())) + { + " deprecated" + } else { + "" + }; if toggled { close_tags.push("
"); write!( w, - "
\ + "
\ ", if rendering_params.toggle_open_by_default { " open" } else { "" } )?; diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 84e93a479b5b..55e36c462f96 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -217,6 +217,10 @@ fn toggle_close(mut w: impl fmt::Write) { } fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> impl fmt::Display { + fn deprecation_class_attr(is_deprecated: bool) -> &'static str { + if is_deprecated { " class=\"deprecated\"" } else { "" } + } + fmt::from_fn(|w| { write!(w, "{}", document(cx, item, None, HeadingOffset::H2))?; @@ -370,11 +374,18 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i write!(w, "")? } clean::ImportItem(ref import) => { - let stab_tags = - import.source.did.map_or_else(String::new, |import_def_id| { - print_extra_info_tags(tcx, myitem, item, Some(import_def_id)) - .to_string() - }); + let (stab_tags, deprecation) = match import.source.did { + Some(import_def_id) => { + let stab_tags = + print_extra_info_tags(tcx, myitem, item, Some(import_def_id)) + .to_string(); + let deprecation = tcx + .lookup_deprecation(import_def_id) + .is_some_and(|deprecation| deprecation.is_in_effect()); + (stab_tags, deprecation) + } + None => (String::new(), item.is_deprecated(tcx)), + }; let id = match import.kind { clean::ImportKind::Simple(s) => { format!(" id=\"{}\"", cx.derive_id(format!("reexport.{s}"))) @@ -383,8 +394,8 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i }; write!( w, - "\ - " + "", + deprecation_attr = deprecation_class_attr(deprecation) )?; render_attributes_in_code(w, myitem, "", cx)?; write!( @@ -396,9 +407,7 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i )?; } _ => { - if myitem.name.is_none() { - continue; - } + let Some(item_name) = myitem.name else { continue }; let unsafety_flag = match myitem.kind { clean::FunctionItem(_) | clean::ForeignFunctionItem(..) @@ -431,9 +440,10 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i .into_string(); let (docs_before, docs_after) = if docs.is_empty() { ("", "") } else { ("
", "
") }; + let deprecation_attr = deprecation_class_attr(myitem.is_deprecated(tcx)); write!( w, - "
\ + "\ \ {name}\ \ @@ -442,12 +452,12 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i {stab_tags}\
\ {docs_before}{docs}{docs_after}", - name = EscapeBodyTextWithWbr(myitem.name.unwrap().as_str()), + name = EscapeBodyTextWithWbr(item_name.as_str()), visibility_and_hidden = visibility_and_hidden, stab_tags = print_extra_info_tags(tcx, myitem, item, None), class = type_, unsafety_flag = unsafety_flag, - href = print_item_path(type_, myitem.name.unwrap().as_str()), + href = print_item_path(type_, item_name.as_str()), title1 = myitem.type_(), title2 = full_path(cx, myitem), )?; @@ -778,15 +788,24 @@ fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt: let content = document_full(m, cx, HeadingOffset::H5).to_string(); + let mut deprecation_class = + if m.is_deprecated(cx.tcx()) { " deprecated" } else { "" }; + let toggled = !content.is_empty(); if toggled { let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" }; - write!(w, "
")?; + write!( + w, + "
" + )?; + deprecation_class = ""; } write!( w, - "
\ + "
\ {}\

{}

", render_rightside(cx, m, RenderMode::Normal), diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index b770a0e2a0e4..73eb260a2b8e 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -2642,6 +2642,15 @@ However, it's not needed with smaller screen width because the doc/code block is } } +/* Items on module pages */ +.hide-deprecated-items dt.deprecated, +.hide-deprecated-items dt.deprecated + dd, +/* Items on item pages */ +.hide-deprecated-items .deprecated, +.hide-deprecated-items .deprecated + .item-info { + display: none; +} + /* WARNING: RUSTDOC_MOBILE_BREAKPOINT MEDIA QUERY If you update this line, then you also need to update the line with the same warning diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index b2880a2f4c2b..9961c1447ec2 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -4920,6 +4920,9 @@ async function addTab(results, query, display, finishedCallback, isTypeSearch) { const link = document.createElement("a"); link.className = "result-" + type; + if (obj.item.deprecated) { + link.className += " deprecated"; + } link.href = obj.href; const resultName = document.createElement("span"); diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js index 347d3d0750ec..b28b76b0d2e7 100644 --- a/src/librustdoc/html/static/js/settings.js +++ b/src/librustdoc/html/static/js/settings.js @@ -78,6 +78,12 @@ removeClass(document.documentElement, "word-wrap-source-code"); } break; + case "hide-deprecated-items": + if (value === true) { + addClass(document.documentElement, "hide-deprecated-items"); + } else { + removeClass(document.documentElement, "hide-deprecated-items"); + } } } @@ -274,6 +280,11 @@ "js_name": "word-wrap-source-code", "default": false, }, + { + "name": "Hide deprecated items", + "js_name": "hide-deprecated-items", + "default": false, + }, ]; // Then we build the DOM. diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js index 40ab8be03c93..7cc70374378b 100644 --- a/src/librustdoc/html/static/js/storage.js +++ b/src/librustdoc/html/static/js/storage.js @@ -334,6 +334,9 @@ if (getSettingValue("sans-serif-fonts") === "true") { if (getSettingValue("word-wrap-source-code") === "true") { addClass(document.documentElement, "word-wrap-source-code"); } +if (getSettingValue("hide-deprecated-items") === "true") { + addClass(document.documentElement, "hide-deprecated-items"); +} function updateSidebarWidth() { const desktopSidebarWidth = getSettingValue("desktop-sidebar-width"); if (desktopSidebarWidth && desktopSidebarWidth !== "null") { diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 892cc483dbd6..2edf7891be40 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -711,7 +711,8 @@ impl FromClean for PolyTrait { impl FromClean for Impl { fn from_clean(impl_: &clean::Impl, renderer: &JsonRenderer<'_>) -> Self { let provided_trait_methods = impl_.provided_trait_methods(renderer.tcx); - let clean::Impl { safety, generics, trait_, for_, items, polarity, kind } = impl_; + let clean::Impl { safety, generics, trait_, for_, items, polarity, kind, is_deprecated: _ } = + impl_; // FIXME: use something like ImplKind in JSON? let (is_synthetic, blanket_impl) = match kind { clean::ImplKind::Normal | clean::ImplKind::FakeVariadic => (false, None), From df386c5b48e2b3a3dabae22f2b018501f5abcbe8 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Tue, 30 Dec 2025 16:57:56 -0500 Subject: [PATCH 14/69] Test that -Zbuild-std=core works on a variety of profiles --- Cargo.lock | 1 + .../src/spec/targets/i586_unknown_redox.rs | 2 +- .../spec/targets/x86_64_unknown_linux_none.rs | 2 +- .../src/spec/targets/xtensa_esp32_espidf.rs | 2 +- .../src/spec/targets/xtensa_esp32s2_espidf.rs | 2 +- .../src/spec/targets/xtensa_esp32s3_espidf.rs | 2 +- src/bootstrap/mk/Makefile.in | 5 + src/bootstrap/src/core/build_steps/test.rs | 8 +- src/bootstrap/src/core/builder/cli_paths.rs | 1 + .../cli_paths/snapshots/x_test_tests.snap | 3 + .../snapshots/x_test_tests_skip_coverage.snap | 3 + src/bootstrap/src/core/builder/mod.rs | 1 + src/bootstrap/src/core/builder/tests.rs | 4 +- .../rustc-dev-guide/src/tests/compiletest.md | 10 ++ src/tools/compiletest/src/common.rs | 1 + src/tools/compiletest/src/runtest/run_make.rs | 4 +- src/tools/run-make-support/Cargo.toml | 1 + src/tools/run-make-support/src/command.rs | 13 ++ src/tools/run-make-support/src/lib.rs | 4 +- src/tools/run-make-support/src/util.rs | 3 + tests/build-std/configurations/rmake.rs | 131 ++++++++++++++++++ 21 files changed, 192 insertions(+), 11 deletions(-) create mode 100644 tests/build-std/configurations/rmake.rs diff --git a/Cargo.lock b/Cargo.lock index bf28939ac87b..bf0bc9234d39 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3334,6 +3334,7 @@ dependencies = [ "rustdoc-json-types", "serde_json", "similar", + "tempfile", "wasmparser 0.236.1", ] diff --git a/compiler/rustc_target/src/spec/targets/i586_unknown_redox.rs b/compiler/rustc_target/src/spec/targets/i586_unknown_redox.rs index 935666630dae..56e1c8a9705d 100644 --- a/compiler/rustc_target/src/spec/targets/i586_unknown_redox.rs +++ b/compiler/rustc_target/src/spec/targets/i586_unknown_redox.rs @@ -11,7 +11,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "i586-unknown-redox".into(), - metadata: TargetMetadata { description: None, tier: None, host_tools: None, std: None }, + metadata: TargetMetadata { description: None, tier: Some(3), host_tools: None, std: None }, pointer_width: 32, data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128" diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_none.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_none.rs index ecf232f1ab2f..768b1a1ba112 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_none.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_none.rs @@ -15,7 +15,7 @@ pub(crate) fn target() -> Target { llvm_target: "x86_64-unknown-linux-none".into(), metadata: TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, std: Some(false), }, diff --git a/compiler/rustc_target/src/spec/targets/xtensa_esp32_espidf.rs b/compiler/rustc_target/src/spec/targets/xtensa_esp32_espidf.rs index 83835705f3da..0fb4d186087b 100644 --- a/compiler/rustc_target/src/spec/targets/xtensa_esp32_espidf.rs +++ b/compiler/rustc_target/src/spec/targets/xtensa_esp32_espidf.rs @@ -9,7 +9,7 @@ pub(crate) fn target() -> Target { pointer_width: 32, data_layout: "e-m:e-p:32:32-v1:8:8-i64:64-i128:128-n32".into(), arch: Arch::Xtensa, - metadata: TargetMetadata { description: None, tier: None, host_tools: None, std: None }, + metadata: TargetMetadata { description: None, tier: Some(3), host_tools: None, std: None }, options: TargetOptions { endian: Endian::Little, diff --git a/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_espidf.rs b/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_espidf.rs index 42e4c7bf8bec..81eab657db71 100644 --- a/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_espidf.rs +++ b/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_espidf.rs @@ -9,7 +9,7 @@ pub(crate) fn target() -> Target { pointer_width: 32, data_layout: "e-m:e-p:32:32-v1:8:8-i64:64-i128:128-n32".into(), arch: Arch::Xtensa, - metadata: TargetMetadata { description: None, tier: None, host_tools: None, std: None }, + metadata: TargetMetadata { description: None, tier: Some(3), host_tools: None, std: None }, options: TargetOptions { endian: Endian::Little, diff --git a/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_espidf.rs b/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_espidf.rs index 498daf4e6063..fb1f4f471979 100644 --- a/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_espidf.rs +++ b/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_espidf.rs @@ -9,7 +9,7 @@ pub(crate) fn target() -> Target { pointer_width: 32, data_layout: "e-m:e-p:32:32-v1:8:8-i64:64-i128:128-n32".into(), arch: Arch::Xtensa, - metadata: TargetMetadata { description: None, tier: None, host_tools: None, std: None }, + metadata: TargetMetadata { description: None, tier: Some(3), host_tools: None, std: None }, options: TargetOptions { endian: Endian::Little, diff --git a/src/bootstrap/mk/Makefile.in b/src/bootstrap/mk/Makefile.in index 6bef58a0ac20..5f956f03ecb1 100644 --- a/src/bootstrap/mk/Makefile.in +++ b/src/bootstrap/mk/Makefile.in @@ -53,6 +53,11 @@ check-aux: src/tools/cargotest \ src/tools/test-float-parse \ $(BOOTSTRAP_ARGS) + # The build-std suite is off by default because it is uncommonly slow + # and memory-hungry. + $(Q)$(BOOTSTRAP) test --stage 2 \ + build-std \ + $(BOOTSTRAP_ARGS) # Run standard library tests in Miri. $(Q)MIRIFLAGS="-Zmiri-strict-provenance" \ $(BOOTSTRAP) miri --stage 2 \ diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index f3a1c6b0e3dd..080931fc0a93 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -1625,6 +1625,12 @@ test!(RunMakeCargo { suite: "run-make-cargo", default: true }); +test!(BuildStd { + path: "tests/build-std", + mode: CompiletestMode::RunMake, + suite: "build-std", + default: false +}); test!(AssemblyLlvm { path: "tests/assembly-llvm", @@ -1948,7 +1954,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the let stage0_rustc_path = builder.compiler(0, test_compiler.host); cmd.arg("--stage0-rustc-path").arg(builder.rustc(stage0_rustc_path)); - if suite == "run-make-cargo" { + if matches!(suite, "run-make-cargo" | "build-std") { let cargo_path = if test_compiler.stage == 0 { // If we're using `--stage 0`, we should provide the bootstrap cargo. builder.initial_cargo.clone() diff --git a/src/bootstrap/src/core/builder/cli_paths.rs b/src/bootstrap/src/core/builder/cli_paths.rs index 5ff2b380e4b9..1b0caa980e1d 100644 --- a/src/bootstrap/src/core/builder/cli_paths.rs +++ b/src/bootstrap/src/core/builder/cli_paths.rs @@ -20,6 +20,7 @@ pub(crate) const PATH_REMAP: &[(&str, &[&str])] = &[ &[ // tidy-alphabetical-start "tests/assembly-llvm", + "tests/build-std", "tests/codegen-llvm", "tests/codegen-units", "tests/coverage", diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_tests.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_tests.snap index ad9660ef5c91..65349a59a1e8 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_tests.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_tests.snap @@ -5,6 +5,9 @@ expression: test tests [Test] test::AssemblyLlvm targets: [aarch64-unknown-linux-gnu] - Suite(test::tests/assembly-llvm) +[Test] test::BuildStd + targets: [aarch64-unknown-linux-gnu] + - Suite(test::tests/build-std) [Test] test::CodegenLlvm targets: [aarch64-unknown-linux-gnu] - Suite(test::tests/codegen-llvm) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_tests_skip_coverage.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_tests_skip_coverage.snap index 4572f089b0ae..694bb0672ee4 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_tests_skip_coverage.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_tests_skip_coverage.snap @@ -5,6 +5,9 @@ expression: test tests --skip=coverage [Test] test::AssemblyLlvm targets: [aarch64-unknown-linux-gnu] - Suite(test::tests/assembly-llvm) +[Test] test::BuildStd + targets: [aarch64-unknown-linux-gnu] + - Suite(test::tests/build-std) [Test] test::CodegenLlvm targets: [aarch64-unknown-linux-gnu] - Suite(test::tests/codegen-llvm) diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 4a04b97c549a..7f98e42227af 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -927,6 +927,7 @@ impl<'a> Builder<'a> { test::CollectLicenseMetadata, test::RunMake, test::RunMakeCargo, + test::BuildStd, ), Kind::Miri => describe!(test::Crate), Kind::Bench => describe!(test::Crate, test::CrateLibrustc, test::CrateRustdoc), diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 66614cc6cced..af26c2f87d02 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -2077,7 +2077,7 @@ mod snapshot { let ctx = TestCtx::new(); insta::assert_snapshot!( prepare_test_config(&ctx) - .render_steps(), @r" + .render_steps(), @" [build] rustc 0 -> Tidy 1 [test] tidy <> [build] rustdoc 0 @@ -2255,7 +2255,7 @@ mod snapshot { insta::assert_snapshot!( prepare_test_config(&ctx) .stage(2) - .render_steps(), @r" + .render_steps(), @" [build] rustc 0 -> Tidy 1 [test] tidy <> [build] rustdoc 0 diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md index d69e0a5ce988..7f22bc27600c 100644 --- a/src/doc/rustc-dev-guide/src/tests/compiletest.md +++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md @@ -78,6 +78,10 @@ The following test suites are available, with links for more information: [`run-make`](#run-make-tests) are general purpose tests using Rust programs. +### The build-std test suite + +[`build-std`](#build-std-tests) test that -Zbuild-std works. + ### Rustdoc test suites | Test suite | Purpose | @@ -429,6 +433,12 @@ use cases that require testing in-tree `cargo` in conjunction with in-tree `rust The `run-make` test suite does not have access to in-tree `cargo` (so it can be the faster-to-iterate test suite). +### `build-std` tests + +The tests in [`tests/build-std`] check that `-Zbuild-std` works. This is currently +just a run-make test suite with a single recipe. The recipe generates test cases +and runs them in parallel. + #### Using Rust recipes Each test should be in a separate directory with a `rmake.rs` Rust program, diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index a2f3a8f00dac..843b4ad2975d 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -77,6 +77,7 @@ string_enum! { RustdocUi => "rustdoc-ui", Ui => "ui", UiFullDeps => "ui-fulldeps", + BuildStd => "build-std", } } diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs index 4eb8f91fe894..ac8846a263c0 100644 --- a/src/tools/compiletest/src/runtest/run_make.rs +++ b/src/tools/compiletest/src/runtest/run_make.rs @@ -190,8 +190,8 @@ impl TestCx<'_> { // through a specific CI runner). .env("LLVM_COMPONENTS", &self.config.llvm_components); - // Only `run-make-cargo` test suite gets an in-tree `cargo`, not `run-make`. - if self.config.suite == TestSuite::RunMakeCargo { + // The `run-make-cargo` and `build-std` suites need an in-tree `cargo`, `run-make` does not. + if matches!(self.config.suite, TestSuite::RunMakeCargo | TestSuite::BuildStd) { cmd.env( "CARGO", self.config.cargo_path.as_ref().expect("cargo must be built and made available"), diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml index 9d9cd656f570..918f5ef0d506 100644 --- a/src/tools/run-make-support/Cargo.toml +++ b/src/tools/run-make-support/Cargo.toml @@ -17,6 +17,7 @@ object = "0.37" regex = "1.11" serde_json = "1.0" similar = "2.7" +tempfile = "3" wasmparser = { version = "0.236", default-features = false, features = ["std", "features", "validate"] } # tidy-alphabetical-end diff --git a/src/tools/run-make-support/src/command.rs b/src/tools/run-make-support/src/command.rs index 0aeb189bb6a5..f4a09f9faae8 100644 --- a/src/tools/run-make-support/src/command.rs +++ b/src/tools/run-make-support/src/command.rs @@ -46,6 +46,8 @@ pub struct Command { // Emulate linear type semantics. drop_bomb: DropBomb, already_executed: bool, + + context: String, } impl Command { @@ -60,6 +62,7 @@ impl Command { stdout: None, stderr: None, already_executed: false, + context: String::new(), } } @@ -69,6 +72,16 @@ impl Command { self.cmd } + pub(crate) fn get_context(&self) -> &str { + &self.context + } + + /// Appends context to the command, to provide a better error message if the command fails. + pub fn context(&mut self, ctx: &str) -> &mut Self { + self.context.push_str(&format!("{ctx}\n")); + self + } + /// Specify a stdin input buffer. This is a convenience helper, pub fn stdin_buf>(&mut self, input: I) -> &mut Self { self.stdin_buf = Some(input.as_ref().to_vec().into_boxed_slice()); diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index 5253dc04a93e..b19d73b78a94 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -34,7 +34,9 @@ pub mod rfs { } // Re-exports of third-party library crates. -pub use {bstr, gimli, libc, object, regex, rustdoc_json_types, serde_json, similar, wasmparser}; +pub use { + bstr, gimli, libc, object, regex, rustdoc_json_types, serde_json, similar, tempfile, wasmparser, +}; // Helpers for building names of output artifacts that are potentially target-specific. pub use crate::artifact_names::{ diff --git a/src/tools/run-make-support/src/util.rs b/src/tools/run-make-support/src/util.rs index af01758447b9..7908dc1f7b3d 100644 --- a/src/tools/run-make-support/src/util.rs +++ b/src/tools/run-make-support/src/util.rs @@ -21,6 +21,9 @@ pub(crate) fn handle_failed_output( eprintln!("output status: `{}`", output.status()); eprintln!("=== STDOUT ===\n{}\n\n", output.stdout_utf8()); eprintln!("=== STDERR ===\n{}\n\n", output.stderr_utf8()); + if !cmd.get_context().is_empty() { + eprintln!("Context:\n{}", cmd.get_context()); + } std::process::exit(1) } diff --git a/tests/build-std/configurations/rmake.rs b/tests/build-std/configurations/rmake.rs new file mode 100644 index 000000000000..99cb9f9b0f4d --- /dev/null +++ b/tests/build-std/configurations/rmake.rs @@ -0,0 +1,131 @@ +// This test ensures we are able to compile -Zbuild-std=core under a variety of profiles. +// Currently, it tests that we can compile to all Tier 1 targets, and it does this by checking what +// the tier metadata in target-spec JSON. This means that all in-tree targets must have a tier set. + +#![deny(warnings)] + +use std::collections::HashMap; +use std::sync::{Arc, Mutex}; +use std::thread; + +use run_make_support::serde_json::{self, Value}; +use run_make_support::tempfile::TempDir; +use run_make_support::{cargo, rfs, rustc}; + +#[derive(Clone)] +struct Task { + target: String, + opt_level: u8, + debug: u8, + panic: &'static str, +} + +fn manifest(task: &Task) -> String { + let Task { opt_level, debug, panic, target: _ } = task; + format!( + r#"[package] +name = "scratch" +version = "0.1.0" +edition = "2024" + +[lib] +path = "lib.rs" + +[profile.release] +opt-level = {opt_level} +debug = {debug} +panic = "{panic}" +"# + ) +} + +fn main() { + let mut targets = Vec::new(); + let all_targets = + rustc().args(&["--print=all-target-specs-json", "-Zunstable-options"]).run().stdout_utf8(); + let all_targets: HashMap = serde_json::from_str(&all_targets).unwrap(); + for (target, spec) in all_targets { + let metadata = spec.as_object().unwrap()["metadata"].as_object().unwrap(); + let tier = metadata["tier"] + .as_u64() + .expect(&format!("Target {} is missing tier metadata", target)); + if tier == 1 { + targets.push(target); + } + } + + let mut tasks = Vec::new(); + + // Testing every combination of compiler flags is infeasible. So we are making some attempt to + // choose combinations that will tend to run into problems. + // + // The particular combination of settings below is tuned to look for problems generating the + // code for compiler-builtins. + // We only exercise opt-level 0 and 3 to exercise mir-opt-level 1 and 2. + // We only exercise debug 0 and 2 because level 2 turns off some MIR optimizations. + // We only test abort and immediate-abort because abort vs unwind doesn't change MIR much at + // all. but immediate-abort does. + // + // Currently this only tests that we can compile the tier 1 targets. But since we are using + // -Zbuild-std=core, we could have any list of targets. + + for opt_level in [0, 3] { + for debug in [0, 2] { + for panic in ["abort", "immediate-abort"] { + for target in &targets { + tasks.push(Task { target: target.clone(), opt_level, debug, panic }); + } + } + } + } + + let tasks = Arc::new(Mutex::new(tasks)); + let mut threads = Vec::new(); + + // Try to obey the -j argument passed to bootstrap, otherwise fall back to using all the system + // resouces. This test can be rather memory-hungry (~1 GB/thread); if it causes trouble in + // practice do not hesitate to limit its parallelism. + for _ in 0..run_make_support::env::jobs() { + let tasks = Arc::clone(&tasks); + let handle = thread::spawn(move || { + loop { + let maybe_task = tasks.lock().unwrap().pop(); + if let Some(task) = maybe_task { + test(task); + } else { + break; + } + } + }); + threads.push(handle); + } + + for t in threads { + t.join().unwrap(); + } +} + +fn test(task: Task) { + let dir = TempDir::new().unwrap(); + + let manifest = manifest(&task); + rfs::write(dir.path().join("Cargo.toml"), &manifest); + rfs::write(dir.path().join("lib.rs"), "#![no_std]"); + + let mut args = vec!["build", "--release", "-Zbuild-std=core", "--target", &task.target, "-j1"]; + if task.panic == "immediate-abort" { + args.push("-Zpanic-immediate-abort"); + } + cargo() + .current_dir(dir.path()) + .args(&args) + .env("RUSTC_BOOTSTRAP", "1") + // Visual Studio 2022 requires that the LIB env var be set so it can + // find the Windows SDK. + .env("LIB", std::env::var("LIB").unwrap_or_default()) + .context(&format!( + "build-std for target `{}` failed with the following Cargo.toml:\n\n{manifest}", + task.target + )) + .run(); +} From 316627ee24c2214037631c227dc073bde46001e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 16 Dec 2025 21:19:20 +0100 Subject: [PATCH 15/69] Refactor `Enzyme` step --- src/bootstrap/src/core/build_steps/compile.rs | 22 +++------ src/bootstrap/src/core/build_steps/llvm.rs | 49 +++++++++++++++---- 2 files changed, 46 insertions(+), 25 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 11f2a28bb935..651ff03a8690 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -2292,23 +2292,13 @@ impl Step for Assemble { builder.compiler(target_compiler.stage - 1, builder.config.host_target); // Build enzyme - if builder.config.llvm_enzyme && !builder.config.dry_run() { + if builder.config.llvm_enzyme { debug!("`llvm_enzyme` requested"); - let enzyme_install = builder.ensure(llvm::Enzyme { target: build_compiler.host }); - if let Some(llvm_config) = builder.llvm_config(builder.config.host_target) { - let llvm_version_major = llvm::get_llvm_version_major(builder, &llvm_config); - let lib_ext = std::env::consts::DLL_EXTENSION; - let libenzyme = format!("libEnzyme-{llvm_version_major}"); - let src_lib = - enzyme_install.join("build/Enzyme").join(&libenzyme).with_extension(lib_ext); - let libdir = builder.sysroot_target_libdir(build_compiler, build_compiler.host); - let target_libdir = - builder.sysroot_target_libdir(target_compiler, target_compiler.host); - let dst_lib = libdir.join(&libenzyme).with_extension(lib_ext); - let target_dst_lib = target_libdir.join(&libenzyme).with_extension(lib_ext); - builder.copy_link(&src_lib, &dst_lib, FileType::NativeLibrary); - builder.copy_link(&src_lib, &target_dst_lib, FileType::NativeLibrary); - } + let enzyme = builder.ensure(llvm::Enzyme { target: build_compiler.host }); + let target_libdir = + builder.sysroot_target_libdir(target_compiler, target_compiler.host); + let target_dst_lib = target_libdir.join(enzyme.enzyme_filename()); + builder.copy_link(&enzyme.enzyme_path(), &target_dst_lib, FileType::NativeLibrary); } if builder.config.llvm_offload && !builder.config.dry_run() { diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index c3935d9810e9..f6a763b94075 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -17,6 +17,7 @@ use std::{env, fs}; use build_helper::exit; use build_helper::git::PathFreshness; +use crate::core::build_steps::llvm; use crate::core::builder::{Builder, RunConfig, ShouldRun, Step, StepMetadata}; use crate::core::config::{Config, TargetSelection}; use crate::utils::build_stamp::{BuildStamp, generate_smart_stamp_hash}; @@ -1077,13 +1078,28 @@ impl Step for OmpOffload { } } +#[derive(Clone)] +pub struct BuiltEnzyme { + /// Path to the libEnzyme dylib. + enzyme: PathBuf, +} + +impl BuiltEnzyme { + pub fn enzyme_path(&self) -> PathBuf { + self.enzyme.clone() + } + pub fn enzyme_filename(&self) -> String { + self.enzyme.file_name().unwrap().to_str().unwrap().to_owned() + } +} + #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct Enzyme { pub target: TargetSelection, } impl Step for Enzyme { - type Output = PathBuf; + type Output = BuiltEnzyme; const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -1095,17 +1111,17 @@ impl Step for Enzyme { } /// Compile Enzyme for `target`. - fn run(self, builder: &Builder<'_>) -> PathBuf { + fn run(self, builder: &Builder<'_>) -> Self::Output { builder.require_submodule( "src/tools/enzyme", Some("The Enzyme sources are required for autodiff."), ); - if builder.config.dry_run() { - let out_dir = builder.enzyme_out(self.target); - return out_dir; - } let target = self.target; + if builder.config.dry_run() { + return BuiltEnzyme { enzyme: builder.config.tempdir().join("enzyme-dryrun") }; + } + let LlvmResult { host_llvm_config, llvm_cmake_dir } = builder.ensure(Llvm { target }); static STAMP_HASH_MEMO: OnceLock = OnceLock::new(); @@ -1120,6 +1136,12 @@ impl Step for Enzyme { let out_dir = builder.enzyme_out(target); let stamp = BuildStamp::new(&out_dir).with_prefix("enzyme").add_stamp(smart_stamp_hash); + let llvm_version_major = llvm::get_llvm_version_major(builder, &host_llvm_config); + let lib_ext = std::env::consts::DLL_EXTENSION; + let libenzyme = format!("libEnzyme-{llvm_version_major}"); + let build_dir = out_dir.join("lib"); + let dylib = build_dir.join(&libenzyme).with_extension(lib_ext); + trace!("checking build stamp to see if we need to rebuild enzyme artifacts"); if stamp.is_up_to_date() { trace!(?out_dir, "enzyme build artifacts are up to date"); @@ -1133,7 +1155,7 @@ impl Step for Enzyme { stamp.path().display() )); } - return out_dir; + return BuiltEnzyme { enzyme: dylib }; } if !builder.config.dry_run() && !llvm_cmake_dir.is_dir() { @@ -1149,7 +1171,6 @@ impl Step for Enzyme { let _time = helpers::timeit(builder); t!(fs::create_dir_all(&out_dir)); - builder.config.update_submodule("src/tools/enzyme"); let mut cfg = cmake::Config::new(builder.src.join("src/tools/enzyme/enzyme/")); // Enzyme devs maintain upstream compatibility, but only fix deprecations when they are about // to turn into a hard error. As such, Enzyme generates various warnings which could make it @@ -1178,8 +1199,18 @@ impl Step for Enzyme { cfg.build(); + // At this point, `out_dir` should contain the built libEnzyme-. + // file. + if !dylib.exists() { + eprintln!( + "`{libenzyme}` not found in `{}`. Either the build has failed or Enzyme was built with a wrong version of LLVM", + build_dir.display() + ); + exit!(1); + } + t!(stamp.write()); - out_dir + BuiltEnzyme { enzyme: dylib } } } From 85e01e3c4e328a63b39ea1189de03b79cc02b0cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 16 Dec 2025 21:55:56 +0100 Subject: [PATCH 16/69] Add dist step for `Enzyme` --- src/bootstrap/src/core/build_steps/dist.rs | 49 ++++++++++++++++++++++ src/bootstrap/src/core/builder/mod.rs | 1 + src/bootstrap/src/utils/tarball.rs | 3 ++ 3 files changed, 53 insertions(+) diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index cfcb144e0993..8ba05a7fa3a7 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -2717,6 +2717,55 @@ impl Step for LlvmBitcodeLinker { } } +/// Distributes the `enzyme` library so that it can be used by a compiler whose host +/// is `target`. +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct Enzyme { + /// Enzyme will by usable by rustc on this host. + pub target: TargetSelection, +} + +impl Step for Enzyme { + type Output = Option; + const IS_HOST: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.alias("enzyme") + } + + fn is_default_step(builder: &Builder<'_>) -> bool { + builder.config.llvm_enzyme + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(Enzyme { target: run.target }); + } + + fn run(self, builder: &Builder<'_>) -> Option { + // This prevents Enzyme from being built for "dist" + // or "install" on the stable/beta channels. It is not yet stable and + // should not be included. + if !builder.build.unstable_features() { + return None; + } + + let target = self.target; + + let enzyme = builder.ensure(llvm::Enzyme { target }); + + let target_libdir = format!("lib/rustlib/{}/lib", target.triple); + + // Prepare the image directory + let mut tarball = Tarball::new(builder, "enzyme", &target.triple); + tarball.set_overlay(OverlayKind::Enzyme); + tarball.is_preview(true); + + tarball.add_file(enzyme.enzyme_path(), target_libdir, FileType::NativeLibrary); + + Some(tarball.generate()) + } +} + /// Tarball intended for internal consumption to ease rustc/std development. /// /// Should not be considered stable by end users. diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index f63b8e044550..14cae2d0efc4 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -980,6 +980,7 @@ impl<'a> Builder<'a> { dist::LlvmTools, dist::LlvmBitcodeLinker, dist::RustDev, + dist::Enzyme, dist::Bootstrap, dist::Extended, // It seems that PlainSourceTarball somehow changes how some of the tools diff --git a/src/bootstrap/src/utils/tarball.rs b/src/bootstrap/src/utils/tarball.rs index cd6a03c84870..17d75e83daea 100644 --- a/src/bootstrap/src/utils/tarball.rs +++ b/src/bootstrap/src/utils/tarball.rs @@ -28,6 +28,7 @@ pub(crate) enum OverlayKind { RustcCodegenGcc, Gcc, LlvmBitcodeLinker, + Enzyme, } impl OverlayKind { @@ -37,6 +38,7 @@ impl OverlayKind { OverlayKind::Llvm => { &["src/llvm-project/llvm/LICENSE.TXT", "src/llvm-project/llvm/README.txt"] } + OverlayKind::Enzyme => &["src/tools/enzyme/LICENSE", "src/tools/enzyme/Readme.md"], OverlayKind::Cargo => &[ "src/tools/cargo/README.md", "src/tools/cargo/LICENSE-MIT", @@ -111,6 +113,7 @@ impl OverlayKind { OverlayKind::RustcCodegenGcc => builder.rust_version(), OverlayKind::LlvmBitcodeLinker => builder.rust_version(), OverlayKind::Gcc => builder.rust_version(), + OverlayKind::Enzyme => builder.rust_version(), } } } From f19591ad27ba306ffe0d2665790894420e0f96bc Mon Sep 17 00:00:00 2001 From: Redddy Date: Fri, 16 Jan 2026 18:39:38 +0900 Subject: [PATCH 17/69] Add rust-analyzer setup for out-of-tree rustc_private crates --- .../remarks-on-perma-unstable-features.md | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/doc/rustc-dev-guide/src/rustc-driver/remarks-on-perma-unstable-features.md b/src/doc/rustc-dev-guide/src/rustc-driver/remarks-on-perma-unstable-features.md index b434cfc9cf14..3c2fe133be84 100644 --- a/src/doc/rustc-dev-guide/src/rustc-driver/remarks-on-perma-unstable-features.md +++ b/src/doc/rustc-dev-guide/src/rustc-driver/remarks-on-perma-unstable-features.md @@ -49,6 +49,29 @@ For custom-built toolchains or environments not using rustup, additional configu ``` 3. **Check version compatibility**: Ensure your LLVM version is compatible with your Rust toolchain +### Configuring `rust-analyzer` for Out-of-Tree Projects + +When developing out-of-tree projects that use `rustc_private` crates, you can configure `rust-analyzer` to recognize these crates. + +#### Configuration Steps + +1. **Set rust-analyzer configuration** + Configure `rust-analyzer.rustc.source` to `"discover"` in your editor settings. + For VS Code, add to `rust_analyzer_settings.json`: + ```json + { + "rust-analyzer.rustc.source": "discover" + } + ``` +2. **Add metadata to Cargo.toml** + Add the following to the `Cargo.toml` of every crate that uses `rustc_private`: + ```toml + [package.metadata.rust-analyzer] + rustc_private = true + ``` + +This configuration allows `rust-analyzer` to properly recognize and provide IDE support for `rustc_private` crates in out-of-tree projects. + ### Additional Resources - [GitHub Issue #137421](https://github.com/rust-lang/rust/issues/137421): Explains that `rustc_private` linker failures often occur because `llvm-tools` is not installed From 21f269e72ed90f73abbd531243d6f563d2689650 Mon Sep 17 00:00:00 2001 From: lcnr Date: Fri, 16 Jan 2026 10:47:01 +0100 Subject: [PATCH 18/69] add RPITIT cycle issue --- .../src/solve/candidate-preference.md | 41 ++++++++++++++++++- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/solve/candidate-preference.md b/src/doc/rustc-dev-guide/src/solve/candidate-preference.md index 8b28f56760a7..d5a75b41ff1f 100644 --- a/src/doc/rustc-dev-guide/src/solve/candidate-preference.md +++ b/src/doc/rustc-dev-guide/src/solve/candidate-preference.md @@ -260,11 +260,13 @@ We prefer builtin trait object impls over user-written impls. This is **unsound* The candidate preference behavior during normalization is implemented in [`fn assemble_and_merge_candidates`]. -### Where-bounds shadow impls +### Trait where-bounds shadow impls Normalization of associated items does not consider impls if the corresponding trait goal has been proven via a `ParamEnv` or `AliasBound` candidate. This means that for where-bounds which do not constrain associated types, the associated types remain *rigid*. +#### Using impls results in different region constraints + This is necessary to avoid unnecessary region constraints from applying impls. ```rust trait Trait<'a> { @@ -286,6 +288,39 @@ where } ``` +#### RPITIT `type_of` cycles + +We currently have to avoid impl candidates if there are where-bounds to avoid query cycles for RPITIT, see [#139762]. It feels desirable to me to stop relying on auto-trait leakage of during RPITIT computation to remove this issue, see [#139788]. + +```rust +use std::future::Future; +pub trait ReactiveFunction: Send { + type Output; + + fn invoke(self) -> Self::Output; +} + +trait AttributeValue { + fn resolve(self) -> impl Future + Send; +} + +impl AttributeValue for F +where + F: ReactiveFunction, + V: AttributeValue, +{ + async fn resolve(self) { + // We're awaiting `::{synthetic#0}` here. + // Normalizing that one via the the impl we're currently in + // relies on `collect_return_position_impl_trait_in_trait_tys` which + // ends up relying on auto-trait leakage when checking that the + // opaque return type of this function implements the `Send` item + // bound of the trait definition. + self.invoke().resolve().await + } +} +``` + ### We always consider `AliasBound` candidates In case the where-bound does not specify the associated item, we consider `AliasBound` candidates instead of treating the alias as rigid, even though the trait goal was proven via a `ParamEnv` candidate. @@ -424,4 +459,6 @@ where [`fn assemble_and_merge_candidates`]: https://github.com/rust-lang/rust/blob/e3ee7f7aea5b45af3b42b5e4713da43876a65ac9/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs#L920-L1003 [trait-system-refactor-initiative#76]: https://github.com/rust-lang/trait-system-refactor-initiative/issues/76 [#24066]: https://github.com/rust-lang/rust/issues/24066 -[#133044]: https://github.com/rust-lang/rust/issues/133044 \ No newline at end of file +[#133044]: https://github.com/rust-lang/rust/issues/133044 +[#139762]: https://github.com/rust-lang/rust/pull/139762 +[#139788]: https://github.com/rust-lang/rust/issues/139788 \ No newline at end of file From 908ae5a86f5191bf316456dcc9257a942d8bce7f Mon Sep 17 00:00:00 2001 From: lcnr Date: Fri, 16 Jan 2026 10:48:01 +0100 Subject: [PATCH 19/69] move section --- .../src/solve/candidate-preference.md | 121 +++++++++--------- 1 file changed, 61 insertions(+), 60 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/solve/candidate-preference.md b/src/doc/rustc-dev-guide/src/solve/candidate-preference.md index d5a75b41ff1f..461523424b3c 100644 --- a/src/doc/rustc-dev-guide/src/solve/candidate-preference.md +++ b/src/doc/rustc-dev-guide/src/solve/candidate-preference.md @@ -260,66 +260,6 @@ We prefer builtin trait object impls over user-written impls. This is **unsound* The candidate preference behavior during normalization is implemented in [`fn assemble_and_merge_candidates`]. -### Trait where-bounds shadow impls - -Normalization of associated items does not consider impls if the corresponding trait goal has been proven via a `ParamEnv` or `AliasBound` candidate. -This means that for where-bounds which do not constrain associated types, the associated types remain *rigid*. - -#### Using impls results in different region constraints - -This is necessary to avoid unnecessary region constraints from applying impls. -```rust -trait Trait<'a> { - type Assoc; -} -impl Trait<'static> for u32 { - type Assoc = u32; -} - -fn bar<'b, T: Trait<'b>>() -> T::Assoc { todo!() } -fn foo<'a>() -where - u32: Trait<'a>, -{ - // Normalizing the return type would use the impl, proving - // the `T: Trait` where-bound would use the where-bound, resulting - // in different region constraints. - bar::<'_, u32>(); -} -``` - -#### RPITIT `type_of` cycles - -We currently have to avoid impl candidates if there are where-bounds to avoid query cycles for RPITIT, see [#139762]. It feels desirable to me to stop relying on auto-trait leakage of during RPITIT computation to remove this issue, see [#139788]. - -```rust -use std::future::Future; -pub trait ReactiveFunction: Send { - type Output; - - fn invoke(self) -> Self::Output; -} - -trait AttributeValue { - fn resolve(self) -> impl Future + Send; -} - -impl AttributeValue for F -where - F: ReactiveFunction, - V: AttributeValue, -{ - async fn resolve(self) { - // We're awaiting `::{synthetic#0}` here. - // Normalizing that one via the the impl we're currently in - // relies on `collect_return_position_impl_trait_in_trait_tys` which - // ends up relying on auto-trait leakage when checking that the - // opaque return type of this function implements the `Send` item - // bound of the trait definition. - self.invoke().resolve().await - } -} -``` ### We always consider `AliasBound` candidates @@ -453,6 +393,67 @@ where } ``` +### Trait where-bounds shadow impls + +Normalization of associated items does not consider impls if the corresponding trait goal has been proven via a `ParamEnv` or `AliasBound` candidate. +This means that for where-bounds which do not constrain associated types, the associated types remain *rigid*. + +#### Using impls results in different region constraints + +This is necessary to avoid unnecessary region constraints from applying impls. +```rust +trait Trait<'a> { + type Assoc; +} +impl Trait<'static> for u32 { + type Assoc = u32; +} + +fn bar<'b, T: Trait<'b>>() -> T::Assoc { todo!() } +fn foo<'a>() +where + u32: Trait<'a>, +{ + // Normalizing the return type would use the impl, proving + // the `T: Trait` where-bound would use the where-bound, resulting + // in different region constraints. + bar::<'_, u32>(); +} +``` + +#### RPITIT `type_of` cycles + +We currently have to avoid impl candidates if there are where-bounds to avoid query cycles for RPITIT, see [#139762]. It feels desirable to me to stop relying on auto-trait leakage of during RPITIT computation to remove this issue, see [#139788]. + +```rust +use std::future::Future; +pub trait ReactiveFunction: Send { + type Output; + + fn invoke(self) -> Self::Output; +} + +trait AttributeValue { + fn resolve(self) -> impl Future + Send; +} + +impl AttributeValue for F +where + F: ReactiveFunction, + V: AttributeValue, +{ + async fn resolve(self) { + // We're awaiting `::{synthetic#0}` here. + // Normalizing that one via the the impl we're currently in + // relies on `collect_return_position_impl_trait_in_trait_tys` which + // ends up relying on auto-trait leakage when checking that the + // opaque return type of this function implements the `Send` item + // bound of the trait definition. + self.invoke().resolve().await + } +} +``` + [`Candidate`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_next_trait_solver/solve/assembly/struct.Candidate.html [`CandidateSource`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_next_trait_solver/solve/enum.CandidateSource.html [`fn merge_trait_candidates`]: https://github.com/rust-lang/rust/blob/e3ee7f7aea5b45af3b42b5e4713da43876a65ac9/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs#L1342-L1424 From 9e4c55ac8d9d1083983bdc2c35a3513cddb3fc69 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Fri, 16 Jan 2026 19:26:34 +0900 Subject: [PATCH 20/69] Rename "remarks on perma unstable features" to "external rustc_drivers" --- src/doc/rustc-dev-guide/src/SUMMARY.md | 2 +- ...-on-perma-unstable-features.md => external-rustc-drivers.md} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/doc/rustc-dev-guide/src/rustc-driver/{remarks-on-perma-unstable-features.md => external-rustc-drivers.md} (98%) diff --git a/src/doc/rustc-dev-guide/src/SUMMARY.md b/src/doc/rustc-dev-guide/src/SUMMARY.md index 2dd07144a78c..daaaef42d909 100644 --- a/src/doc/rustc-dev-guide/src/SUMMARY.md +++ b/src/doc/rustc-dev-guide/src/SUMMARY.md @@ -140,7 +140,7 @@ - [Command-line arguments](./cli.md) - [rustc_driver and rustc_interface](./rustc-driver/intro.md) - - [Remarks on perma-unstable features](./rustc-driver/remarks-on-perma-unstable-features.md) + - [External rustc_drivers](./rustc-driver/external-rustc-drivers.md) - [Example: Type checking](./rustc-driver/interacting-with-the-ast.md) - [Example: Getting diagnostics](./rustc-driver/getting-diagnostics.md) - [Errors and lints](diagnostics.md) diff --git a/src/doc/rustc-dev-guide/src/rustc-driver/remarks-on-perma-unstable-features.md b/src/doc/rustc-dev-guide/src/rustc-driver/external-rustc-drivers.md similarity index 98% rename from src/doc/rustc-dev-guide/src/rustc-driver/remarks-on-perma-unstable-features.md rename to src/doc/rustc-dev-guide/src/rustc-driver/external-rustc-drivers.md index 3c2fe133be84..1049d7a82ddd 100644 --- a/src/doc/rustc-dev-guide/src/rustc-driver/remarks-on-perma-unstable-features.md +++ b/src/doc/rustc-dev-guide/src/rustc-driver/external-rustc-drivers.md @@ -1,4 +1,4 @@ -# Remarks on perma unstable features +# External `rustc_driver`s ## `rustc_private` From 308b736541d4fdc000cf8abb1577f0d55e05c77c Mon Sep 17 00:00:00 2001 From: Redddy Date: Thu, 15 Jan 2026 23:56:14 +0900 Subject: [PATCH 21/69] Added section on using GitHub Dev for PR inspection --- src/doc/rustc-dev-guide/src/git.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/doc/rustc-dev-guide/src/git.md b/src/doc/rustc-dev-guide/src/git.md index e7e84e2ea7f5..abe72b29cb1e 100644 --- a/src/doc/rustc-dev-guide/src/git.md +++ b/src/doc/rustc-dev-guide/src/git.md @@ -495,6 +495,14 @@ command to check it out locally. See for more info. ![`gh` suggestion](./img/github-cli.png) +### Using GitHub dev + +As an alternative to the GitHub web UI, GitHub Dev provides a web-based editor for browsing +repository and PRs. It can be opened by replacing `github.com` with `github.dev` in the URL +or by pressing `.` on a GitHub page. +See [the docs for github.dev editor](https://docs.github.com/en/codespaces/the-githubdev-web-based-editor) +for more details. + ### Moving large sections of code Git and Github's default diff view for large moves *within* a file is quite poor; it will show each From b3a5f53e8f50e5c2532b52cb7dc9222692e739f4 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 17 Jan 2026 17:02:46 +0100 Subject: [PATCH 22/69] rustdoc: Fix ICE when deprecated note is not resolved on the correct `DefId` --- .../passes/collect_intra_doc_links.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 018e20b76dc8..a68e9dc87ae5 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -11,10 +11,11 @@ use rustc_ast::util::comments::may_have_doc_links; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::intern::Interned; use rustc_errors::{Applicability, Diag, DiagMessage}; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::Namespace::*; use rustc_hir::def::{DefKind, MacroKinds, Namespace, PerNS}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LOCAL_CRATE}; -use rustc_hir::{Mutability, Safety}; +use rustc_hir::{Attribute, Mutability, Safety}; use rustc_middle::ty::{Ty, TyCtxt}; use rustc_middle::{bug, span_bug, ty}; use rustc_resolve::rustdoc::pulldown_cmark::LinkType; @@ -1108,10 +1109,8 @@ impl LinkCollector<'_, '_> { // Also resolve links in the note text of `#[deprecated]`. for attr in &item.attrs.other_attrs { - let rustc_hir::Attribute::Parsed(rustc_hir::attrs::AttributeKind::Deprecation { - span, - deprecation, - }) = attr + let Attribute::Parsed(AttributeKind::Deprecation { span: depr_span, deprecation }) = + attr else { continue; }; @@ -1128,8 +1127,14 @@ impl LinkCollector<'_, '_> { // inlined item. // let item_id = if let Some(inline_stmt_id) = item.inline_stmt_id - && item.span(tcx).is_none_or(|item_span| !item_span.inner().contains(*span)) - { + && tcx.get_all_attrs(inline_stmt_id).iter().any(|attr| { + matches!( + attr, + Attribute::Parsed(AttributeKind::Deprecation { + span: attr_span, .. + }) if attr_span == depr_span, + ) + }) { inline_stmt_id.to_def_id() } else { item.item_id.expect_def_id() From 1faaa769615cda3c662f96b5f48678e6f97ee245 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 17 Jan 2026 17:03:04 +0100 Subject: [PATCH 23/69] Add regression test for ICE when deprecated note is not resolved on the correct `DefId` --- .../deprecated-note-from-reexported.rs | 16 +++++++++ .../deprecated-note-from-reexported.stderr | 34 +++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 tests/rustdoc-ui/intra-doc/deprecated-note-from-reexported.rs create mode 100644 tests/rustdoc-ui/intra-doc/deprecated-note-from-reexported.stderr diff --git a/tests/rustdoc-ui/intra-doc/deprecated-note-from-reexported.rs b/tests/rustdoc-ui/intra-doc/deprecated-note-from-reexported.rs new file mode 100644 index 000000000000..3d1e48a4c638 --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/deprecated-note-from-reexported.rs @@ -0,0 +1,16 @@ +// This test ensures that the intra-doc link from reexported deprecated attribute note +// are resolved where they are declared. + +#![deny(rustdoc::broken_intra_doc_links)] + +#[doc(inline)] +pub use bar::sql_function_proc as sql_function; + +pub fn define_sql_function() {} + +pub mod bar { + #[deprecated(note = "Use [`define_sql_function`] instead")] + //~^ ERROR: unresolved link + //~| ERROR: unresolved link + pub fn sql_function_proc() {} +} diff --git a/tests/rustdoc-ui/intra-doc/deprecated-note-from-reexported.stderr b/tests/rustdoc-ui/intra-doc/deprecated-note-from-reexported.stderr new file mode 100644 index 000000000000..25f10b24d9fb --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/deprecated-note-from-reexported.stderr @@ -0,0 +1,34 @@ +error: unresolved link to `define_sql_function` + --> $DIR/deprecated-note-from-reexported.rs:12:25 + | +LL | #[deprecated(note = "Use [`define_sql_function`] instead")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the link appears in this line: + + Use [`define_sql_function`] instead + ^^^^^^^^^^^^^^^^^^^^^ + = note: no item named `define_sql_function` in scope + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` +note: the lint level is defined here + --> $DIR/deprecated-note-from-reexported.rs:4:9 + | +LL | #![deny(rustdoc::broken_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: unresolved link to `define_sql_function` + --> $DIR/deprecated-note-from-reexported.rs:12:25 + | +LL | #[deprecated(note = "Use [`define_sql_function`] instead")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the link appears in this line: + + Use [`define_sql_function`] instead + ^^^^^^^^^^^^^^^^^^^^^ + = note: no item named `define_sql_function` in scope + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 2 previous errors + From eed703ccc0d534f38e5d7eca30c6675364566b69 Mon Sep 17 00:00:00 2001 From: Oscar Bray Date: Sun, 18 Jan 2026 08:22:33 +0000 Subject: [PATCH 24/69] Fix typo. Match "build-rust-analyzer" in src/building/how-to-build-and-run.md and in the default editor settings from the rustc repo. --- src/doc/rustc-dev-guide/src/building/suggested.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/building/suggested.md b/src/doc/rustc-dev-guide/src/building/suggested.md index 1c75de3e9042..c87dc6b28d87 100644 --- a/src/doc/rustc-dev-guide/src/building/suggested.md +++ b/src/doc/rustc-dev-guide/src/building/suggested.md @@ -97,7 +97,7 @@ for two reasons: additional rebuilds in some cases. To avoid these problems: -- Add `--build-dir=build/rust-analyzer` to all of the custom `x` commands in +- Add `--build-dir=build-rust-analyzer` to all of the custom `x` commands in your editor's rust-analyzer configuration. (Feel free to choose a different directory name if desired.) - Modify the `rust-analyzer.rustfmt.overrideCommand` setting so that it points From e519c686cfe9220609929e014642bf99eebb48c4 Mon Sep 17 00:00:00 2001 From: yukang Date: Mon, 19 Jan 2026 11:15:20 +0800 Subject: [PATCH 25/69] Deduplicate diagnostics for const trait supertraits --- .../traits/fulfillment_errors.rs | 31 +++++++++++++++++++ tests/ui/consts/issue-94675.rs | 1 - tests/ui/consts/issue-94675.stderr | 13 +------- tests/ui/traits/const-traits/call.rs | 1 - tests/ui/traits/const-traits/call.stderr | 11 +------ ...closure-const_trait_impl-ice-113381.stderr | 11 +------ ...-const-op-const-closure-non-const-outer.rs | 3 +- ...st-op-const-closure-non-const-outer.stderr | 15 ++------- 8 files changed, 39 insertions(+), 47 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 00d06779e652..6872d038fb7f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -1442,6 +1442,31 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self.can_eq(param_env, goal.trait_ref, trait_assumption.trait_ref) } + fn can_match_host_effect( + &self, + param_env: ty::ParamEnv<'tcx>, + goal: ty::HostEffectPredicate<'tcx>, + assumption: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>, + ) -> bool { + let assumption = self.instantiate_binder_with_fresh_vars( + DUMMY_SP, + infer::BoundRegionConversionTime::HigherRankedType, + assumption, + ); + + assumption.constness.satisfies(goal.constness) + && self.can_eq(param_env, goal.trait_ref, assumption.trait_ref) + } + + fn as_host_effect_clause( + predicate: ty::Predicate<'tcx>, + ) -> Option>> { + predicate.as_clause().and_then(|clause| match clause.kind().skip_binder() { + ty::ClauseKind::HostEffect(pred) => Some(clause.kind().rebind(pred)), + _ => None, + }) + } + fn can_match_projection( &self, param_env: ty::ParamEnv<'tcx>, @@ -1484,6 +1509,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { .filter_map(|implied| implied.as_trait_clause()) .any(|implied| self.can_match_trait(param_env, error, implied)) }) + } else if let Some(error) = Self::as_host_effect_clause(error.predicate) { + self.enter_forall(error, |error| { + elaborate(self.tcx, std::iter::once(cond.predicate)) + .filter_map(Self::as_host_effect_clause) + .any(|implied| self.can_match_host_effect(param_env, error, implied)) + }) } else if let Some(error) = error.predicate.as_projection_clause() { self.enter_forall(error, |error| { elaborate(self.tcx, std::iter::once(cond.predicate)) diff --git a/tests/ui/consts/issue-94675.rs b/tests/ui/consts/issue-94675.rs index f2ddc928d122..0553b676bc3e 100644 --- a/tests/ui/consts/issue-94675.rs +++ b/tests/ui/consts/issue-94675.rs @@ -10,7 +10,6 @@ impl<'a> Foo<'a> { const fn spam(&mut self, baz: &mut Vec) { self.bar[0] = baz.len(); //~^ ERROR: `Vec: [const] Index<_>` is not satisfied - //~| ERROR: `Vec: [const] Index` is not satisfied //~| ERROR: `Vec: [const] IndexMut` is not satisfied } } diff --git a/tests/ui/consts/issue-94675.stderr b/tests/ui/consts/issue-94675.stderr index 771f2a14c305..ab7a76a90e02 100644 --- a/tests/ui/consts/issue-94675.stderr +++ b/tests/ui/consts/issue-94675.stderr @@ -16,17 +16,6 @@ LL | self.bar[0] = baz.len(); note: trait `IndexMut` is implemented but not `const` --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL -error[E0277]: the trait bound `Vec: [const] Index` is not satisfied - --> $DIR/issue-94675.rs:11:9 - | -LL | self.bar[0] = baz.len(); - | ^^^^^^^^^^^ - | -note: trait `Index` is implemented but not `const` - --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL -note: required by a bound in `std::ops::IndexMut::index_mut` - --> $SRC_DIR/core/src/ops/index.rs:LL:COL - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/call.rs b/tests/ui/traits/const-traits/call.rs index c36adc4248f0..360c08e1b7fe 100644 --- a/tests/ui/traits/const-traits/call.rs +++ b/tests/ui/traits/const-traits/call.rs @@ -6,7 +6,6 @@ const _: () = { assert!((const || true)()); //~^ ERROR }: [const] Fn()` is not satisfied - //~| ERROR }: [const] FnMut()` is not satisfied }; fn main() {} diff --git a/tests/ui/traits/const-traits/call.stderr b/tests/ui/traits/const-traits/call.stderr index b688746e2506..8e32cab6dfcf 100644 --- a/tests/ui/traits/const-traits/call.stderr +++ b/tests/ui/traits/const-traits/call.stderr @@ -4,15 +4,6 @@ error[E0277]: the trait bound `{closure@$DIR/call.rs:7:14: 7:22}: [const] Fn()` LL | assert!((const || true)()); | ^^^^^^^^^^^^^^^^^ -error[E0277]: the trait bound `{closure@$DIR/call.rs:7:14: 7:22}: [const] FnMut()` is not satisfied - --> $DIR/call.rs:7:13 - | -LL | assert!((const || true)()); - | ^^^^^^^^^^^^^^^^^ - | -note: required by a bound in `call` - --> $SRC_DIR/core/src/ops/function.rs:LL:COL - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.stderr b/tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.stderr index 90e87c724f54..dab3f14161fa 100644 --- a/tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.stderr +++ b/tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.stderr @@ -4,15 +4,6 @@ error[E0277]: the trait bound `{closure@$DIR/const_closure-const_trait_impl-ice- LL | (const || (()).foo())(); | ^^^^^^^^^^^^^^^^^^^^^^^ -error[E0277]: the trait bound `{closure@$DIR/const_closure-const_trait_impl-ice-113381.rs:15:6: 15:14}: [const] FnMut()` is not satisfied - --> $DIR/const_closure-const_trait_impl-ice-113381.rs:15:5 - | -LL | (const || (()).foo())(); - | ^^^^^^^^^^^^^^^^^^^^^^^ - | -note: required by a bound in `call` - --> $SRC_DIR/core/src/ops/function.rs:LL:COL - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.rs b/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.rs index ee4ff02f4c7c..de5bedf0ace7 100644 --- a/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.rs +++ b/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.rs @@ -10,7 +10,8 @@ impl Foo for () { } fn main() { + // #150052 deduplicate diagnostics for const trait supertraits + // so we only get one error here (const || { (()).foo() })(); //~^ ERROR: }: [const] Fn()` is not satisfied - //~| ERROR: }: [const] FnMut()` is not satisfied } diff --git a/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.stderr b/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.stderr index 69d289537da1..efbedca1c7e7 100644 --- a/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.stderr +++ b/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.stderr @@ -1,18 +1,9 @@ -error[E0277]: the trait bound `{closure@$DIR/non-const-op-const-closure-non-const-outer.rs:13:6: 13:14}: [const] Fn()` is not satisfied - --> $DIR/non-const-op-const-closure-non-const-outer.rs:13:5 +error[E0277]: the trait bound `{closure@$DIR/non-const-op-const-closure-non-const-outer.rs:15:6: 15:14}: [const] Fn()` is not satisfied + --> $DIR/non-const-op-const-closure-non-const-outer.rs:15:5 | LL | (const || { (()).foo() })(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0277]: the trait bound `{closure@$DIR/non-const-op-const-closure-non-const-outer.rs:13:6: 13:14}: [const] FnMut()` is not satisfied - --> $DIR/non-const-op-const-closure-non-const-outer.rs:13:5 - | -LL | (const || { (()).foo() })(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: required by a bound in `call` - --> $SRC_DIR/core/src/ops/function.rs:LL:COL - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0277`. From aa20181087be5c653337aee1eed5655333b968e5 Mon Sep 17 00:00:00 2001 From: Boxy Date: Fri, 16 Jan 2026 21:41:57 +0000 Subject: [PATCH 26/69] add relnotes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Laine Taffin Altman Co-authored-by: 许杰友 Jieyou Xu (Joe) <39484203+jieyouxu@users.noreply.github.com> Co-authored-by: Kevin Reid Co-authored-by: Josh Triplett --- RELEASES.md | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index 0dffe931e6eb..96be2e7cd14a 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,114 @@ +Version 1.93 (2026-01-22) +========================== + + + +Language +-------- +- [Stabilize several s390x `vector`-related target features and the `is_s390x_feature_detected!` macro](https://github.com/rust-lang/rust/pull/145656) +- [Stabilize declaration of C-style variadic functions for the `system` ABI](https://github.com/rust-lang/rust/pull/145954) +- [Emit error when using some keyword as a `cfg` predicate](https://github.com/rust-lang/rust/pull/146978) +- [Stabilize `asm_cfg`](https://github.com/rust-lang/rust/pull/147736) +- [During const-evaluation, support copying pointers byte-by-byte](https://github.com/rust-lang/rust/pull/148259) +- [LUB coercions now correctly handle function item types, and functions with differing safeties](https://github.com/rust-lang/rust/pull/148602) +- [Allow `const` items that contain mutable references to `static` (which is *very* unsafe, but not *always* UB)](https://github.com/rust-lang/rust/pull/148746) +- [Add warn-by-default `const_item_interior_mutations` lint to warn against calls which mutate interior mutable `const` items](https://github.com/rust-lang/rust/pull/148407) +- [Add warn-by-default `function_casts_as_integer` lint](https://github.com/rust-lang/rust/pull/141470) + + + + +Compiler +-------- +- [Stabilize `-Cjump-tables=bool`](https://github.com/rust-lang/rust/pull/145974). The flag was previously called `-Zno-jump-tables`. + + + +Platform Support +---------------- + +- [Promote `riscv64a23-unknown-linux-gnu` to Tier 2 (without host tools)](https://github.com/rust-lang/rust/pull/148435) + +Refer to Rust's [platform support page][platform-support-doc] +for more information on Rust's tiered platform support. + +[platform-support-doc]: https://doc.rust-lang.org/rustc/platform-support.html + + + +Libraries +--------- +- [Stop internally using `specialization` on the `Copy` trait as it is unsound in the presence of lifetime dependent `Copy` implementations. This may result in some performance regressions as some standard library APIs may now call `Clone::clone` instead of performing bitwise copies](https://github.com/rust-lang/rust/pull/135634) +- [Allow the global allocator to use thread-local storage and `std::thread::current()`](https://github.com/rust-lang/rust/pull/144465) +- [Make `BTree::append` not update existing keys when appending an entry which already exists](https://github.com/rust-lang/rust/pull/145628) +- [Don't require `T: RefUnwindSafe` for `vec::IntoIter: UnwindSafe`](https://github.com/rust-lang/rust/pull/145665) + + + + +Stabilized APIs +--------------- + +- [`>::assume_init_drop`](https://doc.rust-lang.org/stable/core/mem/union.MaybeUninit.html#method.assume_init_drop) +- [`>::assume_init_ref`](https://doc.rust-lang.org/stable/core/mem/union.MaybeUninit.html#method.assume_init_ref) +- [`>::assume_init_mut`](https://doc.rust-lang.org/stable/core/mem/union.MaybeUninit.html#method.assume_init_mut) +- [`<[MaybeUninit]>::write_copy_of_slice`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.write_copy_of_slice) +- [`<[MaybeUninit]>::write_clone_of_slice`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.write_clone_of_slice) +- [`String::into_raw_parts`](https://doc.rust-lang.org/stable/std/string/struct.String.html#method.into_raw_parts) +- [`Vec::into_raw_parts`](https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.into_raw_parts) +- [`::unchecked_neg`](https://doc.rust-lang.org/stable/std/primitive.isize.html#method.unchecked_neg) +- [`::unchecked_shl`](https://doc.rust-lang.org/stable/std/primitive.isize.html#method.unchecked_shl) +- [`::unchecked_shr`](https://doc.rust-lang.org/stable/std/primitive.isize.html#method.unchecked_shr) +- [`::unchecked_shl`](https://doc.rust-lang.org/stable/std/primitive.usize.html#method.unchecked_shl) +- [`::unchecked_shr`](https://doc.rust-lang.org/stable/std/primitive.usize.html#method.unchecked_shr) +- [`<[T]>::as_array`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.as_array) +- [`<[T]>::as_array_mut`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.as_mut_array) +- [`<*const [T]>::as_array`](https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.as_array) +- [`<*mut [T]>::as_array_mut`](https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.as_mut_array) +- [`VecDeque::pop_front_if`](https://doc.rust-lang.org/stable/std/collections/struct.VecDeque.html#method.pop_front_if) +- [`VecDeque::pop_back_if`](https://doc.rust-lang.org/stable/std/collections/struct.VecDeque.html#method.pop_back_if) +- [`Duration::from_nanos_u128`](https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.from_nanos_u128) +- [`char::MAX_LEN_UTF8`](https://doc.rust-lang.org/stable/std/primitive.char.html#associatedconstant.MAX_LEN_UTF8) +- [`char::MAX_LEN_UTF16`](https://doc.rust-lang.org/stable/std/primitive.char.html#associatedconstant.MAX_LEN_UTF16) +- [`std::fmt::from_fn`](https://doc.rust-lang.org/stable/std/fmt/fn.from_fn.html) +- [`std::fmt::FromFn`](https://doc.rust-lang.org/stable/std/fmt/struct.FromFn.html) + + + + +Cargo +----- +- [Enable CARGO_CFG_DEBUG_ASSERTIONS in build scripts based on profile](https://github.com/rust-lang/cargo/pull/16160/) +- [In `cargo tree`, support long forms for `--format` variables](https://github.com/rust-lang/cargo/pull/16204/) +- [Add `--workspace` to `cargo clean`](https://github.com/rust-lang/cargo/pull/16263/) + + + +Rustdoc +----- +- [Remove `#![doc(document_private_items)]`](https://github.com/rust-lang/rust/pull/146495) +- [Include attribute and derive macros in search filters for "macros"](https://github.com/rust-lang/rust/pull/148176) +- [Include extern crates in search filters for `import`](https://github.com/rust-lang/rust/pull/148301) +- [Validate usage of crate-level doc attributes](https://github.com/rust-lang/rust/pull/149197). This means if any of `html_favicon_url`, `html_logo_url`, `html_playground_url`, `issue_tracker_base_url`, or `html_no_source` either has a missing value, an unexpected value, or a value of the wrong type, rustdoc will emit the deny-by-default lint `rustdoc::invalid_doc_attributes`. + + + + +Compatibility Notes +------------------- +- [Introduce `pin_v2` into the builtin attributes namespace](https://github.com/rust-lang/rust/pull/139751) +- [Update bundled musl to 1.2.5](https://github.com/rust-lang/rust/pull/142682) +- [On Emscripten, the unwinding ABI used when compiling with `panic=unwind` was changed from the JS exception handling ABI to the wasm exception handling ABI.](https://github.com/rust-lang/rust/pull/147224) If linking C/C++ object files with Rust objects, `-fwasm-exceptions` must be passed to the linker now. On nightly Rust, it is possible to get the old behavior with `-Zwasm-emscripten-eh=false -Zbuild-std`, but it will be removed in a future release. +- The `#[test]` attribute, used to define tests, was previously ignored in various places where it had no meaning (e.g on trait methods or types). Putting the `#[test]` attribute in these places is no longer ignored, and will now result in an error; this may also result in errors when generating rustdoc. [Error when `test` attribute is applied to structs](https://github.com/rust-lang/rust/pull/147841) +- Cargo now sets the `CARGO_CFG_DEBUG_ASSERTIONS` environment variable in more situations. This will cause crates depending on `static-init` versions 1.0.1 to 1.0.3 to fail compilation with "failed to resolve: use of unresolved module or unlinked crate `parking_lot`". See [the linked issue](https://github.com/rust-lang/rust/issues/150646#issuecomment-3718964342) for details. +- [User written types in the `offset_of!` macro are now checked to be well formed.](https://github.com/rust-lang/rust/issues/150465/) +- `cargo publish` no longer emits `.crate` files as a final artifact for user access when the `build.build-dir` config is unset +- [Upgrade the `deref_nullptr` lint from warn-by-default to deny-by-default](https://github.com/rust-lang/rust/pull/148122) +- [Add future-incompatibility warning for `...` function parameters without a pattern outside of `extern` blocks](https://github.com/rust-lang/rust/pull/143619) +- [Introduce future-compatibility warning for `repr(C)` enums whose discriminant values do not fit into a `c_int` or `c_uint`](https://github.com/rust-lang/rust/pull/147017) +- [Introduce future-compatibility warning against ignoring `repr(C)` types as part of `repr(transparent)`](https://github.com/rust-lang/rust/pull/147185) + + Version 1.92.0 (2025-12-11) ========================== From b0958e58bf2f5a3a6ce7f433e6da7a84b20c08c2 Mon Sep 17 00:00:00 2001 From: Matthew Tejo Date: Mon, 19 Jan 2026 08:53:43 -0800 Subject: [PATCH 27/69] Fix issue query deadlink in getting-started.md The link for finding issues leads to a 404. This adds the repo to the query in the url so the search works. --- src/doc/rustc-dev-guide/src/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/getting-started.md b/src/doc/rustc-dev-guide/src/getting-started.md index 6ccc5a75497a..a5da912aa4c4 100644 --- a/src/doc/rustc-dev-guide/src/getting-started.md +++ b/src/doc/rustc-dev-guide/src/getting-started.md @@ -96,7 +96,7 @@ For example: Not all important or beginner work has issue labels. See below for how to find work that isn't labelled. -[help-wanted-search]: https://github.com/issues?q=is%3Aopen%20is%3Aissue%20org%3Arust-lang%20no%3Aassignee%20label%3AE-easy%2CE-medium%2CE-help-wanted%2CE-mentor%20-label%3AS-blocked%20-linked%3Apr +[help-wanted-search]: https://github.com/rust-lang/rust/issues?q=is%3Aopen%20is%3Aissue%20org%3Arust-lang%20no%3Aassignee%20label%3AE-easy%2CE-medium%2CE-help-wanted%2CE-mentor%20-label%3AS-blocked%20-linked%3Apr [Triage]: ./contributing.md#issue-triage ### Recurring work From 8a573580f842fd2ebc888439ceaff75aabc13b0c Mon Sep 17 00:00:00 2001 From: Boxy Date: Mon, 19 Jan 2026 16:46:12 +0000 Subject: [PATCH 28/69] Fix terminal dependent tests --- src/doc/rustc-dev-guide/src/tests/ui.md | 4 ++++ tests/ui/compiletest-self-test/ui-testing-optout.rs | 2 +- tests/ui/consts/missing_span_in_backtrace.rs | 2 +- tests/ui/consts/missing_span_in_backtrace.stderr | 12 ++++++------ .../ui/error-emitter/trimmed_multiline_suggestion.rs | 2 +- .../trimmed_multiline_suggestion.stderr | 2 +- tests/ui/modules/issue-107649.rs | 2 +- tests/ui/span/issue-71363.rs | 2 +- 8 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tests/ui.md b/src/doc/rustc-dev-guide/src/tests/ui.md index 759029e67132..e13419d1e01c 100644 --- a/src/doc/rustc-dev-guide/src/tests/ui.md +++ b/src/doc/rustc-dev-guide/src/tests/ui.md @@ -139,6 +139,10 @@ prefixing each source line are replaced with `LL`). In extremely rare situations, this mode can be disabled with the directive `//@ compile-flags: -Z ui-testing=no`. +When using `-Z ui-testing=no` the `--diagnostic-width` argument should also +be set to avoid tests failing or passing depending on the width of the terminal +from which the UI test suite is being run. + Note: The line and column numbers for `-->` lines pointing to the test are *not* normalized, and left as-is. This ensures that the compiler continues to point to the correct location, and keeps the stderr files readable. Ideally all diff --git a/tests/ui/compiletest-self-test/ui-testing-optout.rs b/tests/ui/compiletest-self-test/ui-testing-optout.rs index 62920a86c3b3..2f2bc54676a8 100644 --- a/tests/ui/compiletest-self-test/ui-testing-optout.rs +++ b/tests/ui/compiletest-self-test/ui-testing-optout.rs @@ -1,4 +1,4 @@ -//@ compile-flags: -Z ui-testing=no +//@ compile-flags: -Z ui-testing=no --diagnostic-width=80 // Line number < 10 type A = B; //~ ERROR diff --git a/tests/ui/consts/missing_span_in_backtrace.rs b/tests/ui/consts/missing_span_in_backtrace.rs index 09a11c75fd7b..b679493eb08e 100644 --- a/tests/ui/consts/missing_span_in_backtrace.rs +++ b/tests/ui/consts/missing_span_in_backtrace.rs @@ -1,6 +1,6 @@ //! Check what happens when the error occurs inside a std function that we can't print the span of. //@ ignore-backends: gcc -//@ compile-flags: -Z ui-testing=no +//@ compile-flags: -Z ui-testing=no --diagnostic-width=80 use std::{ mem::{self, MaybeUninit}, diff --git a/tests/ui/consts/missing_span_in_backtrace.stderr b/tests/ui/consts/missing_span_in_backtrace.stderr index 55dde6358286..b86911e7057b 100644 --- a/tests/ui/consts/missing_span_in_backtrace.stderr +++ b/tests/ui/consts/missing_span_in_backtrace.stderr @@ -1,12 +1,12 @@ error[E0080]: memory access failed: attempting to access 1 byte, but got ALLOC0+0x4 which is at or beyond the end of the allocation of size 4 bytes --> $DIR/missing_span_in_backtrace.rs:16:9 | -16 | / ptr::swap_nonoverlapping( -17 | | &mut x1 as *mut _ as *mut MaybeUninit, -18 | | &mut x2 as *mut _ as *mut MaybeUninit, -19 | | 10, -20 | | ); - | |_________^ evaluation of `X` failed inside this call +16 | / ... ptr::swap_nonoverlapping( +17 | | ... &mut x1 as *mut _ as *mut MaybeUninit, +18 | | ... &mut x2 as *mut _ as *mut MaybeUninit, +19 | | ... 10, +20 | | ... ); + | |_______^ evaluation of `X` failed inside this call | note: inside `swap_nonoverlapping::compiletime::>` --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL diff --git a/tests/ui/error-emitter/trimmed_multiline_suggestion.rs b/tests/ui/error-emitter/trimmed_multiline_suggestion.rs index c0ae9af4e9b2..7100a96611c6 100644 --- a/tests/ui/error-emitter/trimmed_multiline_suggestion.rs +++ b/tests/ui/error-emitter/trimmed_multiline_suggestion.rs @@ -1,4 +1,4 @@ -//@ compile-flags: -Z ui-testing=no +//@ compile-flags: -Z ui-testing=no --diagnostic-width=80 fn function_with_lots_of_arguments(a: i32, b: char, c: i32, d: i32, e: i32, f: i32) {} fn main() { diff --git a/tests/ui/error-emitter/trimmed_multiline_suggestion.stderr b/tests/ui/error-emitter/trimmed_multiline_suggestion.stderr index 6e1ffb153bc2..d593647a5ba0 100644 --- a/tests/ui/error-emitter/trimmed_multiline_suggestion.stderr +++ b/tests/ui/error-emitter/trimmed_multiline_suggestion.stderr @@ -10,7 +10,7 @@ error[E0061]: this function takes 6 arguments but 5 arguments were supplied note: function defined here --> $DIR/trimmed_multiline_suggestion.rs:2:4 | -2 | fn function_with_lots_of_arguments(a: i32, b: char, c: i32, d: i32, e: i32, f: i32) {} +2 | fn function_with_lots_of_arguments(a: i32, b: char, c: i32, d: i32, e: i3... | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ------- help: provide the argument | diff --git a/tests/ui/modules/issue-107649.rs b/tests/ui/modules/issue-107649.rs index f93fb07e17af..731c2c0592a1 100644 --- a/tests/ui/modules/issue-107649.rs +++ b/tests/ui/modules/issue-107649.rs @@ -1,4 +1,4 @@ -//@ compile-flags: -Z ui-testing=no +//@ compile-flags: -Z ui-testing=no --diagnostic-width=80 #[path = "auxiliary/dummy_lib.rs"] mod lib; diff --git a/tests/ui/span/issue-71363.rs b/tests/ui/span/issue-71363.rs index f186e0526a42..4ce8a942c7b1 100644 --- a/tests/ui/span/issue-71363.rs +++ b/tests/ui/span/issue-71363.rs @@ -1,4 +1,4 @@ -//@ compile-flags: -Z ui-testing=no +//@ compile-flags: -Z ui-testing=no --diagnostic-width=80 struct MyError; impl std::error::Error for MyError {} From 9f965bcf76f2fb5128b41e2e898e2d7ed537c72c Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 14 Jan 2026 00:45:39 +0100 Subject: [PATCH 29/69] Add rustdoc GUI test to ensure that the "hide deprecated items" setting is working as expected --- .../rustdoc-gui/setting-hide-deprecated.goml | 83 +++++++++++++++++++ tests/rustdoc-gui/src/lib2/lib.rs | 43 ++++++++++ tests/rustdoc-gui/utils.goml | 12 ++- 3 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 tests/rustdoc-gui/setting-hide-deprecated.goml diff --git a/tests/rustdoc-gui/setting-hide-deprecated.goml b/tests/rustdoc-gui/setting-hide-deprecated.goml new file mode 100644 index 000000000000..77f84493c67b --- /dev/null +++ b/tests/rustdoc-gui/setting-hide-deprecated.goml @@ -0,0 +1,83 @@ +// Test that the "hide deprecated" setting is correctly handled. + +include: "utils.goml" + +go-to: "file://" + |DOC_PATH| + "/lib2/deprecated/index.html" + +// There should be two deprecated items listed on the module page: +// `DeprecatedStruct` and `DeprecatedTrait`. +assert-count: ("dt.deprecated", 2) +// `DeprecatedStruct` and `DeprecatedTrait` should be displayed for now. +assert-css: ("dt.deprecated", {"display": "block"}, ALL) + +// We enable the "hide deprecated items" setting. +call-function: ("open-settings-menu", {}) +click: "#hide-deprecated-items" +// None of them should be displayed anymore. +wait-for-css: ("dt.deprecated", {"display": "none"}, ALL) + +// We disable the setting. +click: "#hide-deprecated-items" +// All of them should be displayed back. +wait-for-css: ("dt.deprecated", {"display": "block"}, ALL) + +// Now we go to a trait with a deprecated method and a deprecated associated const. +go-to: "file://" + |DOC_PATH| + "/lib2/deprecated/trait.NormalTrait.html" + +// There should be two deprecated items. +assert-count: ("details.deprecated", 2) +// They should be displayed for now. +assert-css: ("details.deprecated", {"display": "block"}, ALL) + +// We enable the "hide deprecated items" setting. +call-function: ("open-settings-menu", {}) +click: "#hide-deprecated-items" + +// They shouldn't be displayed anymore. +wait-for-css: ("details.deprecated", {"display": "none"}, ALL) + +// We disable the setting. +click: "#hide-deprecated-items" +// All of them should be displayed back. +wait-for-css: ("details.deprecated", {"display": "block"}, ALL) + +// We now go to a struct with a deprecated method which implements a deprecated trait and a trait +// with deprecated associated items. +go-to: "file://" + |DOC_PATH| + "/lib2/deprecated/struct.NonDeprecatedStruct.html" + +// There should be five deprecated items (six minus one "future" deprecated)... +assert-count: ("details.deprecated", 5) +// One of which being a deprecated impl because the trait itself is deprecated. +assert-count: ("details.implementors-toggle.deprecated", 1) +// And another has `since = "TBD"` and should NOT have the `deprecated` class. +assert: "details:not(.deprecated) #method\.future_deprecated_fn" +// They should all be displayed for now. +assert-css: ("details.deprecated", {"display": "block"}, ALL) + +// We enable the "hide deprecated items" setting. +call-function: ("open-settings-menu", {}) +click: "#hide-deprecated-items" + +// They shouldn't be displayed anymore. +wait-for-css: ("details.deprecated", {"display": "none"}, ALL) + +// We disable the setting. +click: "#hide-deprecated-items" +// All of them should be displayed back. +wait-for-css: ("details.deprecated", {"display": "block"}, ALL) + +// And now we check with the search results. +call-function: ("perform-search", {"query": "deprecated::depr"}) +// There should at least 7 results. +store-count: ("#results ul.search-results.active > a", nb_search_results) +assert: |nb_search_results| >= 7 +// There should be at least 5 deprecated items. +store-count: ("#results ul.search-results.active > a.deprecated", nb_deprecated_results) +assert: |nb_search_results| >= 5 +// Deprecated items should all be displayed. +assert-css: ("#results ul.search-results.active > a.deprecated", {"display": "grid"}, ALL) +// We enable the "hide deprecated items" setting. +call-function: ("open-settings-menu", {}) +click: "#hide-deprecated-items" +// None of them should be displayed anymore. +wait-for-css: ("#results ul.search-results.active > a.deprecated", {"display": "none"}, ALL) diff --git a/tests/rustdoc-gui/src/lib2/lib.rs b/tests/rustdoc-gui/src/lib2/lib.rs index b87fdeea89da..1367b17b37b2 100644 --- a/tests/rustdoc-gui/src/lib2/lib.rs +++ b/tests/rustdoc-gui/src/lib2/lib.rs @@ -368,3 +368,46 @@ impl std::ops::Deref for Derefer { &self.0 } } + +pub mod deprecated { + #[deprecated(since = "1.26.0", note = "deprecated")] + pub struct DeprecatedStruct; + + pub struct NonDeprecatedStruct; + + pub trait NormalTrait { + #[deprecated(since = "1.26.0", note = "deprecated")] + /// doc + const X: usize = 12; + + #[deprecated(since = "1.26.0", note = "deprecated")] + /// doc + fn normal_deprecated_fn(); + } + + #[deprecated(since = "1.26.0", note = "deprecated")] + pub trait DeprecatedTrait { + fn depr_deprecated_fn(); + } + + impl NonDeprecatedStruct { + #[deprecated(since = "1.26.0", note = "deprecated")] + /// doc + pub fn deprecated_fn() {} + + /// doc + pub fn non_deprecated_fn() {} + + #[deprecated(since = "TBD", note = "deprecated")] + /// doc + pub fn future_deprecated_fn() {} + } + + impl NormalTrait for NonDeprecatedStruct { + fn normal_deprecated_fn() {} + } + + impl DeprecatedTrait for NonDeprecatedStruct { + fn depr_deprecated_fn() {} + } +} diff --git a/tests/rustdoc-gui/utils.goml b/tests/rustdoc-gui/utils.goml index c0625ead2f1a..29f1af293d6d 100644 --- a/tests/rustdoc-gui/utils.goml +++ b/tests/rustdoc-gui/utils.goml @@ -60,8 +60,8 @@ define-function: ( ) define-function: ( - "perform-search", - [query], + "open-search", + [], block { // Block requests with doubled `//`. // Amazon S3 doesn't support them, but other web hosts do, @@ -72,6 +72,14 @@ define-function: ( // Perform search click: "#search-button" wait-for: ".search-input" + } +) + +define-function: ( + "perform-search", + [query], + block { + call-function: ("open-search", {}) write-into: (".search-input", |query|) press-key: 'Enter' // wait for the search to start From 5561167fbde6bc9f5968b093acd404ed1dbb499a Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 14 Jan 2026 22:16:23 +0100 Subject: [PATCH 30/69] Improve JS source code for settings --- src/librustdoc/html/static/js/settings.js | 34 ++++------------------- src/librustdoc/html/static/js/storage.js | 30 ++++++++++---------- 2 files changed, 20 insertions(+), 44 deletions(-) diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js index b28b76b0d2e7..94d71f9222d2 100644 --- a/src/librustdoc/html/static/js/settings.js +++ b/src/librustdoc/html/static/js/settings.js @@ -43,27 +43,6 @@ } } break; - case "hide-sidebar": - if (value === true) { - addClass(document.documentElement, "hide-sidebar"); - } else { - removeClass(document.documentElement, "hide-sidebar"); - } - break; - case "hide-toc": - if (value === true) { - addClass(document.documentElement, "hide-toc"); - } else { - removeClass(document.documentElement, "hide-toc"); - } - break; - case "hide-modnav": - if (value === true) { - addClass(document.documentElement, "hide-modnav"); - } else { - removeClass(document.documentElement, "hide-modnav"); - } - break; case "sans-serif-fonts": if (value === true) { addClass(document.documentElement, "sans-serif"); @@ -71,18 +50,15 @@ removeClass(document.documentElement, "sans-serif"); } break; + case "hide-sidebar": + case "hide-toc": + case "hide-modnav": case "word-wrap-source-code": - if (value === true) { - addClass(document.documentElement, "word-wrap-source-code"); - } else { - removeClass(document.documentElement, "word-wrap-source-code"); - } - break; case "hide-deprecated-items": if (value === true) { - addClass(document.documentElement, "hide-deprecated-items"); + addClass(document.documentElement, settingName); } else { - removeClass(document.documentElement, "hide-deprecated-items"); + removeClass(document.documentElement, settingName); } } } diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js index 7cc70374378b..fc83578ddd83 100644 --- a/src/librustdoc/html/static/js/storage.js +++ b/src/librustdoc/html/static/js/storage.js @@ -319,24 +319,24 @@ updateTheme(); if (getSettingValue("source-sidebar-show") === "true") { addClass(document.documentElement, "src-sidebar-expanded"); } -if (getSettingValue("hide-sidebar") === "true") { - addClass(document.documentElement, "hide-sidebar"); -} -if (getSettingValue("hide-toc") === "true") { - addClass(document.documentElement, "hide-toc"); -} -if (getSettingValue("hide-modnav") === "true") { - addClass(document.documentElement, "hide-modnav"); -} if (getSettingValue("sans-serif-fonts") === "true") { addClass(document.documentElement, "sans-serif"); } -if (getSettingValue("word-wrap-source-code") === "true") { - addClass(document.documentElement, "word-wrap-source-code"); -} -if (getSettingValue("hide-deprecated-items") === "true") { - addClass(document.documentElement, "hide-deprecated-items"); -} +(function() { + const settings = [ + "hide-sidebar", + "hide-toc", + "hide-modnav", + "word-wrap-source-code", + "hide-deprecated-items", + ]; + for (const setting of settings) { + if (getSettingValue(setting) === "true") { + addClass(document.documentElement, setting); + } + } +})(); + function updateSidebarWidth() { const desktopSidebarWidth = getSettingValue("desktop-sidebar-width"); if (desktopSidebarWidth && desktopSidebarWidth !== "null") { From bf49068b724fc2d8ae0984c5c8869857dffd3373 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 14 Jan 2026 22:20:31 +0100 Subject: [PATCH 31/69] Rename `sans-serif` CSS class into `sans-serif-fonts` to match the JS setting, allowing to simplify JS code --- src/librustdoc/html/static/css/rustdoc.css | 2 +- src/librustdoc/html/static/js/settings.js | 6 ------ src/librustdoc/html/static/js/storage.js | 4 +--- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 73eb260a2b8e..ad7ef9b453ce 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -64,7 +64,7 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\ '); } -:root.sans-serif { +:root.sans-serif-fonts { --font-family: "Fira Sans", sans-serif; --font-family-code: "Fira Mono", monospace; } diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js index 94d71f9222d2..0bbeb6931e44 100644 --- a/src/librustdoc/html/static/js/settings.js +++ b/src/librustdoc/html/static/js/settings.js @@ -44,12 +44,6 @@ } break; case "sans-serif-fonts": - if (value === true) { - addClass(document.documentElement, "sans-serif"); - } else { - removeClass(document.documentElement, "sans-serif"); - } - break; case "hide-sidebar": case "hide-toc": case "hide-modnav": diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js index fc83578ddd83..1ec7cb4ced06 100644 --- a/src/librustdoc/html/static/js/storage.js +++ b/src/librustdoc/html/static/js/storage.js @@ -319,9 +319,6 @@ updateTheme(); if (getSettingValue("source-sidebar-show") === "true") { addClass(document.documentElement, "src-sidebar-expanded"); } -if (getSettingValue("sans-serif-fonts") === "true") { - addClass(document.documentElement, "sans-serif"); -} (function() { const settings = [ "hide-sidebar", @@ -329,6 +326,7 @@ if (getSettingValue("sans-serif-fonts") === "true") { "hide-modnav", "word-wrap-source-code", "hide-deprecated-items", + "sans-serif-fonts", ]; for (const setting of settings) { if (getSettingValue(setting) === "true") { From 28eca4a8fe72ba498761e567ed92fa2a61c5e726 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Mon, 19 Jan 2026 21:50:15 +0100 Subject: [PATCH 32/69] Only run finalizers of accepted attributes --- compiler/rustc_attr_parsing/src/context.rs | 22 +++++++++----------- compiler/rustc_attr_parsing/src/interface.rs | 8 +++++-- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 0c882fee01c8..693ee0391171 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -103,18 +103,18 @@ type GroupType = LazyLock>; pub(super) struct GroupTypeInner { pub(super) accepters: BTreeMap<&'static [Symbol], Vec>>, - pub(super) finalizers: Vec>, } pub(super) struct GroupTypeInnerAccept { pub(super) template: AttributeTemplate, pub(super) accept_fn: AcceptFn, pub(super) allowed_targets: AllowedTargets, + pub(super) finalizer: FinalizeFn, } -type AcceptFn = +pub(crate) type AcceptFn = Box Fn(&mut AcceptContext<'_, 'sess, S>, &ArgParser) + Send + Sync>; -type FinalizeFn = +pub(crate) type FinalizeFn = Box) -> Option>; macro_rules! attribute_parsers { @@ -142,8 +142,7 @@ macro_rules! attribute_parsers { @[$stage: ty] pub(crate) static $name: ident = [$($names: ty),* $(,)?]; ) => { pub(crate) static $name: GroupType<$stage> = LazyLock::new(|| { - let mut accepts = BTreeMap::<_, Vec>>::new(); - let mut finalizes = Vec::>::new(); + let mut accepters = BTreeMap::<_, Vec>>::new(); $( { thread_local! { @@ -151,7 +150,7 @@ macro_rules! attribute_parsers { }; for (path, template, accept_fn) in <$names>::ATTRIBUTES { - accepts.entry(*path).or_default().push(GroupTypeInnerAccept { + accepters.entry(*path).or_default().push(GroupTypeInnerAccept { template: *template, accept_fn: Box::new(|cx, args| { STATE_OBJECT.with_borrow_mut(|s| { @@ -159,17 +158,16 @@ macro_rules! attribute_parsers { }) }), allowed_targets: <$names as crate::attributes::AttributeParser<$stage>>::ALLOWED_TARGETS, + finalizer: Box::new(|cx| { + let state = STATE_OBJECT.take(); + state.finalize(cx) + }), }); } - - finalizes.push(Box::new(|cx| { - let state = STATE_OBJECT.take(); - state.finalize(cx) - })); } )* - GroupTypeInner { accepters:accepts, finalizers:finalizes } + GroupTypeInner { accepters } }); }; } diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index c6be18321b5e..bac4936c20d2 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -12,7 +12,7 @@ use rustc_session::Session; use rustc_session::lint::{BuiltinLintDiag, LintId}; use rustc_span::{DUMMY_SP, Span, Symbol, sym}; -use crate::context::{AcceptContext, FinalizeContext, SharedContext, Stage}; +use crate::context::{AcceptContext, FinalizeContext, FinalizeFn, SharedContext, Stage}; use crate::early_parsed::{EARLY_PARSED_ATTRIBUTES, EarlyParsedState}; use crate::parser::{ArgParser, PathParser, RefPathParser}; use crate::session_diagnostics::ParsedDescription; @@ -270,6 +270,8 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { let mut attr_paths: Vec> = Vec::new(); let mut early_parsed_state = EarlyParsedState::default(); + let mut finalizers: Vec<&FinalizeFn> = Vec::with_capacity(attrs.len()); + for attr in attrs { // If we're only looking for a single attribute, skip all the ones we don't care about. if let Some(expected) = self.parse_only { @@ -383,6 +385,8 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { }; (accept.accept_fn)(&mut cx, &args); + finalizers.push(&accept.finalizer); + if !matches!(cx.stage.should_emit(), ShouldEmit::Nothing) { Self::check_target(&accept.allowed_targets, target, &mut cx); } @@ -417,7 +421,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { } early_parsed_state.finalize_early_parsed_attributes(&mut attributes); - for f in &S::parsers().finalizers { + for f in &finalizers { if let Some(attr) = f(&mut FinalizeContext { shared: SharedContext { cx: self, target_span, target, emit_lint: &mut emit_lint }, all_attrs: &attr_paths, From 6dc27bf7de8dd306baba6e6904fa5234bc56d9f1 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Mon, 19 Jan 2026 21:50:29 +0100 Subject: [PATCH 33/69] Update uitests --- tests/pretty/delegation-inherit-attributes.pp | 2 +- tests/pretty/delegation-inline-attribute.pp | 20 ++++++------- ...ute-rejects-already-stable-features.stderr | 28 +++++++++---------- tests/ui/loop-match/invalid-attribute.stderr | 16 +++++------ 4 files changed, 33 insertions(+), 33 deletions(-) diff --git a/tests/pretty/delegation-inherit-attributes.pp b/tests/pretty/delegation-inherit-attributes.pp index 2398cae90fdb..9d51a80da7b9 100644 --- a/tests/pretty/delegation-inherit-attributes.pp +++ b/tests/pretty/delegation-inherit-attributes.pp @@ -21,8 +21,8 @@ mod to_reuse { #[attr = Cold] fn foo_no_reason(x: usize) -> usize { x } - #[attr = Deprecation {deprecation: Deprecation {since: Unspecified}}] #[attr = Cold] + #[attr = Deprecation {deprecation: Deprecation {since: Unspecified}}] fn bar(x: usize) -> usize { x } } diff --git a/tests/pretty/delegation-inline-attribute.pp b/tests/pretty/delegation-inline-attribute.pp index 5235fd8d0ef2..f83ae47b81f6 100644 --- a/tests/pretty/delegation-inline-attribute.pp +++ b/tests/pretty/delegation-inline-attribute.pp @@ -44,9 +44,9 @@ impl Trait for S { fn foo0(arg0: _) -> _ { to_reuse::foo(self + 1) } // Check that #[inline(hint)] is added when other attributes present in inner reuse - #[attr = Deprecation {deprecation: Deprecation {since: Unspecified}}] - #[attr = MustUse] #[attr = Cold] + #[attr = MustUse] + #[attr = Deprecation {deprecation: Deprecation {since: Unspecified}}] #[attr = Inline(Hint)] fn foo1(arg0: _) -> _ { to_reuse::foo(self / 2) } @@ -59,18 +59,18 @@ impl Trait for S { fn foo3(arg0: _) -> _ { to_reuse::foo(self / 2) } // Check that #[inline(never)] is preserved when there are other attributes in inner reuse - #[attr = Deprecation {deprecation: Deprecation {since: Unspecified}}] - #[attr = Inline(Never)] - #[attr = MustUse] #[attr = Cold] + #[attr = MustUse] + #[attr = Inline(Never)] + #[attr = Deprecation {deprecation: Deprecation {since: Unspecified}}] fn foo4(arg0: _) -> _ { to_reuse::foo(self / 2) } }.foo() } // Check that #[inline(hint)] is added when there are other attributes present in trait reuse - #[attr = Deprecation {deprecation: Deprecation {since: Unspecified}}] - #[attr = MustUse] #[attr = Cold] + #[attr = MustUse] + #[attr = Deprecation {deprecation: Deprecation {since: Unspecified}}] #[attr = Inline(Hint)] fn foo1(self: _) -> _ { self.0.foo1() } @@ -83,10 +83,10 @@ impl Trait for S { fn foo3(self: _) -> _ { self.0.foo3() } // Check that #[inline(never)] is preserved when there are other attributes in trait reuse - #[attr = Deprecation {deprecation: Deprecation {since: Unspecified}}] - #[attr = Inline(Never)] - #[attr = MustUse] #[attr = Cold] + #[attr = MustUse] + #[attr = Inline(Never)] + #[attr = Deprecation {deprecation: Deprecation {since: Unspecified}}] fn foo4(self: _) -> _ { self.0.foo4() } } diff --git a/tests/ui/feature-gates/unstable-attribute-rejects-already-stable-features.stderr b/tests/ui/feature-gates/unstable-attribute-rejects-already-stable-features.stderr index d599523c7274..319056a9c889 100644 --- a/tests/ui/feature-gates/unstable-attribute-rejects-already-stable-features.stderr +++ b/tests/ui/feature-gates/unstable-attribute-rejects-already-stable-features.stderr @@ -1,17 +1,3 @@ -error: can't mark as unstable using an already stable feature - --> $DIR/unstable-attribute-rejects-already-stable-features.rs:7:1 - | -LL | #[rustc_const_unstable(feature = "arbitrary_enum_discriminant", issue = "42")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this feature is already stable -LL | const fn my_fun() {} - | -------------------- the stability attribute annotates this item - | -help: consider removing the attribute - --> $DIR/unstable-attribute-rejects-already-stable-features.rs:7:1 - | -LL | #[rustc_const_unstable(feature = "arbitrary_enum_discriminant", issue = "42")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: can't mark as unstable using an already stable feature --> $DIR/unstable-attribute-rejects-already-stable-features.rs:6:1 | @@ -27,5 +13,19 @@ help: consider removing the attribute LL | #[unstable(feature = "arbitrary_enum_discriminant", issue = "42")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: can't mark as unstable using an already stable feature + --> $DIR/unstable-attribute-rejects-already-stable-features.rs:7:1 + | +LL | #[rustc_const_unstable(feature = "arbitrary_enum_discriminant", issue = "42")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this feature is already stable +LL | const fn my_fun() {} + | -------------------- the stability attribute annotates this item + | +help: consider removing the attribute + --> $DIR/unstable-attribute-rejects-already-stable-features.rs:7:1 + | +LL | #[rustc_const_unstable(feature = "arbitrary_enum_discriminant", issue = "42")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: aborting due to 2 previous errors diff --git a/tests/ui/loop-match/invalid-attribute.stderr b/tests/ui/loop-match/invalid-attribute.stderr index ddb68aea31b6..c23452b9b844 100644 --- a/tests/ui/loop-match/invalid-attribute.stderr +++ b/tests/ui/loop-match/invalid-attribute.stderr @@ -94,14 +94,6 @@ LL | #[const_continue] | = help: `#[const_continue]` can be applied to -error: `#[const_continue]` should be applied to a break expression - --> $DIR/invalid-attribute.rs:40:9 - | -LL | #[const_continue] - | ^^^^^^^^^^^^^^^^^ -LL | 5 - | - not a break expression - error: `#[loop_match]` should be applied to a loop --> $DIR/invalid-attribute.rs:39:9 | @@ -111,5 +103,13 @@ LL | #[const_continue] LL | 5 | - not a loop +error: `#[const_continue]` should be applied to a break expression + --> $DIR/invalid-attribute.rs:40:9 + | +LL | #[const_continue] + | ^^^^^^^^^^^^^^^^^ +LL | 5 + | - not a break expression + error: aborting due to 14 previous errors From 307a4fcdf803f7f1032bd317d8a34413d2d1e2c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelo=20Dom=C3=ADnguez?= Date: Mon, 22 Dec 2025 21:03:02 +0100 Subject: [PATCH 34/69] Add scalar support for both host and device --- compiler/rustc_codegen_llvm/src/back/write.rs | 40 +++++++++++- compiler/rustc_codegen_llvm/src/builder.rs | 15 +++++ .../src/builder/gpu_offload.rs | 62 ++++++++++++++----- compiler/rustc_codegen_llvm/src/intrinsic.rs | 4 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 12 +++- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 21 ++++++- compiler/rustc_middle/src/ty/offload_meta.rs | 17 +++-- .../codegen-llvm/gpu_offload/scalar_device.rs | 36 +++++++++++ tests/codegen-llvm/gpu_offload/scalar_host.rs | 37 +++++++++++ 9 files changed, 210 insertions(+), 34 deletions(-) create mode 100644 tests/codegen-llvm/gpu_offload/scalar_device.rs create mode 100644 tests/codegen-llvm/gpu_offload/scalar_host.rs diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index bcadb6f0de92..09d1ca1a5952 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -13,6 +13,7 @@ use rustc_codegen_ssa::back::write::{ TargetMachineFactoryConfig, TargetMachineFactoryFn, }; use rustc_codegen_ssa::base::wants_wasm_eh; +use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{CompiledModule, ModuleCodegen, ModuleKind}; use rustc_data_structures::profiling::SelfProfilerRef; @@ -33,6 +34,8 @@ use crate::back::owned_target_machine::OwnedTargetMachine; use crate::back::profiling::{ LlvmSelfProfiler, selfprofile_after_pass_callback, selfprofile_before_pass_callback, }; +use crate::builder::SBuilder; +use crate::builder::gpu_offload::scalar_width; use crate::common::AsCCharPtr; use crate::errors::{ CopyBitcode, FromLlvmDiag, FromLlvmOptimizationDiag, LlvmError, UnknownCompression, @@ -669,7 +672,17 @@ pub(crate) unsafe fn llvm_optimize( // Create the new parameter list, with ptr as the first argument let mut new_param_types = Vec::with_capacity(old_param_count as usize + 1); new_param_types.push(cx.type_ptr()); - new_param_types.extend(old_param_types); + + // This relies on undocumented LLVM knowledge that scalars must be passed as i64 + for &old_ty in &old_param_types { + let new_ty = match cx.type_kind(old_ty) { + TypeKind::Half | TypeKind::Float | TypeKind::Double | TypeKind::Integer => { + cx.type_i64() + } + _ => old_ty, + }; + new_param_types.push(new_ty); + } // Create the new function type let ret_ty = unsafe { llvm::LLVMGetReturnType(old_fn_ty) }; @@ -682,10 +695,33 @@ pub(crate) unsafe fn llvm_optimize( let a0 = llvm::get_param(new_fn, 0); llvm::set_value_name(a0, CString::new("dyn_ptr").unwrap().as_bytes()); + let bb = SBuilder::append_block(cx, new_fn, "entry"); + let mut builder = SBuilder::build(cx, bb); + + let mut old_args_rebuilt = Vec::with_capacity(old_param_types.len()); + + for (i, &old_ty) in old_param_types.iter().enumerate() { + let new_arg = llvm::get_param(new_fn, (i + 1) as u32); + + let rebuilt = match cx.type_kind(old_ty) { + TypeKind::Half | TypeKind::Float | TypeKind::Double | TypeKind::Integer => { + let num_bits = scalar_width(cx, old_ty); + + let trunc = builder.trunc(new_arg, cx.type_ix(num_bits)); + builder.bitcast(trunc, old_ty) + } + _ => new_arg, + }; + + old_args_rebuilt.push(rebuilt); + } + + builder.ret_void(); + // Here we map the old arguments to the new arguments, with an offset of 1 to make sure // that we don't use the newly added `%dyn_ptr`. unsafe { - llvm::LLVMRustOffloadMapper(old_fn, new_fn); + llvm::LLVMRustOffloadMapper(old_fn, new_fn, old_args_rebuilt.as_ptr()); } llvm::set_linkage(new_fn, llvm::get_linkage(old_fn)); diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 7a49ba64029e..557ae7b0333e 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -97,6 +97,21 @@ impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { GenericBuilder { llbuilder, cx: scx } } + pub(crate) fn append_block( + cx: &'a GenericCx<'ll, CX>, + llfn: &'ll Value, + name: &str, + ) -> &'ll BasicBlock { + unsafe { + let name = SmallCStr::new(name); + llvm::LLVMAppendBasicBlockInContext(cx.llcx(), llfn, name.as_ptr()) + } + } + + pub(crate) fn trunc(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { + unsafe { llvm::LLVMBuildTrunc(self.llbuilder, val, dest_ty, UNNAMED) } + } + pub(crate) fn bitcast(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { unsafe { llvm::LLVMBuildBitCast(self.llbuilder, val, dest_ty, UNNAMED) } } diff --git a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs index 7817755dafe4..c591c785cae3 100644 --- a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs +++ b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs @@ -2,6 +2,7 @@ use std::ffi::CString; use llvm::Linkage::*; use rustc_abi::Align; +use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, BuilderMethods}; use rustc_middle::bug; @@ -357,7 +358,6 @@ pub(crate) fn add_global<'ll>( pub(crate) fn gen_define_handling<'ll>( cx: &CodegenCx<'ll, '_>, metadata: &[OffloadMetadata], - types: &[&'ll Type], symbol: String, offload_globals: &OffloadGlobals<'ll>, ) -> OffloadKernelGlobals<'ll> { @@ -367,25 +367,18 @@ pub(crate) fn gen_define_handling<'ll>( let offload_entry_ty = offload_globals.offload_entry_ty; - // It seems like non-pointer values are automatically mapped. So here, we focus on pointer (or - // reference) types. - let ptr_meta = types.iter().zip(metadata).filter_map(|(&x, meta)| match cx.type_kind(x) { - rustc_codegen_ssa::common::TypeKind::Pointer => Some(meta), - _ => None, - }); - // FIXME(Sa4dUs): add `OMP_MAP_TARGET_PARAM = 0x20` only if necessary - let (ptr_sizes, ptr_transfer): (Vec<_>, Vec<_>) = - ptr_meta.map(|m| (m.payload_size, m.mode.bits() | 0x20)).unzip(); + let (sizes, transfer): (Vec<_>, Vec<_>) = + metadata.iter().map(|m| (m.payload_size, m.mode.bits() | 0x20)).unzip(); - let offload_sizes = add_priv_unnamed_arr(&cx, &format!(".offload_sizes.{symbol}"), &ptr_sizes); + let offload_sizes = add_priv_unnamed_arr(&cx, &format!(".offload_sizes.{symbol}"), &sizes); // Here we figure out whether something needs to be copied to the gpu (=1), from the gpu (=2), // or both to and from the gpu (=3). Other values shouldn't affect us for now. // A non-mutable reference or pointer will be 1, an array that's not read, but fully overwritten // will be 2. For now, everything is 3, until we have our frontend set up. // 1+2+32: 1 (MapTo), 2 (MapFrom), 32 (Add one extra input ptr per function, to be used later). let memtransfer_types = - add_priv_unnamed_arr(&cx, &format!(".offload_maptypes.{symbol}"), &ptr_transfer); + add_priv_unnamed_arr(&cx, &format!(".offload_maptypes.{symbol}"), &transfer); // Next: For each function, generate these three entries. A weak constant, // the llvm.rodata entry name, and the llvm_offload_entries value @@ -441,11 +434,24 @@ fn declare_offload_fn<'ll>( ) } +pub(crate) fn scalar_width<'ll>(cx: &'ll SimpleCx<'_>, ty: &'ll Type) -> u64 { + match cx.type_kind(ty) { + TypeKind::Half + | TypeKind::Float + | TypeKind::Double + | TypeKind::X86_FP80 + | TypeKind::FP128 + | TypeKind::PPC_FP128 => cx.float_width(ty) as u64, + TypeKind::Integer => cx.int_width(ty), + other => bug!("scalar_width was called on a non scalar type {other:?}"), + } +} + // For each kernel *call*, we now use some of our previous declared globals to move data to and from // the gpu. For now, we only handle the data transfer part of it. // If two consecutive kernels use the same memory, we still move it to the host and back to the gpu. // Since in our frontend users (by default) don't have to specify data transfer, this is something -// we should optimize in the future! In some cases we can directly zero-allocate ont he device and +// we should optimize in the future! In some cases we can directly zero-allocate on the device and // only move data back, or if something is immutable, we might only copy it to the device. // // Current steps: @@ -533,8 +539,34 @@ pub(crate) fn gen_call_handling<'ll, 'tcx>( let mut geps = vec![]; let i32_0 = cx.get_const_i32(0); for &v in args { - let gep = builder.inbounds_gep(cx.type_f32(), v, &[i32_0]); - vals.push(v); + let ty = cx.val_ty(v); + let ty_kind = cx.type_kind(ty); + let (base_val, gep_base) = match ty_kind { + TypeKind::Pointer => (v, v), + TypeKind::Half | TypeKind::Float | TypeKind::Double | TypeKind::Integer => { + // FIXME(Sa4dUs): check for `f128` support, latest NVIDIA cards support it + let num_bits = scalar_width(cx, ty); + + let bb = builder.llbb(); + unsafe { + llvm::LLVMRustPositionBuilderPastAllocas(builder.llbuilder, builder.llfn()); + } + let addr = builder.direct_alloca(cx.type_i64(), Align::EIGHT, "addr"); + unsafe { + llvm::LLVMPositionBuilderAtEnd(builder.llbuilder, bb); + } + + let cast = builder.bitcast(v, cx.type_ix(num_bits)); + let value = builder.zext(cast, cx.type_i64()); + builder.store(value, addr, Align::EIGHT); + (value, addr) + } + other => bug!("offload does not support {other:?}"), + }; + + let gep = builder.inbounds_gep(cx.type_f32(), gep_base, &[i32_0]); + + vals.push(base_val); geps.push(gep); } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 59cbcd78dd0f..c2975df4b6a0 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -1388,7 +1388,7 @@ fn codegen_offload<'ll, 'tcx>( let args = get_args_from_tuple(bx, args[3], fn_target); let target_symbol = symbol_name_for_instance_in_crate(tcx, fn_target, LOCAL_CRATE); - let sig = tcx.fn_sig(fn_target.def_id()).instantiate_identity(); + let sig = tcx.fn_sig(fn_target.def_id()).skip_binder(); let sig = tcx.instantiate_bound_regions_with_erased(sig); let inputs = sig.inputs(); @@ -1404,7 +1404,7 @@ fn codegen_offload<'ll, 'tcx>( return; } }; - let offload_data = gen_define_handling(&cx, &metadata, &types, target_symbol, offload_globals); + let offload_data = gen_define_handling(&cx, &metadata, target_symbol, offload_globals); gen_call_handling(bx, &offload_data, &args, &types, &metadata, offload_globals, &offload_dims); } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index a90013801c8c..c535fade9c04 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1675,7 +1675,11 @@ mod Offload { _M: &'a Module, _host_out: *const c_char, ) -> bool; - pub(crate) fn LLVMRustOffloadMapper<'a>(OldFn: &'a Value, NewFn: &'a Value); + pub(crate) fn LLVMRustOffloadMapper<'a>( + OldFn: &'a Value, + NewFn: &'a Value, + RebuiltArgs: *const &Value, + ); } } @@ -1702,7 +1706,11 @@ mod Offload_fallback { unimplemented!("This rustc version was not built with LLVM Offload support!"); } #[allow(unused_unsafe)] - pub(crate) unsafe fn LLVMRustOffloadMapper<'a>(_OldFn: &'a Value, _NewFn: &'a Value) { + pub(crate) unsafe fn LLVMRustOffloadMapper<'a>( + _OldFn: &'a Value, + _NewFn: &'a Value, + _RebuiltArgs: *const &Value, + ) { unimplemented!("This rustc version was not built with LLVM Offload support!"); } } diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 38e8886910f7..336d58974036 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -223,7 +223,12 @@ extern "C" bool LLVMRustOffloadEmbedBufferInModule(LLVMModuleRef HostM, return true; } -extern "C" void LLVMRustOffloadMapper(LLVMValueRef OldFn, LLVMValueRef NewFn) { +// Clone OldFn into NewFn, remapping its arguments to RebuiltArgs. +// Each arg of OldFn is replaced with the corresponding value in RebuiltArgs. +// For scalars, RebuiltArgs contains the value cast and/or truncated to the +// original type. +extern "C" void LLVMRustOffloadMapper(LLVMValueRef OldFn, LLVMValueRef NewFn, + const LLVMValueRef *RebuiltArgs) { llvm::Function *oldFn = llvm::unwrap(OldFn); llvm::Function *newFn = llvm::unwrap(NewFn); @@ -232,15 +237,25 @@ extern "C" void LLVMRustOffloadMapper(LLVMValueRef OldFn, LLVMValueRef NewFn) { llvm::ValueToValueMapTy vmap; auto newArgIt = newFn->arg_begin(); newArgIt->setName("dyn_ptr"); - ++newArgIt; // skip %dyn_ptr + + unsigned i = 0; for (auto &oldArg : oldFn->args()) { - vmap[&oldArg] = &*newArgIt++; + vmap[&oldArg] = unwrap(RebuiltArgs[i++]); } llvm::SmallVector returns; llvm::CloneFunctionInto(newFn, oldFn, vmap, llvm::CloneFunctionChangeType::LocalChangesOnly, returns); + + BasicBlock &entry = newFn->getEntryBlock(); + BasicBlock &clonedEntry = *std::next(newFn->begin()); + + if (entry.getTerminator()) + entry.getTerminator()->eraseFromParent(); + + IRBuilder<> B(&entry); + B.CreateBr(&clonedEntry); } #endif diff --git a/compiler/rustc_middle/src/ty/offload_meta.rs b/compiler/rustc_middle/src/ty/offload_meta.rs index 04a7cd2c75f2..67c00765ed57 100644 --- a/compiler/rustc_middle/src/ty/offload_meta.rs +++ b/compiler/rustc_middle/src/ty/offload_meta.rs @@ -78,16 +78,13 @@ impl MappingFlags { use rustc_ast::Mutability::*; match ty.kind() { - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Adt(_, _) - | ty::Tuple(_) - | ty::Array(_, _) - | ty::Alias(_, _) - | ty::Param(_) => MappingFlags::TO, + ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) => { + MappingFlags::LITERAL | MappingFlags::IMPLICIT + } + + ty::Adt(_, _) | ty::Tuple(_) | ty::Array(_, _) | ty::Alias(_, _) | ty::Param(_) => { + MappingFlags::TO + } ty::RawPtr(_, Not) | ty::Ref(_, _, Not) => MappingFlags::TO, diff --git a/tests/codegen-llvm/gpu_offload/scalar_device.rs b/tests/codegen-llvm/gpu_offload/scalar_device.rs new file mode 100644 index 000000000000..61772d404063 --- /dev/null +++ b/tests/codegen-llvm/gpu_offload/scalar_device.rs @@ -0,0 +1,36 @@ +//@ add-minicore +//@ revisions: amdgpu nvptx +//@[nvptx] compile-flags: -Copt-level=0 -Zunstable-options -Zoffload=Device --target nvptx64-nvidia-cuda --crate-type=rlib +//@[nvptx] needs-llvm-components: nvptx +//@[amdgpu] compile-flags: -Copt-level=0 -Zunstable-options -Zoffload=Device --target amdgcn-amd-amdhsa -Ctarget-cpu=gfx900 --crate-type=rlib +//@[amdgpu] needs-llvm-components: amdgpu +//@ no-prefer-dynamic +//@ needs-offload + +// This test verifies that the offload intrinsic is properly handling scalar args on the device, +// replacing the args by i64 and then trunc and cast them to the original type + +#![feature(abi_gpu_kernel, rustc_attrs, no_core)] +#![no_core] + +extern crate minicore; + +// CHECK: ; Function Attrs +// nvptx-NEXT: define ptx_kernel void @foo(ptr %dyn_ptr, ptr %0, i64 %1) +// amdgpu-NEXT: define amdgpu_kernel void @foo(ptr %dyn_ptr, ptr %0, i64 %1) +// CHECK-NEXT: entry: +// CHECK-NEXT: %2 = trunc i64 %1 to i32 +// CHECK-NEXT: %3 = bitcast i32 %2 to float +// CHECK-NEXT: br label %start +// CHECK: start: +// CHECK-NEXT: store float %3, ptr %0, align 4 +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +#[unsafe(no_mangle)] +#[rustc_offload_kernel] +pub unsafe extern "gpu-kernel" fn foo(x: *mut f32, k: f32) { + unsafe { + *x = k; + }; +} diff --git a/tests/codegen-llvm/gpu_offload/scalar_host.rs b/tests/codegen-llvm/gpu_offload/scalar_host.rs new file mode 100644 index 000000000000..8c7dcd4dd581 --- /dev/null +++ b/tests/codegen-llvm/gpu_offload/scalar_host.rs @@ -0,0 +1,37 @@ +//@ compile-flags: -Zoffload=Test -Zunstable-options -C opt-level=1 -Clto=fat +//@ no-prefer-dynamic +//@ needs-offload + +// This test verifies that the offload intrinsic is properly handling scalar args, passing them to +// the kernel as i64 + +#![feature(abi_gpu_kernel)] +#![feature(rustc_attrs)] +#![feature(core_intrinsics)] +#![no_main] + +// CHECK: define{{( dso_local)?}} void @main() +// CHECK-NOT: define +// CHECK: %addr = alloca i64, align 8 +// CHECK: store double 4.200000e+01, ptr %0, align 8 +// CHECK: %_0.i = load double, ptr %0, align 8 +// CHECK: store double %_0.i, ptr %addr, align 8 +// CHECK: %1 = getelementptr inbounds nuw i8, ptr %.offload_baseptrs, i64 8 +// CHECK-NEXT: store double %_0.i, ptr %1, align 8 +// CHECK-NEXT: %2 = getelementptr inbounds nuw i8, ptr %.offload_ptrs, i64 8 +// CHECK-NEXT: store ptr %addr, ptr %2, align 8 +// CHECK-NEXT: %3 = getelementptr inbounds nuw i8, ptr %.offload_sizes, i64 8 +// CHECK-NEXT: store i64 4, ptr %3, align 8 +// CHECK-NEXT: call void @__tgt_target_data_begin_mapper + +#[unsafe(no_mangle)] +fn main() { + let mut x = 0.0; + let k = core::hint::black_box(42.0); + + core::intrinsics::offload::<_, _, ()>(foo, [1, 1, 1], [1, 1, 1], (&mut x, k)); +} + +unsafe extern "C" { + pub fn foo(x: *mut f32, k: f32); +} From 60e488ec07c18a5ef314c46bba0d61bd61579ffe Mon Sep 17 00:00:00 2001 From: cyrgani Date: Mon, 19 Jan 2026 21:41:02 +0000 Subject: [PATCH 35/69] add basic `TokenStream` api tests --- .../auxiliary/api/proc_macro_api_tests.rs | 2 ++ .../proc-macro/auxiliary/api/tokenstream.rs | 28 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 tests/ui/proc-macro/auxiliary/api/tokenstream.rs diff --git a/tests/ui/proc-macro/auxiliary/api/proc_macro_api_tests.rs b/tests/ui/proc-macro/auxiliary/api/proc_macro_api_tests.rs index 4083604e1885..d778e46da6b9 100644 --- a/tests/ui/proc-macro/auxiliary/api/proc_macro_api_tests.rs +++ b/tests/ui/proc-macro/auxiliary/api/proc_macro_api_tests.rs @@ -8,6 +8,7 @@ extern crate proc_macro; mod cmp; mod ident; mod literal; +mod tokenstream; use proc_macro::TokenStream; @@ -18,6 +19,7 @@ pub fn run(input: TokenStream) -> TokenStream { cmp::test(); ident::test(); literal::test(); + tokenstream::test(); TokenStream::new() } diff --git a/tests/ui/proc-macro/auxiliary/api/tokenstream.rs b/tests/ui/proc-macro/auxiliary/api/tokenstream.rs new file mode 100644 index 000000000000..7078f4413051 --- /dev/null +++ b/tests/ui/proc-macro/auxiliary/api/tokenstream.rs @@ -0,0 +1,28 @@ +use proc_macro::*; + +fn assert_eq(l: TokenStream, r: TokenStream) { + assert_eq!(l.to_string(), r.to_string()); + for (lt, rt) in l.into_iter().zip(r) { + assert_eq!(lt.to_string(), rt.to_string()); + } +} + +pub fn test() { + assert_eq(TokenStream::new(), TokenStream::new()); + let mut stream = TokenStream::new(); + assert!(stream.is_empty()); + stream.extend(TokenStream::new()); + assert_eq(stream.clone(), TokenStream::new()); + + let old = stream.clone(); + stream.extend(vec![TokenTree::Ident(Ident::new("foo", Span::call_site()))]); + assert!(!stream.is_empty()); + assert!(old.is_empty()); + + let stream2 = stream + .clone() + .into_iter() + .inspect(|tree| assert_eq!(tree.to_string(), "foo")) + .collect::(); + assert_eq(stream.clone(), stream2); +} From 9e5e4326624d6da2c0412ec48c972de3e4c7d2f8 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 19 Jan 2026 23:10:20 +0100 Subject: [PATCH 36/69] Simplify `IndexItem::deprecation` field: since we only need a boolean value in the end, no need to keep it around. Also: takes into account "is_in_effect" --- src/librustdoc/formats/cache.rs | 4 ++-- src/librustdoc/html/render/mod.rs | 2 +- src/librustdoc/html/render/search_index.rs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 56f10b03d62d..64e361d566c3 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -593,7 +593,7 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It cache, ); let aliases = item.attrs.get_doc_aliases(); - let deprecation = item.deprecation(tcx); + let is_deprecated = item.is_deprecated(tcx); let index_item = IndexItem { ty: item.type_(), defid: Some(defid), @@ -608,7 +608,7 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It impl_id, search_type, aliases, - deprecation, + is_deprecated, }; cache.search_index.push(index_item); diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 106aefa4626b..46a60846f141 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -141,7 +141,7 @@ pub(crate) struct IndexItem { pub(crate) impl_id: Option, pub(crate) search_type: Option, pub(crate) aliases: Box<[Symbol]>, - pub(crate) deprecation: Option, + pub(crate) is_deprecated: bool, } /// A type used for the search index. diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 12b207dda569..30b534003da1 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -1282,7 +1282,7 @@ pub(crate) fn build_index( cache, ), aliases: item.attrs.get_doc_aliases(), - deprecation: item.deprecation(tcx), + is_deprecated: item.is_deprecated(tcx), }); } } @@ -1519,7 +1519,7 @@ pub(crate) fn build_index( trait_parent: item.trait_parent_idx, module_path, exact_module_path, - deprecated: item.deprecation.is_some(), + deprecated: item.is_deprecated, associated_item_disambiguator: if let Some(impl_id) = item.impl_id && let Some(parent_idx) = item.parent_idx && associated_item_duplicates From aef8112b26099c1f68b227a25792cde689828f94 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 19 Jan 2026 23:14:09 +0100 Subject: [PATCH 37/69] Add test for search results of not yet deprecated search items Improve the `perform-search` goml function to work when search is already open --- .../rustdoc-gui/setting-hide-deprecated.goml | 47 ++++++++++++------- tests/rustdoc-gui/utils.goml | 31 ++++++++---- 2 files changed, 52 insertions(+), 26 deletions(-) diff --git a/tests/rustdoc-gui/setting-hide-deprecated.goml b/tests/rustdoc-gui/setting-hide-deprecated.goml index 77f84493c67b..6dc5a6bff175 100644 --- a/tests/rustdoc-gui/setting-hide-deprecated.goml +++ b/tests/rustdoc-gui/setting-hide-deprecated.goml @@ -3,68 +3,69 @@ include: "utils.goml" go-to: "file://" + |DOC_PATH| + "/lib2/deprecated/index.html" +store-value: (deprecated_class, ".deprecated") // There should be two deprecated items listed on the module page: // `DeprecatedStruct` and `DeprecatedTrait`. -assert-count: ("dt.deprecated", 2) +assert-count: ("dt" + |deprecated_class|, 2) // `DeprecatedStruct` and `DeprecatedTrait` should be displayed for now. -assert-css: ("dt.deprecated", {"display": "block"}, ALL) +assert-css: ("dt" + |deprecated_class|, {"display": "block"}, ALL) // We enable the "hide deprecated items" setting. call-function: ("open-settings-menu", {}) click: "#hide-deprecated-items" // None of them should be displayed anymore. -wait-for-css: ("dt.deprecated", {"display": "none"}, ALL) +wait-for-css: ("dt" + |deprecated_class|, {"display": "none"}, ALL) // We disable the setting. click: "#hide-deprecated-items" // All of them should be displayed back. -wait-for-css: ("dt.deprecated", {"display": "block"}, ALL) +wait-for-css: ("dt" + |deprecated_class|, {"display": "block"}, ALL) // Now we go to a trait with a deprecated method and a deprecated associated const. go-to: "file://" + |DOC_PATH| + "/lib2/deprecated/trait.NormalTrait.html" // There should be two deprecated items. -assert-count: ("details.deprecated", 2) +assert-count: ("details" + |deprecated_class|, 2) // They should be displayed for now. -assert-css: ("details.deprecated", {"display": "block"}, ALL) +assert-css: ("details" + |deprecated_class|, {"display": "block"}, ALL) // We enable the "hide deprecated items" setting. call-function: ("open-settings-menu", {}) click: "#hide-deprecated-items" // They shouldn't be displayed anymore. -wait-for-css: ("details.deprecated", {"display": "none"}, ALL) +wait-for-css: ("details" + |deprecated_class|, {"display": "none"}, ALL) // We disable the setting. click: "#hide-deprecated-items" // All of them should be displayed back. -wait-for-css: ("details.deprecated", {"display": "block"}, ALL) +wait-for-css: ("details" + |deprecated_class|, {"display": "block"}, ALL) // We now go to a struct with a deprecated method which implements a deprecated trait and a trait // with deprecated associated items. go-to: "file://" + |DOC_PATH| + "/lib2/deprecated/struct.NonDeprecatedStruct.html" // There should be five deprecated items (six minus one "future" deprecated)... -assert-count: ("details.deprecated", 5) +assert-count: ("details" + |deprecated_class|, 5) // One of which being a deprecated impl because the trait itself is deprecated. -assert-count: ("details.implementors-toggle.deprecated", 1) +assert-count: ("details.implementors-toggle" + |deprecated_class|, 1) // And another has `since = "TBD"` and should NOT have the `deprecated` class. -assert: "details:not(.deprecated) #method\.future_deprecated_fn" +assert: "details:not(" + |deprecated_class| + ") #method\.future_deprecated_fn" // They should all be displayed for now. -assert-css: ("details.deprecated", {"display": "block"}, ALL) +assert-css: ("details" + |deprecated_class|, {"display": "block"}, ALL) // We enable the "hide deprecated items" setting. call-function: ("open-settings-menu", {}) click: "#hide-deprecated-items" // They shouldn't be displayed anymore. -wait-for-css: ("details.deprecated", {"display": "none"}, ALL) +wait-for-css: ("details" + |deprecated_class|, {"display": "none"}, ALL) // We disable the setting. click: "#hide-deprecated-items" // All of them should be displayed back. -wait-for-css: ("details.deprecated", {"display": "block"}, ALL) +wait-for-css: ("details" + |deprecated_class|, {"display": "block"}, ALL) // And now we check with the search results. call-function: ("perform-search", {"query": "deprecated::depr"}) @@ -72,12 +73,24 @@ call-function: ("perform-search", {"query": "deprecated::depr"}) store-count: ("#results ul.search-results.active > a", nb_search_results) assert: |nb_search_results| >= 7 // There should be at least 5 deprecated items. -store-count: ("#results ul.search-results.active > a.deprecated", nb_deprecated_results) +store-count: ("#results ul.search-results.active > a" + |deprecated_class|, nb_deprecated_results) assert: |nb_search_results| >= 5 // Deprecated items should all be displayed. -assert-css: ("#results ul.search-results.active > a.deprecated", {"display": "grid"}, ALL) +assert-css: ("#results ul.search-results.active > a" + |deprecated_class|, {"display": "grid"}, ALL) // We enable the "hide deprecated items" setting. call-function: ("open-settings-menu", {}) click: "#hide-deprecated-items" // None of them should be displayed anymore. -wait-for-css: ("#results ul.search-results.active > a.deprecated", {"display": "none"}, ALL) +wait-for-css: ( + "#results ul.search-results.active > a" + |deprecated_class|, + {"display": "none"}, + ALL, +) + +// Finally we check that the future deprecated item doesn't have the deprecated class in the search +// and therefore isn't impact by the setting. +call-function: ("perform-search", {"query": "deprecated::future_deprecated"}) +assert-text: ( + "#results ul.search-results.active > a:not(" + |deprecated_class| + ") .path", + " lib2::deprecated::NonDeprecatedStruct::future_deprecated_fn", +) diff --git a/tests/rustdoc-gui/utils.goml b/tests/rustdoc-gui/utils.goml index 29f1af293d6d..3d9bdd1ac149 100644 --- a/tests/rustdoc-gui/utils.goml +++ b/tests/rustdoc-gui/utils.goml @@ -63,15 +63,25 @@ define-function: ( "open-search", [], block { - // Block requests with doubled `//`. - // Amazon S3 doesn't support them, but other web hosts do, - // and so do file:/// URLs, which means we need to block - // it here if we want to avoid breaking the main docs site. - // https://github.com/rust-lang/rust/issues/145646 - block-network-request: "file://*//*" - // Perform search - click: "#search-button" - wait-for: ".search-input" + store-count: (".search-input", __search_input_count) + if: (|__search_input_count| != 0, block { + store-css: ("#alternative-display", {"display": __search_input_display}) + }) + else: block { + // Block requests with doubled `//`. + // Amazon S3 doesn't support them, but other web hosts do, + // and so do file:/// URLs, which means we need to block + // it here if we want to avoid breaking the main docs site. + // https://github.com/rust-lang/rust/issues/145646 + block-network-request: "file://*//*" + store-value: (__search_input_display, "none") + } + + if: (|__search_input_display| == "none", block { + // Open search + click: "#search-button" + wait-for: ".search-input" + }) } ) @@ -80,6 +90,9 @@ define-function: ( [query], block { call-function: ("open-search", {}) + // We empty the search input in case it wasn't empty. + set-property: (".search-input", {"value": ""}) + // We write the actual query. write-into: (".search-input", |query|) press-key: 'Enter' // wait for the search to start From 279f8799e3559f52f84b710f171df3ea680b8634 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Mon, 19 Jan 2026 16:08:11 -0800 Subject: [PATCH 38/69] Update slice `assume_init_*` relnotes --- RELEASES.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 96be2e7cd14a..74b574924489 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -49,9 +49,9 @@ Libraries Stabilized APIs --------------- -- [`>::assume_init_drop`](https://doc.rust-lang.org/stable/core/mem/union.MaybeUninit.html#method.assume_init_drop) -- [`>::assume_init_ref`](https://doc.rust-lang.org/stable/core/mem/union.MaybeUninit.html#method.assume_init_ref) -- [`>::assume_init_mut`](https://doc.rust-lang.org/stable/core/mem/union.MaybeUninit.html#method.assume_init_mut) +- [`<[MaybeUninit]>::assume_init_drop`](https://doc.rust-lang.org/stable/core/primitive.slice.html#method.assume_init_drop) +- [`<[MaybeUninit]>::assume_init_ref`](https://doc.rust-lang.org/stable/core/primitive.slice.html#method.assume_init_ref) +- [`<[MaybeUninit]>::assume_init_mut`](https://doc.rust-lang.org/stable/core/primitive.slice.html#method.assume_init_mut) - [`<[MaybeUninit]>::write_copy_of_slice`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.write_copy_of_slice) - [`<[MaybeUninit]>::write_clone_of_slice`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.write_clone_of_slice) - [`String::into_raw_parts`](https://doc.rust-lang.org/stable/std/string/struct.String.html#method.into_raw_parts) From 1f423a6e42cc2eb868fefbab1b3d81e152b34186 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 2 Dec 2025 20:49:26 +1100 Subject: [PATCH 39/69] coverage: Add a test for macros that only contain other macros --- tests/coverage/macros/pass-through.cov-map | 57 +++++++++++++++++ tests/coverage/macros/pass-through.coverage | 71 +++++++++++++++++++++ tests/coverage/macros/pass-through.rs | 70 ++++++++++++++++++++ 3 files changed, 198 insertions(+) create mode 100644 tests/coverage/macros/pass-through.cov-map create mode 100644 tests/coverage/macros/pass-through.coverage create mode 100644 tests/coverage/macros/pass-through.rs diff --git a/tests/coverage/macros/pass-through.cov-map b/tests/coverage/macros/pass-through.cov-map new file mode 100644 index 000000000000..83937e4fb8b7 --- /dev/null +++ b/tests/coverage/macros/pass-through.cov-map @@ -0,0 +1,57 @@ +Function name: pass_through::uses_inner_macro +Raw bytes (51): 0x[01, 01, 01, 01, 05, 09, 01, 24, 01, 00, 16, 01, 01, 08, 00, 1d, 05, 01, 09, 00, 0c, 05, 00, 0d, 00, 21, 05, 01, 09, 00, 15, 05, 01, 09, 00, 0c, 05, 00, 0d, 00, 20, 02, 01, 05, 00, 06, 01, 01, 01, 00, 02] +Number of files: 1 +- file 0 => $DIR/pass-through.rs +Number of expressions: 1 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +Number of file 0 mappings: 9 +- Code(Counter(0)) at (prev + 36, 1) to (start + 0, 22) +- Code(Counter(0)) at (prev + 1, 8) to (start + 0, 29) +- Code(Counter(1)) at (prev + 1, 9) to (start + 0, 12) +- Code(Counter(1)) at (prev + 0, 13) to (start + 0, 33) +- Code(Counter(1)) at (prev + 1, 9) to (start + 0, 21) +- Code(Counter(1)) at (prev + 1, 9) to (start + 0, 12) +- Code(Counter(1)) at (prev + 0, 13) to (start + 0, 32) +- Code(Expression(0, Sub)) at (prev + 1, 5) to (start + 0, 6) + = (c0 - c1) +- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2) +Highest counter ID seen: c1 + +Function name: pass_through::uses_middle_macro +Raw bytes (51): 0x[01, 01, 01, 01, 05, 09, 01, 2c, 01, 00, 17, 01, 01, 08, 00, 1d, 05, 01, 09, 00, 0c, 05, 00, 0d, 00, 22, 05, 01, 09, 00, 16, 05, 01, 09, 00, 0c, 05, 00, 0d, 00, 21, 02, 01, 05, 00, 06, 01, 01, 01, 00, 02] +Number of files: 1 +- file 0 => $DIR/pass-through.rs +Number of expressions: 1 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +Number of file 0 mappings: 9 +- Code(Counter(0)) at (prev + 44, 1) to (start + 0, 23) +- Code(Counter(0)) at (prev + 1, 8) to (start + 0, 29) +- Code(Counter(1)) at (prev + 1, 9) to (start + 0, 12) +- Code(Counter(1)) at (prev + 0, 13) to (start + 0, 34) +- Code(Counter(1)) at (prev + 1, 9) to (start + 0, 22) +- Code(Counter(1)) at (prev + 1, 9) to (start + 0, 12) +- Code(Counter(1)) at (prev + 0, 13) to (start + 0, 33) +- Code(Expression(0, Sub)) at (prev + 1, 5) to (start + 0, 6) + = (c0 - c1) +- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2) +Highest counter ID seen: c1 + +Function name: pass_through::uses_outer_macro +Raw bytes (51): 0x[01, 01, 01, 01, 05, 09, 01, 34, 01, 00, 16, 01, 01, 08, 00, 1d, 05, 01, 09, 00, 0c, 05, 00, 0d, 00, 21, 05, 01, 09, 00, 15, 05, 01, 09, 00, 0c, 05, 00, 0d, 00, 20, 02, 01, 05, 00, 06, 01, 01, 01, 00, 02] +Number of files: 1 +- file 0 => $DIR/pass-through.rs +Number of expressions: 1 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +Number of file 0 mappings: 9 +- Code(Counter(0)) at (prev + 52, 1) to (start + 0, 22) +- Code(Counter(0)) at (prev + 1, 8) to (start + 0, 29) +- Code(Counter(1)) at (prev + 1, 9) to (start + 0, 12) +- Code(Counter(1)) at (prev + 0, 13) to (start + 0, 33) +- Code(Counter(1)) at (prev + 1, 9) to (start + 0, 21) +- Code(Counter(1)) at (prev + 1, 9) to (start + 0, 12) +- Code(Counter(1)) at (prev + 0, 13) to (start + 0, 32) +- Code(Expression(0, Sub)) at (prev + 1, 5) to (start + 0, 6) + = (c0 - c1) +- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2) +Highest counter ID seen: c1 + diff --git a/tests/coverage/macros/pass-through.coverage b/tests/coverage/macros/pass-through.coverage new file mode 100644 index 000000000000..f1adb235f74a --- /dev/null +++ b/tests/coverage/macros/pass-through.coverage @@ -0,0 +1,71 @@ + LL| |#![feature(coverage_attribute)] + LL| |//@ edition: 2024 + LL| | + LL| |// Test that when a macro expands to another macro, without any significant + LL| |// spans of its own, that this doesn't cause coverage instrumentation to give + LL| |// up and ignore the inner spans. + LL| | + LL| |macro_rules! inner_macro { + LL| | () => { + LL| | if core::hint::black_box(true) { + LL| | say("true"); + LL| | } else { + LL| | say("false"); + LL| | } + LL| | }; + LL| |} + LL| | + LL| |macro_rules! middle_macro { + LL| | () => { + LL| | inner_macro!() + LL| | }; + LL| |} + LL| | + LL| |macro_rules! outer_macro { + LL| | () => { + LL| | middle_macro!() + LL| | }; + LL| |} + LL| | + LL| |// In each of these three functions, the macro call should be instrumented, + LL| |// and should have an execution count of 1. + LL| |// + LL| |// Each function contains some extra code to ensure that control flow is + LL| |// non-trivial. + LL| | + LL| 1|fn uses_inner_macro() { + LL| 1| if core::hint::black_box(true) { + LL| 1| say("before inner_macro"); + LL| 1| inner_macro!(); + LL| 1| say("after inner_macro"); + LL| 0| } + LL| 1|} + LL| | + LL| 1|fn uses_middle_macro() { + LL| 1| if core::hint::black_box(true) { + LL| 1| say("before middle_macro"); + LL| 1| middle_macro!(); + LL| 1| say("after middle_macro") + LL| 0| } + LL| 1|} + LL| | + LL| 1|fn uses_outer_macro() { + LL| 1| if core::hint::black_box(true) { + LL| 1| say("before outer_macro"); + LL| 1| outer_macro!(); + LL| 1| say("after outer_macro"); + LL| 0| } + LL| 1|} + LL| | + LL| |#[coverage(off)] + LL| |fn main() { + LL| | uses_inner_macro(); + LL| | uses_middle_macro(); + LL| | uses_outer_macro(); + LL| |} + LL| | + LL| |#[coverage(off)] + LL| |fn say(message: &str) { + LL| | println!("{message}"); + LL| |} + diff --git a/tests/coverage/macros/pass-through.rs b/tests/coverage/macros/pass-through.rs new file mode 100644 index 000000000000..6e5ad3d93b73 --- /dev/null +++ b/tests/coverage/macros/pass-through.rs @@ -0,0 +1,70 @@ +#![feature(coverage_attribute)] +//@ edition: 2024 + +// Test that when a macro expands to another macro, without any significant +// spans of its own, that this doesn't cause coverage instrumentation to give +// up and ignore the inner spans. + +macro_rules! inner_macro { + () => { + if core::hint::black_box(true) { + say("true"); + } else { + say("false"); + } + }; +} + +macro_rules! middle_macro { + () => { + inner_macro!() + }; +} + +macro_rules! outer_macro { + () => { + middle_macro!() + }; +} + +// In each of these three functions, the macro call should be instrumented, +// and should have an execution count of 1. +// +// Each function contains some extra code to ensure that control flow is +// non-trivial. + +fn uses_inner_macro() { + if core::hint::black_box(true) { + say("before inner_macro"); + inner_macro!(); + say("after inner_macro"); + } +} + +fn uses_middle_macro() { + if core::hint::black_box(true) { + say("before middle_macro"); + middle_macro!(); + say("after middle_macro") + } +} + +fn uses_outer_macro() { + if core::hint::black_box(true) { + say("before outer_macro"); + outer_macro!(); + say("after outer_macro"); + } +} + +#[coverage(off)] +fn main() { + uses_inner_macro(); + uses_middle_macro(); + uses_outer_macro(); +} + +#[coverage(off)] +fn say(message: &str) { + println!("{message}"); +} From 7a3e5cd57e67cc8c0715996ec4cccc1b63a2506f Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 2 Dec 2025 13:11:33 +1100 Subject: [PATCH 40/69] coverage: Sort the expansion tree in depth-first order This makes it possible for subsequent operations to iterate over all nodes, while assuming that every node occurs before all of its descendants. --- .../src/coverage/expansion.rs | 38 ++++++++++++++++++- .../src/coverage/mappings.rs | 17 +++++++-- .../rustc_mir_transform/src/coverage/mod.rs | 13 ++++--- 3 files changed, 57 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_mir_transform/src/coverage/expansion.rs b/compiler/rustc_mir_transform/src/coverage/expansion.rs index 0288afd95990..16c37d6e6cf0 100644 --- a/compiler/rustc_mir_transform/src/coverage/expansion.rs +++ b/compiler/rustc_mir_transform/src/coverage/expansion.rs @@ -6,6 +6,7 @@ use rustc_span::{ExpnId, ExpnKind, Span}; use crate::coverage::from_mir; use crate::coverage::graph::CoverageGraph; use crate::coverage::hir_info::ExtractedHirInfo; +use crate::coverage::mappings::MappingsError; #[derive(Clone, Copy, Debug)] pub(crate) struct SpanWithBcb { @@ -62,6 +63,8 @@ pub(crate) struct ExpnNode { /// but is helpful for debugging and might be useful later. #[expect(dead_code)] pub(crate) expn_id: ExpnId, + /// Index of this node in a depth-first traversal from the root. + pub(crate) dfs_rank: usize, // Useful info extracted from `ExpnData`. pub(crate) expn_kind: ExpnKind, @@ -100,6 +103,7 @@ impl ExpnNode { Self { expn_id, + dfs_rank: usize::MAX, expn_kind: expn_data.kind, call_site, @@ -124,7 +128,7 @@ pub(crate) fn build_expn_tree( mir_body: &mir::Body<'_>, hir_info: &ExtractedHirInfo, graph: &CoverageGraph, -) -> ExpnTree { +) -> Result { let raw_spans = from_mir::extract_raw_spans_from_mir(mir_body, graph); let mut nodes = FxIndexMap::default(); @@ -157,6 +161,9 @@ pub(crate) fn build_expn_tree( } } + // Sort the tree nodes into depth-first order. + sort_nodes_depth_first(&mut nodes)?; + // If we have a span for the function signature, associate it with the // corresponding expansion tree node. if let Some(fn_sig_span) = hir_info.fn_sig_span @@ -189,5 +196,32 @@ pub(crate) fn build_expn_tree( } } - ExpnTree { nodes } + Ok(ExpnTree { nodes }) +} + +/// Sorts the tree nodes in the map into depth-first order. +/// +/// This allows subsequent operations to iterate over all nodes, while assuming +/// that every node occurs before all of its descendants. +fn sort_nodes_depth_first(nodes: &mut FxIndexMap) -> Result<(), MappingsError> { + let mut dfs_stack = vec![ExpnId::root()]; + let mut next_dfs_rank = 0usize; + while let Some(expn_id) = dfs_stack.pop() { + if let Some(node) = nodes.get_mut(&expn_id) { + node.dfs_rank = next_dfs_rank; + next_dfs_rank += 1; + dfs_stack.extend(node.child_expn_ids.iter().rev().copied()); + } + } + nodes.sort_by_key(|_expn_id, node| node.dfs_rank); + + // Verify that the depth-first search visited each node exactly once. + for (i, &ExpnNode { dfs_rank, .. }) in nodes.values().enumerate() { + if dfs_rank != i { + tracing::debug!(dfs_rank, i, "expansion tree node's rank does not match its index"); + return Err(MappingsError::TreeSortFailure); + } + } + + Ok(()) } diff --git a/compiler/rustc_mir_transform/src/coverage/mappings.rs b/compiler/rustc_mir_transform/src/coverage/mappings.rs index 56f2db90ff8c..4581afa99a31 100644 --- a/compiler/rustc_mir_transform/src/coverage/mappings.rs +++ b/compiler/rustc_mir_transform/src/coverage/mappings.rs @@ -11,6 +11,13 @@ use crate::coverage::graph::CoverageGraph; use crate::coverage::hir_info::ExtractedHirInfo; use crate::coverage::spans::extract_refined_covspans; +/// Indicates why mapping extraction failed, for debug-logging purposes. +#[derive(Debug)] +pub(crate) enum MappingsError { + NoMappings, + TreeSortFailure, +} + #[derive(Default)] pub(crate) struct ExtractedMappings { pub(crate) mappings: Vec, @@ -23,8 +30,8 @@ pub(crate) fn extract_mappings_from_mir<'tcx>( mir_body: &mir::Body<'tcx>, hir_info: &ExtractedHirInfo, graph: &CoverageGraph, -) -> ExtractedMappings { - let expn_tree = expansion::build_expn_tree(mir_body, hir_info, graph); +) -> Result { + let expn_tree = expansion::build_expn_tree(mir_body, hir_info, graph)?; let mut mappings = vec![]; @@ -33,7 +40,11 @@ pub(crate) fn extract_mappings_from_mir<'tcx>( extract_branch_mappings(mir_body, hir_info, graph, &expn_tree, &mut mappings); - ExtractedMappings { mappings } + if mappings.is_empty() { + tracing::debug!("no mappings were extracted"); + return Err(MappingsError::NoMappings); + } + Ok(ExtractedMappings { mappings }) } fn resolve_block_markers( diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index 24a61c9b4a1b..be8b02f61133 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -73,12 +73,13 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir: //////////////////////////////////////////////////// // Extract coverage spans and other mapping info from MIR. let ExtractedMappings { mappings } = - mappings::extract_mappings_from_mir(tcx, mir_body, &hir_info, &graph); - if mappings.is_empty() { - // No spans could be converted into valid mappings, so skip this function. - debug!("no spans could be converted into valid mappings; skipping"); - return; - } + match mappings::extract_mappings_from_mir(tcx, mir_body, &hir_info, &graph) { + Ok(m) => m, + Err(error) => { + tracing::debug!(?error, "mapping extraction failed; skipping this function"); + return; + } + }; // Use the coverage graph to prepare intermediate data that will eventually // be used to assign physical counters and counter expressions to points in From 986db13c178126167b4c7e0c4eb76b1dad3908b7 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 3 Dec 2025 17:28:47 +1100 Subject: [PATCH 41/69] coverage: Use the sorted expansion tree to determine min/max BCBs --- .../src/coverage/expansion.rs | 77 +++++++++++-------- .../rustc_mir_transform/src/coverage/spans.rs | 12 +-- compiler/rustc_mir_transform/src/lib.rs | 1 - 3 files changed, 49 insertions(+), 41 deletions(-) diff --git a/compiler/rustc_mir_transform/src/coverage/expansion.rs b/compiler/rustc_mir_transform/src/coverage/expansion.rs index 16c37d6e6cf0..77aec902faad 100644 --- a/compiler/rustc_mir_transform/src/coverage/expansion.rs +++ b/compiler/rustc_mir_transform/src/coverage/expansion.rs @@ -1,3 +1,4 @@ +use itertools::Itertools; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry}; use rustc_middle::mir; use rustc_middle::mir::coverage::{BasicCoverageBlock, BranchSpan}; @@ -23,38 +24,6 @@ impl ExpnTree { pub(crate) fn get(&self, expn_id: ExpnId) -> Option<&ExpnNode> { self.nodes.get(&expn_id) } - - /// Yields the tree node for the given expansion ID (if present), followed - /// by the nodes of all of its descendants in depth-first order. - pub(crate) fn iter_node_and_descendants( - &self, - root_expn_id: ExpnId, - ) -> impl Iterator { - gen move { - let Some(root_node) = self.get(root_expn_id) else { return }; - yield root_node; - - // Stack of child-node-ID iterators that drives the depth-first traversal. - let mut iter_stack = vec![root_node.child_expn_ids.iter()]; - - while let Some(curr_iter) = iter_stack.last_mut() { - // Pull the next ID from the top of the stack. - let Some(&curr_id) = curr_iter.next() else { - iter_stack.pop(); - continue; - }; - - // Yield this node. - let Some(node) = self.get(curr_id) else { continue }; - yield node; - - // Push the node's children, to be traversed next. - if !node.child_expn_ids.is_empty() { - iter_stack.push(node.child_expn_ids.iter()); - } - } - } - } } #[derive(Debug)] @@ -85,6 +54,10 @@ pub(crate) struct ExpnNode { pub(crate) spans: Vec, /// Expansions whose call-site is in this expansion. pub(crate) child_expn_ids: FxIndexSet, + /// The "minimum" and "maximum" BCBs (in dominator order) of ordinary spans + /// belonging to this tree node and all of its descendants. Used when + /// creating a single code mapping representing an entire child expansion. + pub(crate) minmax_bcbs: Option, /// Branch spans (recorded during MIR building) belonging to this expansion. pub(crate) branch_spans: Vec, @@ -114,6 +87,7 @@ impl ExpnNode { spans: vec![], child_expn_ids: FxIndexSet::default(), + minmax_bcbs: None, branch_spans: vec![], @@ -164,6 +138,17 @@ pub(crate) fn build_expn_tree( // Sort the tree nodes into depth-first order. sort_nodes_depth_first(&mut nodes)?; + // For each node, determine its "minimum" and "maximum" BCBs, based on its + // own spans and its immediate children. This relies on the nodes having + // been sorted, so that each node's children are processed before the node + // itself. + for i in (0..nodes.len()).rev() { + // Computing a node's min/max BCBs requires a shared ref to other nodes. + let minmax_bcbs = minmax_bcbs_for_expn_tree_node(graph, &nodes, &nodes[i]); + // Now we can mutate the current node to set its min/max BCBs. + nodes[i].minmax_bcbs = minmax_bcbs; + } + // If we have a span for the function signature, associate it with the // corresponding expansion tree node. if let Some(fn_sig_span) = hir_info.fn_sig_span @@ -225,3 +210,31 @@ fn sort_nodes_depth_first(nodes: &mut FxIndexMap) -> Result<() Ok(()) } + +#[derive(Clone, Copy, Debug)] +pub(crate) struct MinMaxBcbs { + pub(crate) min: BasicCoverageBlock, + pub(crate) max: BasicCoverageBlock, +} + +/// For a single node in the expansion tree, compute its "minimum" and "maximum" +/// BCBs (in dominator order), from among the BCBs of its immediate spans, +/// and the min/max of its immediate children. +fn minmax_bcbs_for_expn_tree_node( + graph: &CoverageGraph, + nodes: &FxIndexMap, + node: &ExpnNode, +) -> Option { + let immediate_span_bcbs = node.spans.iter().map(|sp: &SpanWithBcb| sp.bcb); + let child_minmax_bcbs = node + .child_expn_ids + .iter() + .flat_map(|id| nodes.get(id)) + .flat_map(|child| child.minmax_bcbs) + .flat_map(|MinMaxBcbs { min, max }| [min, max]); + + let (min, max) = Iterator::chain(immediate_span_bcbs, child_minmax_bcbs) + .minmax_by(|&a, &b| graph.cmp_in_dominator_order(a, b)) + .into_option()?; + Some(MinMaxBcbs { min, max }) +} diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index bc26a8ccc47a..b1ce0069b43a 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -39,8 +39,7 @@ pub(super) fn extract_refined_covspans<'tcx>( // For each expansion with its call-site in the body span, try to // distill a corresponding covspan. for &child_expn_id in &node.child_expn_ids { - if let Some(covspan) = single_covspan_for_child_expn(tcx, graph, &expn_tree, child_expn_id) - { + if let Some(covspan) = single_covspan_for_child_expn(tcx, &expn_tree, child_expn_id) { covspans.push(covspan); } } @@ -127,24 +126,21 @@ pub(super) fn extract_refined_covspans<'tcx>( /// For a single child expansion, try to distill it into a single span+BCB mapping. fn single_covspan_for_child_expn( tcx: TyCtxt<'_>, - graph: &CoverageGraph, expn_tree: &ExpnTree, expn_id: ExpnId, ) -> Option { let node = expn_tree.get(expn_id)?; - - let bcbs = - expn_tree.iter_node_and_descendants(expn_id).flat_map(|n| n.spans.iter().map(|s| s.bcb)); + let minmax_bcbs = node.minmax_bcbs?; let bcb = match node.expn_kind { // For bang-macros (e.g. `assert!`, `trace!`) and for `await`, taking // the "first" BCB in dominator order seems to give good results. ExpnKind::Macro(MacroKind::Bang, _) | ExpnKind::Desugaring(DesugaringKind::Await) => { - bcbs.min_by(|&a, &b| graph.cmp_in_dominator_order(a, b))? + minmax_bcbs.min } // For other kinds of expansion, taking the "last" (most-dominated) BCB // seems to give good results. - _ => bcbs.max_by(|&a, &b| graph.cmp_in_dominator_order(a, b))?, + _ => minmax_bcbs.max, }; // For bang-macro expansions, limit the call-site span to just the macro diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index f12561492de4..afdfa35f97c0 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -5,7 +5,6 @@ #![feature(const_type_name)] #![feature(cow_is_borrowed)] #![feature(file_buffered)] -#![feature(gen_blocks)] #![feature(if_let_guard)] #![feature(impl_trait_in_assoc_type)] #![feature(try_blocks)] From ac505d7fe40a5023f7310a6476faf2651dca3cca Mon Sep 17 00:00:00 2001 From: Bodhi Silberling Date: Mon, 19 Jan 2026 22:17:59 -0800 Subject: [PATCH 42/69] Fix typos: 'occured' -> 'occurred' and 'non_existant' -> 'non_existent' --- src/tools/llvm-bitcode-linker/src/linker.rs | 6 +++--- src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp | 2 +- src/tools/miri/genmc-sys/src/lib.rs | 2 +- src/tools/miri/src/concurrency/genmc/mod.rs | 2 +- src/tools/tidy/src/diagnostics.rs | 2 +- tests/rustdoc-html/intra-doc/deps.rs | 6 +++--- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/tools/llvm-bitcode-linker/src/linker.rs b/src/tools/llvm-bitcode-linker/src/linker.rs index dafd847e7680..e682aecbbeed 100644 --- a/src/tools/llvm-bitcode-linker/src/linker.rs +++ b/src/tools/llvm-bitcode-linker/src/linker.rs @@ -68,7 +68,7 @@ impl Session { .arg("-o") .arg(&self.link_path) .output() - .context("An error occured when calling llvm-link. Make sure the llvm-tools component is installed.")?; + .context("An error occurred when calling llvm-link. Make sure the llvm-tools component is installed.")?; if !llvm_link_output.status.success() { tracing::error!( @@ -115,7 +115,7 @@ impl Session { } let opt_output = opt_cmd.output().context( - "An error occured when calling opt. Make sure the llvm-tools component is installed.", + "An error occurred when calling opt. Make sure the llvm-tools component is installed.", )?; if !opt_output.status.success() { @@ -149,7 +149,7 @@ impl Session { .arg(&self.opt_path) .arg("-o").arg(&self.out_path) .output() - .context("An error occured when calling llc. Make sure the llvm-tools component is installed.")?; + .context("An error occurred when calling llc. Make sure the llvm-tools component is installed.")?; if !lcc_output.status.success() { tracing::error!( diff --git a/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp index b6b7b06509a5..8110c8d24c59 100644 --- a/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp +++ b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp @@ -79,7 +79,7 @@ struct MiriGenmcShim : private GenMCDriver { void handle_execution_start(); // This function must be called at the end of any execution, even if an error was found // during the execution. - // Returns `null`, or a string containing an error message if an error occured. + // Returns `null`, or a string containing an error message if an error occurred. std::unique_ptr handle_execution_end(); /***** Functions for handling events encountered during program execution. *****/ diff --git a/src/tools/miri/genmc-sys/src/lib.rs b/src/tools/miri/genmc-sys/src/lib.rs index b3a9880211de..26de80f295d3 100644 --- a/src/tools/miri/genmc-sys/src/lib.rs +++ b/src/tools/miri/genmc-sys/src/lib.rs @@ -379,7 +379,7 @@ mod ffi { /// This function must be called at the start of any execution, before any events are reported to GenMC. fn handle_execution_start(self: Pin<&mut MiriGenmcShim>); /// This function must be called at the end of any execution, even if an error was found during the execution. - /// Returns `null`, or a string containing an error message if an error occured. + /// Returns `null`, or a string containing an error message if an error occurred. fn handle_execution_end(self: Pin<&mut MiriGenmcShim>) -> UniquePtr; /***** Functions for handling events encountered during program execution. *****/ diff --git a/src/tools/miri/src/concurrency/genmc/mod.rs b/src/tools/miri/src/concurrency/genmc/mod.rs index 36b1d2afade4..740553ab85d6 100644 --- a/src/tools/miri/src/concurrency/genmc/mod.rs +++ b/src/tools/miri/src/concurrency/genmc/mod.rs @@ -220,7 +220,7 @@ impl GenmcCtx { /// Don't call this function if an error was found. /// /// GenMC detects certain errors only when the execution ends. - /// If an error occured, a string containing a short error description is returned. + /// If an error occurred, a string containing a short error description is returned. /// /// GenMC currently doesn't return an error in all cases immediately when one happens. /// This function will also check for those, and return their error description. diff --git a/src/tools/tidy/src/diagnostics.rs b/src/tools/tidy/src/diagnostics.rs index 88816b5abeff..6f53f2fff1a4 100644 --- a/src/tools/tidy/src/diagnostics.rs +++ b/src/tools/tidy/src/diagnostics.rs @@ -213,7 +213,7 @@ impl RunningCheck { } } - /// Has an error already occured for this check? + /// Has an error already occurred for this check? pub fn is_bad(&self) -> bool { self.bad } diff --git a/tests/rustdoc-html/intra-doc/deps.rs b/tests/rustdoc-html/intra-doc/deps.rs index fd40b8326d0f..1670a2d8fe7b 100644 --- a/tests/rustdoc-html/intra-doc/deps.rs +++ b/tests/rustdoc-html/intra-doc/deps.rs @@ -6,7 +6,7 @@ //@ compile-flags: --extern-html-root-url=empty=https://empty.example/ // This one is to ensure that we don't link to any item we see which has // an external html root URL unless it actually exists. -//@ compile-flags: --extern-html-root-url=non_existant=https://non-existant.example/ +//@ compile-flags: --extern-html-root-url=non_existent=https://non-existent.example/ //@ aux-build: empty.rs #![crate_name = "foo"] @@ -14,10 +14,10 @@ //@ has 'foo/index.html' //@ has - '//a[@href="https://empty.example/empty/index.html"]' 'empty' -// There should only be one intra doc links, we should not link `non_existant`. +// There should only be one intra doc links, we should not link `non_existent`. //@ count - '//*[@class="docblock"]//a' 1 //! [`empty`] //! -//! [`non_existant`] +//! [`non_existent`] extern crate empty; From f483cb2587540c487599d324492afcfdfa42c93e Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Tue, 20 Jan 2026 11:32:23 +0300 Subject: [PATCH 43/69] Generate error delegation when delegation is not resolved --- compiler/rustc_ast_lowering/src/delegation.rs | 19 +++++++++++++++---- .../unresolved-delegation-ice-151356.rs | 11 +++++++++++ .../unresolved-delegation-ice-151356.stderr | 19 +++++++++++++++++++ 3 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 tests/ui/delegation/unresolved-delegation-ice-151356.rs create mode 100644 tests/ui/delegation/unresolved-delegation-ice-151356.stderr diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index 5d2531e50393..cccfb112ec2b 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -152,10 +152,21 @@ impl<'hir> LoweringContext<'_, 'hir> { ) -> DelegationResults<'hir> { let span = self.lower_span(delegation.path.segments.last().unwrap().ident.span); - let ids = self.get_delegation_ids( - self.resolver.delegation_infos[&self.local_def_id(item_id)].resolution_node, - span, - ); + // Delegation can be unresolved in illegal places such as function bodies in extern blocks (see #151356) + let ids = if let Some(delegation_info) = + self.resolver.delegation_infos.get(&self.local_def_id(item_id)) + { + self.get_delegation_ids(delegation_info.resolution_node, span) + } else { + return self.generate_delegation_error( + self.dcx().span_delayed_bug( + span, + format!("LoweringContext: the delegation {:?} is unresolved", item_id), + ), + span, + delegation, + ); + }; match ids { Ok(ids) => { diff --git a/tests/ui/delegation/unresolved-delegation-ice-151356.rs b/tests/ui/delegation/unresolved-delegation-ice-151356.rs new file mode 100644 index 000000000000..b61e08551183 --- /dev/null +++ b/tests/ui/delegation/unresolved-delegation-ice-151356.rs @@ -0,0 +1,11 @@ +#![allow(incomplete_features)] +#![feature(fn_delegation)] + +extern "C" { + fn a() { + //~^ ERROR incorrect function inside `extern` block + reuse foo {} + } +} + +pub fn main() {} diff --git a/tests/ui/delegation/unresolved-delegation-ice-151356.stderr b/tests/ui/delegation/unresolved-delegation-ice-151356.stderr new file mode 100644 index 000000000000..407d22e477c4 --- /dev/null +++ b/tests/ui/delegation/unresolved-delegation-ice-151356.stderr @@ -0,0 +1,19 @@ +error: incorrect function inside `extern` block + --> $DIR/unresolved-delegation-ice-151356.rs:5:8 + | +LL | extern "C" { + | ---------- `extern` blocks define existing foreign functions and functions inside of them cannot have a body +LL | fn a() { + | ________^___- + | | | + | | cannot have a body +LL | | +LL | | reuse foo {} +LL | | } + | |_____- help: remove the invalid body: `;` + | + = help: you might have meant to write a function accessible through FFI, which can be done by writing `extern fn` outside of the `extern` block + = note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html + +error: aborting due to 1 previous error + From c222a00e7932d1134de94c9dcc7a5216f8fef3df Mon Sep 17 00:00:00 2001 From: Jakob Koschel Date: Thu, 27 Nov 2025 10:47:38 +0000 Subject: [PATCH 44/69] Create x86_64-unknown-linux-gnuasan target which enables ASAN by default As suggested, in order to distribute sanitizer instrumented standard libraries without introducing new rustc flags, this adds a new dedicated target. With the target, we can distribute the instrumented standard libraries through a separate rustup component. --- compiler/rustc_target/src/spec/mod.rs | 2 + .../targets/x86_64_unknown_linux_gnuasan.rs | 16 ++++++ src/bootstrap/src/core/build_steps/llvm.rs | 1 + src/bootstrap/src/core/sanity.rs | 1 + src/doc/rustc/src/SUMMARY.md | 1 + src/doc/rustc/src/platform-support.md | 1 + .../x86_64-unknown-linux-gnuasan.md | 56 +++++++++++++++++++ tests/assembly-llvm/targets/targets-elf.rs | 3 + 8 files changed, 81 insertions(+) create mode 100644 compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnuasan.rs create mode 100644 src/doc/rustc/src/platform-support/x86_64-unknown-linux-gnuasan.md diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 8650ec00100b..ef475630d494 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1801,6 +1801,8 @@ supported_targets! { ("x86_64-lynx-lynxos178", x86_64_lynx_lynxos178), ("x86_64-pc-cygwin", x86_64_pc_cygwin), + + ("x86_64-unknown-linux-gnuasan", x86_64_unknown_linux_gnuasan), } /// Cow-Vec-Str: Cow<'static, [Cow<'static, str>]> diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnuasan.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnuasan.rs new file mode 100644 index 000000000000..5d0ee0e1eb4c --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnuasan.rs @@ -0,0 +1,16 @@ +use crate::spec::{SanitizerSet, Target, TargetMetadata}; + +pub(crate) fn target() -> Target { + let mut base = super::x86_64_unknown_linux_gnu::target(); + base.metadata = TargetMetadata { + description: Some( + "64-bit Linux (kernel 3.2+, glibc 2.17+) with ASAN enabled by default".into(), + ), + tier: Some(2), + host_tools: Some(false), + std: Some(true), + }; + base.supported_sanitizers = SanitizerSet::ADDRESS; + base.default_sanitizers = SanitizerSet::ADDRESS; + base +} diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index c3935d9810e9..0a838cdf7fc6 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -1484,6 +1484,7 @@ fn supported_sanitizers( "x86_64", &["asan", "dfsan", "lsan", "msan", "safestack", "tsan", "rtsan"], ), + "x86_64-unknown-linux-gnuasan" => common_libs("linux", "x86_64", &["asan"]), "x86_64-unknown-linux-musl" => { common_libs("linux", "x86_64", &["asan", "lsan", "msan", "tsan"]) } diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index 67f4eb37b2c1..d756a38581b2 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -38,6 +38,7 @@ pub struct Finder { const STAGE0_MISSING_TARGETS: &[&str] = &[ // just a dummy comment so the list doesn't get onelined "riscv64im-unknown-none-elf", + "x86_64-unknown-linux-gnuasan", ]; /// Minimum version threshold for libstdc++ required when using prebuilt LLVM diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 3b8852f8ff9f..8ceeff2c9685 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -151,5 +151,6 @@ - [x86_64-pc-cygwin](platform-support/x86_64-pc-cygwin.md) - [x86_64-unknown-linux-none](platform-support/x86_64-unknown-linux-none.md) - [x86_64-unknown-none](platform-support/x86_64-unknown-none.md) + - [x86_64-unknown-linux-gnuasan](platform-support/x86_64-unknown-linux-gnuasan.md) - [xtensa-\*-none-elf](platform-support/xtensa.md) - [\*-nuttx-\*](platform-support/nuttx.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 754d0c975ec3..889f96f5fefa 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -206,6 +206,7 @@ target | std | notes [`x86_64-apple-ios-macabi`](platform-support/apple-ios-macabi.md) | ✓ | Mac Catalyst on x86_64 [`x86_64-fortanix-unknown-sgx`](platform-support/x86_64-fortanix-unknown-sgx.md) | ✓ | [Fortanix ABI] for 64-bit Intel SGX [`x86_64-linux-android`](platform-support/android.md) | ✓ | 64-bit x86 Android +[`x86_64-unknown-linux-gnuasan`](platform-support/x86_64-unknown-linux-gnuasan.md) | ✓ | 64-bit Linux (kernel 3.2+, glibc 2.17+) with ASAN enabled by default [`x86_64-unknown-fuchsia`](platform-support/fuchsia.md) | ✓ | 64-bit x86 Fuchsia `x86_64-unknown-linux-gnux32` | ✓ | 64-bit Linux (x32 ABI) (kernel 4.15+, glibc 2.27) [`x86_64-unknown-none`](platform-support/x86_64-unknown-none.md) | * | Freestanding/bare-metal x86_64, softfloat diff --git a/src/doc/rustc/src/platform-support/x86_64-unknown-linux-gnuasan.md b/src/doc/rustc/src/platform-support/x86_64-unknown-linux-gnuasan.md new file mode 100644 index 000000000000..61934934553b --- /dev/null +++ b/src/doc/rustc/src/platform-support/x86_64-unknown-linux-gnuasan.md @@ -0,0 +1,56 @@ +# `x86_64-unknown-linux-gnuasan` + +**Tier: 2** + +Target mirroring `x86_64-unknown-linux-gnu` with AddressSanitizer enabled by +default. +The goal of this target is to allow shipping ASAN-instrumented standard +libraries through rustup, enabling a fully instrumented binary without requiring +nightly features (build-std). +Once build-std stabilizes, this target is no longer needed and will be removed. + +## Target maintainers + +- [@jakos-sec](https://github.com/jakos-sec) +- [@1c3t3a](https://github.com/1c3t3a) +- [@rust-lang/project-exploit-mitigations][project-exploit-mitigations] + +## Requirements + +The target is for cross-compilation only. Host tools are not supported, since +there is no need to have the host tools instrumented with ASAN. std is fully +supported. + +In all other aspects the target is equivalent to `x86_64-unknown-linux-gnu`. + +## Building the target + +The target can be built by enabling it for a rustc build: + +```toml +[build] +target = ["x86_64-unknown-linux-gnuasan"] +``` + +## Building Rust programs + +Rust programs can be compiled by adding this target via rustup: + +```sh +$ rustup target add x86_64-unknown-linux-gnuasan +``` + +and then compiling with the target: + +```sh +$ rustc foo.rs --target x86_64-unknown-linux-gnuasan +``` + +## Testing + +Created binaries will run on Linux without any external requirements. + +## Cross-compilation toolchains and C code + +The target supports C code and should use the same toolchain target as +`x86_64-unknown-linux-gnu`. diff --git a/tests/assembly-llvm/targets/targets-elf.rs b/tests/assembly-llvm/targets/targets-elf.rs index c8b81cc858d6..0eced9df3c46 100644 --- a/tests/assembly-llvm/targets/targets-elf.rs +++ b/tests/assembly-llvm/targets/targets-elf.rs @@ -673,6 +673,9 @@ //@ revisions: x86_64_unknown_linux_gnux32 //@ [x86_64_unknown_linux_gnux32] compile-flags: --target x86_64-unknown-linux-gnux32 //@ [x86_64_unknown_linux_gnux32] needs-llvm-components: x86 +//@ revisions: x86_64_unknown_linux_gnuasan +//@ [x86_64_unknown_linux_gnuasan] compile-flags: --target x86_64-unknown-linux-gnuasan +//@ [x86_64_unknown_linux_gnuasan] needs-llvm-components: x86 //@ revisions: x86_64_unknown_linux_musl //@ [x86_64_unknown_linux_musl] compile-flags: --target x86_64-unknown-linux-musl //@ [x86_64_unknown_linux_musl] needs-llvm-components: x86 From b65e1fdcb8f85ccb91a99b81e8c4f99d9c8ee643 Mon Sep 17 00:00:00 2001 From: Edvin Bryntesson Date: Sun, 18 Jan 2026 22:21:51 +0100 Subject: [PATCH 45/69] Port `#[patchable_function_entry]` to attr parser --- .../src/attributes/codegen_attrs.rs | 97 +++++++++++++++++++ compiler/rustc_attr_parsing/src/context.rs | 17 +++- .../src/session_diagnostics.rs | 15 +++ compiler/rustc_codegen_ssa/messages.ftl | 11 --- .../rustc_codegen_ssa/src/codegen_attrs.rs | 61 +----------- compiler/rustc_codegen_ssa/src/errors.rs | 33 ------- .../rustc_hir/src/attrs/data_structures.rs | 3 + .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + .../rustc_hir/src/attrs/pretty_printing.rs | 2 +- compiler/rustc_passes/src/check_attr.rs | 2 +- tests/ui/attributes/malformed-attrs.stderr | 15 +-- .../patchable-function-entry-attribute.rs | 19 +++- .../patchable-function-entry-attribute.stderr | 64 ++++++++---- 13 files changed, 205 insertions(+), 135 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 5bbaeda18df3..063fa12d3896 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -717,3 +717,100 @@ impl NoArgsAttributeParser for EiiForeignItemParser { const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::EiiForeignItem; } + +pub(crate) struct PatchableFunctionEntryParser; + +impl SingleAttributeParser for PatchableFunctionEntryParser { + const PATH: &[Symbol] = &[sym::patchable_function_entry]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); + const TEMPLATE: AttributeTemplate = template!(List: &["prefix_nops = m, entry_nops = n"]); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { + let Some(meta_item_list) = args.list() else { + cx.expected_list(cx.attr_span, args); + return None; + }; + + let mut prefix = None; + let mut entry = None; + + if meta_item_list.len() == 0 { + cx.expected_list(meta_item_list.span, args); + return None; + } + + let mut errored = false; + + for item in meta_item_list.mixed() { + let Some(meta_item) = item.meta_item() else { + errored = true; + cx.expected_name_value(item.span(), None); + continue; + }; + + let Some(name_value_lit) = meta_item.args().name_value() else { + errored = true; + cx.expected_name_value(item.span(), None); + continue; + }; + + let attrib_to_write = match meta_item.ident().map(|ident| ident.name) { + Some(sym::prefix_nops) => { + // Duplicate prefixes are not allowed + if prefix.is_some() { + errored = true; + cx.duplicate_key(meta_item.path().span(), sym::prefix_nops); + continue; + } + &mut prefix + } + Some(sym::entry_nops) => { + // Duplicate entries are not allowed + if entry.is_some() { + errored = true; + cx.duplicate_key(meta_item.path().span(), sym::entry_nops); + continue; + } + &mut entry + } + _ => { + errored = true; + cx.expected_specific_argument( + meta_item.path().span(), + &[sym::prefix_nops, sym::entry_nops], + ); + continue; + } + }; + + let rustc_ast::LitKind::Int(val, _) = name_value_lit.value_as_lit().kind else { + errored = true; + cx.expected_integer_literal(name_value_lit.value_span); + continue; + }; + + let Ok(val) = val.get().try_into() else { + errored = true; + cx.expected_integer_literal_in_range( + name_value_lit.value_span, + u8::MIN as isize, + u8::MAX as isize, + ); + continue; + }; + + *attrib_to_write = Some(val); + } + + if errored { + None + } else { + Some(AttributeKind::PatchableFunctionEntry { + prefix: prefix.unwrap_or(0), + entry: entry.unwrap_or(0), + }) + } + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 0c882fee01c8..7bcc1f3448e5 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -23,8 +23,8 @@ use crate::attributes::cfi_encoding::CfiEncodingParser; use crate::attributes::codegen_attrs::{ ColdParser, CoverageParser, EiiForeignItemParser, ExportNameParser, ForceTargetFeatureParser, NakedParser, NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser, - RustcPassIndirectlyInNonRusticAbisParser, SanitizeParser, TargetFeatureParser, - ThreadLocalParser, TrackCallerParser, UsedParser, + PatchableFunctionEntryParser, RustcPassIndirectlyInNonRusticAbisParser, SanitizeParser, + TargetFeatureParser, ThreadLocalParser, TrackCallerParser, UsedParser, }; use crate::attributes::confusables::ConfusablesParser; use crate::attributes::crate_level::{ @@ -223,6 +223,7 @@ attribute_parsers!( Single, Single, Single, + Single, Single, Single, Single, @@ -504,6 +505,18 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedIntegerLiteral) } + pub(crate) fn expected_integer_literal_in_range( + &self, + span: Span, + lower_bound: isize, + upper_bound: isize, + ) -> ErrorGuaranteed { + self.emit_parse_error( + span, + AttributeParseErrorReason::ExpectedIntegerLiteralInRange { lower_bound, upper_bound }, + ) + } + pub(crate) fn expected_list(&self, span: Span, args: &ArgParser) -> ErrorGuaranteed { let span = match args { ArgParser::NoArgs => span, diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 85e7891b1e64..f9748542beb9 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -525,6 +525,10 @@ pub(crate) enum AttributeParseErrorReason<'a> { byte_string: Option, }, ExpectedIntegerLiteral, + ExpectedIntegerLiteralInRange { + lower_bound: isize, + upper_bound: isize, + }, ExpectedAtLeastOneArgument, ExpectedSingleArgument, ExpectedList, @@ -596,6 +600,17 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> { AttributeParseErrorReason::ExpectedIntegerLiteral => { diag.span_label(self.span, "expected an integer literal here"); } + AttributeParseErrorReason::ExpectedIntegerLiteralInRange { + lower_bound, + upper_bound, + } => { + diag.span_label( + self.span, + format!( + "expected an integer literal in the range of {lower_bound}..={upper_bound}" + ), + ); + } AttributeParseErrorReason::ExpectedSingleArgument => { diag.span_label(self.span, "expected a single argument here"); diag.code(E0805); diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 4875f309cca5..a49f411a7df6 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -48,8 +48,6 @@ codegen_ssa_error_creating_remark_dir = failed to create remark directory: {$err codegen_ssa_error_writing_def_file = error writing .DEF file: {$error} -codegen_ssa_expected_name_value_pair = expected name value pair - codegen_ssa_extern_funcs_not_found = some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified codegen_ssa_extract_bundled_libs_archive_member = failed to get data from archive member '{$rlib}': {$error} @@ -90,9 +88,6 @@ codegen_ssa_incorrect_cgu_reuse_type = codegen_ssa_insufficient_vs_code_product = VS Code is a different product, and is not sufficient. -codegen_ssa_invalid_literal_value = invalid literal value - .label = value must be an integer between `0` and `255` - codegen_ssa_invalid_monomorphization_basic_float_type = invalid monomorphization of `{$name}` intrinsic: expected basic float type, found `{$ty}` codegen_ssa_invalid_monomorphization_basic_integer_or_ptr_type = invalid monomorphization of `{$name}` intrinsic: expected basic integer or pointer type, found `{$ty}` @@ -225,9 +220,6 @@ codegen_ssa_no_natvis_directory = error enumerating natvis directory: {$error} codegen_ssa_no_saved_object_file = cached cgu {$cgu_name} should have an object file, but doesn't -codegen_ssa_out_of_range_integer = integer value out of range - .label = value must be between `0` and `255` - codegen_ssa_processing_dymutil_failed = processing debug info with `dsymutil` failed: {$status} .note = {$output} @@ -357,9 +349,6 @@ codegen_ssa_unable_to_run_dsymutil = unable to run `dsymutil`: {$error} codegen_ssa_unable_to_write_debugger_visualizer = unable to write debugger visualizer file `{$path}`: {$error} -codegen_ssa_unexpected_parameter_name = unexpected parameter name - .label = expected `{$prefix_nops}` or `{$entry_nops}` - codegen_ssa_unknown_archive_kind = don't know how to build archive of type: {$kind} diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index e35d884b6711..296c96ce4ae6 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -47,59 +47,6 @@ fn try_fn_sig<'tcx>( } } -// FIXME(jdonszelmann): remove when patchable_function_entry becomes a parsed attr -fn parse_patchable_function_entry( - tcx: TyCtxt<'_>, - attr: &Attribute, -) -> Option { - attr.meta_item_list().and_then(|l| { - let mut prefix = None; - let mut entry = None; - for item in l { - let Some(meta_item) = item.meta_item() else { - tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() }); - continue; - }; - - let Some(name_value_lit) = meta_item.name_value_literal() else { - tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() }); - continue; - }; - - let attrib_to_write = match meta_item.name() { - Some(sym::prefix_nops) => &mut prefix, - Some(sym::entry_nops) => &mut entry, - _ => { - tcx.dcx().emit_err(errors::UnexpectedParameterName { - span: item.span(), - prefix_nops: sym::prefix_nops, - entry_nops: sym::entry_nops, - }); - continue; - } - }; - - let rustc_ast::LitKind::Int(val, _) = name_value_lit.kind else { - tcx.dcx().emit_err(errors::InvalidLiteralValue { span: name_value_lit.span }); - continue; - }; - - let Ok(val) = val.get().try_into() else { - tcx.dcx().emit_err(errors::OutOfRangeInteger { span: name_value_lit.span }); - continue; - }; - - *attrib_to_write = Some(val); - } - - if let (None, None) = (prefix, entry) { - tcx.dcx().span_err(attr.span(), "must specify at least one parameter"); - } - - Some(PatchableFunctionEntry::from_prefix_and_entry(prefix.unwrap_or(0), entry.unwrap_or(0))) - }) -} - /// Spans that are collected when processing built-in attributes, /// that are useful for emitting diagnostics later. #[derive(Default)] @@ -353,6 +300,10 @@ fn process_builtin_attrs( AttributeKind::RustcOffloadKernel => { codegen_fn_attrs.flags |= CodegenFnAttrFlags::OFFLOAD_KERNEL } + AttributeKind::PatchableFunctionEntry { prefix, entry } => { + codegen_fn_attrs.patchable_function_entry = + Some(PatchableFunctionEntry::from_prefix_and_entry(*prefix, *entry)); + } _ => {} } } @@ -362,10 +313,6 @@ fn process_builtin_attrs( }; match name { - sym::patchable_function_entry => { - codegen_fn_attrs.patchable_function_entry = - parse_patchable_function_entry(tcx, attr); - } _ => {} } } diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 39727685aec1..6a97de4c2b13 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -136,39 +136,6 @@ pub(crate) struct RequiresRustAbi { pub span: Span, } -#[derive(Diagnostic)] -#[diag(codegen_ssa_expected_name_value_pair)] -pub(crate) struct ExpectedNameValuePair { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(codegen_ssa_unexpected_parameter_name)] -pub(crate) struct UnexpectedParameterName { - #[primary_span] - #[label] - pub span: Span, - pub prefix_nops: Symbol, - pub entry_nops: Symbol, -} - -#[derive(Diagnostic)] -#[diag(codegen_ssa_invalid_literal_value)] -pub(crate) struct InvalidLiteralValue { - #[primary_span] - #[label] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(codegen_ssa_out_of_range_integer)] -pub(crate) struct OutOfRangeInteger { - #[primary_span] - #[label] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(codegen_ssa_copy_path_buf)] pub(crate) struct CopyPathBuf { diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 314f36d6132d..6e84d60f062f 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -879,6 +879,9 @@ pub enum AttributeKind { /// Represents `#[rustc_pass_by_value]` (used by the `rustc_pass_by_value` lint). PassByValue(Span), + /// Represents `#[patchable_function_entry]` + PatchableFunctionEntry { prefix: u8, entry: u8 }, + /// Represents `#[path]` Path(Symbol, Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index b55a5d0e29e1..1c13cef64fea 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -88,6 +88,7 @@ impl AttributeKind { Optimize(..) => No, ParenSugar(..) => No, PassByValue(..) => Yes, + PatchableFunctionEntry { .. } => Yes, Path(..) => No, PatternComplexityLimit { .. } => No, PinV2(..) => Yes, diff --git a/compiler/rustc_hir/src/attrs/pretty_printing.rs b/compiler/rustc_hir/src/attrs/pretty_printing.rs index 806f5c4d3ed9..c2ad644688fc 100644 --- a/compiler/rustc_hir/src/attrs/pretty_printing.rs +++ b/compiler/rustc_hir/src/attrs/pretty_printing.rs @@ -171,7 +171,7 @@ macro_rules! print_tup { print_tup!(A B C D E F G H); print_skip!(Span, (), ErrorGuaranteed); -print_disp!(u16, u128, usize, bool, NonZero, Limit); +print_disp!(u8, u16, u128, usize, bool, NonZero, Limit); print_debug!( Symbol, Ident, diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 8074ae429892..3ddf51f6c9b1 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -324,6 +324,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcReallocator | AttributeKind::RustcNounwind | AttributeKind::RustcOffloadKernel + | AttributeKind::PatchableFunctionEntry { .. } ) => { /* do nothing */ } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); @@ -349,7 +350,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::deny | sym::forbid // need to be fixed - | sym::patchable_function_entry // FIXME(patchable_function_entry) | sym::deprecated_safe // FIXME(deprecated_safe) // internal | sym::prelude_import diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index 3d51731df792..f817a0b0d91b 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -26,12 +26,6 @@ error[E0463]: can't find crate for `wloop` LL | extern crate wloop; | ^^^^^^^^^^^^^^^^^^^ can't find crate -error: malformed `patchable_function_entry` attribute input - --> $DIR/malformed-attrs.rs:114:1 - | -LL | #[patchable_function_entry] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[patchable_function_entry(prefix_nops = m, entry_nops = n)]` - error: malformed `allow` attribute input --> $DIR/malformed-attrs.rs:184:1 | @@ -444,6 +438,15 @@ LL | #[instruction_set] | = note: for more information, visit +error[E0539]: malformed `patchable_function_entry` attribute input + --> $DIR/malformed-attrs.rs:114:1 + | +LL | #[patchable_function_entry] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#[patchable_function_entry(prefix_nops = m, entry_nops = n)]` + error[E0565]: malformed `coroutine` attribute input --> $DIR/malformed-attrs.rs:117:5 | diff --git a/tests/ui/patchable-function-entry/patchable-function-entry-attribute.rs b/tests/ui/patchable-function-entry/patchable-function-entry-attribute.rs index 1e376c9ff3c1..5cbeccf1b0e4 100644 --- a/tests/ui/patchable-function-entry/patchable-function-entry-attribute.rs +++ b/tests/ui/patchable-function-entry/patchable-function-entry-attribute.rs @@ -1,17 +1,26 @@ #![feature(patchable_function_entry)] fn main() {} -#[patchable_function_entry(prefix_nops = 256, entry_nops = 0)]//~error: integer value out of range +#[patchable_function_entry(prefix_nops = 256, entry_nops = 0)] +//~^ ERROR malformed pub fn too_high_pnops() {} -#[patchable_function_entry(prefix_nops = "stringvalue", entry_nops = 0)]//~error: invalid literal value +#[patchable_function_entry(prefix_nops = "stringvalue", entry_nops = 0)] +//~^ ERROR malformed pub fn non_int_nop() {} -#[patchable_function_entry]//~error: malformed `patchable_function_entry` attribute input +#[patchable_function_entry] +//~^ ERROR malformed `patchable_function_entry` attribute input pub fn malformed_attribute() {} -#[patchable_function_entry(prefix_nops = 10, something = 0)]//~error: unexpected parameter name +#[patchable_function_entry(prefix_nops = 10, something = 0)] +//~^ ERROR malformed pub fn unexpected_parameter_name() {} -#[patchable_function_entry()]//~error: must specify at least one parameter +#[patchable_function_entry()] +//~^ ERROR malformed pub fn no_parameters_given() {} + +#[patchable_function_entry(prefix_nops = 255, prefix_nops = 255)] +//~^ ERROR malformed +pub fn duplicate_parameter() {} diff --git a/tests/ui/patchable-function-entry/patchable-function-entry-attribute.stderr b/tests/ui/patchable-function-entry/patchable-function-entry-attribute.stderr index 9357a86c4153..43fc0c0518af 100644 --- a/tests/ui/patchable-function-entry/patchable-function-entry-attribute.stderr +++ b/tests/ui/patchable-function-entry/patchable-function-entry-attribute.stderr @@ -1,32 +1,58 @@ -error: malformed `patchable_function_entry` attribute input - --> $DIR/patchable-function-entry-attribute.rs:10:1 - | -LL | #[patchable_function_entry] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[patchable_function_entry(prefix_nops = m, entry_nops = n)]` - -error: integer value out of range - --> $DIR/patchable-function-entry-attribute.rs:4:42 +error[E0539]: malformed `patchable_function_entry` attribute input + --> $DIR/patchable-function-entry-attribute.rs:4:1 | LL | #[patchable_function_entry(prefix_nops = 256, entry_nops = 0)] - | ^^^ value must be between `0` and `255` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---^^^^^^^^^^^^^^^^^^ + | | | + | | expected an integer literal in the range of 0..=255 + | help: must be of the form: `#[patchable_function_entry(prefix_nops = m, entry_nops = n)]` -error: invalid literal value - --> $DIR/patchable-function-entry-attribute.rs:7:42 +error[E0539]: malformed `patchable_function_entry` attribute input + --> $DIR/patchable-function-entry-attribute.rs:8:1 | LL | #[patchable_function_entry(prefix_nops = "stringvalue", entry_nops = 0)] - | ^^^^^^^^^^^^^ value must be an integer between `0` and `255` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-------------^^^^^^^^^^^^^^^^^^ + | | | + | | expected an integer literal here + | help: must be of the form: `#[patchable_function_entry(prefix_nops = m, entry_nops = n)]` -error: unexpected parameter name - --> $DIR/patchable-function-entry-attribute.rs:13:46 +error[E0539]: malformed `patchable_function_entry` attribute input + --> $DIR/patchable-function-entry-attribute.rs:12:1 | -LL | #[patchable_function_entry(prefix_nops = 10, something = 0)] - | ^^^^^^^^^^^^^ expected `prefix_nops` or `entry_nops` +LL | #[patchable_function_entry] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#[patchable_function_entry(prefix_nops = m, entry_nops = n)]` -error: must specify at least one parameter +error[E0539]: malformed `patchable_function_entry` attribute input --> $DIR/patchable-function-entry-attribute.rs:16:1 | +LL | #[patchable_function_entry(prefix_nops = 10, something = 0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------^^^^^^ + | | | + | | valid arguments are `prefix_nops` or `entry_nops` + | help: must be of the form: `#[patchable_function_entry(prefix_nops = m, entry_nops = n)]` + +error[E0539]: malformed `patchable_function_entry` attribute input + --> $DIR/patchable-function-entry-attribute.rs:20:1 + | LL | #[patchable_function_entry()] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^--^ + | | | + | | expected this to be a list + | help: must be of the form: `#[patchable_function_entry(prefix_nops = m, entry_nops = n)]` -error: aborting due to 5 previous errors +error[E0538]: malformed `patchable_function_entry` attribute input + --> $DIR/patchable-function-entry-attribute.rs:24:1 + | +LL | #[patchable_function_entry(prefix_nops = 255, prefix_nops = 255)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^^^^^ + | | | + | | found `prefix_nops` used as a key more than once + | help: must be of the form: `#[patchable_function_entry(prefix_nops = m, entry_nops = n)]` +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0538, E0539. +For more information about an error, try `rustc --explain E0538`. From a0b3ee2f76444c8cd82afc77246c868426290b37 Mon Sep 17 00:00:00 2001 From: Edvin Bryntesson Date: Mon, 19 Jan 2026 11:22:24 +0100 Subject: [PATCH 46/69] only process parsed attrs for codegen check --- .../rustc_codegen_ssa/src/codegen_attrs.rs | 447 +++++++++--------- 1 file changed, 216 insertions(+), 231 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 296c96ce4ae6..d55e134109d6 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -68,251 +68,236 @@ fn process_builtin_attrs( let mut interesting_spans = InterestingAttributeDiagnosticSpans::default(); let rust_target_features = tcx.rust_target_features(LOCAL_CRATE); - for attr in attrs.iter() { - if let hir::Attribute::Parsed(p) = attr { - match p { - AttributeKind::Cold(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD, - AttributeKind::ExportName { name, .. } => { - codegen_fn_attrs.symbol_name = Some(*name) + let parsed_attrs = attrs + .iter() + .filter_map(|attr| if let hir::Attribute::Parsed(attr) = attr { Some(attr) } else { None }); + for attr in parsed_attrs { + match attr { + AttributeKind::Cold(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD, + AttributeKind::ExportName { name, .. } => codegen_fn_attrs.symbol_name = Some(*name), + AttributeKind::Inline(inline, span) => { + codegen_fn_attrs.inline = *inline; + interesting_spans.inline = Some(*span); + } + AttributeKind::Naked(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED, + AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align), + AttributeKind::LinkName { name, .. } => { + // FIXME Remove check for foreign functions once #[link_name] on non-foreign + // functions is a hard error + if tcx.is_foreign_item(did) { + codegen_fn_attrs.symbol_name = Some(*name); } - AttributeKind::Inline(inline, span) => { - codegen_fn_attrs.inline = *inline; - interesting_spans.inline = Some(*span); + } + AttributeKind::LinkOrdinal { ordinal, span } => { + codegen_fn_attrs.link_ordinal = Some(*ordinal); + interesting_spans.link_ordinal = Some(*span); + } + AttributeKind::LinkSection { name, .. } => codegen_fn_attrs.link_section = Some(*name), + AttributeKind::NoMangle(attr_span) => { + interesting_spans.no_mangle = Some(*attr_span); + if tcx.opt_item_name(did.to_def_id()).is_some() { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE; + } else { + tcx.dcx() + .span_delayed_bug(*attr_span, "no_mangle should be on a named function"); } - AttributeKind::Naked(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED, - AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align), - AttributeKind::LinkName { name, .. } => { - // FIXME Remove check for foreign functions once #[link_name] on non-foreign - // functions is a hard error - if tcx.is_foreign_item(did) { - codegen_fn_attrs.symbol_name = Some(*name); - } - } - AttributeKind::LinkOrdinal { ordinal, span } => { - codegen_fn_attrs.link_ordinal = Some(*ordinal); - interesting_spans.link_ordinal = Some(*span); - } - AttributeKind::LinkSection { name, .. } => { - codegen_fn_attrs.link_section = Some(*name) - } - AttributeKind::NoMangle(attr_span) => { - interesting_spans.no_mangle = Some(*attr_span); - if tcx.opt_item_name(did.to_def_id()).is_some() { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE; + } + AttributeKind::Optimize(optimize, _) => codegen_fn_attrs.optimize = *optimize, + AttributeKind::TargetFeature { features, attr_span, was_forced } => { + let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else { + tcx.dcx().span_delayed_bug(*attr_span, "target_feature applied to non-fn"); + continue; + }; + let safe_target_features = + matches!(sig.header.safety, hir::HeaderSafety::SafeTargetFeatures); + codegen_fn_attrs.safe_target_features = safe_target_features; + if safe_target_features && !was_forced { + if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc { + // The `#[target_feature]` attribute is allowed on + // WebAssembly targets on all functions. Prior to stabilizing + // the `target_feature_11` feature, `#[target_feature]` was + // only permitted on unsafe functions because on most targets + // execution of instructions that are not supported is + // considered undefined behavior. For WebAssembly which is a + // 100% safe target at execution time it's not possible to + // execute undefined instructions, and even if a future + // feature was added in some form for this it would be a + // deterministic trap. There is no undefined behavior when + // executing WebAssembly so `#[target_feature]` is allowed + // on safe functions (but again, only for WebAssembly) + // + // Note that this is also allowed if `actually_rustdoc` so + // if a target is documenting some wasm-specific code then + // it's not spuriously denied. + // + // Now that `#[target_feature]` is permitted on safe functions, + // this exception must still exist for allowing the attribute on + // `main`, `start`, and other functions that are not usually + // allowed. } else { - tcx.dcx().span_delayed_bug( - *attr_span, - "no_mangle should be on a named function", - ); + check_target_feature_trait_unsafe(tcx, did, *attr_span); } } - AttributeKind::Optimize(optimize, _) => codegen_fn_attrs.optimize = *optimize, - AttributeKind::TargetFeature { features, attr_span, was_forced } => { - let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else { - tcx.dcx().span_delayed_bug(*attr_span, "target_feature applied to non-fn"); - continue; + from_target_feature_attr( + tcx, + did, + features, + *was_forced, + rust_target_features, + &mut codegen_fn_attrs.target_features, + ); + } + AttributeKind::TrackCaller(attr_span) => { + let is_closure = tcx.is_closure_like(did.to_def_id()); + + if !is_closure + && let Some(fn_sig) = try_fn_sig(tcx, did, *attr_span) + && fn_sig.skip_binder().abi() != ExternAbi::Rust + { + tcx.dcx().emit_err(errors::RequiresRustAbi { span: *attr_span }); + } + if is_closure + && !tcx.features().closure_track_caller() + && !attr_span.allows_unstable(sym::closure_track_caller) + { + feature_err( + &tcx.sess, + sym::closure_track_caller, + *attr_span, + "`#[track_caller]` on closures is currently unstable", + ) + .emit(); + } + codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER + } + AttributeKind::Used { used_by, .. } => match used_by { + UsedBy::Compiler => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_COMPILER, + UsedBy::Linker => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER, + UsedBy::Default => { + let used_form = if tcx.sess.target.os == Os::Illumos { + // illumos' `ld` doesn't support a section header that would represent + // `#[used(linker)]`, see + // https://github.com/rust-lang/rust/issues/146169. For that target, + // downgrade as if `#[used(compiler)]` was requested and hope for the + // best. + CodegenFnAttrFlags::USED_COMPILER + } else { + CodegenFnAttrFlags::USED_LINKER }; - let safe_target_features = - matches!(sig.header.safety, hir::HeaderSafety::SafeTargetFeatures); - codegen_fn_attrs.safe_target_features = safe_target_features; - if safe_target_features && !was_forced { - if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc { - // The `#[target_feature]` attribute is allowed on - // WebAssembly targets on all functions. Prior to stabilizing - // the `target_feature_11` feature, `#[target_feature]` was - // only permitted on unsafe functions because on most targets - // execution of instructions that are not supported is - // considered undefined behavior. For WebAssembly which is a - // 100% safe target at execution time it's not possible to - // execute undefined instructions, and even if a future - // feature was added in some form for this it would be a - // deterministic trap. There is no undefined behavior when - // executing WebAssembly so `#[target_feature]` is allowed - // on safe functions (but again, only for WebAssembly) - // - // Note that this is also allowed if `actually_rustdoc` so - // if a target is documenting some wasm-specific code then - // it's not spuriously denied. - // - // Now that `#[target_feature]` is permitted on safe functions, - // this exception must still exist for allowing the attribute on - // `main`, `start`, and other functions that are not usually - // allowed. - } else { - check_target_feature_trait_unsafe(tcx, did, *attr_span); + codegen_fn_attrs.flags |= used_form; + } + }, + AttributeKind::FfiConst(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST, + AttributeKind::FfiPure(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE, + AttributeKind::StdInternalSymbol(_) => { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL + } + AttributeKind::Linkage(linkage, span) => { + let linkage = Some(*linkage); + + if tcx.is_foreign_item(did) { + codegen_fn_attrs.import_linkage = linkage; + + if tcx.is_mutable_static(did.into()) { + let mut diag = tcx.dcx().struct_span_err( + *span, + "extern mutable statics are not allowed with `#[linkage]`", + ); + diag.note( + "marking the extern static mutable would allow changing which \ + symbol the static references rather than make the target of the \ + symbol mutable", + ); + diag.emit(); + } + } else { + codegen_fn_attrs.linkage = linkage; + } + } + AttributeKind::Sanitize { span, .. } => { + interesting_spans.sanitize = Some(*span); + } + AttributeKind::ObjcClass { classname, .. } => { + codegen_fn_attrs.objc_class = Some(*classname); + } + AttributeKind::ObjcSelector { methname, .. } => { + codegen_fn_attrs.objc_selector = Some(*methname); + } + AttributeKind::EiiForeignItem => { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM; + } + AttributeKind::EiiImpls(impls) => { + for i in impls { + let foreign_item = match i.resolution { + EiiImplResolution::Macro(def_id) => { + let Some(extern_item) = find_attr!( + tcx.get_all_attrs(def_id), + AttributeKind::EiiDeclaration(target) => target.foreign_item + ) else { + tcx.dcx().span_delayed_bug( + i.span, + "resolved to something that's not an EII", + ); + continue; + }; + extern_item } - } - from_target_feature_attr( - tcx, - did, - features, - *was_forced, - rust_target_features, - &mut codegen_fn_attrs.target_features, - ); - } - AttributeKind::TrackCaller(attr_span) => { - let is_closure = tcx.is_closure_like(did.to_def_id()); + EiiImplResolution::Known(decl) => decl.foreign_item, + EiiImplResolution::Error(_eg) => continue, + }; - if !is_closure - && let Some(fn_sig) = try_fn_sig(tcx, did, *attr_span) - && fn_sig.skip_binder().abi() != ExternAbi::Rust + // this is to prevent a bug where a single crate defines both the default and explicit implementation + // for an EII. In that case, both of them may be part of the same final object file. I'm not 100% sure + // what happens, either rustc deduplicates the symbol or llvm, or it's random/order-dependent. + // However, the fact that the default one of has weak linkage isn't considered and you sometimes get that + // the default implementation is used while an explicit implementation is given. + if + // if this is a default impl + i.is_default + // iterate over all implementations *in the current crate* + // (this is ok since we generate codegen fn attrs in the local crate) + // if any of them is *not default* then don't emit the alias. + && tcx.externally_implementable_items(LOCAL_CRATE).get(&foreign_item).expect("at least one").1.iter().any(|(_, imp)| !imp.is_default) { - tcx.dcx().emit_err(errors::RequiresRustAbi { span: *attr_span }); + continue; } - if is_closure - && !tcx.features().closure_track_caller() - && !attr_span.allows_unstable(sym::closure_track_caller) - { - feature_err( - &tcx.sess, - sym::closure_track_caller, - *attr_span, - "`#[track_caller]` on closures is currently unstable", - ) - .emit(); - } - codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER - } - AttributeKind::Used { used_by, .. } => match used_by { - UsedBy::Compiler => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_COMPILER, - UsedBy::Linker => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER, - UsedBy::Default => { - let used_form = if tcx.sess.target.os == Os::Illumos { - // illumos' `ld` doesn't support a section header that would represent - // `#[used(linker)]`, see - // https://github.com/rust-lang/rust/issues/146169. For that target, - // downgrade as if `#[used(compiler)]` was requested and hope for the - // best. - CodegenFnAttrFlags::USED_COMPILER - } else { - CodegenFnAttrFlags::USED_LINKER - }; - codegen_fn_attrs.flags |= used_form; - } - }, - AttributeKind::FfiConst(_) => { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST - } - AttributeKind::FfiPure(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE, - AttributeKind::StdInternalSymbol(_) => { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL - } - AttributeKind::Linkage(linkage, span) => { - let linkage = Some(*linkage); - if tcx.is_foreign_item(did) { - codegen_fn_attrs.import_linkage = linkage; - - if tcx.is_mutable_static(did.into()) { - let mut diag = tcx.dcx().struct_span_err( - *span, - "extern mutable statics are not allowed with `#[linkage]`", - ); - diag.note( - "marking the extern static mutable would allow changing which \ - symbol the static references rather than make the target of the \ - symbol mutable", - ); - diag.emit(); - } - } else { - codegen_fn_attrs.linkage = linkage; - } - } - AttributeKind::Sanitize { span, .. } => { - interesting_spans.sanitize = Some(*span); - } - AttributeKind::ObjcClass { classname, .. } => { - codegen_fn_attrs.objc_class = Some(*classname); - } - AttributeKind::ObjcSelector { methname, .. } => { - codegen_fn_attrs.objc_selector = Some(*methname); - } - AttributeKind::EiiForeignItem => { + codegen_fn_attrs.foreign_item_symbol_aliases.push(( + foreign_item, + if i.is_default { Linkage::LinkOnceAny } else { Linkage::External }, + Visibility::Default, + )); codegen_fn_attrs.flags |= CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM; } - AttributeKind::EiiImpls(impls) => { - for i in impls { - let foreign_item = match i.resolution { - EiiImplResolution::Macro(def_id) => { - let Some(extern_item) = find_attr!( - tcx.get_all_attrs(def_id), - AttributeKind::EiiDeclaration(target) => target.foreign_item - ) else { - tcx.dcx().span_delayed_bug( - i.span, - "resolved to something that's not an EII", - ); - continue; - }; - extern_item - } - EiiImplResolution::Known(decl) => decl.foreign_item, - EiiImplResolution::Error(_eg) => continue, - }; - - // this is to prevent a bug where a single crate defines both the default and explicit implementation - // for an EII. In that case, both of them may be part of the same final object file. I'm not 100% sure - // what happens, either rustc deduplicates the symbol or llvm, or it's random/order-dependent. - // However, the fact that the default one of has weak linkage isn't considered and you sometimes get that - // the default implementation is used while an explicit implementation is given. - if - // if this is a default impl - i.is_default - // iterate over all implementations *in the current crate* - // (this is ok since we generate codegen fn attrs in the local crate) - // if any of them is *not default* then don't emit the alias. - && tcx.externally_implementable_items(LOCAL_CRATE).get(&foreign_item).expect("at least one").1.iter().any(|(_, imp)| !imp.is_default) - { - continue; - } - - codegen_fn_attrs.foreign_item_symbol_aliases.push(( - foreign_item, - if i.is_default { Linkage::LinkOnceAny } else { Linkage::External }, - Visibility::Default, - )); - codegen_fn_attrs.flags |= CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM; - } - } - AttributeKind::ThreadLocal => { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL - } - AttributeKind::InstructionSet(instruction_set) => { - codegen_fn_attrs.instruction_set = Some(*instruction_set) - } - AttributeKind::RustcAllocator => { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR - } - AttributeKind::RustcDeallocator => { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::DEALLOCATOR - } - AttributeKind::RustcReallocator => { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::REALLOCATOR - } - AttributeKind::RustcAllocatorZeroed => { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED - } - AttributeKind::RustcNounwind => { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND - } - AttributeKind::RustcOffloadKernel => { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::OFFLOAD_KERNEL - } - AttributeKind::PatchableFunctionEntry { prefix, entry } => { - codegen_fn_attrs.patchable_function_entry = - Some(PatchableFunctionEntry::from_prefix_and_entry(*prefix, *entry)); - } - _ => {} } - } - - let Some(name) = attr.name() else { - continue; - }; - - match name { + AttributeKind::ThreadLocal => { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL + } + AttributeKind::InstructionSet(instruction_set) => { + codegen_fn_attrs.instruction_set = Some(*instruction_set) + } + AttributeKind::RustcAllocator => { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR + } + AttributeKind::RustcDeallocator => { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::DEALLOCATOR + } + AttributeKind::RustcReallocator => { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::REALLOCATOR + } + AttributeKind::RustcAllocatorZeroed => { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED + } + AttributeKind::RustcNounwind => { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND + } + AttributeKind::RustcOffloadKernel => { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::OFFLOAD_KERNEL + } + AttributeKind::PatchableFunctionEntry { prefix, entry } => { + codegen_fn_attrs.patchable_function_entry = + Some(PatchableFunctionEntry::from_prefix_and_entry(*prefix, *entry)); + } _ => {} } } From 6b8432bee3cd9956481d3f661bebcd3580e82bbf Mon Sep 17 00:00:00 2001 From: Asger Hautop Drewsen Date: Tue, 20 Jan 2026 12:57:16 +0100 Subject: [PATCH 47/69] Use version 1.93.0 in `RELEASES.md` instead of 1.93 --- RELEASES.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 74b574924489..10a400fda5c3 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,7 +1,7 @@ -Version 1.93 (2026-01-22) +Version 1.93.0 (2026-01-22) ========================== - + Language -------- @@ -16,13 +16,13 @@ Language - [Add warn-by-default `function_casts_as_integer` lint](https://github.com/rust-lang/rust/pull/141470) - + Compiler -------- - [Stabilize `-Cjump-tables=bool`](https://github.com/rust-lang/rust/pull/145974). The flag was previously called `-Zno-jump-tables`. - + Platform Support ---------------- @@ -34,7 +34,7 @@ for more information on Rust's tiered platform support. [platform-support-doc]: https://doc.rust-lang.org/rustc/platform-support.html - + Libraries --------- @@ -44,7 +44,7 @@ Libraries - [Don't require `T: RefUnwindSafe` for `vec::IntoIter: UnwindSafe`](https://github.com/rust-lang/rust/pull/145665) - + Stabilized APIs --------------- @@ -74,7 +74,7 @@ Stabilized APIs - [`std::fmt::FromFn`](https://doc.rust-lang.org/stable/std/fmt/struct.FromFn.html) - + Cargo ----- @@ -82,7 +82,7 @@ Cargo - [In `cargo tree`, support long forms for `--format` variables](https://github.com/rust-lang/cargo/pull/16204/) - [Add `--workspace` to `cargo clean`](https://github.com/rust-lang/cargo/pull/16263/) - + Rustdoc ----- @@ -92,7 +92,7 @@ Rustdoc - [Validate usage of crate-level doc attributes](https://github.com/rust-lang/rust/pull/149197). This means if any of `html_favicon_url`, `html_logo_url`, `html_playground_url`, `issue_tracker_base_url`, or `html_no_source` either has a missing value, an unexpected value, or a value of the wrong type, rustdoc will emit the deny-by-default lint `rustdoc::invalid_doc_attributes`. - + Compatibility Notes ------------------- From 3369a17e519980b28a3695eb5f2d44d154f63234 Mon Sep 17 00:00:00 2001 From: LorrensP-2158466 Date: Wed, 22 Oct 2025 18:59:22 +0300 Subject: [PATCH 48/69] Implements a FCW lint to detect uses of ambiguously glob imported traits. --- compiler/rustc_hir/src/hir.rs | 5 + .../rustc_hir_typeck/src/method/confirm.rs | 29 ++- compiler/rustc_hir_typeck/src/method/probe.rs | 46 +++-- compiler/rustc_lint_defs/src/builtin.rs | 55 +++++ compiler/rustc_resolve/src/lib.rs | 32 ++- tests/ui/imports/ambiguous-trait-in-scope.rs | 84 ++++++++ .../imports/ambiguous-trait-in-scope.stderr | 191 ++++++++++++++++++ .../auxiliary/ambiguous-trait-reexport.rs | 23 +++ ...mbiguous-trait-lint-on-redundant-import.rs | 27 +++ 9 files changed, 473 insertions(+), 19 deletions(-) create mode 100644 tests/ui/imports/ambiguous-trait-in-scope.rs create mode 100644 tests/ui/imports/ambiguous-trait-in-scope.stderr create mode 100644 tests/ui/imports/auxiliary/ambiguous-trait-reexport.rs create mode 100644 tests/ui/imports/no-ambiguous-trait-lint-on-redundant-import.rs diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 05ecb35bc5a4..e16ab8d485a2 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -4595,6 +4595,11 @@ pub struct Upvar { pub struct TraitCandidate { pub def_id: DefId, pub import_ids: SmallVec<[LocalDefId; 1]>, + // Indicates whether this trait candidate is ambiguously glob imported + // in it's scope. Related to the AMBIGUOUS_GLOB_IMPORTED_TRAITS lint. + // If this is set to true and the trait is used as a result of method lookup, this + // lint is thrown. + pub lint_ambiguous: bool, } #[derive(Copy, Clone, Debug, HashStable_Generic)] diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index aab4e3985555..1881a75448b6 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -1,3 +1,4 @@ +use std::fmt::Debug; use std::ops::Deref; use rustc_hir as hir; @@ -12,7 +13,9 @@ use rustc_hir_analysis::hir_ty_lowering::{ use rustc_infer::infer::{ BoundRegionConversionTime, DefineOpaqueTypes, InferOk, RegionVariableOrigin, }; -use rustc_lint::builtin::RESOLVING_TO_ITEMS_SHADOWING_SUPERTRAIT_ITEMS; +use rustc_lint::builtin::{ + AMBIGUOUS_GLOB_IMPORTED_TRAITS, RESOLVING_TO_ITEMS_SHADOWING_SUPERTRAIT_ITEMS, +}; use rustc_middle::traits::ObligationCauseCode; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCoercion, @@ -149,6 +152,9 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // Lint when an item is shadowing a supertrait item. self.lint_shadowed_supertrait_items(pick, segment); + // Lint when a trait is ambiguously imported + self.lint_ambiguously_glob_imported_traits(pick, segment); + // Add any trait/regions obligations specified on the method's type parameters. // We won't add these if we encountered an illegal sized bound, so that we can use // a custom error in that case. @@ -322,7 +328,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { }) } - probe::TraitPick => { + probe::TraitPick(_) => { let trait_def_id = pick.item.container_id(self.tcx); // Make a trait reference `$0 : Trait<$1...$n>` @@ -716,6 +722,25 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { ); } + fn lint_ambiguously_glob_imported_traits( + &self, + pick: &probe::Pick<'_>, + segment: &hir::PathSegment<'tcx>, + ) { + if pick.kind != probe::PickKind::TraitPick(true) { + return; + } + let trait_name = self.tcx.item_name(pick.item.container_id(self.tcx)); + let import_span = self.tcx.hir_span_if_local(pick.import_ids[0].to_def_id()).unwrap(); + + self.tcx.node_lint(AMBIGUOUS_GLOB_IMPORTED_TRAITS, segment.hir_id, |diag| { + diag.primary_message(format!("Use of ambiguously glob imported trait `{trait_name}`")) + .span(segment.ident.span) + .span_label(import_span, format!("`{trait_name}` imported ambiguously here")) + .help(format!("Import `{trait_name}` explicitly")); + }); + } + fn upcast( &mut self, source_trait_ref: ty::PolyTraitRef<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index beb0337d8c59..59df5689b614 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -106,7 +106,7 @@ pub(crate) struct Candidate<'tcx> { pub(crate) enum CandidateKind<'tcx> { InherentImplCandidate { impl_def_id: DefId, receiver_steps: usize }, ObjectCandidate(ty::PolyTraitRef<'tcx>), - TraitCandidate(ty::PolyTraitRef<'tcx>), + TraitCandidate(ty::PolyTraitRef<'tcx>, bool /* lint_ambiguous */), WhereClauseCandidate(ty::PolyTraitRef<'tcx>), } @@ -235,7 +235,10 @@ pub(crate) struct Pick<'tcx> { pub(crate) enum PickKind<'tcx> { InherentImplPick, ObjectPick, - TraitPick, + TraitPick( + // Is Ambiguously Imported + bool, + ), WhereClausePick( // Trait ty::PolyTraitRef<'tcx>, @@ -560,7 +563,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { probe_cx.push_candidate( Candidate { item, - kind: CandidateKind::TraitCandidate(ty::Binder::dummy(trait_ref)), + kind: CandidateKind::TraitCandidate( + ty::Binder::dummy(trait_ref), + false, + ), import_ids: smallvec![], }, false, @@ -1018,6 +1024,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.assemble_extension_candidates_for_trait( &trait_candidate.import_ids, trait_did, + trait_candidate.lint_ambiguous, ); } } @@ -1029,7 +1036,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let mut duplicates = FxHashSet::default(); for trait_info in suggest::all_traits(self.tcx) { if duplicates.insert(trait_info.def_id) { - self.assemble_extension_candidates_for_trait(&smallvec![], trait_info.def_id); + self.assemble_extension_candidates_for_trait( + &smallvec![], + trait_info.def_id, + false, + ); } } } @@ -1055,6 +1066,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { &mut self, import_ids: &SmallVec<[LocalDefId; 1]>, trait_def_id: DefId, + lint_ambiguous: bool, ) { let trait_args = self.fresh_args_for_item(self.span, trait_def_id); let trait_ref = ty::TraitRef::new_from_args(self.tcx, trait_def_id, trait_args); @@ -1076,7 +1088,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { Candidate { item, import_ids: import_ids.clone(), - kind: TraitCandidate(bound_trait_ref), + kind: TraitCandidate(bound_trait_ref, lint_ambiguous), }, false, ); @@ -1099,7 +1111,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { Candidate { item, import_ids: import_ids.clone(), - kind: TraitCandidate(ty::Binder::dummy(trait_ref)), + kind: TraitCandidate(ty::Binder::dummy(trait_ref), lint_ambiguous), }, false, ); @@ -1842,7 +1854,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { ObjectCandidate(_) | WhereClauseCandidate(_) => { CandidateSource::Trait(candidate.item.container_id(self.tcx)) } - TraitCandidate(trait_ref) => self.probe(|_| { + TraitCandidate(trait_ref, _) => self.probe(|_| { let trait_ref = self.instantiate_binder_with_fresh_vars( self.span, BoundRegionConversionTime::FnCall, @@ -1872,7 +1884,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn candidate_source_from_pick(&self, pick: &Pick<'tcx>) -> CandidateSource { match pick.kind { InherentImplPick => CandidateSource::Impl(pick.item.container_id(self.tcx)), - ObjectPick | WhereClausePick(_) | TraitPick => { + ObjectPick | WhereClausePick(_) | TraitPick(_) => { CandidateSource::Trait(pick.item.container_id(self.tcx)) } } @@ -1948,7 +1960,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { impl_bounds, )); } - TraitCandidate(poly_trait_ref) => { + TraitCandidate(poly_trait_ref, _) => { // Some trait methods are excluded for arrays before 2021. // (`array.into_iter()` wants a slice iterator for compatibility.) if let Some(method_name) = self.method_name { @@ -2274,11 +2286,16 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } + let lint_ambiguous = match probes[0].0.kind { + TraitCandidate(_, lint) => lint, + _ => false, + }; + // FIXME: check the return type here somehow. // If so, just use this trait and call it a day. Some(Pick { item: probes[0].0.item, - kind: TraitPick, + kind: TraitPick(lint_ambiguous), import_ids: probes[0].0.import_ids.clone(), autoderefs: 0, autoref_or_ptr_adjustment: None, @@ -2348,9 +2365,14 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } + let lint_ambiguous = match probes[0].0.kind { + TraitCandidate(_, lint) => lint, + _ => false, + }; + Some(Pick { item: child_candidate.item, - kind: TraitPick, + kind: TraitPick(lint_ambiguous), import_ids: child_candidate.import_ids.clone(), autoderefs: 0, autoref_or_ptr_adjustment: None, @@ -2613,7 +2635,7 @@ impl<'tcx> Candidate<'tcx> { kind: match self.kind { InherentImplCandidate { .. } => InherentImplPick, ObjectCandidate(_) => ObjectPick, - TraitCandidate(_) => TraitPick, + TraitCandidate(_, lint_ambiguous) => TraitPick(lint_ambiguous), WhereClauseCandidate(trait_ref) => { // Only trait derived from where-clauses should // appear here, so they should not contain any diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 4230aa7568e2..3ccc0f66c7d5 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -17,6 +17,7 @@ declare_lint_pass! { AARCH64_SOFTFLOAT_NEON, ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, AMBIGUOUS_ASSOCIATED_ITEMS, + AMBIGUOUS_GLOB_IMPORTED_TRAITS, AMBIGUOUS_GLOB_IMPORTS, AMBIGUOUS_GLOB_REEXPORTS, AMBIGUOUS_PANIC_IMPORTS, @@ -4473,6 +4474,60 @@ declare_lint! { }; } +declare_lint! { + /// The `ambiguous_glob_imported_traits` lint reports uses of traits that are + /// imported ambiguously via glob imports. Previously, this was not enforced + /// due to a bug in rustc. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(ambiguous_glob_imported_traits)] + /// mod m1 { + /// pub trait Trait { + /// fn method1(&self) {} + /// } + /// impl Trait for u8 {} + /// } + /// mod m2 { + /// pub trait Trait { + /// fn method2(&self) {} + /// } + /// impl Trait for u8 {} + /// } + /// + /// fn main() { + /// use m1::*; + /// use m2::*; + /// 0u8.method1(); + /// 0u8.method2(); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// When multiple traits with the same name are brought into scope through glob imports, + /// one trait becomes the "primary" one while the others are shadowed. Methods from the + /// shadowed traits (e.g. `method2`) become inaccessible, while methods from the "primary" + /// trait (e.g. `method1`) still resolve. Ideally, none of the ambiguous traits would be in scope, + /// but we have to allow this for now because of backwards compatibility. + /// This lint reports uses of these "primary" traits that are ambiguous. + /// + /// This is a [future-incompatible] lint to transition this to a + /// hard error in the future. + /// + /// [future-incompatible]: ../index.md#future-incompatible-lints + pub AMBIGUOUS_GLOB_IMPORTED_TRAITS, + Warn, + "detects uses of ambiguously glob imported traits", + @future_incompatible = FutureIncompatibleInfo { + reason: fcw!(FutureReleaseError #147992), + report_in_deps: false, + }; +} + declare_lint! { /// The `ambiguous_panic_imports` lint detects ambiguous core and std panic imports, but /// previously didn't do that due to `#[macro_use]` prelude macro import. diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 2c22aacb3241..280d73bece6c 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -622,7 +622,18 @@ struct ModuleData<'ra> { globs: CmRefCell>>, /// Used to memoize the traits in this module for faster searches through all traits in scope. - traits: CmRefCell, Option>)]>>>, + traits: CmRefCell< + Option< + Box< + [( + Macros20NormalizedIdent, + Decl<'ra>, + Option>, + bool, /* lint ambiguous */ + )], + >, + >, + >, /// Span of the module itself. Used for error reporting. span: Span, @@ -719,7 +730,12 @@ impl<'ra> Module<'ra> { return; } if let Res::Def(DefKind::Trait | DefKind::TraitAlias, def_id) = binding.res() { - collected_traits.push((name, binding, r.as_ref().get_module(def_id))) + collected_traits.push(( + name, + binding, + r.as_ref().get_module(def_id), + binding.is_ambiguity_recursive(), + )); } }); *traits = Some(collected_traits.into_boxed_slice()); @@ -1925,7 +1941,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if let Some(module) = current_trait { if self.trait_may_have_item(Some(module), assoc_item) { let def_id = module.def_id(); - found_traits.push(TraitCandidate { def_id, import_ids: smallvec![] }); + found_traits.push(TraitCandidate { + def_id, + import_ids: smallvec![], + lint_ambiguous: false, + }); } } @@ -1963,11 +1983,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ) { module.ensure_traits(self); let traits = module.traits.borrow(); - for &(trait_name, trait_binding, trait_module) in traits.as_ref().unwrap().iter() { + for &(trait_name, trait_binding, trait_module, lint_ambiguous) in + traits.as_ref().unwrap().iter() + { if self.trait_may_have_item(trait_module, assoc_item) { let def_id = trait_binding.res().def_id(); let import_ids = self.find_transitive_imports(&trait_binding.kind, trait_name.0); - found_traits.push(TraitCandidate { def_id, import_ids }); + found_traits.push(TraitCandidate { def_id, import_ids, lint_ambiguous }); } } } diff --git a/tests/ui/imports/ambiguous-trait-in-scope.rs b/tests/ui/imports/ambiguous-trait-in-scope.rs new file mode 100644 index 000000000000..d12276297396 --- /dev/null +++ b/tests/ui/imports/ambiguous-trait-in-scope.rs @@ -0,0 +1,84 @@ +//@ edition:2018 +//@ aux-crate:external=ambiguous-trait-reexport.rs + +mod m1 { + pub trait Trait { + fn method1(&self) {} + } + impl Trait for u8 {} +} +mod m2 { + pub trait Trait { + fn method2(&self) {} + } + impl Trait for u8 {} +} +mod m1_reexport { + pub use crate::m1::Trait; +} +mod m2_reexport { + pub use crate::m2::Trait; +} + +mod ambig_reexport { + pub use crate::m1::*; + pub use crate::m2::*; +} + +fn test1() { + // Create an ambiguous import for `Trait` in one order + use m1::*; + use m2::*; + 0u8.method1(); //~ WARNING Use of ambiguously glob imported trait `Trait` [ambiguous_glob_imported_traits] + //~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + 0u8.method2(); //~ ERROR: no method named `method2` found for type `u8` in the current scope +} + +fn test2() { + // Create an ambiguous import for `Trait` in another order + use m2::*; + use m1::*; + 0u8.method1(); //~ ERROR: no method named `method1` found for type `u8` in the current scope + 0u8.method2(); //~ WARNING Use of ambiguously glob imported trait `Trait` [ambiguous_glob_imported_traits] + //~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +} + +fn test_indirect_reexport() { + use m1_reexport::*; + use m2_reexport::*; + 0u8.method1(); //~ WARNING Use of ambiguously glob imported trait `Trait` [ambiguous_glob_imported_traits] + //~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + 0u8.method2(); //~ ERROR: no method named `method2` found for type `u8` in the current scope +} + +fn test_ambig_reexport() { + use ambig_reexport::*; + 0u8.method1(); //~ WARNING Use of ambiguously glob imported trait `Trait` [ambiguous_glob_imported_traits] + //~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + 0u8.method2(); //~ ERROR: no method named `method2` found for type `u8` in the current scope +} + +fn test_external() { + use external::m1::*; + use external::m2::*; + 0u8.method1(); //~ WARNING Use of ambiguously glob imported trait `Trait` [ambiguous_glob_imported_traits] + //~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + 0u8.method2(); //~ ERROR: no method named `method2` found for type `u8` in the current scope +} + +fn test_external_indirect_reexport() { + use external::m1_reexport::*; + use external::m2_reexport::*; + 0u8.method1(); //~ WARNING Use of ambiguously glob imported trait `Trait` [ambiguous_glob_imported_traits] + //~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + 0u8.method2(); //~ ERROR: no method named `method2` found for type `u8` in the current scope +} + +fn test_external_ambig_reexport() { + use external::ambig_reexport::*; + 0u8.method1(); //~ WARNING Use of ambiguously glob imported trait `Trait` [ambiguous_glob_imported_traits] + //~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + 0u8.method2(); //~ ERROR: no method named `method2` found for type `u8` in the current scope +} + +fn main() {} diff --git a/tests/ui/imports/ambiguous-trait-in-scope.stderr b/tests/ui/imports/ambiguous-trait-in-scope.stderr new file mode 100644 index 000000000000..cac1f4bb73fb --- /dev/null +++ b/tests/ui/imports/ambiguous-trait-in-scope.stderr @@ -0,0 +1,191 @@ +warning: Use of ambiguously glob imported trait `Trait` + --> $DIR/ambiguous-trait-in-scope.rs:32:9 + | +LL | use m1::*; + | -- `Trait` imported ambiguously here +LL | use m2::*; +LL | 0u8.method1(); + | ^^^^^^^ + | + = 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 #147992 + = help: Import `Trait` explicitly + = note: `#[warn(ambiguous_glob_imported_traits)]` (part of `#[warn(future_incompatible)]`) on by default + +error[E0599]: no method named `method2` found for type `u8` in the current scope + --> $DIR/ambiguous-trait-in-scope.rs:34:9 + | +LL | 0u8.method2(); + | ^^^^^^^ method not found in `u8` + | + = help: items from traits can only be used if the trait is in scope +help: the following traits which provide `method2` are implemented but not in scope; perhaps you want to import one of them + | +LL + use ambiguous_trait_reexport::m2::Trait; + | +LL + use crate::m2::Trait; + | + +error[E0599]: no method named `method1` found for type `u8` in the current scope + --> $DIR/ambiguous-trait-in-scope.rs:41:9 + | +LL | 0u8.method1(); + | ^^^^^^^ method not found in `u8` + | + = help: items from traits can only be used if the trait is in scope +help: the following traits which provide `method1` are implemented but not in scope; perhaps you want to import one of them + | +LL + use ambiguous_trait_reexport::m1::Trait; + | +LL + use crate::m1::Trait; + | + +warning: Use of ambiguously glob imported trait `Trait` + --> $DIR/ambiguous-trait-in-scope.rs:42:9 + | +LL | use m2::*; + | -- `Trait` imported ambiguously here +... +LL | 0u8.method2(); + | ^^^^^^^ + | + = 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 #147992 + = help: Import `Trait` explicitly + +warning: Use of ambiguously glob imported trait `Trait` + --> $DIR/ambiguous-trait-in-scope.rs:49:9 + | +LL | use m1_reexport::*; + | ----------- `Trait` imported ambiguously here +LL | use m2_reexport::*; +LL | 0u8.method1(); + | ^^^^^^^ + | + = 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 #147992 + = help: Import `Trait` explicitly + +error[E0599]: no method named `method2` found for type `u8` in the current scope + --> $DIR/ambiguous-trait-in-scope.rs:51:9 + | +LL | 0u8.method2(); + | ^^^^^^^ method not found in `u8` + | + = help: items from traits can only be used if the trait is in scope +help: the following traits which provide `method2` are implemented but not in scope; perhaps you want to import one of them + | +LL + use ambiguous_trait_reexport::m2::Trait; + | +LL + use crate::m2::Trait; + | + +warning: Use of ambiguously glob imported trait `Trait` + --> $DIR/ambiguous-trait-in-scope.rs:56:9 + | +LL | use ambig_reexport::*; + | -------------- `Trait` imported ambiguously here +LL | 0u8.method1(); + | ^^^^^^^ + | + = 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 #147992 + = help: Import `Trait` explicitly + +error[E0599]: no method named `method2` found for type `u8` in the current scope + --> $DIR/ambiguous-trait-in-scope.rs:58:9 + | +LL | 0u8.method2(); + | ^^^^^^^ method not found in `u8` + | + = help: items from traits can only be used if the trait is in scope +help: the following traits which provide `method2` are implemented but not in scope; perhaps you want to import one of them + | +LL + use ambiguous_trait_reexport::m2::Trait; + | +LL + use crate::m2::Trait; + | + +warning: Use of ambiguously glob imported trait `Trait` + --> $DIR/ambiguous-trait-in-scope.rs:64:9 + | +LL | use external::m1::*; + | ------------ `Trait` imported ambiguously here +LL | use external::m2::*; +LL | 0u8.method1(); + | ^^^^^^^ + | + = 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 #147992 + = help: Import `Trait` explicitly + +error[E0599]: no method named `method2` found for type `u8` in the current scope + --> $DIR/ambiguous-trait-in-scope.rs:66:9 + | +LL | 0u8.method2(); + | ^^^^^^^ method not found in `u8` + | + = help: items from traits can only be used if the trait is in scope +help: the following traits which provide `method2` are implemented but not in scope; perhaps you want to import one of them + | +LL + use ambiguous_trait_reexport::m2::Trait; + | +LL + use crate::m2::Trait; + | + +warning: Use of ambiguously glob imported trait `Trait` + --> $DIR/ambiguous-trait-in-scope.rs:72:9 + | +LL | use external::m1_reexport::*; + | --------------------- `Trait` imported ambiguously here +LL | use external::m2_reexport::*; +LL | 0u8.method1(); + | ^^^^^^^ + | + = 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 #147992 + = help: Import `Trait` explicitly + +error[E0599]: no method named `method2` found for type `u8` in the current scope + --> $DIR/ambiguous-trait-in-scope.rs:74:9 + | +LL | 0u8.method2(); + | ^^^^^^^ method not found in `u8` + | + = help: items from traits can only be used if the trait is in scope +help: the following traits which provide `method2` are implemented but not in scope; perhaps you want to import one of them + | +LL + use ambiguous_trait_reexport::m2::Trait; + | +LL + use crate::m2::Trait; + | + +warning: Use of ambiguously glob imported trait `Trait` + --> $DIR/ambiguous-trait-in-scope.rs:79:9 + | +LL | use external::ambig_reexport::*; + | ------------------------ `Trait` imported ambiguously here +LL | 0u8.method1(); + | ^^^^^^^ + | + = 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 #147992 + = help: Import `Trait` explicitly + +error[E0599]: no method named `method2` found for type `u8` in the current scope + --> $DIR/ambiguous-trait-in-scope.rs:81:9 + | +LL | 0u8.method2(); + | ^^^^^^^ method not found in `u8` + | + = help: items from traits can only be used if the trait is in scope +help: the following traits which provide `method2` are implemented but not in scope; perhaps you want to import one of them + | +LL + use ambiguous_trait_reexport::m2::Trait; + | +LL + use crate::m2::Trait; + | + +error: aborting due to 7 previous errors; 7 warnings emitted + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/imports/auxiliary/ambiguous-trait-reexport.rs b/tests/ui/imports/auxiliary/ambiguous-trait-reexport.rs new file mode 100644 index 000000000000..77fb0c7d3934 --- /dev/null +++ b/tests/ui/imports/auxiliary/ambiguous-trait-reexport.rs @@ -0,0 +1,23 @@ +pub mod m1 { + pub trait Trait { + fn method1(&self) {} + } + impl Trait for u8 {} +} +pub mod m2 { + pub trait Trait { + fn method2(&self) {} + } + impl Trait for u8 {} +} +pub mod m1_reexport { + pub use crate::m1::Trait; +} +pub mod m2_reexport { + pub use crate::m2::Trait; +} + +pub mod ambig_reexport { + pub use crate::m1::*; + pub use crate::m2::*; +} diff --git a/tests/ui/imports/no-ambiguous-trait-lint-on-redundant-import.rs b/tests/ui/imports/no-ambiguous-trait-lint-on-redundant-import.rs new file mode 100644 index 000000000000..ee08fd8ea800 --- /dev/null +++ b/tests/ui/imports/no-ambiguous-trait-lint-on-redundant-import.rs @@ -0,0 +1,27 @@ +//@ check-pass +// The AMBIGUOUS_GLOB_IMPORTED_TRAITS lint is reported on uses of traits that are +// ambiguously glob imported. This test checks that we don't report this lint +// when the same trait is glob imported multiple times. + +mod t { + pub trait Trait { + fn method(&self) {} + } + + impl Trait for i8 {} +} + +mod m1 { + pub use t::Trait; +} + +mod m2 { + pub use t::Trait; +} + +use m1::*; +use m2::*; + +fn main() { + 0i8.method(); +} From c14d78f4ae755603acab21253817ca883d0b29d5 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 16 Jan 2026 16:19:35 +0100 Subject: [PATCH 49/69] Make popover menus content scrollable on mobile devices --- src/librustdoc/html/static/css/rustdoc.css | 25 ++++++++++++++++------ src/librustdoc/html/static/js/main.js | 13 +++++++---- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index ad7ef9b453ce..486ca9b22539 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -20,6 +20,7 @@ --src-sidebar-width: 300px; --desktop-sidebar-z-index: 100; --sidebar-elems-left-padding: 24px; + --popover-top-margin: 7px; /* clipboard */ --clipboard-image: url('data:image/svg+xml,\ @@ -1435,7 +1436,7 @@ so that we can apply CSS-filters to change the arrow color in themes */ top: 100%; right: 0; z-index: calc(var(--desktop-sidebar-z-index) + 1); - margin-top: 7px; + margin-top: var(--popover-top-margin); border-radius: 3px; border: 1px solid var(--border-color); background-color: var(--main-background-color); @@ -2659,6 +2660,7 @@ in src-script.js and main.js @media (max-width: 700px) { :root { --impl-items-indent: 0.7em; + --topbar-height: 45px; } /* When linking to an item with an `id` (for instance, by clicking a link in the sidebar, @@ -2666,7 +2668,7 @@ in src-script.js and main.js by the topbar. Anything with an `id` gets scroll-margin-top equal to rustdoc-topbar's size. */ *[id] { - scroll-margin-top: 45px; + scroll-margin-top: var(--topbar-height); } /* We don't display this button on mobile devices. */ @@ -2698,14 +2700,23 @@ in src-script.js and main.js background: var(--main-background-color); border-radius: 0; } - #settings.popover { + #settings.popover, #help.popover { top: 32px; + /* The `+ 1px` part is for the bottom border to be visible. */ + height: calc(100vh - var(--topbar-height) + var(--popover-top-margin) - 1px); + } + #settings.popover { --popover-arrow-offset: 48px; } #help.popover { - top: 32px; --popover-arrow-offset: 12px; } + /* The `overflow-y` property is used on an internal content instead of the top one otherwise the + little arrow at the top can't be displayed. */ + #settings.popover .settings, #help.popover .content { + overflow-y: scroll; + height: 100%; + } .rustdoc { /* Sidebar should overlay main content, rather than pushing main content to the right. @@ -2728,13 +2739,13 @@ in src-script.js and main.js .sidebar { position: fixed; - top: 45px; + top: var(--topbar-height); /* Hide the sidebar offscreen while not in use. Doing this instead of display: none means the sidebar stays visible for screen readers, which is useful for navigation. */ left: -1000px; z-index: 11; /* Reduce height slightly to account for mobile topbar. */ - height: calc(100vh - 45px); + height: calc(100vh - var(--topbar-height)); /* resize indicator: hide this when on touch or mobile */ border-right: none; width: 100%; @@ -2799,7 +2810,7 @@ in src-script.js and main.js flex-direction: row; position: sticky; z-index: 10; - height: 45px; + height: var(--topbar-height); width: 100%; left: 0; top: 0; diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index f438fe173810..42cd321849cf 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -1692,7 +1692,7 @@ function preLoadCss(cssUrl) { const container = document.createElement("div"); if (!isHelpPage) { - container.className = "popover content"; + container.className = "popover"; } container.id = "help"; @@ -1701,9 +1701,14 @@ function preLoadCss(cssUrl) { side_by_side.appendChild(div_shortcuts); side_by_side.appendChild(div_infos); - container.appendChild(book_info); - container.appendChild(side_by_side); - container.appendChild(rustdoc_version); + const content = document.createElement("div"); + content.className = "content"; + + content.appendChild(book_info); + content.appendChild(side_by_side); + content.appendChild(rustdoc_version); + + container.appendChild(content); if (isHelpPage) { const help_section = document.createElement("section"); From 76ea822db244c38979df47650be185e679b30222 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 16 Jan 2026 16:19:58 +0100 Subject: [PATCH 50/69] Add rustdoc GUI regression test for #151209 --- .../mobile-topbar-menu-popovers.goml | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 tests/rustdoc-gui/mobile-topbar-menu-popovers.goml diff --git a/tests/rustdoc-gui/mobile-topbar-menu-popovers.goml b/tests/rustdoc-gui/mobile-topbar-menu-popovers.goml new file mode 100644 index 000000000000..1f4adc70101f --- /dev/null +++ b/tests/rustdoc-gui/mobile-topbar-menu-popovers.goml @@ -0,0 +1,33 @@ +// Ensure that topbar popover menus content can be scrolled on mobile. +go-to: "file://" + |DOC_PATH| + "/lib2/index.html" +store-value: (window_height, 500) +set-window-size: (400, |window_height|) + +include: "utils.goml" + +// We open the settings menu +call-function: ("open-settings-menu", {}) +// We ensure it's not scrolled down yet. +assert-property: ("#settings .settings", {"scrollTop": 0}) +// We ensure its height is smaller than the window's, but its content's height is bigger. +store-property: ("#settings .settings", {"offsetHeight": menu_height, "scrollHeight": scroll_height}) +assert: |menu_height| < |window_height| && |scroll_height| > |window_height| + +// We scroll to the last element of the menu. +scroll-to: "#settings .setting-line:last-of-type input" +// The item should be visible now, and so the Y scroll value should have changed. +assert-property: ("#settings .settings", {"scrollTop": 295}) + +// Now we open the help menu. +click: ".help-menu a" +wait-for: "#help" +// We ensure it's not scrolled down yet. +assert-property: ("#help .content", {"scrollTop": 0}) +// We ensure its height is smaller than the window's, but its content's height is bigger. +store-property: ("#help .content", {"offsetHeight": menu_height, "scrollHeight": scroll_height}) +assert: |menu_height| < |window_height| && |scroll_height| > |window_height| + +// We scroll to the last element of the menu. +scroll-to: "#help .infos > :last-child" +// The item should be visible now, and so the Y scroll value should have changed. +assert-property: ("#help .content", {"scrollTop": 339}) From bf3ac98d6930ba4e258cf33240c2fe7c99d19eae Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 6 Jan 2026 11:51:33 +0100 Subject: [PATCH 51/69] Update amdgpu data layout This changed in: https://github.com/llvm/llvm-project/commit/853760bca6aa7a960b154cef8c61f87271870b8a --- compiler/rustc_codegen_llvm/src/context.rs | 5 +++++ compiler/rustc_target/src/spec/targets/amdgcn_amd_amdhsa.rs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 4b2544b7efdf..4328b15c73f3 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -215,6 +215,11 @@ pub(crate) unsafe fn create_module<'ll>( // LLVM 22 updated the ABI alignment for double on AIX: https://github.com/llvm/llvm-project/pull/144673 target_data_layout = target_data_layout.replace("-f64:32:64", ""); } + if sess.target.arch == Arch::AmdGpu { + // LLVM 22 specified ELF mangling in the amdgpu data layout: + // https://github.com/llvm/llvm-project/pull/163011 + target_data_layout = target_data_layout.replace("-m:e", ""); + } } // Ensure the data-layout values hardcoded remain the defaults. diff --git a/compiler/rustc_target/src/spec/targets/amdgcn_amd_amdhsa.rs b/compiler/rustc_target/src/spec/targets/amdgcn_amd_amdhsa.rs index 828d853ac65e..d6a2cfc2aab5 100644 --- a/compiler/rustc_target/src/spec/targets/amdgcn_amd_amdhsa.rs +++ b/compiler/rustc_target/src/spec/targets/amdgcn_amd_amdhsa.rs @@ -5,7 +5,7 @@ use crate::spec::{ pub(crate) fn target() -> Target { Target { arch: Arch::AmdGpu, - data_layout: "e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32-p7:160:256:256:32-p8:128:128:128:48-p9:192:256:256:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5-G1-ni:7:8:9".into(), + data_layout: "e-m:e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32-p7:160:256:256:32-p8:128:128:128:48-p9:192:256:256:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5-G1-ni:7:8:9".into(), llvm_target: "amdgcn-amd-amdhsa".into(), metadata: TargetMetadata { description: Some("AMD GPU".into()), From 0be66603ac37474fe884ad1b384510d1ddc37417 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 6 Jan 2026 12:14:36 +0100 Subject: [PATCH 52/69] Avoid passing addrspacecast to lifetime intrinsics Since LLVM 22 the alloca must be passed directly. Do this by stripping the addrspacecast if it exists. --- compiler/rustc_codegen_llvm/src/builder.rs | 3 +++ compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 1 + compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 4 ++++ tests/codegen-llvm/amdgpu-addrspacecast.rs | 11 ++++++++++- 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 557ae7b0333e..9379faf1156f 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1788,6 +1788,9 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { } if crate::llvm_util::get_version() >= (22, 0, 0) { + // LLVM 22 requires the lifetime intrinsic to act directly on the alloca, + // there can't be an addrspacecast in between. + let ptr = unsafe { llvm::LLVMRustStripPointerCasts(ptr) }; self.call_intrinsic(intrinsic, &[self.val_ty(ptr)], &[ptr]); } else { self.call_intrinsic(intrinsic, &[self.val_ty(ptr)], &[self.cx.const_u64(size), ptr]); diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index c535fade9c04..a3d4e9f9d32a 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1967,6 +1967,7 @@ unsafe extern "C" { Metadata: &'a Metadata, ); pub(crate) fn LLVMRustIsNonGVFunctionPointerTy(Val: &Value) -> bool; + pub(crate) fn LLVMRustStripPointerCasts<'a>(Val: &'a Value) -> &'a Value; // Operations on scalar constants pub(crate) fn LLVMRustConstIntGetZExtValue(ConstantVal: &ConstantInt, Value: &mut u64) -> bool; diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 336d58974036..599f79d01198 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1760,6 +1760,10 @@ extern "C" bool LLVMRustIsNonGVFunctionPointerTy(LLVMValueRef V) { return false; } +extern "C" LLVMValueRef LLVMRustStripPointerCasts(LLVMValueRef V) { + return wrap(unwrap(V)->stripPointerCasts()); +} + extern "C" bool LLVMRustLLVMHasZlibCompression() { return llvm::compression::zlib::isAvailable(); } diff --git a/tests/codegen-llvm/amdgpu-addrspacecast.rs b/tests/codegen-llvm/amdgpu-addrspacecast.rs index 16a0c276ac0e..144565f7e28c 100644 --- a/tests/codegen-llvm/amdgpu-addrspacecast.rs +++ b/tests/codegen-llvm/amdgpu-addrspacecast.rs @@ -1,16 +1,25 @@ // Check that pointers are casted to addrspace(0) before they are used -//@ compile-flags: --crate-type=rlib --target=amdgcn-amd-amdhsa -Ctarget-cpu=gfx900 +//@ compile-flags: --crate-type=rlib --target=amdgcn-amd-amdhsa -Ctarget-cpu=gfx900 -O //@ needs-llvm-components: amdgpu //@ add-minicore +//@ revisions: LLVM21 LLVM22 +//@ [LLVM21] max-llvm-major-version: 21 +//@ [LLVM22] min-llvm-version: 22 #![feature(no_core)] #![no_core] extern crate minicore; +// Make sure that on LLVM 22, the alloca is passed directly to the lifetime intrinsics, +// not the addrspacecast. + // CHECK-LABEL: @ref_of_local // CHECK: [[alloca:%[0-9]]] = alloca // CHECK: %i = addrspacecast ptr addrspace(5) [[alloca]] to ptr +// LLVM22: call void @llvm.lifetime.start.p5(ptr addrspace(5) [[alloca]]) +// CHECK: call void %f(ptr{{.*}}%i) +// LLVM22: call void @llvm.lifetime.end.p5(ptr addrspace(5) [[alloca]]) #[no_mangle] pub fn ref_of_local(f: fn(&i32)) { let i = 0; From 08da3685eddca11092fecab35c053843e02df0aa Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Fri, 9 Jan 2026 14:56:11 +0100 Subject: [PATCH 53/69] Don't use evex512 with LLVM 22 As Intel has walked back on the existence of AVX 10.1-256, LLVM no longer uses evex512 and avx-10.n-512 are now avx-10.n instead, so we can skip all the special handling on LLVM 22. --- compiler/rustc_codegen_llvm/src/llvm_util.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 63f820dc2918..fbb582fe8601 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -287,12 +287,12 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option Some(LLVMFeature::new("cx16")), "lahfsahf" => Some(LLVMFeature::new("sahf")), // Enable the evex512 target feature if an avx512 target feature is enabled. - s if s.starts_with("avx512") => Some(LLVMFeature::with_dependencies( + s if s.starts_with("avx512") && major < 22 => Some(LLVMFeature::with_dependencies( s, smallvec![TargetFeatureFoldStrength::EnableOnly("evex512")], )), - "avx10.1" => Some(LLVMFeature::new("avx10.1-512")), - "avx10.2" => Some(LLVMFeature::new("avx10.2-512")), + "avx10.1" if major < 22 => Some(LLVMFeature::new("avx10.1-512")), + "avx10.2" if major < 22 => Some(LLVMFeature::new("avx10.2-512")), "apxf" => Some(LLVMFeature::with_dependencies( "egpr", smallvec![ From d5bacddca9e47836c5c1678ba8669d335a5a8168 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 20 Jan 2026 15:58:34 +0100 Subject: [PATCH 54/69] Update GUI test to new number of settings and add note about it --- tests/rustdoc-gui/mobile-topbar-menu-popovers.goml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/rustdoc-gui/mobile-topbar-menu-popovers.goml b/tests/rustdoc-gui/mobile-topbar-menu-popovers.goml index 1f4adc70101f..29a2096bba89 100644 --- a/tests/rustdoc-gui/mobile-topbar-menu-popovers.goml +++ b/tests/rustdoc-gui/mobile-topbar-menu-popovers.goml @@ -16,7 +16,8 @@ assert: |menu_height| < |window_height| && |scroll_height| > |window_height| // We scroll to the last element of the menu. scroll-to: "#settings .setting-line:last-of-type input" // The item should be visible now, and so the Y scroll value should have changed. -assert-property: ("#settings .settings", {"scrollTop": 295}) +// Note: The `scrollTop` value will change if settings are added or removed. +assert-property: ("#settings .settings", {"scrollTop": 335}) // Now we open the help menu. click: ".help-menu a" From 328893e1a6d71c8017d6229960da13700076ca38 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Mon, 19 Jan 2026 21:35:14 +0900 Subject: [PATCH 55/69] Fix an ICE on transmute goals with placeholders in `param_env` --- .../src/solve/trait_goals.rs | 2 +- .../transmute-with-type-params.rs | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 tests/ui/transmutability/transmute-with-type-params.rs diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 651f073efb82..003841f3af3f 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -659,7 +659,7 @@ where } // `rustc_transmute` does not have support for type or const params - if goal.has_non_region_placeholders() { + if goal.predicate.has_non_region_placeholders() { return Err(NoSolution); } diff --git a/tests/ui/transmutability/transmute-with-type-params.rs b/tests/ui/transmutability/transmute-with-type-params.rs new file mode 100644 index 000000000000..f1630449a41b --- /dev/null +++ b/tests/ui/transmutability/transmute-with-type-params.rs @@ -0,0 +1,21 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver +//@ check-pass + +// A regression test for https://github.com/rust-lang/rust/issues/151300 + +#![feature(transmutability)] +use std::mem::TransmuteFrom; + +pub fn is_maybe_transmutable() +where + Dst: TransmuteFrom, +{ +} + +fn function_with_generic() { + is_maybe_transmutable::<(), ()>(); +} + +fn main() {} From 603955f542fda7250dc63c35c93dd607fdb2291e Mon Sep 17 00:00:00 2001 From: Shun Sakai Date: Wed, 21 Jan 2026 00:51:19 +0900 Subject: [PATCH 56/69] chore: Remove redundant conversion --- library/std/src/sys/process/windows.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/sys/process/windows.rs b/library/std/src/sys/process/windows.rs index dba647c502d1..b40833ad212c 100644 --- a/library/std/src/sys/process/windows.rs +++ b/library/std/src/sys/process/windows.rs @@ -818,7 +818,7 @@ impl From for ExitCode { impl From for ExitCode { fn from(code: u32) -> Self { - ExitCode(u32::from(code)) + ExitCode(code) } } From 711ea738759f84108799907a6db4b0b178cf3754 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Tue, 20 Jan 2026 18:00:56 +0100 Subject: [PATCH 57/69] Avoid pulling in unicode when calling io::Error::kind --- library/std/src/sys/io/error/windows.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/sys/io/error/windows.rs b/library/std/src/sys/io/error/windows.rs index e02197ac6775..d7607082a30a 100644 --- a/library/std/src/sys/io/error/windows.rs +++ b/library/std/src/sys/io/error/windows.rs @@ -126,7 +126,7 @@ pub fn error_string(mut errnum: i32) -> String { match String::from_utf16(&buf[..res]) { Ok(mut msg) => { // Trim trailing CRLF inserted by FormatMessageW - let len = msg.trim_end().len(); + let len = msg.trim_ascii_end().len(); msg.truncate(len); msg } From dd9241d150d6645ea61c65563ff38e685a2f906b Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 27 Dec 2025 17:17:54 +0100 Subject: [PATCH 58/69] `c_variadic`: use `Clone` instead of LLVM `va_copy` --- .../src/intrinsics/mod.rs | 2 +- .../rustc_codegen_gcc/src/intrinsic/mod.rs | 3 - compiler/rustc_codegen_llvm/src/intrinsic.rs | 8 -- .../rustc_hir_analysis/src/check/intrinsic.rs | 12 +-- library/core/src/ffi/va_list.rs | 83 +++++++++++++------ library/core/src/intrinsics/mod.rs | 27 +++--- tests/codegen-llvm/cffi/c-variadic-copy.rs | 16 ---- tests/codegen-llvm/cffi/c-variadic-opt.rs | 11 --- tests/ui/c-variadic/copy.rs | 24 ++++++ 9 files changed, 101 insertions(+), 85 deletions(-) delete mode 100644 tests/codegen-llvm/cffi/c-variadic-copy.rs create mode 100644 tests/ui/c-variadic/copy.rs diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index a78c6e0a4e7a..ab9a11305baa 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -1506,7 +1506,7 @@ fn codegen_regular_intrinsic_call<'tcx>( } // FIXME implement variadics in cranelift - sym::va_copy | sym::va_arg | sym::va_end => { + sym::va_arg | sym::va_end => { fx.tcx.dcx().span_fatal( source_info.span, "Defining variadic functions is not yet supported by Cranelift", diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 36ea76cbc51a..553e4d3d2fe0 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -391,9 +391,6 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc sym::breakpoint => { unimplemented!(); } - sym::va_copy => { - unimplemented!(); - } sym::va_arg => { unimplemented!(); } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index c6aae89f1e51..83a256e33e12 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -269,14 +269,6 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { return Ok(()); } sym::breakpoint => self.call_intrinsic("llvm.debugtrap", &[], &[]), - sym::va_copy => { - let dest = args[0].immediate(); - self.call_intrinsic( - "llvm.va_copy", - &[self.val_ty(dest)], - &[dest, args[1].immediate()], - ) - } sym::va_arg => { match result.layout.backend_repr { BackendRepr::Scalar(scalar) => { diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index d3d167f6e254..98f9548058f4 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -216,6 +216,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::type_name | sym::type_of | sym::ub_checks + | sym::va_copy | sym::variant_count | sym::vtable_for | sym::wrapping_add @@ -629,14 +630,13 @@ pub(crate) fn check_intrinsic_type( ) } - sym::va_start | sym::va_end => { - (0, 0, vec![mk_va_list_ty(hir::Mutability::Mut).0], tcx.types.unit) - } - sym::va_copy => { let (va_list_ref_ty, va_list_ty) = mk_va_list_ty(hir::Mutability::Not); - let va_list_ptr_ty = Ty::new_mut_ptr(tcx, va_list_ty); - (0, 0, vec![va_list_ptr_ty, va_list_ref_ty], tcx.types.unit) + (0, 0, vec![va_list_ref_ty], va_list_ty) + } + + sym::va_start | sym::va_end => { + (0, 0, vec![mk_va_list_ty(hir::Mutability::Mut).0], tcx.types.unit) } sym::va_arg => (1, 0, vec![mk_va_list_ty(hir::Mutability::Mut).0], param(0)), diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs index d5166baf0c7c..390062190707 100644 --- a/library/core/src/ffi/va_list.rs +++ b/library/core/src/ffi/va_list.rs @@ -34,6 +34,10 @@ use crate::marker::PhantomCovariantLifetime; // // The Clang `BuiltinVaListKind` enumerates the `va_list` variations that Clang supports, // and we mirror these here. +// +// For all current LLVM targets, `va_copy` lowers to `memcpy`. Hence the inner structs below all +// derive `Copy`. However, in the future we might want to support a target where `va_copy` +// allocates, or otherwise violates the requirements of `Copy`. Therefore `VaList` is only `Clone`. crate::cfg_select! { all( target_arch = "aarch64", @@ -45,10 +49,12 @@ crate::cfg_select! { /// /// See the [AArch64 Procedure Call Standard] for more details. /// + /// `va_copy` is `memcpy`: + /// /// [AArch64 Procedure Call Standard]: /// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf #[repr(C)] - #[derive(Debug)] + #[derive(Debug, Clone, Copy)] struct VaListInner { stack: *const c_void, gr_top: *const c_void, @@ -62,11 +68,13 @@ crate::cfg_select! { /// /// See the [LLVM source] and [GCC header] for more details. /// + /// `va_copy` is `memcpy`: + /// /// [LLVM source]: /// https://github.com/llvm/llvm-project/blob/af9a4263a1a209953a1d339ef781a954e31268ff/llvm/lib/Target/PowerPC/PPCISelLowering.cpp#L4089-L4111 /// [GCC header]: https://web.mit.edu/darwin/src/modules/gcc/gcc/ginclude/va-ppc.h #[repr(C)] - #[derive(Debug)] + #[derive(Debug, Clone, Copy)] #[rustc_pass_indirectly_in_non_rustic_abis] struct VaListInner { gpr: u8, @@ -81,10 +89,12 @@ crate::cfg_select! { /// /// See the [S/390x ELF Application Binary Interface Supplement] for more details. /// + /// `va_copy` is `memcpy`: + /// /// [S/390x ELF Application Binary Interface Supplement]: /// https://docs.google.com/gview?embedded=true&url=https://github.com/IBM/s390x-abi/releases/download/v1.7/lzsabi_s390x.pdf #[repr(C)] - #[derive(Debug)] + #[derive(Debug, Clone, Copy)] #[rustc_pass_indirectly_in_non_rustic_abis] struct VaListInner { gpr: i64, @@ -98,10 +108,13 @@ crate::cfg_select! { /// /// See the [System V AMD64 ABI] for more details. /// + /// `va_copy` is `memcpy`: + /// (github won't render that file, look for `SDValue LowerVACOPY`) + /// /// [System V AMD64 ABI]: /// https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf #[repr(C)] - #[derive(Debug)] + #[derive(Debug, Clone, Copy)] #[rustc_pass_indirectly_in_non_rustic_abis] struct VaListInner { gp_offset: i32, @@ -115,10 +128,12 @@ crate::cfg_select! { /// /// See the [LLVM source] for more details. /// + /// `va_copy` is `memcpy`: + /// /// [LLVM source]: /// https://github.com/llvm/llvm-project/blob/af9a4263a1a209953a1d339ef781a954e31268ff/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp#L1211-L1215 #[repr(C)] - #[derive(Debug)] + #[derive(Debug, Clone, Copy)] #[rustc_pass_indirectly_in_non_rustic_abis] struct VaListInner { stk: *const i32, @@ -132,10 +147,12 @@ crate::cfg_select! { /// /// See the [LLVM source] for more details. On bare metal Hexagon uses an opaque pointer. /// + /// `va_copy` is `memcpy`: + /// /// [LLVM source]: /// https://github.com/llvm/llvm-project/blob/0cdc1b6dd4a870fc41d4b15ad97e0001882aba58/clang/lib/CodeGen/Targets/Hexagon.cpp#L407-L417 #[repr(C)] - #[derive(Debug)] + #[derive(Debug, Clone, Copy)] #[rustc_pass_indirectly_in_non_rustic_abis] struct VaListInner { __current_saved_reg_area_pointer: *const c_void, @@ -156,8 +173,10 @@ crate::cfg_select! { // That pointer is probably just the next variadic argument on the caller's stack. _ => { /// Basic implementation of a `va_list`. + /// + /// `va_copy` is `memcpy`: #[repr(transparent)] - #[derive(Debug)] + #[derive(Debug, Clone, Copy)] struct VaListInner { ptr: *const c_void, } @@ -179,6 +198,36 @@ impl fmt::Debug for VaList<'_> { } } +impl VaList<'_> { + // Helper used in the implementation of the `va_copy` intrinsic. + pub(crate) fn duplicate(&self) -> Self { + Self { inner: self.inner.clone(), _marker: self._marker } + } +} + +impl Clone for VaList<'_> { + #[inline] + fn clone(&self) -> Self { + // We only implement Clone and not Copy because some future target might not be able to + // implement Copy (e.g. because it allocates). For the same reason we use an intrinsic + // to do the copying: the fact that on all current targets, this is just `memcpy`, is an implementation + // detail. The intrinsic lets Miri catch UB from code incorrectly relying on that implementation detail. + va_copy(self) + } +} + +impl Drop for VaList<'_> { + fn drop(&mut self) { + // For all current LLVM targets `va_end` is a no-op. + // + // We implement `Drop` here because some future target might need to actually run + // destructors (e.g. to deallocate). + // + // Rust requires that not calling `va_end` on a `va_list` does not cause undefined + // behaviour: it is safe to leak values. + } +} + mod sealed { pub trait Sealed {} @@ -253,26 +302,6 @@ impl<'f> VaList<'f> { } } -impl<'f> Clone for VaList<'f> { - #[inline] - fn clone(&self) -> Self { - let mut dest = crate::mem::MaybeUninit::uninit(); - // SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal. - unsafe { - va_copy(dest.as_mut_ptr(), self); - dest.assume_init() - } - } -} - -impl<'f> Drop for VaList<'f> { - fn drop(&mut self) { - // Rust requires that not calling `va_end` on a `va_list` does not cause undefined behaviour - // (as it is safe to leak values). As `va_end` is a no-op on all current LLVM targets, this - // destructor is empty. - } -} - // Checks (via an assert in `compiler/rustc_ty_utils/src/abi.rs`) that the C ABI for the current // target correctly implements `rustc_pass_indirectly_in_non_rustic_abis`. const _: () = { diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 8d112b4c5d18..6d231faf38f7 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -3451,19 +3451,6 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize ) } -/// Copies the current location of arglist `src` to the arglist `dst`. -/// -/// # Safety -/// -/// You must check the following invariants before you call this function: -/// -/// - `dest` must be non-null and point to valid, writable memory. -/// - `dest` must not alias `src`. -/// -#[rustc_intrinsic] -#[rustc_nounwind] -pub unsafe fn va_copy<'f>(dest: *mut VaList<'f>, src: &VaList<'f>); - /// Loads an argument of type `T` from the `va_list` `ap` and increment the /// argument `ap` points to. /// @@ -3482,6 +3469,20 @@ pub unsafe fn va_copy<'f>(dest: *mut VaList<'f>, src: &VaList<'f>); #[rustc_nounwind] pub unsafe fn va_arg(ap: &mut VaList<'_>) -> T; +/// Duplicates a variable argument list. The returned list is initially at the same position as +/// the one in `src`, but can be advanced independently. +/// +/// Codegen backends should not have custom behavior for this intrinsic, they should always use +/// this fallback implementation. This intrinsic *does not* map to the LLVM `va_copy` intrinsic. +/// +/// This intrinsic exists only as a hook for Miri and constant evaluation, and is used to detect UB +/// when a variable argument list is used incorrectly. +#[rustc_intrinsic] +#[rustc_nounwind] +pub fn va_copy<'f>(src: &VaList<'f>) -> VaList<'f> { + src.duplicate() +} + /// Destroy the arglist `ap` after initialization with `va_start` or `va_copy`. /// /// # Safety diff --git a/tests/codegen-llvm/cffi/c-variadic-copy.rs b/tests/codegen-llvm/cffi/c-variadic-copy.rs deleted file mode 100644 index 0cbdcb4bbb85..000000000000 --- a/tests/codegen-llvm/cffi/c-variadic-copy.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Tests that `VaList::clone` gets inlined into a call to `llvm.va_copy` - -#![crate_type = "lib"] -#![feature(c_variadic)] -#![no_std] -use core::ffi::VaList; - -extern "C" { - fn foreign_c_variadic_1(_: VaList, ...); -} - -pub unsafe extern "C" fn clone_variadic(ap: VaList) { - let mut ap2 = ap.clone(); - // CHECK: call void @llvm.va_copy - foreign_c_variadic_1(ap2, 42i32); -} diff --git a/tests/codegen-llvm/cffi/c-variadic-opt.rs b/tests/codegen-llvm/cffi/c-variadic-opt.rs index 3cc0c3e9f9bd..c779b25450fd 100644 --- a/tests/codegen-llvm/cffi/c-variadic-opt.rs +++ b/tests/codegen-llvm/cffi/c-variadic-opt.rs @@ -17,14 +17,3 @@ pub unsafe extern "C" fn c_variadic_no_use(fmt: *const i8, mut ap: ...) -> i32 { vprintf(fmt, ap) // CHECK: call void @llvm.va_end } - -// Check that `VaList::clone` gets inlined into a direct call to `llvm.va_copy` -#[no_mangle] -pub unsafe extern "C" fn c_variadic_clone(fmt: *const i8, mut ap: ...) -> i32 { - // CHECK: call void @llvm.va_start - let mut ap2 = ap.clone(); - // CHECK: call void @llvm.va_copy - let res = vprintf(fmt, ap2); - res - // CHECK: call void @llvm.va_end -} diff --git a/tests/ui/c-variadic/copy.rs b/tests/ui/c-variadic/copy.rs new file mode 100644 index 000000000000..e9171738aa15 --- /dev/null +++ b/tests/ui/c-variadic/copy.rs @@ -0,0 +1,24 @@ +//@ run-pass +//@ ignore-backends: gcc +#![feature(c_variadic)] + +// Test the behavior of `VaList::clone`. In C a `va_list` is duplicated using `va_copy`, but the +// rust api just uses `Clone`. This should create a completely independent cursor into the +// variable argument list: advancing the original has no effect on the copy and vice versa. + +fn main() { + unsafe { variadic(1, 2, 3) } +} + +unsafe extern "C" fn variadic(mut ap1: ...) { + let mut ap2 = ap1.clone(); + + assert_eq!(ap1.arg::(), 1); + assert_eq!(ap2.arg::(), 1); + + assert_eq!(ap2.arg::(), 2); + assert_eq!(ap1.arg::(), 2); + + drop(ap1); + assert_eq!(ap2.arg::(), 3); +} From 922057cfa663174a1cbf22e05aa6b0475cbaa79f Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Fri, 9 Jan 2026 17:34:55 +0100 Subject: [PATCH 59/69] c-variadic: make `VaList::drop` call the rust `va_end` --- library/core/src/ffi/va_list.rs | 13 ++++--------- library/core/src/intrinsics/mod.rs | 13 +++++++++++-- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs index 390062190707..d0f155316a10 100644 --- a/library/core/src/ffi/va_list.rs +++ b/library/core/src/ffi/va_list.rs @@ -5,7 +5,7 @@ #[cfg(not(target_arch = "xtensa"))] use crate::ffi::c_void; use crate::fmt; -use crate::intrinsics::{va_arg, va_copy}; +use crate::intrinsics::{va_arg, va_copy, va_end}; use crate::marker::PhantomCovariantLifetime; // There are currently three flavors of how a C `va_list` is implemented for @@ -216,15 +216,10 @@ impl Clone for VaList<'_> { } } -impl Drop for VaList<'_> { +impl<'f> Drop for VaList<'f> { fn drop(&mut self) { - // For all current LLVM targets `va_end` is a no-op. - // - // We implement `Drop` here because some future target might need to actually run - // destructors (e.g. to deallocate). - // - // Rust requires that not calling `va_end` on a `va_list` does not cause undefined - // behaviour: it is safe to leak values. + // SAFETY: this variable argument list is being dropped, so won't be read from again. + unsafe { va_end(self) } } } diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 6d231faf38f7..b134c0ba4c8b 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -3483,7 +3483,14 @@ pub fn va_copy<'f>(src: &VaList<'f>) -> VaList<'f> { src.duplicate() } -/// Destroy the arglist `ap` after initialization with `va_start` or `va_copy`. +/// Destroy the variable argument list `ap` after initialization with `va_start` (part of the +/// desugaring of `...`) or `va_copy`. +/// +/// Code generation backends should not provide a custom implementation for this intrinsic. This +/// intrinsic *does not* map to the LLVM `va_end` intrinsic. +/// +/// This function is a no-op on all current targets, but used as a hook for const evaluation to +/// detect UB when a variable argument list is used incorrectly. /// /// # Safety /// @@ -3491,4 +3498,6 @@ pub fn va_copy<'f>(src: &VaList<'f>) -> VaList<'f> { /// #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn va_end(ap: &mut VaList<'_>); +pub unsafe fn va_end(ap: &mut VaList<'_>) { + /* deliberately does nothing */ +} From 9304071904374a1e29bac4ae65116347381d1505 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Tue, 20 Jan 2026 13:05:41 -0500 Subject: [PATCH 60/69] missing colon after the compile-flags directive --- tests/ui/instrument-coverage/coverage-options.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/instrument-coverage/coverage-options.rs b/tests/ui/instrument-coverage/coverage-options.rs index ead2e3221d8c..091ccdf38a08 100644 --- a/tests/ui/instrument-coverage/coverage-options.rs +++ b/tests/ui/instrument-coverage/coverage-options.rs @@ -1,5 +1,5 @@ //@ revisions: block branch condition bad -//@ compile-flags -Cinstrument-coverage -Zno-profiler-runtime +//@ compile-flags: -Cinstrument-coverage -Zno-profiler-runtime //@ [block] check-pass //@ [block] compile-flags: -Zcoverage-options=block From c3205f98dda8ab49d4774f8af61211d75ae7b2d4 Mon Sep 17 00:00:00 2001 From: Zachary S Date: Fri, 7 Nov 2025 00:22:14 -0600 Subject: [PATCH 61/69] Replace #[rustc_do_not_implement_via_object] with #[rustc_dyn_incompatible_trait], which makes the marked trait dyn-incompatible. Removes the attribute from `MetaSized` and `PointeeSized`, with a special case in the trait solvers for `MetaSized`. `dyn MetaSized` is a perfectly cromulent type, and seems to only have had #[rustc_do_not_implement_via_object] so the builtin object candidate does not overlap with the builtin MetaSized impl that all `dyn` types get. Resolves this with a special case by checking `is_sizedness_trait` where the trait solvers previously checked `implement_via_object`. `dyn PointeeSized` alone is rejected for other reasons (since `dyn PointeeSized` is considered to have no principal trait because `PointeeSized` is removed at an earlier stage of the compiler), but `(dyn PointeeSized + Send)` is valid and equivalent to `dyn Send`. Add suggestions from code review Update compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs and tests Co-authored-by: lcnr --- .../src/attributes/traits.rs | 8 +- compiler/rustc_attr_parsing/src/context.rs | 6 +- compiler/rustc_feature/src/builtin_attrs.rs | 6 +- .../rustc_hir/src/attrs/data_structures.rs | 6 +- .../rustc_hir/src/attrs/encode_cross_crate.rs | 2 +- .../rustc_hir_analysis/src/coherence/mod.rs | 4 +- compiler/rustc_hir_analysis/src/collect.rs | 5 +- compiler/rustc_middle/src/traits/mod.rs | 8 ++ compiler/rustc_middle/src/ty/context.rs | 4 - compiler/rustc_middle/src/ty/trait_def.rs | 8 +- .../src/solve/assembly/mod.rs | 4 +- compiler/rustc_passes/src/check_attr.rs | 2 +- compiler/rustc_public/src/ty.rs | 2 +- .../src/unstable/convert/stable/ty.rs | 2 +- compiler/rustc_span/src/symbol.rs | 2 +- .../src/traits/dyn_compatibility.rs | 3 + .../src/traits/project.rs | 4 - .../src/traits/select/candidate_assembly.rs | 4 +- compiler/rustc_type_ir/src/interner.rs | 2 - library/core/src/marker.rs | 16 ++-- library/core/src/mem/transmutability.rs | 2 +- library/core/src/ptr/metadata.rs | 2 +- tests/ui/dyn-compatibility/metasized.rs | 25 ++++++ tests/ui/dyn-compatibility/pointeesized.rs | 17 ++++ tests/ui/dyn-compatibility/sized-3.rs | 29 ++++++ tests/ui/dyn-compatibility/sized-3.stderr | 88 +++++++++++++++++++ .../deny-builtin-object-impl.current.stderr | 74 +++++++++++----- .../deny-builtin-object-impl.next.stderr | 74 +++++++++++----- tests/ui/traits/deny-builtin-object-impl.rs | 13 +-- .../ui/traits/ice-with-dyn-pointee-errors.rs | 4 +- .../traits/ice-with-dyn-pointee-errors.stderr | 46 +++++++--- tests/ui/traits/ice-with-dyn-pointee.rs | 11 --- tests/ui/unsized/issue-71659.current.stderr | 28 +++--- tests/ui/unsized/issue-71659.next.stderr | 28 +++--- tests/ui/unsized/issue-71659.rs | 2 +- 35 files changed, 387 insertions(+), 154 deletions(-) create mode 100644 tests/ui/dyn-compatibility/metasized.rs create mode 100644 tests/ui/dyn-compatibility/pointeesized.rs create mode 100644 tests/ui/dyn-compatibility/sized-3.rs create mode 100644 tests/ui/dyn-compatibility/sized-3.stderr delete mode 100644 tests/ui/traits/ice-with-dyn-pointee.rs diff --git a/compiler/rustc_attr_parsing/src/attributes/traits.rs b/compiler/rustc_attr_parsing/src/attributes/traits.rs index ee5895a6efd0..c0db5b4d442a 100644 --- a/compiler/rustc_attr_parsing/src/attributes/traits.rs +++ b/compiler/rustc_attr_parsing/src/attributes/traits.rs @@ -94,12 +94,12 @@ impl NoArgsAttributeParser for DenyExplicitImplParser { const CREATE: fn(Span) -> AttributeKind = AttributeKind::DenyExplicitImpl; } -pub(crate) struct DoNotImplementViaObjectParser; -impl NoArgsAttributeParser for DoNotImplementViaObjectParser { - const PATH: &[Symbol] = &[sym::rustc_do_not_implement_via_object]; +pub(crate) struct DynIncompatibleTraitParser; +impl NoArgsAttributeParser for DynIncompatibleTraitParser { + const PATH: &[Symbol] = &[sym::rustc_dyn_incompatible_trait]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]); - const CREATE: fn(Span) -> AttributeKind = AttributeKind::DoNotImplementViaObject; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::DynIncompatibleTrait; } // Specialization diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 6aae2b90a504..e6d91e936e63 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -88,8 +88,8 @@ use crate::attributes::stability::{ use crate::attributes::test_attrs::{IgnoreParser, ShouldPanicParser}; use crate::attributes::traits::{ AllowIncoherentImplParser, CoinductiveParser, DenyExplicitImplParser, - DoNotImplementViaObjectParser, FundamentalParser, MarkerParser, ParenSugarParser, - PointeeParser, SkipDuringMethodDispatchParser, SpecializationTraitParser, TypeConstParser, + DynIncompatibleTraitParser, FundamentalParser, MarkerParser, ParenSugarParser, PointeeParser, + SkipDuringMethodDispatchParser, SpecializationTraitParser, TypeConstParser, UnsafeSpecializationMarkerParser, }; use crate::attributes::transparency::TransparencyParser; @@ -254,7 +254,7 @@ attribute_parsers!( Single>, Single>, Single>, - Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 97dc9dd21a65..ded8a5a4ae51 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -1315,13 +1315,13 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ "`#[rustc_deny_explicit_impl]` enforces that a trait can have no user-provided impls" ), rustc_attr!( - rustc_do_not_implement_via_object, + rustc_dyn_incompatible_trait, AttributeType::Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::No, - "`#[rustc_do_not_implement_via_object]` opts out of the automatic trait impl for trait objects \ - (`impl Trait for dyn Trait`)" + "`#[rustc_dyn_incompatible_trait]` marks a trait as dyn-incompatible, \ + even if it otherwise satisfies the requirements to be dyn-compatible." ), rustc_attr!( rustc_has_incoherent_inherent_impls, AttributeType::Normal, template!(Word), diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 8d2baae703b8..3f0546e800fa 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -728,9 +728,6 @@ pub enum AttributeKind { /// Represents [`#[deprecated]`](https://doc.rust-lang.org/stable/reference/attributes/diagnostics.html#the-deprecated-attribute). Deprecation { deprecation: Deprecation, span: Span }, - /// Represents `#[rustc_do_not_implement_via_object]`. - DoNotImplementViaObject(Span), - /// Represents `#[diagnostic::do_not_recommend]`. DoNotRecommend { attr_span: Span }, @@ -746,6 +743,9 @@ pub enum AttributeKind { /// Represents `#[rustc_dummy]`. Dummy, + /// Represents `#[rustc_dyn_incompatible_trait]`. + DynIncompatibleTrait(Span), + /// Implementation detail of `#[eii]` EiiDeclaration(EiiDecl), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index fd764ad458b1..991c7003cb09 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -43,11 +43,11 @@ impl AttributeKind { DebuggerVisualizer(..) => No, DenyExplicitImpl(..) => No, Deprecation { .. } => Yes, - DoNotImplementViaObject(..) => No, DoNotRecommend { .. } => Yes, Doc(_) => Yes, DocComment { .. } => Yes, Dummy => No, + DynIncompatibleTrait(..) => No, EiiDeclaration(_) => Yes, EiiForeignItem => No, EiiImpls(..) => No, diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs index bc3231cff9b4..58483dc44fe9 100644 --- a/compiler/rustc_hir_analysis/src/coherence/mod.rs +++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs @@ -211,9 +211,7 @@ fn check_object_overlap<'tcx>( // This is a WF error tested by `coherence-impl-trait-for-trait-dyn-compatible.rs`. } else { let mut supertrait_def_ids = elaborate::supertrait_def_ids(tcx, component_def_id); - if supertrait_def_ids - .any(|d| d == trait_def_id && tcx.trait_def(d).implement_via_object) - { + if supertrait_def_ids.any(|d| d == trait_def_id) { let span = tcx.def_span(impl_def_id); return Err(struct_span_code_err!( tcx.dcx(), diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 9c0b638c1482..dd399f9d90de 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -924,7 +924,8 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { ); let deny_explicit_impl = find_attr!(attrs, AttributeKind::DenyExplicitImpl(_)); - let implement_via_object = !find_attr!(attrs, AttributeKind::DoNotImplementViaObject(_)); + let force_dyn_incompatible = + find_attr!(attrs, AttributeKind::DynIncompatibleTrait(span) => *span); ty::TraitDef { def_id: def_id.to_def_id(), @@ -939,7 +940,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { skip_boxed_slice_during_method_dispatch, specialization_kind, must_implement_one_of, - implement_via_object, + force_dyn_incompatible, deny_explicit_impl, } } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index c064313f67b2..d71df36684f5 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -764,6 +764,9 @@ pub enum DynCompatibilityViolation { /// `Self: Sized` declared on the trait. SizedSelf(SmallVec<[Span; 1]>), + /// Trait is marked `#[rustc_dyn_incompatible_trait]`. + ExplicitlyDynIncompatible(SmallVec<[Span; 1]>), + /// Supertrait reference references `Self` an in illegal location /// (e.g., `trait Foo : Bar`). SupertraitSelf(SmallVec<[Span; 1]>), @@ -788,6 +791,9 @@ impl DynCompatibilityViolation { pub fn error_msg(&self) -> Cow<'static, str> { match self { DynCompatibilityViolation::SizedSelf(_) => "it requires `Self: Sized`".into(), + DynCompatibilityViolation::ExplicitlyDynIncompatible(_) => { + "it opted out of dyn-compatibility".into() + } DynCompatibilityViolation::SupertraitSelf(spans) => { if spans.iter().any(|sp| *sp != DUMMY_SP) { "it uses `Self` as a type parameter".into() @@ -861,6 +867,7 @@ impl DynCompatibilityViolation { pub fn solution(&self) -> DynCompatibilityViolationSolution { match self { DynCompatibilityViolation::SizedSelf(_) + | DynCompatibilityViolation::ExplicitlyDynIncompatible(_) | DynCompatibilityViolation::SupertraitSelf(_) | DynCompatibilityViolation::SupertraitNonLifetimeBinder(..) | DynCompatibilityViolation::SupertraitConst(_) => { @@ -894,6 +901,7 @@ impl DynCompatibilityViolation { match self { DynCompatibilityViolation::SupertraitSelf(spans) | DynCompatibilityViolation::SizedSelf(spans) + | DynCompatibilityViolation::ExplicitlyDynIncompatible(spans) | DynCompatibilityViolation::SupertraitNonLifetimeBinder(spans) | DynCompatibilityViolation::SupertraitConst(spans) => spans.clone(), DynCompatibilityViolation::AssocConst(_, span) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index fb515cf7f778..2f7a31a957bc 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -709,10 +709,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.trait_def(def_id).is_fundamental } - fn trait_may_be_implemented_via_object(self, trait_def_id: DefId) -> bool { - self.trait_def(trait_def_id).implement_via_object - } - fn trait_is_unsafe(self, trait_def_id: Self::DefId) -> bool { self.trait_def(trait_def_id).safety.is_unsafe() } diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index 2f6b38a619d2..0553561fac2c 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -7,6 +7,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::{self as hir, find_attr}; use rustc_macros::{Decodable, Encodable, HashStable}; +use rustc_span::Span; use tracing::debug; use crate::query::LocalCrate; @@ -69,10 +70,9 @@ pub struct TraitDef { /// must be implemented. pub must_implement_one_of: Option>, - /// Whether to add a builtin `dyn Trait: Trait` implementation. - /// This is enabled for all traits except ones marked with - /// `#[rustc_do_not_implement_via_object]`. - pub implement_via_object: bool, + /// Whether the trait should be considered dyn-incompatible, even if it otherwise + /// satisfies the requirements to be dyn-compatible. + pub force_dyn_incompatible: Option, /// Whether a trait is fully built-in, and any implementation is disallowed. /// This only applies to built-in traits, and is marked via diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index d27d80a086ad..63f246db8a5f 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -792,7 +792,9 @@ where candidates: &mut Vec>, ) { let cx = self.cx(); - if !cx.trait_may_be_implemented_via_object(goal.predicate.trait_def_id(cx)) { + if cx.is_sizedness_trait(goal.predicate.trait_def_id(cx)) { + // `dyn MetaSized` is valid, but should get its `MetaSized` impl from + // being `dyn` (SizedCandidate), not from the object candidate. return; } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 4266731cf0a2..f3cb05aed6d5 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -244,7 +244,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::SkipDuringMethodDispatch { .. } | AttributeKind::Coinductive(..) | AttributeKind::DenyExplicitImpl(..) - | AttributeKind::DoNotImplementViaObject(..) + | AttributeKind::DynIncompatibleTrait(..) | AttributeKind::SpecializationTrait(..) | AttributeKind::UnsafeSpecializationMarker(..) | AttributeKind::ParenSugar(..) diff --git a/compiler/rustc_public/src/ty.rs b/compiler/rustc_public/src/ty.rs index 14656a2e594a..f0a18ef08fc6 100644 --- a/compiler/rustc_public/src/ty.rs +++ b/compiler/rustc_public/src/ty.rs @@ -1395,7 +1395,7 @@ pub struct TraitDecl { pub skip_boxed_slice_during_method_dispatch: bool, pub specialization_kind: TraitSpecializationKind, pub must_implement_one_of: Option>, - pub implement_via_object: bool, + pub force_dyn_incompatible: Option, pub deny_explicit_impl: bool, } diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index ca8234280be8..34662c9ca023 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -597,7 +597,7 @@ impl<'tcx> Stable<'tcx> for ty::TraitDef { .must_implement_one_of .as_ref() .map(|idents| idents.iter().map(|ident| opaque(ident)).collect()), - implement_via_object: self.implement_via_object, + force_dyn_incompatible: self.force_dyn_incompatible.stable(tables, cx), deny_explicit_impl: self.deny_explicit_impl, } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index e5949a979074..d6d6ebb49e10 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1950,7 +1950,6 @@ symbols! { rustc_diagnostic_macros, rustc_dirty, rustc_do_not_const_check, - rustc_do_not_implement_via_object, rustc_doc_primitive, rustc_driver, rustc_dummy, @@ -1959,6 +1958,7 @@ symbols! { rustc_dump_predicates, rustc_dump_user_args, rustc_dump_vtable, + rustc_dyn_incompatible_trait, rustc_effective_visibility, rustc_eii_foreign_item, rustc_evaluate_where_clauses, diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index 6ab92531e4ef..13acdad8aad7 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -93,7 +93,10 @@ fn dyn_compatibility_violations_for_trait( // We don't want to include the requirement from `Sized` itself to be `Sized` in the list. let spans = get_sized_bounds(tcx, trait_def_id); violations.push(DynCompatibilityViolation::SizedSelf(spans)); + } else if let Some(span) = tcx.trait_def(trait_def_id).force_dyn_incompatible { + violations.push(DynCompatibilityViolation::ExplicitlyDynIncompatible([span].into())); } + let spans = predicates_reference_self(tcx, trait_def_id, false); if !spans.is_empty() { violations.push(DynCompatibilityViolation::SupertraitSelf(spans)); diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index e5c2adaa261d..5bbbad0b5bbf 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -799,10 +799,6 @@ fn assemble_candidates_from_object_ty<'cx, 'tcx>( let tcx = selcx.tcx(); - if !tcx.trait_def(obligation.predicate.trait_def_id(tcx)).implement_via_object { - return; - } - let self_ty = obligation.predicate.self_ty(); let object_ty = selcx.infcx.shallow_resolve(self_ty); let data = match object_ty.kind() { diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index d0833f030835..f5bf74a79919 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -904,7 +904,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { "assemble_candidates_from_object_ty", ); - if !self.tcx().trait_def(obligation.predicate.def_id()).implement_via_object { + if self.tcx().is_sizedness_trait(obligation.predicate.def_id()) { + // `dyn MetaSized` is valid, but should get its `MetaSized` impl from + // being `dyn` (SizedCandidate), not from the object candidate. return; } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 03cf738c0598..0ab27a65c687 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -378,8 +378,6 @@ pub trait Interner: fn trait_is_fundamental(self, def_id: Self::TraitId) -> bool; - fn trait_may_be_implemented_via_object(self, trait_def_id: Self::TraitId) -> bool; - /// Returns `true` if this is an `unsafe trait`. fn trait_is_unsafe(self, trait_def_id: Self::TraitId) -> bool; diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index da7ba167ef74..57416455e9de 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -153,7 +153,7 @@ unsafe impl Send for &T {} #[fundamental] // for Default, for example, which requires that `[T]: !Default` be evaluatable #[rustc_specialization_trait] #[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] +#[rustc_dyn_incompatible_trait] // `Sized` being coinductive, despite having supertraits, is okay as there are no user-written impls, // and we know that the supertraits are always implemented if the subtrait is just by looking at // the builtin impls. @@ -172,7 +172,6 @@ pub trait Sized: MetaSized { #[fundamental] #[rustc_specialization_trait] #[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] // `MetaSized` being coinductive, despite having supertraits, is okay for the same reasons as // `Sized` above. #[rustc_coinductive] @@ -190,7 +189,6 @@ pub trait MetaSized: PointeeSized { #[fundamental] #[rustc_specialization_trait] #[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] #[rustc_coinductive] pub trait PointeeSized { // Empty @@ -236,7 +234,7 @@ pub trait PointeeSized { #[unstable(feature = "unsize", issue = "18598")] #[lang = "unsize"] #[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] +#[rustc_dyn_incompatible_trait] pub trait Unsize: PointeeSized { // Empty. } @@ -509,7 +507,7 @@ impl Copy for &T {} #[unstable(feature = "bikeshed_guaranteed_no_drop", issue = "none")] #[lang = "bikeshed_guaranteed_no_drop"] #[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] +#[rustc_dyn_incompatible_trait] #[doc(hidden)] pub trait BikeshedGuaranteedNoDrop {} @@ -884,7 +882,7 @@ impl StructuralPartialEq for PhantomData {} )] #[lang = "discriminant_kind"] #[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] +#[rustc_dyn_incompatible_trait] pub trait DiscriminantKind { /// The type of the discriminant, which must satisfy the trait /// bounds required by `mem::Discriminant`. @@ -1054,7 +1052,7 @@ marker_impls! { #[lang = "destruct"] #[rustc_on_unimplemented(message = "can't drop `{Self}`", append_const_msg)] #[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] +#[rustc_dyn_incompatible_trait] pub const trait Destruct: PointeeSized {} /// A marker for tuple types. @@ -1065,7 +1063,7 @@ pub const trait Destruct: PointeeSized {} #[lang = "tuple_trait"] #[diagnostic::on_unimplemented(message = "`{Self}` is not a tuple")] #[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] +#[rustc_dyn_incompatible_trait] pub trait Tuple {} /// A marker for types which can be used as types of `const` generic parameters. @@ -1123,7 +1121,7 @@ marker_impls! { )] #[lang = "fn_ptr_trait"] #[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] +#[rustc_dyn_incompatible_trait] pub trait FnPtr: Copy + Clone { /// Returns the address of the function pointer. #[lang = "fn_ptr_addr"] diff --git a/library/core/src/mem/transmutability.rs b/library/core/src/mem/transmutability.rs index f36cb8cddb83..e26c1b8fa1e1 100644 --- a/library/core/src/mem/transmutability.rs +++ b/library/core/src/mem/transmutability.rs @@ -86,7 +86,7 @@ use crate::marker::ConstParamTy_; #[unstable_feature_bound(transmutability)] #[lang = "transmute_trait"] #[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] +#[rustc_dyn_incompatible_trait] #[rustc_coinductive] pub unsafe trait TransmuteFrom where diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs index 1a5864316833..1eeadf1217b5 100644 --- a/library/core/src/ptr/metadata.rs +++ b/library/core/src/ptr/metadata.rs @@ -55,7 +55,7 @@ use crate::ptr::NonNull; /// [`to_raw_parts`]: *const::to_raw_parts #[lang = "pointee_trait"] #[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] +#[rustc_dyn_incompatible_trait] pub trait Pointee: PointeeSized { /// The type for metadata in pointers and references to `Self`. #[lang = "metadata_type"] diff --git a/tests/ui/dyn-compatibility/metasized.rs b/tests/ui/dyn-compatibility/metasized.rs new file mode 100644 index 000000000000..ff233c5ca764 --- /dev/null +++ b/tests/ui/dyn-compatibility/metasized.rs @@ -0,0 +1,25 @@ +//@ run-pass +//! This test and `sized-*.rs` and `pointeesized.rs` test that dyn-compatibility correctly +//! handles sizedness traits, which are special in several parts of the compiler. +#![feature(sized_hierarchy)] +use std::marker::MetaSized; + +trait Foo: std::fmt::Debug + MetaSized {} + +impl Foo for T {} + +fn unsize_sized(x: Box) -> Box { + x +} + +fn unsize_subtrait(x: Box) -> Box { + x +} + +fn main() { + let _bx = unsize_sized(Box::new(vec![1, 2, 3])); + + let bx: Box = Box::new(vec![1, 2, 3]); + let _ = format!("{bx:?}"); + let _bx = unsize_subtrait(bx); +} diff --git a/tests/ui/dyn-compatibility/pointeesized.rs b/tests/ui/dyn-compatibility/pointeesized.rs new file mode 100644 index 000000000000..863abd704ead --- /dev/null +++ b/tests/ui/dyn-compatibility/pointeesized.rs @@ -0,0 +1,17 @@ +//@ run-pass +//! This test and `sized-*.rs` and `metasized.rs` test that dyn-compatibility correctly +//! handles sizedness traits, which are special in several parts of the compiler. +#![feature(sized_hierarchy)] +// PointeeSized is effectively removed before reaching the trait solver, +// so it's as though it wasn't even mentioned in the trait list. +use std::marker::PointeeSized; + +fn main() { + let dyn_ref: &(dyn PointeeSized + Send) = &42; + let dyn_ref: &dyn Send = dyn_ref; + let _dyn_ref: &(dyn PointeeSized + Send) = dyn_ref; + assert_eq!( + std::any::TypeId::of::(), + std::any::TypeId::of::(), + ); +} diff --git a/tests/ui/dyn-compatibility/sized-3.rs b/tests/ui/dyn-compatibility/sized-3.rs new file mode 100644 index 000000000000..84ee391445e7 --- /dev/null +++ b/tests/ui/dyn-compatibility/sized-3.rs @@ -0,0 +1,29 @@ +//! This test and `metasized.rs` and `pointeesized.rs` test that dyn-compatibility correctly +//! handles the different sizedness traits, which are special in several parts of the compiler. + +trait Foo: std::fmt::Debug + Sized {} + +impl Foo for T {} + +fn unsize_sized(x: Box) -> Box { + //~^ ERROR the trait `Sized` is not dyn compatible + x +} + +fn unsize_subtrait(x: Box) -> Box { + //~^ ERROR the trait `Foo` is not dyn compatible + //~| ERROR the trait `Sized` is not dyn compatible + x +} + +fn main() { + let _bx = unsize_sized(Box::new(vec![1, 2, 3])); + //~^ ERROR the trait `Sized` is not dyn compatible + + let bx: Box = Box::new(vec![1, 2, 3]); + //~^ ERROR the trait `Foo` is not dyn compatible + let _ = format!("{bx:?}"); + let _bx = unsize_subtrait(bx); + //~^ ERROR the trait `Foo` is not dyn compatible + //~| ERROR the trait `Sized` is not dyn compatible +} diff --git a/tests/ui/dyn-compatibility/sized-3.stderr b/tests/ui/dyn-compatibility/sized-3.stderr new file mode 100644 index 000000000000..88d3bb5810fe --- /dev/null +++ b/tests/ui/dyn-compatibility/sized-3.stderr @@ -0,0 +1,88 @@ +error[E0038]: the trait `Sized` is not dyn compatible + --> $DIR/sized-3.rs:8:47 + | +LL | fn unsize_sized(x: Box) -> Box { + | ^^^^^^^^^ `Sized` is not dyn compatible + | + = note: the trait is not dyn compatible because it requires `Self: Sized` + = note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + +error[E0038]: the trait `Foo` is not dyn compatible + --> $DIR/sized-3.rs:13:27 + | +LL | fn unsize_subtrait(x: Box) -> Box { + | ^^^^^^^ `Foo` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/sized-3.rs:4:30 + | +LL | trait Foo: std::fmt::Debug + Sized {} + | --- ^^^^^ ...because it requires `Self: Sized` + | | + | this trait is not dyn compatible... + +error[E0038]: the trait `Sized` is not dyn compatible + --> $DIR/sized-3.rs:13:44 + | +LL | fn unsize_subtrait(x: Box) -> Box { + | ^^^^^^^^^ `Sized` is not dyn compatible + | + = note: the trait is not dyn compatible because it requires `Self: Sized` + = note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + +error[E0038]: the trait `Sized` is not dyn compatible + --> $DIR/sized-3.rs:20:15 + | +LL | let _bx = unsize_sized(Box::new(vec![1, 2, 3])); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Sized` is not dyn compatible + | + = note: the trait is not dyn compatible because it requires `Self: Sized` + = note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + +error[E0038]: the trait `Foo` is not dyn compatible + --> $DIR/sized-3.rs:23:21 + | +LL | let bx: Box = Box::new(vec![1, 2, 3]); + | ^^^ `Foo` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/sized-3.rs:4:30 + | +LL | trait Foo: std::fmt::Debug + Sized {} + | --- ^^^^^ ...because it requires `Self: Sized` + | | + | this trait is not dyn compatible... + +error[E0038]: the trait `Foo` is not dyn compatible + --> $DIR/sized-3.rs:26:31 + | +LL | let _bx = unsize_subtrait(bx); + | ^^ `Foo` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/sized-3.rs:4:30 + | +LL | trait Foo: std::fmt::Debug + Sized {} + | --- ^^^^^ ...because it requires `Self: Sized` + | | + | this trait is not dyn compatible... + +error[E0038]: the trait `Sized` is not dyn compatible + --> $DIR/sized-3.rs:26:15 + | +LL | let _bx = unsize_subtrait(bx); + | ^^^^^^^^^^^^^^^^^^^ `Sized` is not dyn compatible + | + = note: the trait is not dyn compatible because it requires `Self: Sized` + = note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/traits/deny-builtin-object-impl.current.stderr b/tests/ui/traits/deny-builtin-object-impl.current.stderr index d6f4762d0996..928d16aa4329 100644 --- a/tests/ui/traits/deny-builtin-object-impl.current.stderr +++ b/tests/ui/traits/deny-builtin-object-impl.current.stderr @@ -4,41 +4,67 @@ error[E0322]: explicit impls for the `NotImplYesObject` trait are not permitted LL | impl NotImplYesObject for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of `NotImplYesObject` not allowed -error[E0277]: the trait bound `dyn NotImplNotObject: NotImplNotObject` is not satisfied - --> $DIR/deny-builtin-object-impl.rs:37:32 +error[E0038]: the trait `YesImplNotObject2` is not dyn compatible + --> $DIR/deny-builtin-object-impl.rs:23:28 + | +LL | impl YesImplNotObject2 for dyn YesImplNotObject2 {} + | ^^^^^^^^^^^^^^^^^^^^^ `YesImplNotObject2` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/deny-builtin-object-impl.rs:17:1 + | +LL | #[rustc_dyn_incompatible_trait] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...because it opted out of dyn-compatibility +LL | trait YesImplNotObject2 {} + | ----------------- this trait is not dyn compatible... + +error[E0038]: the trait `NotImplNotObject` is not dyn compatible + --> $DIR/deny-builtin-object-impl.rs:37:36 | LL | test_not_impl_not_object::(); - | ^^^^^^^^^^^^^^^^^^^^ the trait `NotImplNotObject` is not implemented for `dyn NotImplNotObject` + | ^^^^^^^^^^^^^^^^ `NotImplNotObject` is not dyn compatible | -help: this trait has no implementations, consider adding one - --> $DIR/deny-builtin-object-impl.rs:12:1 +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/deny-builtin-object-impl.rs:11:1 | +LL | #[rustc_dyn_incompatible_trait] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...because it opted out of dyn-compatibility LL | trait NotImplNotObject {} - | ^^^^^^^^^^^^^^^^^^^^^^ -note: required by a bound in `test_not_impl_not_object` - --> $DIR/deny-builtin-object-impl.rs:28:32 - | -LL | fn test_not_impl_not_object() {} - | ^^^^^^^^^^^^^^^^ required by this bound in `test_not_impl_not_object` + | ---------------- this trait is not dyn compatible... -error[E0277]: the trait bound `dyn YesImplNotObject: YesImplNotObject` is not satisfied - --> $DIR/deny-builtin-object-impl.rs:40:32 +error[E0038]: the trait `YesImplNotObject` is not dyn compatible + --> $DIR/deny-builtin-object-impl.rs:40:36 | LL | test_yes_impl_not_object::(); - | ^^^^^^^^^^^^^^^^^^^^ the trait `YesImplNotObject` is not implemented for `dyn YesImplNotObject` + | ^^^^^^^^^^^^^^^^ `YesImplNotObject` is not dyn compatible | -help: this trait has no implementations, consider adding one - --> $DIR/deny-builtin-object-impl.rs:15:1 +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/deny-builtin-object-impl.rs:14:1 | +LL | #[rustc_dyn_incompatible_trait] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...because it opted out of dyn-compatibility LL | trait YesImplNotObject {} - | ^^^^^^^^^^^^^^^^^^^^^^ -note: required by a bound in `test_yes_impl_not_object` - --> $DIR/deny-builtin-object-impl.rs:30:32 + | ---------------- this trait is not dyn compatible... + +error[E0038]: the trait `YesImplNotObject2` is not dyn compatible + --> $DIR/deny-builtin-object-impl.rs:43:37 | -LL | fn test_yes_impl_not_object() {} - | ^^^^^^^^^^^^^^^^ required by this bound in `test_yes_impl_not_object` +LL | test_yes_impl_not_object2::(); + | ^^^^^^^^^^^^^^^^^ `YesImplNotObject2` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/deny-builtin-object-impl.rs:17:1 + | +LL | #[rustc_dyn_incompatible_trait] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...because it opted out of dyn-compatibility +LL | trait YesImplNotObject2 {} + | ----------------- this trait is not dyn compatible... -error: aborting due to 3 previous errors +error: aborting due to 5 previous errors -Some errors have detailed explanations: E0277, E0322. -For more information about an error, try `rustc --explain E0277`. +Some errors have detailed explanations: E0038, E0322. +For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/traits/deny-builtin-object-impl.next.stderr b/tests/ui/traits/deny-builtin-object-impl.next.stderr index d6f4762d0996..928d16aa4329 100644 --- a/tests/ui/traits/deny-builtin-object-impl.next.stderr +++ b/tests/ui/traits/deny-builtin-object-impl.next.stderr @@ -4,41 +4,67 @@ error[E0322]: explicit impls for the `NotImplYesObject` trait are not permitted LL | impl NotImplYesObject for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of `NotImplYesObject` not allowed -error[E0277]: the trait bound `dyn NotImplNotObject: NotImplNotObject` is not satisfied - --> $DIR/deny-builtin-object-impl.rs:37:32 +error[E0038]: the trait `YesImplNotObject2` is not dyn compatible + --> $DIR/deny-builtin-object-impl.rs:23:28 + | +LL | impl YesImplNotObject2 for dyn YesImplNotObject2 {} + | ^^^^^^^^^^^^^^^^^^^^^ `YesImplNotObject2` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/deny-builtin-object-impl.rs:17:1 + | +LL | #[rustc_dyn_incompatible_trait] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...because it opted out of dyn-compatibility +LL | trait YesImplNotObject2 {} + | ----------------- this trait is not dyn compatible... + +error[E0038]: the trait `NotImplNotObject` is not dyn compatible + --> $DIR/deny-builtin-object-impl.rs:37:36 | LL | test_not_impl_not_object::(); - | ^^^^^^^^^^^^^^^^^^^^ the trait `NotImplNotObject` is not implemented for `dyn NotImplNotObject` + | ^^^^^^^^^^^^^^^^ `NotImplNotObject` is not dyn compatible | -help: this trait has no implementations, consider adding one - --> $DIR/deny-builtin-object-impl.rs:12:1 +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/deny-builtin-object-impl.rs:11:1 | +LL | #[rustc_dyn_incompatible_trait] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...because it opted out of dyn-compatibility LL | trait NotImplNotObject {} - | ^^^^^^^^^^^^^^^^^^^^^^ -note: required by a bound in `test_not_impl_not_object` - --> $DIR/deny-builtin-object-impl.rs:28:32 - | -LL | fn test_not_impl_not_object() {} - | ^^^^^^^^^^^^^^^^ required by this bound in `test_not_impl_not_object` + | ---------------- this trait is not dyn compatible... -error[E0277]: the trait bound `dyn YesImplNotObject: YesImplNotObject` is not satisfied - --> $DIR/deny-builtin-object-impl.rs:40:32 +error[E0038]: the trait `YesImplNotObject` is not dyn compatible + --> $DIR/deny-builtin-object-impl.rs:40:36 | LL | test_yes_impl_not_object::(); - | ^^^^^^^^^^^^^^^^^^^^ the trait `YesImplNotObject` is not implemented for `dyn YesImplNotObject` + | ^^^^^^^^^^^^^^^^ `YesImplNotObject` is not dyn compatible | -help: this trait has no implementations, consider adding one - --> $DIR/deny-builtin-object-impl.rs:15:1 +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/deny-builtin-object-impl.rs:14:1 | +LL | #[rustc_dyn_incompatible_trait] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...because it opted out of dyn-compatibility LL | trait YesImplNotObject {} - | ^^^^^^^^^^^^^^^^^^^^^^ -note: required by a bound in `test_yes_impl_not_object` - --> $DIR/deny-builtin-object-impl.rs:30:32 + | ---------------- this trait is not dyn compatible... + +error[E0038]: the trait `YesImplNotObject2` is not dyn compatible + --> $DIR/deny-builtin-object-impl.rs:43:37 | -LL | fn test_yes_impl_not_object() {} - | ^^^^^^^^^^^^^^^^ required by this bound in `test_yes_impl_not_object` +LL | test_yes_impl_not_object2::(); + | ^^^^^^^^^^^^^^^^^ `YesImplNotObject2` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/deny-builtin-object-impl.rs:17:1 + | +LL | #[rustc_dyn_incompatible_trait] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...because it opted out of dyn-compatibility +LL | trait YesImplNotObject2 {} + | ----------------- this trait is not dyn compatible... -error: aborting due to 3 previous errors +error: aborting due to 5 previous errors -Some errors have detailed explanations: E0277, E0322. -For more information about an error, try `rustc --explain E0277`. +Some errors have detailed explanations: E0038, E0322. +For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/traits/deny-builtin-object-impl.rs b/tests/ui/traits/deny-builtin-object-impl.rs index 9d02ab7bd469..5924b3fa2857 100644 --- a/tests/ui/traits/deny-builtin-object-impl.rs +++ b/tests/ui/traits/deny-builtin-object-impl.rs @@ -8,20 +8,20 @@ trait NotImplYesObject {} #[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] +#[rustc_dyn_incompatible_trait] trait NotImplNotObject {} -#[rustc_do_not_implement_via_object] +#[rustc_dyn_incompatible_trait] trait YesImplNotObject {} -#[rustc_do_not_implement_via_object] +#[rustc_dyn_incompatible_trait] trait YesImplNotObject2 {} impl NotImplYesObject for () {} //~^ ERROR explicit impls for the `NotImplYesObject` trait are not permitted -// If there is no automatic impl then we can add a manual impl: impl YesImplNotObject2 for dyn YesImplNotObject2 {} +//~^ ERROR the trait `YesImplNotObject2` is not dyn compatible fn test_not_impl_yes_object() {} @@ -35,10 +35,11 @@ fn main() { test_not_impl_yes_object::(); test_not_impl_not_object::(); - //~^ ERROR the trait bound `dyn NotImplNotObject: NotImplNotObject` is not satisfied + //~^ ERROR the trait `NotImplNotObject` is not dyn compatible test_yes_impl_not_object::(); - //~^ ERROR the trait bound `dyn YesImplNotObject: YesImplNotObject` is not satisfied + //~^ ERROR the trait `YesImplNotObject` is not dyn compatible test_yes_impl_not_object2::(); + //~^ ERROR the trait `YesImplNotObject2` is not dyn compatible } diff --git a/tests/ui/traits/ice-with-dyn-pointee-errors.rs b/tests/ui/traits/ice-with-dyn-pointee-errors.rs index 46cef2c8bc09..bb89f103e3f9 100644 --- a/tests/ui/traits/ice-with-dyn-pointee-errors.rs +++ b/tests/ui/traits/ice-with-dyn-pointee-errors.rs @@ -6,10 +6,12 @@ use core::ptr::Pointee; fn unknown_sized_object_ptr_in(_: &(impl Pointee + ?Sized)) {} fn raw_pointer_in(x: &dyn Pointee) { + //~^ ERROR the trait `Pointee` is not dyn compatible unknown_sized_object_ptr_in(x) - //~^ ERROR type mismatch resolving ` as Pointee>::Metadata == ()` + //~^ ERROR the trait `Pointee` is not dyn compatible } fn main() { raw_pointer_in(&42) + //~^ ERROR the trait `Pointee` is not dyn compatible } diff --git a/tests/ui/traits/ice-with-dyn-pointee-errors.stderr b/tests/ui/traits/ice-with-dyn-pointee-errors.stderr index 5299236026d7..9ff0445cda09 100644 --- a/tests/ui/traits/ice-with-dyn-pointee-errors.stderr +++ b/tests/ui/traits/ice-with-dyn-pointee-errors.stderr @@ -1,19 +1,39 @@ -error[E0271]: type mismatch resolving ` as Pointee>::Metadata == ()` - --> $DIR/ice-with-dyn-pointee-errors.rs:9:33 +error[E0038]: the trait `Pointee` is not dyn compatible + --> $DIR/ice-with-dyn-pointee-errors.rs:8:23 + | +LL | fn raw_pointer_in(x: &dyn Pointee) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `Pointee` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $SRC_DIR/core/src/ptr/metadata.rs:LL:COL + | + = note: the trait is not dyn compatible because it opted out of dyn-compatibility + +error[E0038]: the trait `Pointee` is not dyn compatible + --> $DIR/ice-with-dyn-pointee-errors.rs:10:5 | LL | unknown_sized_object_ptr_in(x) - | --------------------------- ^ expected `()`, found `DynMetadata>` - | | - | required by a bound introduced by this call + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Pointee` is not dyn compatible | - = note: expected unit type `()` - found struct `DynMetadata>` -note: required by a bound in `unknown_sized_object_ptr_in` - --> $DIR/ice-with-dyn-pointee-errors.rs:6:50 +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $SRC_DIR/core/src/ptr/metadata.rs:LL:COL | -LL | fn unknown_sized_object_ptr_in(_: &(impl Pointee + ?Sized)) {} - | ^^^^^^^^^^^^^ required by this bound in `unknown_sized_object_ptr_in` + = note: the trait is not dyn compatible because it opted out of dyn-compatibility -error: aborting due to 1 previous error +error[E0038]: the trait `Pointee` is not dyn compatible + --> $DIR/ice-with-dyn-pointee-errors.rs:15:20 + | +LL | raw_pointer_in(&42) + | ^^^ `Pointee` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $SRC_DIR/core/src/ptr/metadata.rs:LL:COL + | + = note: the trait is not dyn compatible because it opted out of dyn-compatibility -For more information about this error, try `rustc --explain E0271`. +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/traits/ice-with-dyn-pointee.rs b/tests/ui/traits/ice-with-dyn-pointee.rs deleted file mode 100644 index 45361cc44600..000000000000 --- a/tests/ui/traits/ice-with-dyn-pointee.rs +++ /dev/null @@ -1,11 +0,0 @@ -//@ run-pass -#![feature(ptr_metadata)] -// Address issue #112737 -- ICE with dyn Pointee -extern crate core; -use core::ptr::Pointee; - -fn raw_pointer_in(_: &dyn Pointee) {} - -fn main() { - raw_pointer_in(&42) -} diff --git a/tests/ui/unsized/issue-71659.current.stderr b/tests/ui/unsized/issue-71659.current.stderr index f7de668ba3a5..22e43e07dbda 100644 --- a/tests/ui/unsized/issue-71659.current.stderr +++ b/tests/ui/unsized/issue-71659.current.stderr @@ -1,18 +1,22 @@ -error[E0277]: the trait bound `dyn Foo: CastTo<[i32]>` is not satisfied - --> $DIR/issue-71659.rs:34:15 +error[E0038]: the trait `Foo` is not dyn compatible + --> $DIR/issue-71659.rs:33:17 | -LL | let x = x.cast::<[i32]>(); - | ^^^^ the trait `CastTo<[i32]>` is not implemented for `dyn Foo` +LL | let x: &dyn Foo = &[]; + | ^^^ `Foo` is not dyn compatible | -note: required by a bound in `Cast::cast` - --> $DIR/issue-71659.rs:23:15 +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $SRC_DIR/core/src/marker.rs:LL:COL | -LL | fn cast(&self) -> &T - | ---- required by a bound in this associated function -LL | where -LL | Self: CastTo, - | ^^^^^^^^^ required by this bound in `Cast::cast` + = note: ...because it opted out of dyn-compatibility + | + ::: $DIR/issue-71659.rs:29:11 + | +LL | pub trait Foo: CastTo<[i32]> {} + | --- this trait is not dyn compatible... + = help: only type `[i32; 0]` implements `Foo` within this crate; consider using it directly instead. + = note: `Foo` may be implemented in other crates; if you want to support your users passing their own types here, you can't refer to a specific type error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/unsized/issue-71659.next.stderr b/tests/ui/unsized/issue-71659.next.stderr index f7de668ba3a5..22e43e07dbda 100644 --- a/tests/ui/unsized/issue-71659.next.stderr +++ b/tests/ui/unsized/issue-71659.next.stderr @@ -1,18 +1,22 @@ -error[E0277]: the trait bound `dyn Foo: CastTo<[i32]>` is not satisfied - --> $DIR/issue-71659.rs:34:15 +error[E0038]: the trait `Foo` is not dyn compatible + --> $DIR/issue-71659.rs:33:17 | -LL | let x = x.cast::<[i32]>(); - | ^^^^ the trait `CastTo<[i32]>` is not implemented for `dyn Foo` +LL | let x: &dyn Foo = &[]; + | ^^^ `Foo` is not dyn compatible | -note: required by a bound in `Cast::cast` - --> $DIR/issue-71659.rs:23:15 +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $SRC_DIR/core/src/marker.rs:LL:COL | -LL | fn cast(&self) -> &T - | ---- required by a bound in this associated function -LL | where -LL | Self: CastTo, - | ^^^^^^^^^ required by this bound in `Cast::cast` + = note: ...because it opted out of dyn-compatibility + | + ::: $DIR/issue-71659.rs:29:11 + | +LL | pub trait Foo: CastTo<[i32]> {} + | --- this trait is not dyn compatible... + = help: only type `[i32; 0]` implements `Foo` within this crate; consider using it directly instead. + = note: `Foo` may be implemented in other crates; if you want to support your users passing their own types here, you can't refer to a specific type error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/unsized/issue-71659.rs b/tests/ui/unsized/issue-71659.rs index c463ed125bb6..9e1387fa8443 100644 --- a/tests/ui/unsized/issue-71659.rs +++ b/tests/ui/unsized/issue-71659.rs @@ -31,6 +31,6 @@ impl Foo for [i32; 0] {} fn main() { let x: &dyn Foo = &[]; + //~^ ERROR: the trait `Foo` is not dyn compatible let x = x.cast::<[i32]>(); - //~^ ERROR: the trait bound `dyn Foo: CastTo<[i32]>` is not satisfied } From 005fcea374fe71f50e96c6b5d04928e6629227c0 Mon Sep 17 00:00:00 2001 From: Oscar Bray Date: Tue, 20 Jan 2026 19:22:25 +0000 Subject: [PATCH 62/69] Port variance attrs to attr parser. --- .../src/attributes/test_attrs.rs | 22 +++++++++++++++++++ compiler/rustc_attr_parsing/src/context.rs | 6 ++++- .../rustc_hir/src/attrs/data_structures.rs | 6 +++++ .../rustc_hir/src/attrs/encode_cross_crate.rs | 2 ++ .../rustc_hir_analysis/src/variance/dump.rs | 7 +++--- compiler/rustc_passes/src/check_attr.rs | 4 ++-- .../feature-gate-rustc-attrs-1.rs | 10 +++++---- .../feature-gate-rustc-attrs-1.stderr | 20 ++++++++--------- 8 files changed, 57 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs index 7f25641b948e..ec7cdd3624dc 100644 --- a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs @@ -91,3 +91,25 @@ impl SingleAttributeParser for ShouldPanicParser { }) } } + +pub(crate) struct RustcVarianceParser; + +impl NoArgsAttributeParser for RustcVarianceParser { + const PATH: &[Symbol] = &[sym::rustc_variance]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Struct), + Allow(Target::Enum), + Allow(Target::Union), + ]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcVariance; +} + +pub(crate) struct RustcVarianceOfOpaquesParser; + +impl NoArgsAttributeParser for RustcVarianceOfOpaquesParser { + const PATH: &[Symbol] = &[sym::rustc_variance_of_opaques]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcVarianceOfOpaques; +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 6aae2b90a504..26c1fc91aecf 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -85,7 +85,9 @@ use crate::attributes::semantics::MayDangleParser; use crate::attributes::stability::{ BodyStabilityParser, ConstStabilityIndirectParser, ConstStabilityParser, StabilityParser, }; -use crate::attributes::test_attrs::{IgnoreParser, ShouldPanicParser}; +use crate::attributes::test_attrs::{ + IgnoreParser, RustcVarianceOfOpaquesParser, RustcVarianceParser, ShouldPanicParser, +}; use crate::attributes::traits::{ AllowIncoherentImplParser, CoinductiveParser, DenyExplicitImplParser, DoNotImplementViaObjectParser, FundamentalParser, MarkerParser, ParenSugarParser, @@ -300,6 +302,8 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 8d2baae703b8..fe48e6f4fafd 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1007,6 +1007,12 @@ pub enum AttributeKind { /// Represents `#[rustc_simd_monomorphize_lane_limit = "N"]`. RustcSimdMonomorphizeLaneLimit(Limit), + /// Represents `#[rustc_variance]` + RustcVariance, + + /// Represents `#[rustc_variance_of_opaques]` + RustcVarianceOfOpaques, + /// Represents `#[sanitize]` /// /// the on set and off set are distjoint since there's a third option: unset. diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index fd764ad458b1..5b593778b75a 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -129,6 +129,8 @@ impl AttributeKind { RustcScalableVector { .. } => Yes, RustcShouldNotBeCalledOnConstItems(..) => Yes, RustcSimdMonomorphizeLaneLimit(..) => Yes, // Affects layout computation, which needs to work cross-crate + RustcVariance => No, + RustcVarianceOfOpaques => No, Sanitize { .. } => No, ShouldPanic { .. } => No, SkipDuringMethodDispatch { .. } => No, diff --git a/compiler/rustc_hir_analysis/src/variance/dump.rs b/compiler/rustc_hir_analysis/src/variance/dump.rs index 31f0adf720d7..e625d9dfe3b5 100644 --- a/compiler/rustc_hir_analysis/src/variance/dump.rs +++ b/compiler/rustc_hir_analysis/src/variance/dump.rs @@ -1,8 +1,9 @@ use std::fmt::Write; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; +use rustc_hir::find_attr; use rustc_middle::ty::{GenericArgs, TyCtxt}; -use rustc_span::sym; fn format_variances(tcx: TyCtxt<'_>, def_id: LocalDefId) -> String { let variances = tcx.variances_of(def_id); @@ -25,7 +26,7 @@ fn format_variances(tcx: TyCtxt<'_>, def_id: LocalDefId) -> String { pub(crate) fn variances(tcx: TyCtxt<'_>) { let crate_items = tcx.hir_crate_items(()); - if tcx.has_attr(CRATE_DEF_ID, sym::rustc_variance_of_opaques) { + if find_attr!(tcx.get_all_attrs(CRATE_DEF_ID), AttributeKind::RustcVarianceOfOpaques) { for id in crate_items.opaques() { tcx.dcx().emit_err(crate::errors::VariancesOf { span: tcx.def_span(id), @@ -35,7 +36,7 @@ pub(crate) fn variances(tcx: TyCtxt<'_>) { } for id in crate_items.free_items() { - if !tcx.has_attr(id.owner_id, sym::rustc_variance) { + if !find_attr!(tcx.get_all_attrs(id.owner_id), AttributeKind::RustcVariance) { continue; } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 4266731cf0a2..d89a71c1deb6 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -274,6 +274,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcScalableVector { .. } | AttributeKind::RustcSimdMonomorphizeLaneLimit(..) | AttributeKind::RustcShouldNotBeCalledOnConstItems(..) + | AttributeKind::RustcVariance + | AttributeKind::RustcVarianceOfOpaques | AttributeKind::ExportStable | AttributeKind::FfiConst(..) | AttributeKind::UnstableFeatureBound(..) @@ -378,8 +380,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::rustc_capture_analysis | sym::rustc_regions | sym::rustc_strict_coherence - | sym::rustc_variance - | sym::rustc_variance_of_opaques | sym::rustc_hidden_type_of_opaques | sym::rustc_mir | sym::rustc_effective_visibility diff --git a/tests/ui/feature-gates/feature-gate-rustc-attrs-1.rs b/tests/ui/feature-gates/feature-gate-rustc-attrs-1.rs index 17556723622e..f3d5bcbd8d78 100644 --- a/tests/ui/feature-gates/feature-gate-rustc-attrs-1.rs +++ b/tests/ui/feature-gates/feature-gate-rustc-attrs-1.rs @@ -1,12 +1,14 @@ // Test that `#[rustc_*]` attributes are gated by `rustc_attrs` feature gate. -#[rustc_variance] -//~^ ERROR use of an internal attribute [E0658] -//~| NOTE the `#[rustc_variance]` attribute is an internal implementation detail that will never be stable -//~| NOTE the `#[rustc_variance]` attribute is used for rustc unit tests #[rustc_nonnull_optimization_guaranteed] //~^ ERROR use of an internal attribute [E0658] //~| NOTE the `#[rustc_nonnull_optimization_guaranteed]` attribute is an internal implementation detail that will never be stable //~| NOTE the `#[rustc_nonnull_optimization_guaranteed]` attribute is just used to document guaranteed niche optimizations in the standard library //~| NOTE the compiler does not even check whether the type indeed is being non-null-optimized; it is your responsibility to ensure that the attribute is only used on types that are optimized fn main() {} + +#[rustc_variance] +//~^ ERROR use of an internal attribute [E0658] +//~| NOTE the `#[rustc_variance]` attribute is an internal implementation detail that will never be stable +//~| NOTE the `#[rustc_variance]` attribute is used for rustc unit tests +enum E {} diff --git a/tests/ui/feature-gates/feature-gate-rustc-attrs-1.stderr b/tests/ui/feature-gates/feature-gate-rustc-attrs-1.stderr index 159d383e4089..6588229b7d56 100644 --- a/tests/ui/feature-gates/feature-gate-rustc-attrs-1.stderr +++ b/tests/ui/feature-gates/feature-gate-rustc-attrs-1.stderr @@ -1,16 +1,6 @@ error[E0658]: use of an internal attribute --> $DIR/feature-gate-rustc-attrs-1.rs:3:1 | -LL | #[rustc_variance] - | ^^^^^^^^^^^^^^^^^ - | - = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable - = note: the `#[rustc_variance]` attribute is an internal implementation detail that will never be stable - = note: the `#[rustc_variance]` attribute is used for rustc unit tests - -error[E0658]: use of an internal attribute - --> $DIR/feature-gate-rustc-attrs-1.rs:7:1 - | LL | #[rustc_nonnull_optimization_guaranteed] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | @@ -19,6 +9,16 @@ LL | #[rustc_nonnull_optimization_guaranteed] = note: the `#[rustc_nonnull_optimization_guaranteed]` attribute is just used to document guaranteed niche optimizations in the standard library = note: the compiler does not even check whether the type indeed is being non-null-optimized; it is your responsibility to ensure that the attribute is only used on types that are optimized +error[E0658]: use of an internal attribute + --> $DIR/feature-gate-rustc-attrs-1.rs:10:1 + | +LL | #[rustc_variance] + | ^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable + = note: the `#[rustc_variance]` attribute is an internal implementation detail that will never be stable + = note: the `#[rustc_variance]` attribute is used for rustc unit tests + error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0658`. From 39296ff8f820ad9a5c1656428c8aae203c0934d6 Mon Sep 17 00:00:00 2001 From: Matthew Maurer Date: Tue, 20 Jan 2026 20:11:35 +0000 Subject: [PATCH 63/69] s390x: Support aligned stack datalayout LLVM 23 will mark the stack as aligned for more efficient code: https://github.com/llvm/llvm-project/pull/176041 --- compiler/rustc_codegen_llvm/src/context.rs | 6 ++++++ .../src/spec/targets/s390x_unknown_linux_gnu.rs | 2 +- .../src/spec/targets/s390x_unknown_linux_musl.rs | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 4b2544b7efdf..04525b142e9b 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -216,6 +216,12 @@ pub(crate) unsafe fn create_module<'ll>( target_data_layout = target_data_layout.replace("-f64:32:64", ""); } } + if llvm_version < (23, 0, 0) { + if sess.target.arch == Arch::S390x { + // LLVM 23 updated the s390x layout to specify the stack alignment: https://github.com/llvm/llvm-project/pull/176041 + target_data_layout = target_data_layout.replace("-S64", ""); + } + } // Ensure the data-layout values hardcoded remain the defaults. { diff --git a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_gnu.rs index a64e867b9a1c..8859e0d650a2 100644 --- a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_gnu.rs @@ -22,7 +22,7 @@ pub(crate) fn target() -> Target { std: Some(true), }, pointer_width: 64, - data_layout: "E-m:e-i1:8:16-i8:8:16-i64:64-f128:64-v128:64-a:8:16-n32:64".into(), + data_layout: "E-S64-m:e-i1:8:16-i8:8:16-i64:64-f128:64-v128:64-a:8:16-n32:64".into(), arch: Arch::S390x, options: base, } diff --git a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs index 69c25d72432c..21e705ebbec5 100644 --- a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs @@ -23,7 +23,7 @@ pub(crate) fn target() -> Target { std: Some(true), }, pointer_width: 64, - data_layout: "E-m:e-i1:8:16-i8:8:16-i64:64-f128:64-v128:64-a:8:16-n32:64".into(), + data_layout: "E-S64-m:e-i1:8:16-i8:8:16-i64:64-f128:64-v128:64-a:8:16-n32:64".into(), arch: Arch::S390x, options: base, } From 17c5b7a4b519ebc2c5ddeb3aa36ac7bec5ca9724 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 20 Jan 2026 20:28:35 +1100 Subject: [PATCH 64/69] Replace `make_dep_kind_name_array!` with a slice constant --- compiler/rustc_interface/src/passes.rs | 2 +- .../rustc_middle/src/dep_graph/dep_node.rs | 23 ++++++++----------- compiler/rustc_middle/src/dep_graph/mod.rs | 6 +++-- compiler/rustc_query_impl/src/plumbing.rs | 4 ---- 4 files changed, 14 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 12a6a616d64f..1684b7535e68 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -940,7 +940,7 @@ pub fn create_and_enter_global_ctxt FnOnce(TyCtxt<'tcx>) -> T>( let outputs = util::build_output_filenames(&pre_configured_attrs, sess); - let dep_type = DepsType { dep_names: rustc_query_impl::dep_kind_names() }; + let dep_type = DepsType { dep_names: rustc_middle::dep_graph::DEP_KIND_NAMES }; let dep_graph = setup_dep_graph(sess, crate_name, stable_crate_id, &dep_type); let cstore = diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs index 3ee1db67911f..a815a03c6458 100644 --- a/compiler/rustc_middle/src/dep_graph/dep_node.rs +++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs @@ -24,15 +24,6 @@ macro_rules! define_dep_nodes { ($mod:ident) => {[ $($mod::$variant()),* ]}; } - #[macro_export] - macro_rules! make_dep_kind_name_array { - ($mod:ident) => { - vec! { - $(*$mod::$variant().name),* - } - }; - } - /// This enum serves as an index into arrays built by `make_dep_kind_array`. // This enum has more than u8::MAX variants so we need some kind of multi-byte // encoding. The derived Encodable/Decodable uses leb128 encoding which is @@ -68,20 +59,24 @@ macro_rules! define_dep_nodes { deps.len() as u16 }; + /// List containing the name of each dep kind as a static string, + /// indexable by `DepKind`. + pub const DEP_KIND_NAMES: &[&str] = &[ + $( self::label_strs::$variant, )* + ]; + pub(super) fn dep_kind_from_label_string(label: &str) -> Result { match label { - $(stringify!($variant) => Ok(dep_kinds::$variant),)* + $( self::label_strs::$variant => Ok(self::dep_kinds::$variant), )* _ => Err(()), } } /// Contains variant => str representations for constructing /// DepNode groups for tests. - #[allow(dead_code, non_upper_case_globals)] + #[expect(non_upper_case_globals)] pub mod label_strs { - $( - pub const $variant: &str = stringify!($variant); - )* + $( pub const $variant: &str = stringify!($variant); )* } }; } diff --git a/compiler/rustc_middle/src/dep_graph/mod.rs b/compiler/rustc_middle/src/dep_graph/mod.rs index b24ea4acc6b7..2fef7b5a9781 100644 --- a/compiler/rustc_middle/src/dep_graph/mod.rs +++ b/compiler/rustc_middle/src/dep_graph/mod.rs @@ -8,7 +8,9 @@ use crate::ty::{self, TyCtxt}; #[macro_use] mod dep_node; -pub use dep_node::{DepKind, DepNode, DepNodeExt, dep_kind_from_label, dep_kinds, label_strs}; +pub use dep_node::{ + DEP_KIND_NAMES, DepKind, DepNode, DepNodeExt, dep_kind_from_label, dep_kinds, label_strs, +}; pub(crate) use dep_node::{make_compile_codegen_unit, make_compile_mono_item, make_metadata}; pub use rustc_query_system::dep_graph::debug::{DepNodeFilter, EdgeFilter}; pub use rustc_query_system::dep_graph::{ @@ -22,7 +24,7 @@ pub type DepKindStruct<'tcx> = rustc_query_system::dep_graph::DepKindStruct, + pub dep_names: &'static [&'static str], } impl Deps for DepsType { diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 67ab1114af62..d6d1dc781f3e 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -921,9 +921,5 @@ macro_rules! define_queries { pub fn query_callbacks<'tcx>(arena: &'tcx Arena<'tcx>) -> &'tcx [DepKindStruct<'tcx>] { arena.alloc_from_iter(rustc_middle::make_dep_kind_array!(query_callbacks)) } - - pub fn dep_kind_names() -> Vec<&'static str> { - rustc_middle::make_dep_kind_name_array!(query_callbacks) - } } } From 154255698ac2686a24b8604fff913cd8d4db9daf Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 20 Jan 2026 20:41:07 +1100 Subject: [PATCH 65/69] Make `Deps::name` lookup a non-self associated function The dep names needed here are statically available from `rustc_middle`. --- compiler/rustc_incremental/src/persist/load.rs | 10 +++------- compiler/rustc_interface/src/passes.rs | 4 +--- compiler/rustc_middle/src/dep_graph/dep_node.rs | 2 +- compiler/rustc_middle/src/dep_graph/mod.rs | 12 ++++-------- compiler/rustc_query_system/src/dep_graph/mod.rs | 2 +- .../rustc_query_system/src/dep_graph/serialized.rs | 6 +++--- 6 files changed, 13 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index 1b2a283a1a0d..e93cba2c8d0f 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs @@ -91,10 +91,7 @@ fn delete_dirty_work_product(sess: &Session, swp: SerializedWorkProduct) { work_product::delete_workproduct_files(sess, &swp.work_product); } -fn load_dep_graph( - sess: &Session, - deps: &DepsType, -) -> LoadResult<(Arc, WorkProductMap)> { +fn load_dep_graph(sess: &Session) -> LoadResult<(Arc, WorkProductMap)> { let prof = sess.prof.clone(); if sess.opts.incremental.is_none() { @@ -174,7 +171,7 @@ fn load_dep_graph( return LoadResult::DataOutOfDate; } - let dep_graph = SerializedDepGraph::decode::(&mut decoder, deps); + let dep_graph = SerializedDepGraph::decode::(&mut decoder); LoadResult::Ok { data: (dep_graph, prev_work_products) } } @@ -212,12 +209,11 @@ pub fn setup_dep_graph( sess: &Session, crate_name: Symbol, stable_crate_id: StableCrateId, - deps: &DepsType, ) -> DepGraph { // `load_dep_graph` can only be called after `prepare_session_directory`. prepare_session_directory(sess, crate_name, stable_crate_id); - let res = sess.opts.build_dep_graph().then(|| load_dep_graph(sess, deps)); + let res = sess.opts.build_dep_graph().then(|| load_dep_graph(sess)); if sess.opts.incremental.is_some() { sess.time("incr_comp_garbage_collect_session_directories", || { diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 1684b7535e68..a7e94e1c0155 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -26,7 +26,6 @@ use rustc_lint::{BufferedEarlyLint, EarlyCheckNode, LintStore, unerased_lint_sto use rustc_metadata::EncodedMetadata; use rustc_metadata::creader::CStore; use rustc_middle::arena::Arena; -use rustc_middle::dep_graph::DepsType; use rustc_middle::ty::{self, CurrentGcx, GlobalCtxt, RegisteredTools, TyCtxt}; use rustc_middle::util::Providers; use rustc_parse::lexer::StripTokens; @@ -940,8 +939,7 @@ pub fn create_and_enter_global_ctxt FnOnce(TyCtxt<'tcx>) -> T>( let outputs = util::build_output_filenames(&pre_configured_attrs, sess); - let dep_type = DepsType { dep_names: rustc_middle::dep_graph::DEP_KIND_NAMES }; - let dep_graph = setup_dep_graph(sess, crate_name, stable_crate_id, &dep_type); + let dep_graph = setup_dep_graph(sess, crate_name, stable_crate_id); let cstore = FreezeLock::new(Box::new(CStore::new(compiler.codegen_backend.metadata_loader())) as _); diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs index a815a03c6458..f5ed0570667c 100644 --- a/compiler/rustc_middle/src/dep_graph/dep_node.rs +++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs @@ -61,7 +61,7 @@ macro_rules! define_dep_nodes { /// List containing the name of each dep kind as a static string, /// indexable by `DepKind`. - pub const DEP_KIND_NAMES: &[&str] = &[ + pub(crate) const DEP_KIND_NAMES: &[&str] = &[ $( self::label_strs::$variant, )* ]; diff --git a/compiler/rustc_middle/src/dep_graph/mod.rs b/compiler/rustc_middle/src/dep_graph/mod.rs index 2fef7b5a9781..c72f75b43dab 100644 --- a/compiler/rustc_middle/src/dep_graph/mod.rs +++ b/compiler/rustc_middle/src/dep_graph/mod.rs @@ -8,9 +8,7 @@ use crate::ty::{self, TyCtxt}; #[macro_use] mod dep_node; -pub use dep_node::{ - DEP_KIND_NAMES, DepKind, DepNode, DepNodeExt, dep_kind_from_label, dep_kinds, label_strs, -}; +pub use dep_node::{DepKind, DepNode, DepNodeExt, dep_kind_from_label, dep_kinds, label_strs}; pub(crate) use dep_node::{make_compile_codegen_unit, make_compile_mono_item, make_metadata}; pub use rustc_query_system::dep_graph::debug::{DepNodeFilter, EdgeFilter}; pub use rustc_query_system::dep_graph::{ @@ -23,9 +21,7 @@ pub type DepGraph = rustc_query_system::dep_graph::DepGraph; pub type DepKindStruct<'tcx> = rustc_query_system::dep_graph::DepKindStruct>; #[derive(Clone)] -pub struct DepsType { - pub dep_names: &'static [&'static str], -} +pub struct DepsType; impl Deps for DepsType { fn with_deps(task_deps: TaskDepsRef<'_>, op: OP) -> R @@ -49,8 +45,8 @@ impl Deps for DepsType { }) } - fn name(&self, dep_kind: DepKind) -> &'static str { - self.dep_names[dep_kind.as_usize()] + fn name(dep_kind: DepKind) -> &'static str { + dep_node::DEP_KIND_NAMES[dep_kind.as_usize()] } const DEP_KIND_NULL: DepKind = dep_kinds::Null; diff --git a/compiler/rustc_query_system/src/dep_graph/mod.rs b/compiler/rustc_query_system/src/dep_graph/mod.rs index d648415c9fc6..8b9e4fe1bf29 100644 --- a/compiler/rustc_query_system/src/dep_graph/mod.rs +++ b/compiler/rustc_query_system/src/dep_graph/mod.rs @@ -103,7 +103,7 @@ pub trait Deps: DynSync { where OP: for<'a> FnOnce(TaskDepsRef<'a>); - fn name(&self, dep_kind: DepKind) -> &'static str; + fn name(dep_kind: DepKind) -> &'static str; /// We use this for most things when incr. comp. is turned off. const DEP_KIND_NULL: DepKind; diff --git a/compiler/rustc_query_system/src/dep_graph/serialized.rs b/compiler/rustc_query_system/src/dep_graph/serialized.rs index 0012bf79a1f5..403394674a02 100644 --- a/compiler/rustc_query_system/src/dep_graph/serialized.rs +++ b/compiler/rustc_query_system/src/dep_graph/serialized.rs @@ -191,8 +191,8 @@ fn mask(bits: usize) -> usize { } impl SerializedDepGraph { - #[instrument(level = "debug", skip(d, deps))] - pub fn decode(d: &mut MemDecoder<'_>, deps: &D) -> Arc { + #[instrument(level = "debug", skip(d))] + pub fn decode(d: &mut MemDecoder<'_>) -> Arc { // The last 16 bytes are the node count and edge count. debug!("position: {:?}", d.position()); @@ -280,7 +280,7 @@ impl SerializedDepGraph { if index[node.kind.as_usize()].insert(node.hash, idx).is_some() { // Empty nodes and side effect nodes can have duplicates if node.kind != D::DEP_KIND_NULL && node.kind != D::DEP_KIND_SIDE_EFFECT { - let name = deps.name(node.kind); + let name = D::name(node.kind); panic!( "Error: A dep graph node ({name}) does not have an unique index. \ Running a clean build on a nightly compiler with `-Z incremental-verify-ich` \ From 395ca3be21385c7c7cdab029798f9d5e66fd567f Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 21 Jan 2026 11:57:21 +1100 Subject: [PATCH 66/69] Remove `#[derive(Clone)]` from `DepsType` --- compiler/rustc_middle/src/dep_graph/mod.rs | 1 - compiler/rustc_query_system/src/dep_graph/graph.rs | 12 +++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_middle/src/dep_graph/mod.rs b/compiler/rustc_middle/src/dep_graph/mod.rs index c72f75b43dab..049e868879e9 100644 --- a/compiler/rustc_middle/src/dep_graph/mod.rs +++ b/compiler/rustc_middle/src/dep_graph/mod.rs @@ -20,7 +20,6 @@ pub type DepGraph = rustc_query_system::dep_graph::DepGraph; pub type DepKindStruct<'tcx> = rustc_query_system::dep_graph::DepKindStruct>; -#[derive(Clone)] pub struct DepsType; impl Deps for DepsType { diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index f32f3f78c04b..f0cc9636b75c 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -28,7 +28,6 @@ use crate::dep_graph::edges::EdgesVec; use crate::ich::StableHashingContext; use crate::query::{QueryContext, QuerySideEffect}; -#[derive(Clone)] pub struct DepGraph { data: Option>>, @@ -39,6 +38,17 @@ pub struct DepGraph { virtual_dep_node_index: Arc, } +/// Manual clone impl that does not require `D: Clone`. +impl Clone for DepGraph { + fn clone(&self) -> Self { + let Self { data, virtual_dep_node_index } = self; + Self { + data: Option::>::clone(data), + virtual_dep_node_index: Arc::clone(virtual_dep_node_index), + } + } +} + rustc_index::newtype_index! { pub struct DepNodeIndex {} } From 5e960ef4187f0cc85a3ad854c3fa1b4a145d03f1 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Wed, 21 Jan 2026 09:58:33 +0800 Subject: [PATCH 67/69] triagebot: label `src/tools/build-manifest` with `T-bootstrap` --- triagebot.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/triagebot.toml b/triagebot.toml index a25c2a0a388c..a5cc93809b72 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -499,6 +499,7 @@ trigger_files = [ "bootstrap.example.toml", "src/bootstrap", "src/build_helper", + "src/tools/build-manifest", "src/tools/rust-installer", "src/tools/x", "src/stage0", From 1973d65ade7b98d1fab453691d9ca1d51e094991 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Wed, 21 Jan 2026 09:59:34 +0800 Subject: [PATCH 68/69] misc: roll bootstrap reviewers for `src/tools/build-manifest` --- triagebot.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/triagebot.toml b/triagebot.toml index a5cc93809b72..1572c4c8d045 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1657,6 +1657,7 @@ dep-bumps = [ "/tests/rustdoc-json" = ["@aDotInTheVoid"] "/tests/rustdoc-ui" = ["rustdoc"] "/tests/ui" = ["compiler"] +"/src/tools/build-manifest" = ["bootstrap"] "/src/tools/cargo" = ["@ehuss"] "/src/tools/compiletest" = ["bootstrap", "@wesleywiser", "@oli-obk", "@jieyouxu"] "/src/tools/linkchecker" = ["@ehuss"] From b97036628f23f3fda9251986f0268aff7eb0d593 Mon Sep 17 00:00:00 2001 From: enthropy7 <221884178+enthropy7@users.noreply.github.com> Date: Thu, 22 Jan 2026 18:14:19 +0300 Subject: [PATCH 69/69] Fix ICE when using zero-length SIMD type in extern static Previously, using a zero-length SIMD type in an extern static would cause an internal compiler error. Now it properly emits a diagnostic error instead of panicking. --- compiler/rustc_hir_analysis/src/check/check.rs | 7 +++++++ tests/ui/simd/extern-static-zero-length.rs | 13 +++++++++++++ tests/ui/simd/extern-static-zero-length.stderr | 14 ++++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 tests/ui/simd/extern-static-zero-length.rs create mode 100644 tests/ui/simd/extern-static-zero-length.stderr diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index c37611ab2ee7..f2b06e1cde71 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -24,6 +24,7 @@ use rustc_middle::ty::{ TypeVisitable, TypeVisitableExt, fold_regions, }; use rustc_session::lint::builtin::UNINHABITED_STATIC; +use rustc_span::source_map::Spanned; use rustc_target::spec::{AbiMap, AbiMapping}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedDirective; @@ -192,6 +193,12 @@ fn check_static_inhabited(tcx: TyCtxt<'_>, def_id: LocalDefId) { tcx.dcx().emit_err(errors::TooLargeStatic { span }); return; } + // SIMD types with invalid layout (e.g., zero-length) should emit an error + Err(e @ LayoutError::InvalidSimd { .. }) => { + let ty_span = tcx.ty_span(def_id); + tcx.dcx().emit_err(Spanned { span: ty_span, node: e.into_diagnostic() }); + return; + } // Generic statics are rejected, but we still reach this case. Err(e) => { tcx.dcx().span_delayed_bug(span, format!("{e:?}")); diff --git a/tests/ui/simd/extern-static-zero-length.rs b/tests/ui/simd/extern-static-zero-length.rs new file mode 100644 index 000000000000..5ca7b63fc523 --- /dev/null +++ b/tests/ui/simd/extern-static-zero-length.rs @@ -0,0 +1,13 @@ +#![feature(repr_simd)] + +#[repr(simd)] +struct Simd([T; N]); + +unsafe extern "C" { + static VAR: Simd; + //~^ ERROR the SIMD type `Simd` has zero elements + static VAR2: Simd; + //~^ ERROR the SIMD type `Simd` has more elements than the limit 32768 +} + +fn main() {} diff --git a/tests/ui/simd/extern-static-zero-length.stderr b/tests/ui/simd/extern-static-zero-length.stderr new file mode 100644 index 000000000000..49c05f27d8c5 --- /dev/null +++ b/tests/ui/simd/extern-static-zero-length.stderr @@ -0,0 +1,14 @@ +error: the SIMD type `Simd` has zero elements + --> $DIR/extern-static-zero-length.rs:7:17 + | +LL | static VAR: Simd; + | ^^^^^^^^^^^ + +error: the SIMD type `Simd` has more elements than the limit 32768 + --> $DIR/extern-static-zero-length.rs:9:18 + | +LL | static VAR2: Simd; + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors +