From aa460dc24751c038a6598a8adff7914065025b93 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Tue, 10 Jun 2025 19:25:36 +0200 Subject: [PATCH 01/34] tests: Add `RUST_BACKTRACE` and `-Cpanic` revisions to `panic-main.rs` test --- tests/ui/panics/panic-main.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/ui/panics/panic-main.rs b/tests/ui/panics/panic-main.rs index 3876dbb37c37d..bf79de78a579c 100644 --- a/tests/ui/panics/panic-main.rs +++ b/tests/ui/panics/panic-main.rs @@ -1,3 +1,26 @@ +//@ revisions: default abort-zero abort-one abort-full unwind-zero unwind-one unwind-full + +//@[abort-zero] compile-flags: -Cpanic=abort +//@[abort-zero] no-prefer-dynamic +//@[abort-zero] exec-env:RUST_BACKTRACE=0 + +//@[abort-one] compile-flags: -Cpanic=abort +//@[abort-one] no-prefer-dynamic +//@[abort-one] exec-env:RUST_BACKTRACE=1 + +//@[abort-full] compile-flags: -Cpanic=abort +//@[abort-full] no-prefer-dynamic +//@[abort-full] exec-env:RUST_BACKTRACE=full + +//@[unwind-zero] compile-flags: -Cpanic=unwind +//@[unwind-zero] exec-env:RUST_BACKTRACE=0 + +//@[unwind-one] compile-flags: -Cpanic=unwind +//@[unwind-one] exec-env:RUST_BACKTRACE=1 + +//@[unwind-full] compile-flags: -Cpanic=unwind +//@[unwind-full] exec-env:RUST_BACKTRACE=full + //@ run-fail //@ error-pattern:moop //@ needs-subprocess From 653bb64c7530c86d00b875e1ef14ac75455d2d80 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 3 Jul 2025 14:24:58 +0000 Subject: [PATCH 02/34] Remove LtoModuleCodegen Most uses of it either contain a fat or thin lto module. Only WorkItem::LTO could contain both, but splitting that enum variant doesn't complicate things much. --- compiler/rustc_codegen_gcc/src/back/lto.rs | 15 +++-- compiler/rustc_codegen_gcc/src/lib.rs | 6 +- compiler/rustc_codegen_llvm/src/back/lto.rs | 17 +++--- compiler/rustc_codegen_llvm/src/lib.rs | 6 +- compiler/rustc_codegen_ssa/src/back/lto.rs | 60 ------------------- compiler/rustc_codegen_ssa/src/back/write.rs | 48 +++++++++++---- .../rustc_codegen_ssa/src/traits/write.rs | 6 +- 7 files changed, 58 insertions(+), 100 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/back/lto.rs b/compiler/rustc_codegen_gcc/src/back/lto.rs index 10fce860b7770..e554dd2500bda 100644 --- a/compiler/rustc_codegen_gcc/src/back/lto.rs +++ b/compiler/rustc_codegen_gcc/src/back/lto.rs @@ -24,7 +24,7 @@ use std::sync::Arc; use gccjit::{Context, OutputKind}; use object::read::archive::ArchiveFile; -use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule, ThinShared}; +use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule, ThinShared}; use rustc_codegen_ssa::back::symbol_export; use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput}; use rustc_codegen_ssa::traits::*; @@ -176,7 +176,7 @@ pub(crate) fn run_fat( cgcx: &CodegenContext, modules: Vec>, cached_modules: Vec<(SerializedModule, WorkProduct)>, -) -> Result, FatalError> { +) -> Result, FatalError> { let dcx = cgcx.create_dcx(); let dcx = dcx.handle(); let lto_data = prepare_lto(cgcx, dcx)?; @@ -201,7 +201,7 @@ fn fat_lto( mut serialized_modules: Vec<(SerializedModule, CString)>, tmp_path: TempDir, //symbols_below_threshold: &[String], -) -> Result, FatalError> { +) -> Result, FatalError> { let _timer = cgcx.prof.generic_activity("GCC_fat_lto_build_monolithic_module"); info!("going for a fat lto"); @@ -334,7 +334,7 @@ fn fat_lto( // of now. module.module_llvm.temp_dir = Some(tmp_path); - Ok(LtoModuleCodegen::Fat(module)) + Ok(module) } pub struct ModuleBuffer(PathBuf); @@ -358,7 +358,7 @@ pub(crate) fn run_thin( cgcx: &CodegenContext, modules: Vec<(String, ThinBuffer)>, cached_modules: Vec<(SerializedModule, WorkProduct)>, -) -> Result<(Vec>, Vec), FatalError> { +) -> Result<(Vec>, Vec), FatalError> { let dcx = cgcx.create_dcx(); let dcx = dcx.handle(); let lto_data = prepare_lto(cgcx, dcx)?; @@ -427,7 +427,7 @@ fn thin_lto( tmp_path: TempDir, cached_modules: Vec<(SerializedModule, WorkProduct)>, //_symbols_below_threshold: &[String], -) -> Result<(Vec>, Vec), FatalError> { +) -> Result<(Vec>, Vec), FatalError> { let _timer = cgcx.prof.generic_activity("LLVM_thin_lto_global_analysis"); info!("going for that thin, thin LTO"); @@ -573,8 +573,7 @@ fn thin_lto( }*/ info!(" - {}: re-compiled", module_name); - opt_jobs - .push(LtoModuleCodegen::Thin(ThinModule { shared: shared.clone(), idx: module_index })); + opt_jobs.push(ThinModule { shared: shared.clone(), idx: module_index }); } // Save the current ThinLTO import information for the next compilation diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index a912678ef2a10..a151a0ab75534 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -97,7 +97,7 @@ use gccjit::{CType, Context, OptimizationLevel}; use gccjit::{TargetInfo, Version}; use rustc_ast::expand::allocator::AllocatorKind; use rustc_ast::expand::autodiff_attrs::AutoDiffItem; -use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule}; +use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule}; use rustc_codegen_ssa::back::write::{ CodegenContext, FatLtoInput, ModuleConfig, TargetMachineFactoryFn, }; @@ -361,7 +361,7 @@ impl WriteBackendMethods for GccCodegenBackend { cgcx: &CodegenContext, modules: Vec>, cached_modules: Vec<(SerializedModule, WorkProduct)>, - ) -> Result, FatalError> { + ) -> Result, FatalError> { back::lto::run_fat(cgcx, modules, cached_modules) } @@ -369,7 +369,7 @@ impl WriteBackendMethods for GccCodegenBackend { cgcx: &CodegenContext, modules: Vec<(String, Self::ThinBuffer)>, cached_modules: Vec<(SerializedModule, WorkProduct)>, - ) -> Result<(Vec>, Vec), FatalError> { + ) -> Result<(Vec>, Vec), FatalError> { back::lto::run_thin(cgcx, modules, cached_modules) } diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 9c62244f3c9ff..0198b9f0cf0c6 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use std::{io, iter, slice}; use object::read::archive::ArchiveFile; -use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule, ThinShared}; +use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule, ThinShared}; use rustc_codegen_ssa::back::symbol_export; use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput}; use rustc_codegen_ssa::traits::*; @@ -201,7 +201,7 @@ pub(crate) fn run_fat( cgcx: &CodegenContext, modules: Vec>, cached_modules: Vec<(SerializedModule, WorkProduct)>, -) -> Result, FatalError> { +) -> Result, FatalError> { let dcx = cgcx.create_dcx(); let dcx = dcx.handle(); let (symbols_below_threshold, upstream_modules) = prepare_lto(cgcx, dcx)?; @@ -217,7 +217,7 @@ pub(crate) fn run_thin( cgcx: &CodegenContext, modules: Vec<(String, ThinBuffer)>, cached_modules: Vec<(SerializedModule, WorkProduct)>, -) -> Result<(Vec>, Vec), FatalError> { +) -> Result<(Vec>, Vec), FatalError> { let dcx = cgcx.create_dcx(); let dcx = dcx.handle(); let (symbols_below_threshold, upstream_modules) = prepare_lto(cgcx, dcx)?; @@ -248,7 +248,7 @@ fn fat_lto( cached_modules: Vec<(SerializedModule, WorkProduct)>, mut serialized_modules: Vec<(SerializedModule, CString)>, symbols_below_threshold: &[*const libc::c_char], -) -> Result, FatalError> { +) -> Result, FatalError> { let _timer = cgcx.prof.generic_activity("LLVM_fat_lto_build_monolithic_module"); info!("going for a fat lto"); @@ -366,7 +366,7 @@ fn fat_lto( save_temp_bitcode(cgcx, &module, "lto.after-restriction"); } - Ok(LtoModuleCodegen::Fat(module)) + Ok(module) } pub(crate) struct Linker<'a>(&'a mut llvm::Linker<'a>); @@ -436,7 +436,7 @@ fn thin_lto( serialized_modules: Vec<(SerializedModule, CString)>, cached_modules: Vec<(SerializedModule, WorkProduct)>, symbols_below_threshold: &[*const libc::c_char], -) -> Result<(Vec>, Vec), FatalError> { +) -> Result<(Vec>, Vec), FatalError> { let _timer = cgcx.prof.generic_activity("LLVM_thin_lto_global_analysis"); unsafe { info!("going for that thin, thin LTO"); @@ -568,10 +568,7 @@ fn thin_lto( } info!(" - {}: re-compiled", module_name); - opt_jobs.push(LtoModuleCodegen::Thin(ThinModule { - shared: Arc::clone(&shared), - idx: module_index, - })); + opt_jobs.push(ThinModule { shared: Arc::clone(&shared), idx: module_index }); } // Save the current ThinLTO import information for the next compilation diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index cdfffbe47bfa5..57b37959e0d01 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -30,7 +30,7 @@ use errors::{AutoDiffWithoutLTO, ParseTargetMachineConfig}; use llvm_util::target_config; use rustc_ast::expand::allocator::AllocatorKind; use rustc_ast::expand::autodiff_attrs::AutoDiffItem; -use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule}; +use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule}; use rustc_codegen_ssa::back::write::{ CodegenContext, FatLtoInput, ModuleConfig, TargetMachineFactoryConfig, TargetMachineFactoryFn, }; @@ -178,14 +178,14 @@ impl WriteBackendMethods for LlvmCodegenBackend { cgcx: &CodegenContext, modules: Vec>, cached_modules: Vec<(SerializedModule, WorkProduct)>, - ) -> Result, FatalError> { + ) -> Result, FatalError> { back::lto::run_fat(cgcx, modules, cached_modules) } fn run_thin_lto( cgcx: &CodegenContext, modules: Vec<(String, Self::ThinBuffer)>, cached_modules: Vec<(SerializedModule, WorkProduct)>, - ) -> Result<(Vec>, Vec), FatalError> { + ) -> Result<(Vec>, Vec), FatalError> { back::lto::run_thin(cgcx, modules, cached_modules) } fn optimize( diff --git a/compiler/rustc_codegen_ssa/src/back/lto.rs b/compiler/rustc_codegen_ssa/src/back/lto.rs index ce6fe8a191b3b..b49b6783bbd9a 100644 --- a/compiler/rustc_codegen_ssa/src/back/lto.rs +++ b/compiler/rustc_codegen_ssa/src/back/lto.rs @@ -1,13 +1,8 @@ use std::ffi::CString; use std::sync::Arc; -use rustc_ast::expand::autodiff_attrs::AutoDiffItem; use rustc_data_structures::memmap::Mmap; -use rustc_errors::FatalError; -use super::write::CodegenContext; -use crate::ModuleCodegen; -use crate::back::write::ModuleConfig; use crate::traits::*; pub struct ThinModule { @@ -42,61 +37,6 @@ pub struct ThinShared { pub module_names: Vec, } -pub enum LtoModuleCodegen { - Fat(ModuleCodegen), - Thin(ThinModule), -} - -impl LtoModuleCodegen { - pub fn name(&self) -> &str { - match *self { - LtoModuleCodegen::Fat(_) => "everything", - LtoModuleCodegen::Thin(ref m) => m.name(), - } - } - - /// Optimize this module within the given codegen context. - pub fn optimize( - self, - cgcx: &CodegenContext, - ) -> Result, FatalError> { - match self { - LtoModuleCodegen::Fat(mut module) => { - B::optimize_fat(cgcx, &mut module)?; - Ok(module) - } - LtoModuleCodegen::Thin(thin) => B::optimize_thin(cgcx, thin), - } - } - - /// A "gauge" of how costly it is to optimize this module, used to sort - /// biggest modules first. - pub fn cost(&self) -> u64 { - match *self { - // Only one module with fat LTO, so the cost doesn't matter. - LtoModuleCodegen::Fat(_) => 0, - LtoModuleCodegen::Thin(ref m) => m.cost(), - } - } - - /// Run autodiff on Fat LTO module - pub fn autodiff( - self, - cgcx: &CodegenContext, - diff_fncs: Vec, - config: &ModuleConfig, - ) -> Result, FatalError> { - match &self { - LtoModuleCodegen::Fat(module) => { - B::autodiff(cgcx, &module, diff_fncs, config)?; - } - _ => panic!("autodiff called with non-fat LTO module"), - } - - Ok(self) - } -} - pub enum SerializedModule { Local(M), FromRlib(Vec), diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 8330e4f7af0c7..5395b55092b3c 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -408,14 +408,16 @@ fn generate_lto_work( if !needs_fat_lto.is_empty() { assert!(needs_thin_lto.is_empty()); - let mut module = + let module = B::run_fat_lto(cgcx, needs_fat_lto, import_only_modules).unwrap_or_else(|e| e.raise()); if cgcx.lto == Lto::Fat && !autodiff.is_empty() { let config = cgcx.config(ModuleKind::Regular); - module = module.autodiff(cgcx, autodiff, config).unwrap_or_else(|e| e.raise()); + if let Err(err) = B::autodiff(cgcx, &module, autodiff, config) { + err.raise(); + } } // We are adding a single work item, so the cost doesn't matter. - vec![(WorkItem::LTO(module), 0)] + vec![(WorkItem::FatLto(module), 0)] } else { if !autodiff.is_empty() { let dcx = cgcx.create_dcx(); @@ -428,7 +430,7 @@ fn generate_lto_work( .into_iter() .map(|module| { let cost = module.cost(); - (WorkItem::LTO(module), cost) + (WorkItem::ThinLto(module), cost) }) .chain(copy_jobs.into_iter().map(|wp| { ( @@ -736,15 +738,19 @@ pub(crate) enum WorkItem { /// Copy the post-LTO artifacts from the incremental cache to the output /// directory. CopyPostLtoArtifacts(CachedModuleCodegen), - /// Performs (Thin)LTO on the given module. - LTO(lto::LtoModuleCodegen), + /// Performs fat LTO on the given module. + FatLto(ModuleCodegen), + /// Performs thin-LTO on the given module. + ThinLto(lto::ThinModule), } impl WorkItem { fn module_kind(&self) -> ModuleKind { match *self { WorkItem::Optimize(ref m) => m.kind, - WorkItem::CopyPostLtoArtifacts(_) | WorkItem::LTO(_) => ModuleKind::Regular, + WorkItem::CopyPostLtoArtifacts(_) | WorkItem::FatLto(_) | WorkItem::ThinLto(_) => { + ModuleKind::Regular + } } } @@ -792,7 +798,8 @@ impl WorkItem { match self { WorkItem::Optimize(m) => desc("opt", "optimize module", &m.name), WorkItem::CopyPostLtoArtifacts(m) => desc("cpy", "copy LTO artifacts for", &m.name), - WorkItem::LTO(m) => desc("lto", "LTO module", m.name()), + WorkItem::FatLto(_) => desc("lto", "fat LTO module", "everything"), + WorkItem::ThinLto(m) => desc("lto", "thin-LTO module", m.name()), } } } @@ -996,12 +1003,21 @@ fn execute_copy_from_cache_work_item( }) } -fn execute_lto_work_item( +fn execute_fat_lto_work_item( + cgcx: &CodegenContext, + mut module: ModuleCodegen, + module_config: &ModuleConfig, +) -> Result, FatalError> { + B::optimize_fat(cgcx, &mut module)?; + finish_intra_module_work(cgcx, module, module_config) +} + +fn execute_thin_lto_work_item( cgcx: &CodegenContext, - module: lto::LtoModuleCodegen, + module: lto::ThinModule, module_config: &ModuleConfig, ) -> Result, FatalError> { - let module = module.optimize(cgcx)?; + let module = B::optimize_thin(cgcx, module)?; finish_intra_module_work(cgcx, module, module_config) } @@ -1842,10 +1858,16 @@ fn spawn_work<'a, B: ExtraBackendMethods>( ); Ok(execute_copy_from_cache_work_item(&cgcx, m, module_config)) } - WorkItem::LTO(m) => { + WorkItem::FatLto(m) => { + let _timer = cgcx + .prof + .generic_activity_with_arg("codegen_module_perform_lto", "everything"); + execute_fat_lto_work_item(&cgcx, m, module_config) + } + WorkItem::ThinLto(m) => { let _timer = cgcx.prof.generic_activity_with_arg("codegen_module_perform_lto", m.name()); - execute_lto_work_item(&cgcx, m, module_config) + execute_thin_lto_work_item(&cgcx, m, module_config) } }) }; diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index 07a0609fda1a1..78380973ffc80 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -2,7 +2,7 @@ use rustc_ast::expand::autodiff_attrs::AutoDiffItem; use rustc_errors::{DiagCtxtHandle, FatalError}; use rustc_middle::dep_graph::WorkProduct; -use crate::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule}; +use crate::back::lto::{SerializedModule, ThinModule}; use crate::back::write::{CodegenContext, FatLtoInput, ModuleConfig}; use crate::{CompiledModule, ModuleCodegen}; @@ -26,7 +26,7 @@ pub trait WriteBackendMethods: Clone + 'static { cgcx: &CodegenContext, modules: Vec>, cached_modules: Vec<(SerializedModule, WorkProduct)>, - ) -> Result, FatalError>; + ) -> Result, FatalError>; /// Performs thin LTO by performing necessary global analysis and returning two /// lists, one of the modules that need optimization and another for modules that /// can simply be copied over from the incr. comp. cache. @@ -34,7 +34,7 @@ pub trait WriteBackendMethods: Clone + 'static { cgcx: &CodegenContext, modules: Vec<(String, Self::ThinBuffer)>, cached_modules: Vec<(SerializedModule, WorkProduct)>, - ) -> Result<(Vec>, Vec), FatalError>; + ) -> Result<(Vec>, Vec), FatalError>; fn print_pass_timings(&self); fn print_statistics(&self); fn optimize( From 7fd78df3463c761a693ea79265a6a1faaf2ed51b Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 3 Jul 2025 14:43:09 +0000 Subject: [PATCH 03/34] Move dcx creation into WriteBackendMethods::codegen --- compiler/rustc_codegen_gcc/src/back/write.rs | 4 +++- compiler/rustc_codegen_gcc/src/lib.rs | 3 +-- compiler/rustc_codegen_llvm/src/back/write.rs | 4 +++- compiler/rustc_codegen_llvm/src/lib.rs | 3 +-- compiler/rustc_codegen_ssa/src/back/write.rs | 7 ++----- compiler/rustc_codegen_ssa/src/traits/write.rs | 1 - 6 files changed, 10 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/back/write.rs b/compiler/rustc_codegen_gcc/src/back/write.rs index d03d063bdace6..113abe70805b0 100644 --- a/compiler/rustc_codegen_gcc/src/back/write.rs +++ b/compiler/rustc_codegen_gcc/src/back/write.rs @@ -16,10 +16,12 @@ use crate::{GccCodegenBackend, GccContext}; pub(crate) fn codegen( cgcx: &CodegenContext, - dcx: DiagCtxtHandle<'_>, module: ModuleCodegen, config: &ModuleConfig, ) -> Result { + let dcx = cgcx.create_dcx(); + let dcx = dcx.handle(); + let _timer = cgcx.prof.generic_activity_with_arg("GCC_module_codegen", &*module.name); { let context = &module.module_llvm.context; diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index a151a0ab75534..34452bdd2005f 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -408,11 +408,10 @@ impl WriteBackendMethods for GccCodegenBackend { fn codegen( cgcx: &CodegenContext, - dcx: DiagCtxtHandle<'_>, module: ModuleCodegen, config: &ModuleConfig, ) -> Result { - back::write::codegen(cgcx, dcx, module, config) + back::write::codegen(cgcx, module, config) } fn prepare_thin( diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index bde6a9cf4bc61..1f7a785bbe725 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -817,10 +817,12 @@ pub(crate) fn link( pub(crate) fn codegen( cgcx: &CodegenContext, - dcx: DiagCtxtHandle<'_>, module: ModuleCodegen, config: &ModuleConfig, ) -> Result { + let dcx = cgcx.create_dcx(); + let dcx = dcx.handle(); + let _timer = cgcx.prof.generic_activity_with_arg("LLVM_module_codegen", &*module.name); { let llmod = module.module_llvm.llmod(); diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 57b37959e0d01..4d85683de8c43 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -212,11 +212,10 @@ impl WriteBackendMethods for LlvmCodegenBackend { } fn codegen( cgcx: &CodegenContext, - dcx: DiagCtxtHandle<'_>, module: ModuleCodegen, config: &ModuleConfig, ) -> Result { - back::write::codegen(cgcx, dcx, module, config) + back::write::codegen(cgcx, module, config) } fn prepare_thin( module: ModuleCodegen, diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 5395b55092b3c..29e67be6eb619 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -1026,11 +1026,8 @@ fn finish_intra_module_work( module: ModuleCodegen, module_config: &ModuleConfig, ) -> Result, FatalError> { - let dcx = cgcx.create_dcx(); - let dcx = dcx.handle(); - if !cgcx.opts.unstable_opts.combine_cgu || module.kind == ModuleKind::Allocator { - let module = B::codegen(cgcx, dcx, module, module_config)?; + let module = B::codegen(cgcx, module, module_config)?; Ok(WorkItemResult::Finished(module)) } else { Ok(WorkItemResult::NeedsLink(module)) @@ -1718,7 +1715,7 @@ fn start_executing_work( let dcx = dcx.handle(); let module = B::run_link(&cgcx, dcx, needs_link).map_err(|_| ())?; let module = - B::codegen(&cgcx, dcx, module, cgcx.config(ModuleKind::Regular)).map_err(|_| ())?; + B::codegen(&cgcx, module, cgcx.config(ModuleKind::Regular)).map_err(|_| ())?; compiled_modules.push(module); } diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index 78380973ffc80..c732c4b2ab860 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -53,7 +53,6 @@ pub trait WriteBackendMethods: Clone + 'static { ) -> Result, FatalError>; fn codegen( cgcx: &CodegenContext, - dcx: DiagCtxtHandle<'_>, module: ModuleCodegen, config: &ModuleConfig, ) -> Result; From ee280a596d3b7c401547be9a6afadf7298ee3a55 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 3 Jul 2025 14:44:26 +0000 Subject: [PATCH 04/34] Fat LTO always produces a single object file, so -Zcombine-cgu has no effect --- compiler/rustc_codegen_ssa/src/back/write.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 29e67be6eb619..762dc8f6c2d6c 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -1009,7 +1009,9 @@ fn execute_fat_lto_work_item( module_config: &ModuleConfig, ) -> Result, FatalError> { B::optimize_fat(cgcx, &mut module)?; - finish_intra_module_work(cgcx, module, module_config) + + let module = B::codegen(cgcx, module, module_config)?; + Ok(WorkItemResult::Finished(module)) } fn execute_thin_lto_work_item( From 797084dc92c251ff0c986adfdd4a7e261f499b02 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 3 Jul 2025 14:50:28 +0000 Subject: [PATCH 05/34] Split generate_lto_work between fat and thin-LTO cases --- compiler/rustc_codegen_ssa/src/back/write.rs | 112 +++++++++++-------- 1 file changed, 63 insertions(+), 49 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 762dc8f6c2d6c..21d5d3ced000c 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -397,52 +397,50 @@ impl CodegenContext { } } -fn generate_lto_work( +fn generate_fat_lto_work( cgcx: &CodegenContext, autodiff: Vec, needs_fat_lto: Vec>, - needs_thin_lto: Vec<(String, B::ThinBuffer)>, import_only_modules: Vec<(SerializedModule, WorkProduct)>, -) -> Vec<(WorkItem, u64)> { +) -> WorkItem { let _prof_timer = cgcx.prof.generic_activity("codegen_generate_lto_work"); - if !needs_fat_lto.is_empty() { - assert!(needs_thin_lto.is_empty()); - let module = - B::run_fat_lto(cgcx, needs_fat_lto, import_only_modules).unwrap_or_else(|e| e.raise()); - if cgcx.lto == Lto::Fat && !autodiff.is_empty() { - let config = cgcx.config(ModuleKind::Regular); - if let Err(err) = B::autodiff(cgcx, &module, autodiff, config) { - err.raise(); - } + let module = + B::run_fat_lto(cgcx, needs_fat_lto, import_only_modules).unwrap_or_else(|e| e.raise()); + if cgcx.lto == Lto::Fat && !autodiff.is_empty() { + let config = cgcx.config(ModuleKind::Regular); + if let Err(err) = B::autodiff(cgcx, &module, autodiff, config) { + err.raise(); } - // We are adding a single work item, so the cost doesn't matter. - vec![(WorkItem::FatLto(module), 0)] - } else { - if !autodiff.is_empty() { - let dcx = cgcx.create_dcx(); - dcx.handle().emit_fatal(AutodiffWithoutLto {}); - } - assert!(needs_fat_lto.is_empty()); - let (lto_modules, copy_jobs) = B::run_thin_lto(cgcx, needs_thin_lto, import_only_modules) - .unwrap_or_else(|e| e.raise()); - lto_modules - .into_iter() - .map(|module| { - let cost = module.cost(); - (WorkItem::ThinLto(module), cost) - }) - .chain(copy_jobs.into_iter().map(|wp| { - ( - WorkItem::CopyPostLtoArtifacts(CachedModuleCodegen { - name: wp.cgu_name.clone(), - source: wp, - }), - 0, // copying is very cheap - ) - })) - .collect() } + WorkItem::FatLto(module) +} + +fn generate_thin_lto_work( + cgcx: &CodegenContext, + needs_thin_lto: Vec<(String, B::ThinBuffer)>, + import_only_modules: Vec<(SerializedModule, WorkProduct)>, +) -> Vec<(WorkItem, u64)> { + let _prof_timer = cgcx.prof.generic_activity("codegen_thin_generate_lto_work"); + + let (lto_modules, copy_jobs) = + B::run_thin_lto(cgcx, needs_thin_lto, import_only_modules).unwrap_or_else(|e| e.raise()); + lto_modules + .into_iter() + .map(|module| { + let cost = module.cost(); + (WorkItem::ThinLto(module), cost) + }) + .chain(copy_jobs.into_iter().map(|wp| { + ( + WorkItem::CopyPostLtoArtifacts(CachedModuleCodegen { + name: wp.cgu_name.clone(), + source: wp, + }), + 0, // copying is very cheap + ) + })) + .collect() } struct CompiledModules { @@ -1489,20 +1487,36 @@ fn start_executing_work( let needs_thin_lto = mem::take(&mut needs_thin_lto); let import_only_modules = mem::take(&mut lto_import_only_modules); - for (work, cost) in generate_lto_work( - &cgcx, - autodiff_items.clone(), - needs_fat_lto, - needs_thin_lto, - import_only_modules, - ) { - let insertion_index = work_items - .binary_search_by_key(&cost, |&(_, cost)| cost) - .unwrap_or_else(|e| e); - work_items.insert(insertion_index, (work, cost)); + if !needs_fat_lto.is_empty() { + assert!(needs_thin_lto.is_empty()); + + let work = generate_fat_lto_work( + &cgcx, + autodiff_items.clone(), + needs_fat_lto, + import_only_modules, + ); + work_items.push((work, 0)); if cgcx.parallel { helper.request_token(); } + } else { + if !autodiff_items.is_empty() { + let dcx = cgcx.create_dcx(); + dcx.handle().emit_fatal(AutodiffWithoutLto {}); + } + + for (work, cost) in + generate_thin_lto_work(&cgcx, needs_thin_lto, import_only_modules) + { + let insertion_index = work_items + .binary_search_by_key(&cost, |&(_, cost)| cost) + .unwrap_or_else(|e| e); + work_items.insert(insertion_index, (work, cost)); + if cgcx.parallel { + helper.request_token(); + } + } } } From 332f1b1bdddb01f2f203f47cd3f30f677ae8d9ea Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 3 Jul 2025 15:39:19 +0000 Subject: [PATCH 06/34] Pass in autodiff items when starting the coordinator thread As opposed to sending a message to the coordinator thread. --- compiler/rustc_codegen_ssa/src/back/write.rs | 16 ++++------------ compiler/rustc_codegen_ssa/src/base.rs | 9 ++------- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 21d5d3ced000c..dcee7aec73392 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -470,6 +470,7 @@ pub(crate) fn start_async_codegen( backend: B, tcx: TyCtxt<'_>, target_cpu: String, + autodiff_items: &[AutoDiffItem], ) -> OngoingCodegen { let (coordinator_send, coordinator_receive) = channel(); @@ -488,6 +489,7 @@ pub(crate) fn start_async_codegen( backend.clone(), tcx, &crate_info, + autodiff_items, shared_emitter, codegen_worker_send, coordinator_receive, @@ -1044,9 +1046,6 @@ pub(crate) enum Message { /// Sent from a backend worker thread. WorkItem { result: Result, Option>, worker_id: usize }, - /// A vector containing all the AutoDiff tasks that we have to pass to Enzyme. - AddAutoDiffItems(Vec), - /// The frontend has finished generating something (backend IR or a /// post-LTO artifact) for a codegen unit, and it should be passed to the /// backend. Sent from the main thread. @@ -1113,6 +1112,7 @@ fn start_executing_work( backend: B, tcx: TyCtxt<'_>, crate_info: &CrateInfo, + autodiff_items: &[AutoDiffItem], shared_emitter: SharedEmitter, codegen_worker_send: Sender, coordinator_receive: Receiver>, @@ -1122,6 +1122,7 @@ fn start_executing_work( ) -> thread::JoinHandle> { let coordinator_send = tx_to_llvm_workers; let sess = tcx.sess; + let autodiff_items = autodiff_items.to_vec(); let mut each_linked_rlib_for_lto = Vec::new(); drop(link::each_linked_rlib(crate_info, None, &mut |cnum, path| { @@ -1375,7 +1376,6 @@ fn start_executing_work( // This is where we collect codegen units that have gone all the way // through codegen and LLVM. - let mut autodiff_items = Vec::new(); let mut compiled_modules = vec![]; let mut compiled_allocator_module = None; let mut needs_link = Vec::new(); @@ -1645,10 +1645,6 @@ fn start_executing_work( main_thread_state = MainThreadState::Idle; } - Message::AddAutoDiffItems(mut items) => { - autodiff_items.append(&mut items); - } - Message::CodegenComplete => { if codegen_state != Aborted { codegen_state = Completed; @@ -2117,10 +2113,6 @@ impl OngoingCodegen { drop(self.coordinator.sender.send(Box::new(Message::CodegenComplete::))); } - pub(crate) fn submit_autodiff_items(&self, items: Vec) { - drop(self.coordinator.sender.send(Box::new(Message::::AddAutoDiffItems(items)))); - } - pub(crate) fn check_for_errors(&self, sess: &Session) { self.shared_emitter_main.check(sess, false); } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 102d4ea2fa6cf..01ce07f4ccfea 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -647,7 +647,7 @@ pub fn codegen_crate( ) -> OngoingCodegen { // Skip crate items and just output metadata in -Z no-codegen mode. if tcx.sess.opts.unstable_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() { - let ongoing_codegen = start_async_codegen(backend, tcx, target_cpu); + let ongoing_codegen = start_async_codegen(backend, tcx, target_cpu, &[]); ongoing_codegen.codegen_finished(tcx); @@ -667,7 +667,6 @@ pub fn codegen_crate( // codegen units. let MonoItemPartitions { codegen_units, autodiff_items, .. } = tcx.collect_and_partition_mono_items(()); - let autodiff_fncs = autodiff_items.to_vec(); // Force all codegen_unit queries so they are already either red or green // when compile_codegen_unit accesses them. We are not able to re-execute @@ -680,7 +679,7 @@ pub fn codegen_crate( } } - let ongoing_codegen = start_async_codegen(backend.clone(), tcx, target_cpu); + let ongoing_codegen = start_async_codegen(backend.clone(), tcx, target_cpu, autodiff_items); // Codegen an allocator shim, if necessary. if let Some(kind) = allocator_kind_for_codegen(tcx) { @@ -710,10 +709,6 @@ pub fn codegen_crate( ); } - if !autodiff_fncs.is_empty() { - ongoing_codegen.submit_autodiff_items(autodiff_fncs); - } - // For better throughput during parallel processing by LLVM, we used to sort // CGUs largest to smallest. This would lead to better thread utilization // by, for example, preventing a large CGU from being processed last and From 779cb00311dc0bbb942dbdaefa24f98bd2d4cbbe Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 3 Jul 2025 16:05:38 +0000 Subject: [PATCH 07/34] Move run_fat_lto call into execute_fat_lto_work_item This will allow merging all fat LTO steps together. In addition it reduces the amount of work done on the coordinator thread without jobserver token. --- compiler/rustc_codegen_ssa/src/back/write.rs | 68 +++++++++++--------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index dcee7aec73392..c7d2fcef29f99 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -397,25 +397,6 @@ impl CodegenContext { } } -fn generate_fat_lto_work( - cgcx: &CodegenContext, - autodiff: Vec, - needs_fat_lto: Vec>, - import_only_modules: Vec<(SerializedModule, WorkProduct)>, -) -> WorkItem { - let _prof_timer = cgcx.prof.generic_activity("codegen_generate_lto_work"); - - let module = - B::run_fat_lto(cgcx, needs_fat_lto, import_only_modules).unwrap_or_else(|e| e.raise()); - if cgcx.lto == Lto::Fat && !autodiff.is_empty() { - let config = cgcx.config(ModuleKind::Regular); - if let Err(err) = B::autodiff(cgcx, &module, autodiff, config) { - err.raise(); - } - } - WorkItem::FatLto(module) -} - fn generate_thin_lto_work( cgcx: &CodegenContext, needs_thin_lto: Vec<(String, B::ThinBuffer)>, @@ -739,7 +720,11 @@ pub(crate) enum WorkItem { /// directory. CopyPostLtoArtifacts(CachedModuleCodegen), /// Performs fat LTO on the given module. - FatLto(ModuleCodegen), + FatLto { + needs_fat_lto: Vec>, + import_only_modules: Vec<(SerializedModule, WorkProduct)>, + autodiff: Vec, + }, /// Performs thin-LTO on the given module. ThinLto(lto::ThinModule), } @@ -748,7 +733,7 @@ impl WorkItem { fn module_kind(&self) -> ModuleKind { match *self { WorkItem::Optimize(ref m) => m.kind, - WorkItem::CopyPostLtoArtifacts(_) | WorkItem::FatLto(_) | WorkItem::ThinLto(_) => { + WorkItem::CopyPostLtoArtifacts(_) | WorkItem::FatLto { .. } | WorkItem::ThinLto(_) => { ModuleKind::Regular } } @@ -798,7 +783,7 @@ impl WorkItem { match self { WorkItem::Optimize(m) => desc("opt", "optimize module", &m.name), WorkItem::CopyPostLtoArtifacts(m) => desc("cpy", "copy LTO artifacts for", &m.name), - WorkItem::FatLto(_) => desc("lto", "fat LTO module", "everything"), + WorkItem::FatLto { .. } => desc("lto", "fat LTO module", "everything"), WorkItem::ThinLto(m) => desc("lto", "thin-LTO module", m.name()), } } @@ -1005,9 +990,21 @@ fn execute_copy_from_cache_work_item( fn execute_fat_lto_work_item( cgcx: &CodegenContext, - mut module: ModuleCodegen, + needs_fat_lto: Vec>, + import_only_modules: Vec<(SerializedModule, WorkProduct)>, + autodiff: Vec, module_config: &ModuleConfig, ) -> Result, FatalError> { + let mut module = + B::run_fat_lto(cgcx, needs_fat_lto, import_only_modules).unwrap_or_else(|e| e.raise()); + + if cgcx.lto == Lto::Fat && !autodiff.is_empty() { + let config = cgcx.config(ModuleKind::Regular); + if let Err(err) = B::autodiff(cgcx, &module, autodiff, config) { + err.raise(); + } + } + B::optimize_fat(cgcx, &mut module)?; let module = B::codegen(cgcx, module, module_config)?; @@ -1490,13 +1487,14 @@ fn start_executing_work( if !needs_fat_lto.is_empty() { assert!(needs_thin_lto.is_empty()); - let work = generate_fat_lto_work( - &cgcx, - autodiff_items.clone(), - needs_fat_lto, - import_only_modules, - ); - work_items.push((work, 0)); + work_items.push(( + WorkItem::FatLto { + needs_fat_lto, + import_only_modules, + autodiff: autodiff_items.clone(), + }, + 0, + )); if cgcx.parallel { helper.request_token(); } @@ -1867,11 +1865,17 @@ fn spawn_work<'a, B: ExtraBackendMethods>( ); Ok(execute_copy_from_cache_work_item(&cgcx, m, module_config)) } - WorkItem::FatLto(m) => { + WorkItem::FatLto { needs_fat_lto, import_only_modules, autodiff } => { let _timer = cgcx .prof .generic_activity_with_arg("codegen_module_perform_lto", "everything"); - execute_fat_lto_work_item(&cgcx, m, module_config) + execute_fat_lto_work_item( + &cgcx, + needs_fat_lto, + import_only_modules, + autodiff, + module_config, + ) } WorkItem::ThinLto(m) => { let _timer = From 9a3aa8fdb192b42e665eb9059a0061f865b3d352 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 3 Jul 2025 16:07:28 +0000 Subject: [PATCH 08/34] Remove unnecessary check for fat LTO --- compiler/rustc_codegen_ssa/src/back/write.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index c7d2fcef29f99..433cb63882f63 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -998,7 +998,7 @@ fn execute_fat_lto_work_item( let mut module = B::run_fat_lto(cgcx, needs_fat_lto, import_only_modules).unwrap_or_else(|e| e.raise()); - if cgcx.lto == Lto::Fat && !autodiff.is_empty() { + if !autodiff.is_empty() { let config = cgcx.config(ModuleKind::Regular); if let Err(err) = B::autodiff(cgcx, &module, autodiff, config) { err.raise(); From 8d63c7a1d68cd30383dceb03a76499c658fa7f40 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 3 Jul 2025 16:09:10 +0000 Subject: [PATCH 09/34] Remove unused config param from WriteBackendMethods::autodiff --- compiler/rustc_codegen_gcc/src/lib.rs | 1 - compiler/rustc_codegen_llvm/src/builder/autodiff.rs | 2 -- compiler/rustc_codegen_llvm/src/lib.rs | 3 +-- compiler/rustc_codegen_ssa/src/back/write.rs | 3 +-- compiler/rustc_codegen_ssa/src/traits/write.rs | 1 - 5 files changed, 2 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 34452bdd2005f..8e63ebc849406 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -437,7 +437,6 @@ impl WriteBackendMethods for GccCodegenBackend { _cgcx: &CodegenContext, _module: &ModuleCodegen, _diff_functions: Vec, - _config: &ModuleConfig, ) -> Result<(), FatalError> { unimplemented!() } diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index b07d9a5cfca8c..c27e161ba9948 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -2,7 +2,6 @@ use std::ptr; use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, AutoDiffItem, DiffActivity, DiffMode}; use rustc_codegen_ssa::ModuleCodegen; -use rustc_codegen_ssa::back::write::ModuleConfig; use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::traits::BaseTypeCodegenMethods; use rustc_errors::FatalError; @@ -461,7 +460,6 @@ pub(crate) fn differentiate<'ll>( module: &'ll ModuleCodegen, cgcx: &CodegenContext, diff_items: Vec, - _config: &ModuleConfig, ) -> Result<(), FatalError> { for item in &diff_items { trace!("{}", item); diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 4d85683de8c43..7f54416ec0e31 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -231,13 +231,12 @@ impl WriteBackendMethods for LlvmCodegenBackend { cgcx: &CodegenContext, module: &ModuleCodegen, diff_fncs: Vec, - config: &ModuleConfig, ) -> Result<(), FatalError> { if cgcx.lto != Lto::Fat { let dcx = cgcx.create_dcx(); return Err(dcx.handle().emit_almost_fatal(AutoDiffWithoutLTO)); } - builder::autodiff::differentiate(module, cgcx, diff_fncs, config) + builder::autodiff::differentiate(module, cgcx, diff_fncs) } } diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 433cb63882f63..24f648bdb2e48 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -999,8 +999,7 @@ fn execute_fat_lto_work_item( B::run_fat_lto(cgcx, needs_fat_lto, import_only_modules).unwrap_or_else(|e| e.raise()); if !autodiff.is_empty() { - let config = cgcx.config(ModuleKind::Regular); - if let Err(err) = B::autodiff(cgcx, &module, autodiff, config) { + if let Err(err) = B::autodiff(cgcx, &module, autodiff) { err.raise(); } } diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index c732c4b2ab860..949fb818c783a 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -65,7 +65,6 @@ pub trait WriteBackendMethods: Clone + 'static { cgcx: &CodegenContext, module: &ModuleCodegen, diff_fncs: Vec, - config: &ModuleConfig, ) -> Result<(), FatalError>; } From 21026cae8d34241b65924198b72c7231ce4f5a3d Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 3 Jul 2025 16:22:32 +0000 Subject: [PATCH 10/34] Merge run_fat_lto, optimize_fat and autodiff into run_and_optimize_fat_lto --- compiler/rustc_codegen_gcc/src/lib.rs | 23 +++-------- compiler/rustc_codegen_llvm/messages.ftl | 1 - compiler/rustc_codegen_llvm/src/errors.rs | 4 -- compiler/rustc_codegen_llvm/src/lib.rs | 39 +++++++------------ compiler/rustc_codegen_ssa/src/back/write.rs | 12 +----- .../rustc_codegen_ssa/src/traits/write.rs | 16 ++------ 6 files changed, 26 insertions(+), 69 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 8e63ebc849406..75c36fffec983 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -357,11 +357,16 @@ impl WriteBackendMethods for GccCodegenBackend { type ThinData = ThinData; type ThinBuffer = ThinBuffer; - fn run_fat_lto( + fn run_and_optimize_fat_lto( cgcx: &CodegenContext, modules: Vec>, cached_modules: Vec<(SerializedModule, WorkProduct)>, + diff_fncs: Vec, ) -> Result, FatalError> { + if !diff_fncs.is_empty() { + unimplemented!(); + } + back::lto::run_fat(cgcx, modules, cached_modules) } @@ -391,14 +396,6 @@ impl WriteBackendMethods for GccCodegenBackend { Ok(()) } - fn optimize_fat( - _cgcx: &CodegenContext, - _module: &mut ModuleCodegen, - ) -> Result<(), FatalError> { - // TODO(antoyo) - Ok(()) - } - fn optimize_thin( cgcx: &CodegenContext, thin: ThinModule, @@ -432,14 +429,6 @@ impl WriteBackendMethods for GccCodegenBackend { ) -> Result, FatalError> { back::write::link(cgcx, dcx, modules) } - - fn autodiff( - _cgcx: &CodegenContext, - _module: &ModuleCodegen, - _diff_functions: Vec, - ) -> Result<(), FatalError> { - unimplemented!() - } } /// This is the entrypoint for a hot plugged rustc_codegen_gccjit diff --git a/compiler/rustc_codegen_llvm/messages.ftl b/compiler/rustc_codegen_llvm/messages.ftl index 3885f18271f1b..f197ea7447326 100644 --- a/compiler/rustc_codegen_llvm/messages.ftl +++ b/compiler/rustc_codegen_llvm/messages.ftl @@ -1,5 +1,4 @@ codegen_llvm_autodiff_without_enable = using the autodiff feature requires -Z autodiff=Enable -codegen_llvm_autodiff_without_lto = using the autodiff feature requires using fat-lto codegen_llvm_copy_bitcode = failed to copy bitcode to object file: {$err} diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index d50ad8a1a9cb4..31d49e8631955 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -37,10 +37,6 @@ impl Diagnostic<'_, G> for ParseTargetMachineConfig<'_> { } } -#[derive(Diagnostic)] -#[diag(codegen_llvm_autodiff_without_lto)] -pub(crate) struct AutoDiffWithoutLTO; - #[derive(Diagnostic)] #[diag(codegen_llvm_autodiff_without_enable)] pub(crate) struct AutoDiffWithoutEnable; diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 7f54416ec0e31..5d551c3af87fb 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -26,7 +26,7 @@ use std::mem::ManuallyDrop; use back::owned_target_machine::OwnedTargetMachine; use back::write::{create_informational_target_machine, create_target_machine}; use context::SimpleCx; -use errors::{AutoDiffWithoutLTO, ParseTargetMachineConfig}; +use errors::ParseTargetMachineConfig; use llvm_util::target_config; use rustc_ast::expand::allocator::AllocatorKind; use rustc_ast::expand::autodiff_attrs::AutoDiffItem; @@ -43,7 +43,7 @@ use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::ty::TyCtxt; use rustc_middle::util::Providers; use rustc_session::Session; -use rustc_session::config::{Lto, OptLevel, OutputFilenames, PrintKind, PrintRequest}; +use rustc_session::config::{OptLevel, OutputFilenames, PrintKind, PrintRequest}; use rustc_span::Symbol; mod back { @@ -174,12 +174,23 @@ impl WriteBackendMethods for LlvmCodegenBackend { ) -> Result, FatalError> { back::write::link(cgcx, dcx, modules) } - fn run_fat_lto( + fn run_and_optimize_fat_lto( cgcx: &CodegenContext, modules: Vec>, cached_modules: Vec<(SerializedModule, WorkProduct)>, + diff_fncs: Vec, ) -> Result, FatalError> { - back::lto::run_fat(cgcx, modules, cached_modules) + let mut module = back::lto::run_fat(cgcx, modules, cached_modules)?; + + if !diff_fncs.is_empty() { + builder::autodiff::differentiate(&module, cgcx, diff_fncs)?; + } + + let dcx = cgcx.create_dcx(); + let dcx = dcx.handle(); + back::lto::run_pass_manager(cgcx, dcx, &mut module, false)?; + + Ok(module) } fn run_thin_lto( cgcx: &CodegenContext, @@ -196,14 +207,6 @@ impl WriteBackendMethods for LlvmCodegenBackend { ) -> Result<(), FatalError> { back::write::optimize(cgcx, dcx, module, config) } - fn optimize_fat( - cgcx: &CodegenContext, - module: &mut ModuleCodegen, - ) -> Result<(), FatalError> { - let dcx = cgcx.create_dcx(); - let dcx = dcx.handle(); - back::lto::run_pass_manager(cgcx, dcx, module, false) - } fn optimize_thin( cgcx: &CodegenContext, thin: ThinModule, @@ -226,18 +229,6 @@ impl WriteBackendMethods for LlvmCodegenBackend { fn serialize_module(module: ModuleCodegen) -> (String, Self::ModuleBuffer) { (module.name, back::lto::ModuleBuffer::new(module.module_llvm.llmod())) } - /// Generate autodiff rules - fn autodiff( - cgcx: &CodegenContext, - module: &ModuleCodegen, - diff_fncs: Vec, - ) -> Result<(), FatalError> { - if cgcx.lto != Lto::Fat { - let dcx = cgcx.create_dcx(); - return Err(dcx.handle().emit_almost_fatal(AutoDiffWithoutLTO)); - } - builder::autodiff::differentiate(module, cgcx, diff_fncs) - } } impl LlvmCodegenBackend { diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 24f648bdb2e48..26fc593da98a7 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -995,17 +995,7 @@ fn execute_fat_lto_work_item( autodiff: Vec, module_config: &ModuleConfig, ) -> Result, FatalError> { - let mut module = - B::run_fat_lto(cgcx, needs_fat_lto, import_only_modules).unwrap_or_else(|e| e.raise()); - - if !autodiff.is_empty() { - if let Err(err) = B::autodiff(cgcx, &module, autodiff) { - err.raise(); - } - } - - B::optimize_fat(cgcx, &mut module)?; - + let module = B::run_and_optimize_fat_lto(cgcx, needs_fat_lto, import_only_modules, autodiff)?; let module = B::codegen(cgcx, module, module_config)?; Ok(WorkItemResult::Finished(module)) } diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index 949fb818c783a..5e993640472d1 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -20,12 +20,13 @@ pub trait WriteBackendMethods: Clone + 'static { dcx: DiagCtxtHandle<'_>, modules: Vec>, ) -> Result, FatalError>; - /// Performs fat LTO by merging all modules into a single one and returning it - /// for further optimization. - fn run_fat_lto( + /// Performs fat LTO by merging all modules into a single one, running autodiff + /// if necessary and running any further optimizations + fn run_and_optimize_fat_lto( cgcx: &CodegenContext, modules: Vec>, cached_modules: Vec<(SerializedModule, WorkProduct)>, + diff_fncs: Vec, ) -> Result, FatalError>; /// Performs thin LTO by performing necessary global analysis and returning two /// lists, one of the modules that need optimization and another for modules that @@ -43,10 +44,6 @@ pub trait WriteBackendMethods: Clone + 'static { module: &mut ModuleCodegen, config: &ModuleConfig, ) -> Result<(), FatalError>; - fn optimize_fat( - cgcx: &CodegenContext, - llmod: &mut ModuleCodegen, - ) -> Result<(), FatalError>; fn optimize_thin( cgcx: &CodegenContext, thin: ThinModule, @@ -61,11 +58,6 @@ pub trait WriteBackendMethods: Clone + 'static { want_summary: bool, ) -> (String, Self::ThinBuffer); fn serialize_module(module: ModuleCodegen) -> (String, Self::ModuleBuffer); - fn autodiff( - cgcx: &CodegenContext, - module: &ModuleCodegen, - diff_fncs: Vec, - ) -> Result<(), FatalError>; } pub trait ThinBufferMethods: Send + Sync { From a7eefc3fc4c16786844841af511b09db832f6a01 Mon Sep 17 00:00:00 2001 From: Jens Reidel Date: Fri, 4 Jul 2025 07:04:46 +0200 Subject: [PATCH 11/34] Enable xgot feature for mips64 musl targets This was missed in b65c2afdfd9aaee977302516c9ef177861abfe74, which only enabled it for the glibc targets. I didn't feel comfortable touching the OpenWRT target, whoever maintains that will probably want to take a look whether it is necessary there as well. Signed-off-by: Jens Reidel --- .../src/spec/targets/mips64_unknown_linux_muslabi64.rs | 2 +- .../src/spec/targets/mips64el_unknown_linux_muslabi64.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_muslabi64.rs b/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_muslabi64.rs index fd50950305300..f95ce75635490 100644 --- a/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_muslabi64.rs +++ b/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_muslabi64.rs @@ -5,7 +5,7 @@ use crate::spec::{Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { let mut base = base::linux_musl::opts(); base.cpu = "mips64r2".into(); - base.features = "+mips64r2".into(); + base.features = "+mips64r2,+xgot".into(); base.max_atomic_width = Some(64); Target { // LLVM doesn't recognize "muslabi64" yet. diff --git a/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs b/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs index aa087b1a35af8..d42e097b0fd8b 100644 --- a/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs +++ b/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs @@ -3,7 +3,7 @@ use crate::spec::{Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { let mut base = base::linux_musl::opts(); base.cpu = "mips64r2".into(); - base.features = "+mips64r2".into(); + base.features = "+mips64r2,+xgot".into(); base.max_atomic_width = Some(64); // FIXME(compiler-team#422): musl targets should be dynamically linked by default. base.crt_static_default = true; From 2412f8921ce39b986664e27c71b07f0ae24ac100 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Mon, 7 Jul 2025 15:39:31 +0000 Subject: [PATCH 12/34] UWP: link ntdll functions using raw-dylib --- library/std/src/sys/pal/windows/c.rs | 69 ++++------------------------ library/windows_targets/src/lib.rs | 23 +++++++++- 2 files changed, 30 insertions(+), 62 deletions(-) diff --git a/library/std/src/sys/pal/windows/c.rs b/library/std/src/sys/pal/windows/c.rs index eee169d410a9c..edac5262a4e9a 100644 --- a/library/std/src/sys/pal/windows/c.rs +++ b/library/std/src/sys/pal/windows/c.rs @@ -197,7 +197,7 @@ compat_fn_optional! { pub fn WakeByAddressSingle(address: *const c_void); } -#[cfg(any(target_vendor = "win7", target_vendor = "uwp"))] +#[cfg(any(target_vendor = "win7"))] compat_fn_with_fallback! { pub static NTDLL: &CStr = c"ntdll"; @@ -228,65 +228,14 @@ compat_fn_with_fallback! { ) -> NTSTATUS { panic!("keyed events not available") } +} - // These functions are available on UWP when lazily loaded. They will fail WACK if loaded statically. - #[cfg(target_vendor = "uwp")] - pub fn NtCreateFile( - filehandle: *mut HANDLE, - desiredaccess: FILE_ACCESS_RIGHTS, - objectattributes: *const OBJECT_ATTRIBUTES, - iostatusblock: *mut IO_STATUS_BLOCK, - allocationsize: *const i64, - fileattributes: FILE_FLAGS_AND_ATTRIBUTES, - shareaccess: FILE_SHARE_MODE, - createdisposition: NTCREATEFILE_CREATE_DISPOSITION, - createoptions: NTCREATEFILE_CREATE_OPTIONS, - eabuffer: *const c_void, - ealength: u32 - ) -> NTSTATUS { - STATUS_NOT_IMPLEMENTED - } - #[cfg(target_vendor = "uwp")] - pub fn NtOpenFile( - filehandle: *mut HANDLE, - desiredaccess: u32, - objectattributes: *const OBJECT_ATTRIBUTES, - iostatusblock: *mut IO_STATUS_BLOCK, - shareaccess: u32, - openoptions: u32 - ) -> NTSTATUS { - STATUS_NOT_IMPLEMENTED - } - #[cfg(target_vendor = "uwp")] - pub fn NtReadFile( - filehandle: HANDLE, - event: HANDLE, - apcroutine: PIO_APC_ROUTINE, - apccontext: *const c_void, - iostatusblock: *mut IO_STATUS_BLOCK, - buffer: *mut c_void, - length: u32, - byteoffset: *const i64, - key: *const u32 - ) -> NTSTATUS { - STATUS_NOT_IMPLEMENTED - } - #[cfg(target_vendor = "uwp")] - pub fn NtWriteFile( - filehandle: HANDLE, - event: HANDLE, - apcroutine: PIO_APC_ROUTINE, - apccontext: *const c_void, - iostatusblock: *mut IO_STATUS_BLOCK, - buffer: *const c_void, - length: u32, - byteoffset: *const i64, - key: *const u32 - ) -> NTSTATUS { - STATUS_NOT_IMPLEMENTED - } - #[cfg(target_vendor = "uwp")] - pub fn RtlNtStatusToDosError(Status: NTSTATUS) -> u32 { - Status as u32 +cfg_if::cfg_if! { + if #[cfg(target_vendor = "uwp")] { + windows_targets::link_raw_dylib!("ntdll.dll" "system" fn NtCreateFile(filehandle : *mut HANDLE, desiredaccess : FILE_ACCESS_RIGHTS, objectattributes : *const OBJECT_ATTRIBUTES, iostatusblock : *mut IO_STATUS_BLOCK, allocationsize : *const i64, fileattributes : FILE_FLAGS_AND_ATTRIBUTES, shareaccess : FILE_SHARE_MODE, createdisposition : NTCREATEFILE_CREATE_DISPOSITION, createoptions : NTCREATEFILE_CREATE_OPTIONS, eabuffer : *const core::ffi::c_void, ealength : u32) -> NTSTATUS); + windows_targets::link_raw_dylib!("ntdll.dll" "system" fn NtOpenFile(filehandle : *mut HANDLE, desiredaccess : u32, objectattributes : *const OBJECT_ATTRIBUTES, iostatusblock : *mut IO_STATUS_BLOCK, shareaccess : u32, openoptions : u32) -> NTSTATUS); + windows_targets::link_raw_dylib!("ntdll.dll" "system" fn NtReadFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *mut core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS); + windows_targets::link_raw_dylib!("ntdll.dll" "system" fn NtWriteFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *const core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS); + windows_targets::link_raw_dylib!("ntdll.dll" "system" fn RtlNtStatusToDosError(status : NTSTATUS) -> u32); } } diff --git a/library/windows_targets/src/lib.rs b/library/windows_targets/src/lib.rs index bce54c5ffcef0..9e82e6a720006 100644 --- a/library/windows_targets/src/lib.rs +++ b/library/windows_targets/src/lib.rs @@ -7,8 +7,7 @@ #![feature(decl_macro)] #![feature(no_core)] -#[cfg(feature = "windows_raw_dylib")] -pub macro link { +pub macro link_raw_dylib { ($library:literal $abi:literal $($link_name:literal)? $(#[$doc:meta])? fn $($function:tt)*) => ( #[cfg_attr(not(target_arch = "x86"), link(name = $library, kind = "raw-dylib", modifiers = "+verbatim"))] #[cfg_attr(target_arch = "x86", link(name = $library, kind = "raw-dylib", modifiers = "+verbatim", import_name_type = "undecorated"))] @@ -18,6 +17,26 @@ pub macro link { } ) } + +pub macro link_dylib { + ($library:literal $abi:literal $($link_name:literal)? $(#[$doc:meta])? fn $($function:tt)*) => ( + // Note: the windows-targets crate uses a pre-built Windows.lib import library which we don't + // have in this repo. So instead we always link kernel32.lib and add the rest of the import + // libraries below by using an empty extern block. This works because extern blocks are not + // connected to the library given in the #[link] attribute. + #[link(name = "kernel32")] + unsafe extern $abi { + $(#[link_name=$link_name])? + pub fn $($function)*; + } + ) +} + +#[cfg(feature = "windows_raw_dylib")] +pub macro link($($tt:tt)*) { + $crate::link_raw_dylib!($($tt)*) +} + #[cfg(not(feature = "windows_raw_dylib"))] pub macro link { ($library:literal $abi:literal $($link_name:literal)? $(#[$doc:meta])? fn $($function:tt)*) => ( From 803b4d262241b22a4dfe75fcec49eaf920e0816b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20du=20Garreau?= Date: Wed, 9 Jul 2025 18:11:26 +0200 Subject: [PATCH 13/34] core: Remove `BorrowedCursor::init_ref` method This method was not really useful: at no point one would only need to read the initialized part of the cursor without mutating it. --- library/core/src/io/borrowed_buf.rs | 10 ---------- library/coretests/tests/io/borrowed_buf.rs | 4 +--- library/std/src/io/mod.rs | 6 +++--- 3 files changed, 4 insertions(+), 16 deletions(-) diff --git a/library/core/src/io/borrowed_buf.rs b/library/core/src/io/borrowed_buf.rs index 854e03cf18279..11ace04199890 100644 --- a/library/core/src/io/borrowed_buf.rs +++ b/library/core/src/io/borrowed_buf.rs @@ -227,16 +227,6 @@ impl<'a> BorrowedCursor<'a> { self.buf.filled - self.start } - /// Returns a shared reference to the initialized portion of the cursor. - #[inline] - pub fn init_ref(&self) -> &[u8] { - // SAFETY: We only slice the initialized part of the buffer, which is always valid - unsafe { - let buf = self.buf.buf.get_unchecked(self.buf.filled..self.buf.init); - buf.assume_init_ref() - } - } - /// Returns a mutable reference to the initialized portion of the cursor. #[inline] pub fn init_mut(&mut self) -> &mut [u8] { diff --git a/library/coretests/tests/io/borrowed_buf.rs b/library/coretests/tests/io/borrowed_buf.rs index fbd3864dcac14..fe0e26f3e9d62 100644 --- a/library/coretests/tests/io/borrowed_buf.rs +++ b/library/coretests/tests/io/borrowed_buf.rs @@ -61,7 +61,7 @@ fn clear() { assert_eq!(rbuf.filled().len(), 0); assert_eq!(rbuf.unfilled().capacity(), 16); - assert_eq!(rbuf.unfilled().init_ref(), [255; 16]); + assert_eq!(rbuf.unfilled().init_mut(), [255; 16]); } #[test] @@ -142,7 +142,6 @@ fn cursor_set_init() { } assert_eq!(rbuf.init_len(), 8); - assert_eq!(rbuf.unfilled().init_ref().len(), 8); assert_eq!(rbuf.unfilled().init_mut().len(), 8); assert_eq!(rbuf.unfilled().uninit_mut().len(), 8); assert_eq!(unsafe { rbuf.unfilled().as_mut().len() }, 16); @@ -160,7 +159,6 @@ fn cursor_set_init() { } assert_eq!(rbuf.init_len(), 12); - assert_eq!(rbuf.unfilled().init_ref().len(), 8); assert_eq!(rbuf.unfilled().init_mut().len(), 8); assert_eq!(rbuf.unfilled().uninit_mut().len(), 4); assert_eq!(unsafe { rbuf.unfilled().as_mut().len() }, 12); diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 17c32d7a571c8..d351ee5e739d3 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -489,7 +489,7 @@ pub(crate) fn default_read_to_end( } }; - let unfilled_but_initialized = cursor.init_ref().len(); + let unfilled_but_initialized = cursor.init_mut().len(); let bytes_read = cursor.written(); let was_fully_initialized = read_buf.init_len() == buf_len; @@ -3053,7 +3053,7 @@ impl Read for Take { // The condition above guarantees that `self.limit` fits in `usize`. let limit = self.limit as usize; - let extra_init = cmp::min(limit, buf.init_ref().len()); + let extra_init = cmp::min(limit, buf.init_mut().len()); // SAFETY: no uninit data is written to ibuf let ibuf = unsafe { &mut buf.as_mut()[..limit] }; @@ -3068,7 +3068,7 @@ impl Read for Take { let mut cursor = sliced_buf.unfilled(); let result = self.inner.read_buf(cursor.reborrow()); - let new_init = cursor.init_ref().len(); + let new_init = cursor.init_mut().len(); let filled = sliced_buf.len(); // cursor / sliced_buf / ibuf must drop here From 34555f1b0b49e4fd944b2cb9c298f15b6b5da144 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20du=20Garreau?= Date: Wed, 9 Jul 2025 18:11:26 +0200 Subject: [PATCH 14/34] core: Remove `BorrowedCursor::uninit_mut` I assume that this method was there for completeness, but there is hardly any useful use of it: the buffer it gave was not always connected to the start of the cursor and its use required `unsafe` anyway to mark the bytes as initialized. --- library/core/src/io/borrowed_buf.rs | 13 +++---------- library/coretests/tests/io/borrowed_buf.rs | 2 -- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/library/core/src/io/borrowed_buf.rs b/library/core/src/io/borrowed_buf.rs index 11ace04199890..bd468c9ee853c 100644 --- a/library/core/src/io/borrowed_buf.rs +++ b/library/core/src/io/borrowed_buf.rs @@ -237,15 +237,6 @@ impl<'a> BorrowedCursor<'a> { } } - /// Returns a mutable reference to the uninitialized part of the cursor. - /// - /// It is safe to uninitialize any of these bytes. - #[inline] - pub fn uninit_mut(&mut self) -> &mut [MaybeUninit] { - // SAFETY: always in bounds - unsafe { self.buf.buf.get_unchecked_mut(self.buf.init..) } - } - /// Returns a mutable reference to the whole cursor. /// /// # Safety @@ -298,7 +289,9 @@ impl<'a> BorrowedCursor<'a> { /// Initializes all bytes in the cursor. #[inline] pub fn ensure_init(&mut self) -> &mut Self { - let uninit = self.uninit_mut(); + // SAFETY: always in bounds and we never uninitialize these bytes. + let uninit = unsafe { self.buf.buf.get_unchecked_mut(self.buf.init..) }; + // SAFETY: 0 is a valid value for MaybeUninit and the length matches the allocation // since it is comes from a slice reference. unsafe { diff --git a/library/coretests/tests/io/borrowed_buf.rs b/library/coretests/tests/io/borrowed_buf.rs index fe0e26f3e9d62..60829109d50bd 100644 --- a/library/coretests/tests/io/borrowed_buf.rs +++ b/library/coretests/tests/io/borrowed_buf.rs @@ -143,7 +143,6 @@ fn cursor_set_init() { assert_eq!(rbuf.init_len(), 8); assert_eq!(rbuf.unfilled().init_mut().len(), 8); - assert_eq!(rbuf.unfilled().uninit_mut().len(), 8); assert_eq!(unsafe { rbuf.unfilled().as_mut().len() }, 16); rbuf.unfilled().advance(4); @@ -160,6 +159,5 @@ fn cursor_set_init() { assert_eq!(rbuf.init_len(), 12); assert_eq!(rbuf.unfilled().init_mut().len(), 8); - assert_eq!(rbuf.unfilled().uninit_mut().len(), 4); assert_eq!(unsafe { rbuf.unfilled().as_mut().len() }, 12); } From 65df66831f3fdb7f20a74e843a46bb90993221eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20du=20Garreau?= Date: Wed, 9 Jul 2025 18:11:27 +0200 Subject: [PATCH 15/34] core: Change `BorrowedCursor::written`'s origin This enable removing the `start` field, so `BorrowedCursor` fits in a single register. Because `written` is almost always used in difference with another call, this changes nothing else in practice. --- library/core/src/io/borrowed_buf.rs | 12 +++--------- library/coretests/tests/io/borrowed_buf.rs | 2 +- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/library/core/src/io/borrowed_buf.rs b/library/core/src/io/borrowed_buf.rs index bd468c9ee853c..b2f10d93b39d7 100644 --- a/library/core/src/io/borrowed_buf.rs +++ b/library/core/src/io/borrowed_buf.rs @@ -132,7 +132,6 @@ impl<'data> BorrowedBuf<'data> { #[inline] pub fn unfilled<'this>(&'this mut self) -> BorrowedCursor<'this> { BorrowedCursor { - start: self.filled, // SAFETY: we never assign into `BorrowedCursor::buf`, so treating its // lifetime covariantly is safe. buf: unsafe { @@ -188,9 +187,6 @@ pub struct BorrowedCursor<'a> { // we create a `BorrowedCursor`. This is only safe if we never replace `buf` by assigning into // it, so don't do that! buf: &'a mut BorrowedBuf<'a>, - /// The length of the filled portion of the underlying buffer at the time of the cursor's - /// creation. - start: usize, } impl<'a> BorrowedCursor<'a> { @@ -208,7 +204,6 @@ impl<'a> BorrowedCursor<'a> { self.buf, ) }, - start: self.start, } } @@ -218,13 +213,12 @@ impl<'a> BorrowedCursor<'a> { self.buf.capacity() - self.buf.filled } - /// Returns the number of bytes written to this cursor since it was created from a `BorrowedBuf`. + /// Returns the number of bytes written to the `BorrowedBuf` this cursor was created from. /// - /// Note that if this cursor is a reborrowed clone of another, then the count returned is the - /// count written via either cursor, not the count since the cursor was reborrowed. + /// In particular, the count returned is shared by all reborrows of the cursor. #[inline] pub fn written(&self) -> usize { - self.buf.filled - self.start + self.buf.filled } /// Returns a mutable reference to the initialized portion of the cursor. diff --git a/library/coretests/tests/io/borrowed_buf.rs b/library/coretests/tests/io/borrowed_buf.rs index 60829109d50bd..869fc31fee999 100644 --- a/library/coretests/tests/io/borrowed_buf.rs +++ b/library/coretests/tests/io/borrowed_buf.rs @@ -124,7 +124,7 @@ fn reborrow_written() { assert_eq!(cursor2.written(), 32); assert_eq!(cursor.written(), 32); - assert_eq!(buf.unfilled().written(), 0); + assert_eq!(buf.unfilled().written(), 32); assert_eq!(buf.init_len(), 32); assert_eq!(buf.filled().len(), 32); let filled = buf.filled(); From 81a6f189408b71b452a9804818f2cd50d2ed9108 Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Wed, 9 Jul 2025 15:24:28 +0500 Subject: [PATCH 16/34] added error for invalid char cast --- compiler/rustc_lint/messages.ftl | 7 ++ compiler/rustc_lint/src/lints.rs | 14 +++ compiler/rustc_lint/src/types/literal.rs | 42 ++++++-- tests/ui/cast/cast-char.rs | 56 +++++++++- tests/ui/cast/cast-char.stderr | 124 +++++++++++++++++++++-- 5 files changed, 222 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 8d9f2385b710f..5d07afdaf17bd 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -440,6 +440,7 @@ lint_invalid_asm_label_named = avoid using named labels in inline assembly .help = only local labels of the form `:` should be used in inline asm .note = see the asm section of Rust By Example for more information lint_invalid_asm_label_no_span = the label may be declared in the expansion of a macro + lint_invalid_crate_type_value = invalid `crate_type` value .suggestion = did you mean @@ -790,6 +791,9 @@ lint_supertrait_as_deref_target = this `Deref` implementation is covered by an i .label2 = target type is a supertrait of `{$self_ty}` .help = consider removing this implementation or replacing it with a method instead +lint_surrogate_char_cast = surrogate values are not valid for `char` + .note = `0xD800..=0xDFFF` are reserved for Unicode surrogates and are not valid `char` values + lint_suspicious_double_ref_clone = using `.clone()` on a double reference, which returns `{$ty}` instead of cloning the inner type @@ -799,6 +803,9 @@ lint_suspicious_double_ref_deref = lint_symbol_intern_string_literal = using `Symbol::intern` on a string literal .help = consider adding the symbol to `compiler/rustc_span/src/symbol.rs` +lint_too_large_char_cast = value exceeds maximum `char` value + .note = maximum valid `char` value is `0x10FFFF` + lint_trailing_semi_macro = trailing semicolon in macro used in expression position .note1 = macro invocations at the end of a block are treated as expressions .note2 = to ignore the value produced by the macro, add a semicolon after the invocation of `{$name}` diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 21148833eaf72..19989cbcce680 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -1746,6 +1746,20 @@ pub(crate) struct OverflowingLiteral<'a> { pub lit: String, } +#[derive(LintDiagnostic)] +#[diag(lint_surrogate_char_cast)] +#[note] +pub(crate) struct SurrogateCharCast { + pub literal: u128, +} + +#[derive(LintDiagnostic)] +#[diag(lint_too_large_char_cast)] +#[note] +pub(crate) struct TooLargeCharCast { + pub literal: u128, +} + #[derive(LintDiagnostic)] #[diag(lint_uses_power_alignment)] pub(crate) struct UsesPowerAlignment; diff --git a/compiler/rustc_lint/src/types/literal.rs b/compiler/rustc_lint/src/types/literal.rs index d44f45177bde0..2bac58ba23d07 100644 --- a/compiler/rustc_lint/src/types/literal.rs +++ b/compiler/rustc_lint/src/types/literal.rs @@ -12,7 +12,7 @@ use crate::context::LintContext; use crate::lints::{ OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign, OverflowingBinHexSignBitSub, OverflowingBinHexSub, OverflowingInt, OverflowingIntHelp, OverflowingLiteral, OverflowingUInt, - RangeEndpointOutOfRange, UseInclusiveRange, + RangeEndpointOutOfRange, SurrogateCharCast, TooLargeCharCast, UseInclusiveRange, }; use crate::types::{OVERFLOWING_LITERALS, TypeLimits}; @@ -38,12 +38,18 @@ fn lint_overflowing_range_endpoint<'tcx>( // We only want to handle exclusive (`..`) ranges, // which are represented as `ExprKind::Struct`. - let Node::ExprField(field) = cx.tcx.parent_hir_node(hir_id) else { return false }; - let Node::Expr(struct_expr) = cx.tcx.parent_hir_node(field.hir_id) else { return false }; + let Node::ExprField(field) = cx.tcx.parent_hir_node(hir_id) else { + return false; + }; + let Node::Expr(struct_expr) = cx.tcx.parent_hir_node(field.hir_id) else { + return false; + }; if !is_range_literal(struct_expr) { return false; }; - let ExprKind::Struct(_, [start, end], _) = &struct_expr.kind else { return false }; + let ExprKind::Struct(_, [start, end], _) = &struct_expr.kind else { + return false; + }; // We can suggest using an inclusive range // (`..=`) instead only if it is the `end` that is @@ -61,7 +67,9 @@ fn lint_overflowing_range_endpoint<'tcx>( }; let sub_sugg = if span.lo() == lit_span.lo() { - let Ok(start) = cx.sess().source_map().span_to_snippet(start.span) else { return false }; + let Ok(start) = cx.sess().source_map().span_to_snippet(start.span) else { + return false; + }; UseInclusiveRange::WithoutParen { sugg: struct_expr.span.shrink_to_lo().to(lit_span.shrink_to_hi()), start, @@ -316,11 +324,25 @@ fn lint_uint_literal<'tcx>( match par_e.kind { hir::ExprKind::Cast(..) => { if let ty::Char = cx.typeck_results().expr_ty(par_e).kind() { - cx.emit_span_lint( - OVERFLOWING_LITERALS, - par_e.span, - OnlyCastu8ToChar { span: par_e.span, literal: lit_val }, - ); + if lit_val > 0x10FFFF { + cx.emit_span_lint( + OVERFLOWING_LITERALS, + par_e.span, + TooLargeCharCast { literal: lit_val }, + ); + } else if (0xD800..=0xDFFF).contains(&lit_val) { + cx.emit_span_lint( + OVERFLOWING_LITERALS, + par_e.span, + SurrogateCharCast { literal: lit_val }, + ); + } else { + cx.emit_span_lint( + OVERFLOWING_LITERALS, + par_e.span, + OnlyCastu8ToChar { span: par_e.span, literal: lit_val }, + ); + } return; } } diff --git a/tests/ui/cast/cast-char.rs b/tests/ui/cast/cast-char.rs index 9634ed56f7b72..5bf05072253fd 100644 --- a/tests/ui/cast/cast-char.rs +++ b/tests/ui/cast/cast-char.rs @@ -1,10 +1,58 @@ #![deny(overflowing_literals)] fn main() { - const XYZ: char = 0x1F888 as char; + // Valid cases - should suggest char literal + + // u8 range (0-255) + const VALID_U8_1: char = 0x41 as char; // 'A' + const VALID_U8_2: char = 0xFF as char; // maximum u8 + const VALID_U8_3: char = 0x00 as char; // minimum u8 + + // Valid Unicode in lower range [0x0, 0xD7FF] + const VALID_LOW_1: char = 0x1000 as char; // 4096 + //~^ ERROR: only `u8` can be cast into `char` + const VALID_LOW_2: char = 0xD7FF as char; // last valid in lower range + //~^ ERROR: only `u8` can be cast into `char` + const VALID_LOW_3: char = 0x0500 as char; // cyrillic range + //~^ ERROR: only `u8` can be cast into `char` + + // Valid Unicode in upper range [0xE000, 0x10FFFF] + const VALID_HIGH_1: char = 0xE000 as char; // first valid in upper range + //~^ ERROR only `u8` can be cast into `char` + const VALID_HIGH_2: char = 0x1F888 as char; // 129160 - example from issue + //~^ ERROR only `u8` can be cast into `char` + const VALID_HIGH_3: char = 0x10FFFF as char; // maximum valid Unicode + //~^ ERROR only `u8` can be cast into `char` + const VALID_HIGH_4: char = 0xFFFD as char; // replacement character + //~^ ERROR only `u8` can be cast into `char` + const VALID_HIGH_5: char = 0x1F600 as char; // emoji + //~^ ERROR only `u8` can be cast into `char` + + // Invalid cases - should show InvalidCharCast + + // Surrogate range [0xD800, 0xDFFF] - reserved for UTF-16 + const INVALID_SURROGATE_1: char = 0xD800 as char; // first surrogate + //~^ ERROR: surrogate values are not valid + const INVALID_SURROGATE_2: char = 0xDFFF as char; // last surrogate + //~^ ERROR: surrogate values are not valid + const INVALID_SURROGATE_3: char = 0xDB00 as char; // middle of surrogate range + //~^ ERROR: surrogate values are not valid + + // Too large values (> 0x10FFFF) + const INVALID_TOO_BIG_1: char = 0x110000 as char; // one more than maximum + //~^ ERROR: value exceeds maximum `char` value + const INVALID_TOO_BIG_2: char = 0xEF8888 as char; // example from issue + //~^ ERROR: value exceeds maximum `char` value + const INVALID_TOO_BIG_3: char = 0x1FFFFF as char; // much larger + //~^ ERROR: value exceeds maximum `char` value + const INVALID_TOO_BIG_4: char = 0xFFFFFF as char; // 24-bit maximum + //~^ ERROR: value exceeds maximum `char` value + + // Boundary cases + const BOUNDARY_1: char = 0xD7FE as char; // valid, before surrogate + //~^ ERROR only `u8` can be cast into `char` + const BOUNDARY_2: char = 0xE001 as char; // valid, after surrogate //~^ ERROR only `u8` can be cast into `char` - const XY: char = 129160 as char; + const BOUNDARY_3: char = 0x10FFFE as char; // valid, near maximum //~^ ERROR only `u8` can be cast into `char` - const ZYX: char = '\u{01F888}'; - println!("{}", XYZ); } diff --git a/tests/ui/cast/cast-char.stderr b/tests/ui/cast/cast-char.stderr index 211937c9d6faf..a8d0b3b04b0c2 100644 --- a/tests/ui/cast/cast-char.stderr +++ b/tests/ui/cast/cast-char.stderr @@ -1,8 +1,8 @@ error: only `u8` can be cast into `char` - --> $DIR/cast-char.rs:4:23 + --> $DIR/cast-char.rs:12:31 | -LL | const XYZ: char = 0x1F888 as char; - | ^^^^^^^^^^^^^^^ help: use a `char` literal instead: `'\u{1F888}'` +LL | const VALID_LOW_1: char = 0x1000 as char; // 4096 + | ^^^^^^^^^^^^^^ help: use a `char` literal instead: `'\u{1000}'` | note: the lint level is defined here --> $DIR/cast-char.rs:1:9 @@ -11,10 +11,120 @@ LL | #![deny(overflowing_literals)] | ^^^^^^^^^^^^^^^^^^^^ error: only `u8` can be cast into `char` - --> $DIR/cast-char.rs:6:22 + --> $DIR/cast-char.rs:14:31 | -LL | const XY: char = 129160 as char; - | ^^^^^^^^^^^^^^ help: use a `char` literal instead: `'\u{1F888}'` +LL | const VALID_LOW_2: char = 0xD7FF as char; // last valid in lower range + | ^^^^^^^^^^^^^^ help: use a `char` literal instead: `'\u{D7FF}'` -error: aborting due to 2 previous errors +error: only `u8` can be cast into `char` + --> $DIR/cast-char.rs:16:31 + | +LL | const VALID_LOW_3: char = 0x0500 as char; // cyrillic range + | ^^^^^^^^^^^^^^ help: use a `char` literal instead: `'\u{500}'` + +error: only `u8` can be cast into `char` + --> $DIR/cast-char.rs:20:32 + | +LL | const VALID_HIGH_1: char = 0xE000 as char; // first valid in upper range + | ^^^^^^^^^^^^^^ help: use a `char` literal instead: `'\u{E000}'` + +error: only `u8` can be cast into `char` + --> $DIR/cast-char.rs:22:32 + | +LL | const VALID_HIGH_2: char = 0x1F888 as char; // 129160 - example from issue + | ^^^^^^^^^^^^^^^ help: use a `char` literal instead: `'\u{1F888}'` + +error: only `u8` can be cast into `char` + --> $DIR/cast-char.rs:24:32 + | +LL | const VALID_HIGH_3: char = 0x10FFFF as char; // maximum valid Unicode + | ^^^^^^^^^^^^^^^^ help: use a `char` literal instead: `'\u{10FFFF}'` + +error: only `u8` can be cast into `char` + --> $DIR/cast-char.rs:26:32 + | +LL | const VALID_HIGH_4: char = 0xFFFD as char; // replacement character + | ^^^^^^^^^^^^^^ help: use a `char` literal instead: `'\u{FFFD}'` + +error: only `u8` can be cast into `char` + --> $DIR/cast-char.rs:28:32 + | +LL | const VALID_HIGH_5: char = 0x1F600 as char; // emoji + | ^^^^^^^^^^^^^^^ help: use a `char` literal instead: `'\u{1F600}'` + +error: surrogate values are not valid for `char` + --> $DIR/cast-char.rs:34:39 + | +LL | const INVALID_SURROGATE_1: char = 0xD800 as char; // first surrogate + | ^^^^^^^^^^^^^^ + | + = note: `0xD800..=0xDFFF` are reserved for Unicode surrogates and are not valid `char` values + +error: surrogate values are not valid for `char` + --> $DIR/cast-char.rs:36:39 + | +LL | const INVALID_SURROGATE_2: char = 0xDFFF as char; // last surrogate + | ^^^^^^^^^^^^^^ + | + = note: `0xD800..=0xDFFF` are reserved for Unicode surrogates and are not valid `char` values + +error: surrogate values are not valid for `char` + --> $DIR/cast-char.rs:38:39 + | +LL | const INVALID_SURROGATE_3: char = 0xDB00 as char; // middle of surrogate range + | ^^^^^^^^^^^^^^ + | + = note: `0xD800..=0xDFFF` are reserved for Unicode surrogates and are not valid `char` values + +error: value exceeds maximum `char` value + --> $DIR/cast-char.rs:42:37 + | +LL | const INVALID_TOO_BIG_1: char = 0x110000 as char; // one more than maximum + | ^^^^^^^^^^^^^^^^ + | + = note: maximum valid `char` value is `0x10FFFF` + +error: value exceeds maximum `char` value + --> $DIR/cast-char.rs:44:37 + | +LL | const INVALID_TOO_BIG_2: char = 0xEF8888 as char; // example from issue + | ^^^^^^^^^^^^^^^^ + | + = note: maximum valid `char` value is `0x10FFFF` + +error: value exceeds maximum `char` value + --> $DIR/cast-char.rs:46:37 + | +LL | const INVALID_TOO_BIG_3: char = 0x1FFFFF as char; // much larger + | ^^^^^^^^^^^^^^^^ + | + = note: maximum valid `char` value is `0x10FFFF` + +error: value exceeds maximum `char` value + --> $DIR/cast-char.rs:48:37 + | +LL | const INVALID_TOO_BIG_4: char = 0xFFFFFF as char; // 24-bit maximum + | ^^^^^^^^^^^^^^^^ + | + = note: maximum valid `char` value is `0x10FFFF` + +error: only `u8` can be cast into `char` + --> $DIR/cast-char.rs:52:30 + | +LL | const BOUNDARY_1: char = 0xD7FE as char; // valid, before surrogate + | ^^^^^^^^^^^^^^ help: use a `char` literal instead: `'\u{D7FE}'` + +error: only `u8` can be cast into `char` + --> $DIR/cast-char.rs:54:30 + | +LL | const BOUNDARY_2: char = 0xE001 as char; // valid, after surrogate + | ^^^^^^^^^^^^^^ help: use a `char` literal instead: `'\u{E001}'` + +error: only `u8` can be cast into `char` + --> $DIR/cast-char.rs:56:30 + | +LL | const BOUNDARY_3: char = 0x10FFFE as char; // valid, near maximum + | ^^^^^^^^^^^^^^^^ help: use a `char` literal instead: `'\u{10FFFE}'` + +error: aborting due to 18 previous errors From 5d7db7e16ec5af85c39138dd7c669bc2219d1507 Mon Sep 17 00:00:00 2001 From: nazo6 Date: Sat, 12 Jul 2025 10:59:19 +0900 Subject: [PATCH 17/34] Fixed a core crate compilation failure when enabling the `optimize_for_size` feature on some targets --- library/core/src/fmt/num.rs | 8 ++++---- library/core/src/lib.rs | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index e1381ace44151..7d41ae45093ea 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -358,7 +358,7 @@ macro_rules! impl_Display { } #[cfg(feature = "optimize_for_size")] { - offset = _inner_slow_integer_to_str(self.unsigned_abs().$conv_fn(), &mut buf.buf); + offset = ${concat(_inner_slow_integer_to_str, $gen_name)}(self.unsigned_abs().$conv_fn(), &mut buf.buf); } // Only difference between signed and unsigned are these 4 lines. if self < 0 { @@ -401,7 +401,7 @@ macro_rules! impl_Display { } #[cfg(feature = "optimize_for_size")] { - offset = _inner_slow_integer_to_str(self.$conv_fn(), &mut buf.buf); + offset = ${concat(_inner_slow_integer_to_str, $gen_name)}(self.$conv_fn(), &mut buf.buf); } // SAFETY: Starting from `offset`, all elements of the slice have been set. unsafe { slice_buffer_to_str(&buf.buf, offset) } @@ -412,7 +412,7 @@ macro_rules! impl_Display { )* #[cfg(feature = "optimize_for_size")] - fn _inner_slow_integer_to_str(mut n: $u, buf: &mut [MaybeUninit::]) -> usize { + fn ${concat(_inner_slow_integer_to_str, $gen_name)}(mut n: $u, buf: &mut [MaybeUninit::]) -> usize { let mut curr = buf.len(); // SAFETY: To show that it's OK to copy into `buf_ptr`, notice that at the beginning @@ -437,7 +437,7 @@ macro_rules! impl_Display { const MAX_DEC_N: usize = $u::MAX.ilog(10) as usize + 1; let mut buf = [MaybeUninit::::uninit(); MAX_DEC_N]; - let offset = _inner_slow_integer_to_str(n, &mut buf); + let offset = ${concat(_inner_slow_integer_to_str, $gen_name)}(n, &mut buf); // SAFETY: Starting from `offset`, all elements of the slice have been set. let buf_slice = unsafe { slice_buffer_to_str(&buf, offset) }; f.pad_integral(is_nonnegative, "", buf_slice) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 2f701171505c7..e08edde3b38bb 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -160,6 +160,7 @@ #![feature(lang_items)] #![feature(link_llvm_intrinsics)] #![feature(macro_metavar_expr)] +#![feature(macro_metavar_expr_concat)] #![feature(marker_trait_attr)] #![feature(min_specialization)] #![feature(multiple_supertrait_upcastable)] From ef726a4cef26710fb190d9a179f7be62be16e95c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 13 Jul 2025 15:47:17 +0000 Subject: [PATCH 18/34] Ensure proper item queries for assoc tys --- compiler/rustc_hir_analysis/src/check/check.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index f4bbf03f0c26e..604add6ffb90f 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -889,8 +889,6 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), tcx.ensure_ok().predicates_of(def_id); tcx.ensure_ok().explicit_item_bounds(def_id); tcx.ensure_ok().explicit_item_self_bounds(def_id); - tcx.ensure_ok().item_bounds(def_id); - tcx.ensure_ok().item_self_bounds(def_id); if tcx.is_conditionally_const(def_id) { tcx.ensure_ok().explicit_implied_const_bounds(def_id); tcx.ensure_ok().const_conditions(def_id); @@ -1042,8 +1040,12 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), let has_type = match assoc_item.container { ty::AssocItemContainer::Impl => true, ty::AssocItemContainer::Trait => { - tcx.ensure_ok().item_bounds(def_id); - tcx.ensure_ok().item_self_bounds(def_id); + tcx.ensure_ok().explicit_item_bounds(def_id); + tcx.ensure_ok().explicit_item_self_bounds(def_id); + if tcx.is_conditionally_const(def_id) { + tcx.ensure_ok().explicit_implied_const_bounds(def_id); + tcx.ensure_ok().const_conditions(def_id); + } res = res.and(check_trait_item(tcx, def_id)); assoc_item.defaultness(tcx).has_value() } From 549a5d8b2843cff903a872ffa682f34d74bb341a Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 13 Jul 2025 16:23:20 +0000 Subject: [PATCH 19/34] Dont collect assoc ty item bounds from trait where clause for host effect predicates --- .../src/collect/item_bounds.rs | 56 +++++++++++-------- tests/crashes/133275-1.rs | 13 ----- tests/crashes/133275-2.rs | 15 ----- .../const-assoc-bound-in-trait-wc.rs | 13 +++++ 4 files changed, 47 insertions(+), 50 deletions(-) delete mode 100644 tests/crashes/133275-1.rs delete mode 100644 tests/crashes/133275-2.rs create mode 100644 tests/ui/traits/const-traits/const-assoc-bound-in-trait-wc.rs diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index e51ef46afb72f..7e3fb5448f642 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -38,12 +38,13 @@ fn associated_type_bounds<'tcx>( let icx = ItemCtxt::new(tcx, assoc_item_def_id); let mut bounds = Vec::new(); icx.lowerer().lower_bounds(item_ty, hir_bounds, &mut bounds, ty::List::empty(), filter); - // Implicit bounds are added to associated types unless a `?Trait` bound is found + match filter { PredicateFilter::All | PredicateFilter::SelfOnly | PredicateFilter::SelfTraitThatDefines(_) | PredicateFilter::SelfAndAssociatedTypeBounds => { + // Implicit bounds are added to associated types unless a `?Trait` bound is found. icx.lowerer().add_sizedness_bounds( &mut bounds, item_ty, @@ -53,37 +54,48 @@ fn associated_type_bounds<'tcx>( span, ); icx.lowerer().add_default_traits(&mut bounds, item_ty, hir_bounds, None, span); + + // Also collect `where Self::Assoc: Trait` from the parent trait's where clauses. + let trait_def_id = tcx.local_parent(assoc_item_def_id); + let trait_predicates = tcx.trait_explicit_predicates_and_bounds(trait_def_id); + + let item_trait_ref = + ty::TraitRef::identity(tcx, tcx.parent(assoc_item_def_id.to_def_id())); + bounds.extend(trait_predicates.predicates.iter().copied().filter_map( + |(clause, span)| { + remap_gat_vars_and_recurse_into_nested_projections( + tcx, + filter, + item_trait_ref, + assoc_item_def_id, + span, + clause, + ) + }, + )); } // `ConstIfConst` is only interested in `[const]` bounds. - PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => {} + PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => { + // FIXME(const_trait_impl): We *could* uplift the + // `where Self::Assoc: [const] Trait` bounds from the parent trait + // here too, but we'd need to split `const_conditions` into two + // queries (like we do for `trait_explicit_predicates_and_bounds`) + // since we need to also filter the predicates *out* of the const + // conditions or they lead to cycles in the trait solver when + // utilizing these bounds. For now, let's do nothing. + } } - let trait_def_id = tcx.local_parent(assoc_item_def_id); - let trait_predicates = tcx.trait_explicit_predicates_and_bounds(trait_def_id); - - let item_trait_ref = ty::TraitRef::identity(tcx, tcx.parent(assoc_item_def_id.to_def_id())); - let bounds_from_parent = - trait_predicates.predicates.iter().copied().filter_map(|(clause, span)| { - remap_gat_vars_and_recurse_into_nested_projections( - tcx, - filter, - item_trait_ref, - assoc_item_def_id, - span, - clause, - ) - }); - - let all_bounds = tcx.arena.alloc_from_iter(bounds.into_iter().chain(bounds_from_parent)); + let bounds = tcx.arena.alloc_from_iter(bounds); debug!( "associated_type_bounds({}) = {:?}", tcx.def_path_str(assoc_item_def_id.to_def_id()), - all_bounds + bounds ); - assert_only_contains_predicates_from(filter, all_bounds, item_ty); + assert_only_contains_predicates_from(filter, bounds, item_ty); - all_bounds + bounds }) } diff --git a/tests/crashes/133275-1.rs b/tests/crashes/133275-1.rs deleted file mode 100644 index 73c04f5d6e410..0000000000000 --- a/tests/crashes/133275-1.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ known-bug: #133275 -#![feature(const_trait_impl)] -#![feature(associated_type_defaults)] - -#[const_trait] -trait Foo3 -where - Self::Baz: Clone, -{ - type Baz = T; -} - -pub fn main() {} diff --git a/tests/crashes/133275-2.rs b/tests/crashes/133275-2.rs deleted file mode 100644 index a774b3cdb6905..0000000000000 --- a/tests/crashes/133275-2.rs +++ /dev/null @@ -1,15 +0,0 @@ -//@ known-bug: #133275 -#![feature(const_trait_impl)] -#[const_trait] -pub trait Owo::T> {} - -#[const_trait] -trait Foo3 -where - Self::Bar: Clone, - Self::Baz: Clone, -{ - type Bar = Vec; - type Baz = T; - //~^ ERROR the trait bound `T: Clone` is not satisfied -} diff --git a/tests/ui/traits/const-traits/const-assoc-bound-in-trait-wc.rs b/tests/ui/traits/const-traits/const-assoc-bound-in-trait-wc.rs new file mode 100644 index 0000000000000..81acce65f2a9d --- /dev/null +++ b/tests/ui/traits/const-traits/const-assoc-bound-in-trait-wc.rs @@ -0,0 +1,13 @@ +//@ check-pass + +#![feature(const_clone)] +#![feature(const_trait_impl)] + +#[const_trait] +trait A where Self::Target: [const] Clone { + type Target; +} + +const fn foo() where T: [const] A {} + +fn main() {} From 8daf98b6236bed2db074b8e8beb7e3225201b71a Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 13 Jul 2025 16:26:13 +0000 Subject: [PATCH 20/34] Imply always-const host effects the same as any other item bound --- .../src/collect/item_bounds.rs | 1 + .../traits/const-traits/imply-always-const.rs | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 tests/ui/traits/const-traits/imply-always-const.rs diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index 7e3fb5448f642..548ba343aaeee 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -124,6 +124,7 @@ fn remap_gat_vars_and_recurse_into_nested_projections<'tcx>( ty::ClauseKind::Trait(tr) => tr.self_ty(), ty::ClauseKind::Projection(proj) => proj.projection_term.self_ty(), ty::ClauseKind::TypeOutlives(outlives) => outlives.0, + ty::ClauseKind::HostEffect(host) => host.self_ty(), _ => return None, }; diff --git a/tests/ui/traits/const-traits/imply-always-const.rs b/tests/ui/traits/const-traits/imply-always-const.rs new file mode 100644 index 0000000000000..f6cab0681ec2e --- /dev/null +++ b/tests/ui/traits/const-traits/imply-always-const.rs @@ -0,0 +1,19 @@ +//@ check-pass + +#![feature(const_trait_impl)] + +#[const_trait] +trait A where Self::Assoc: const B { + type Assoc; +} + +#[const_trait] +trait B {} + +fn needs_b() {} + +fn test() { + needs_b::(); +} + +fn main() {} From 4a77a62e5611890dd275e17de0fb1694e4ff9a28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Ml=C3=A1dek?= Date: Tue, 15 Jul 2025 15:26:18 +0200 Subject: [PATCH 21/34] rustc_resolve: rename `check_hidden_glob_reexports` to `lint_reexports` --- compiler/rustc_resolve/src/imports.rs | 5 +---- compiler/rustc_resolve/src/lib.rs | 4 +--- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 40c63a3edf3c1..b2f6ee6563c56 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -634,10 +634,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } - pub(crate) fn check_hidden_glob_reexports( - &mut self, - exported_ambiguities: FxHashSet>, - ) { + pub(crate) fn lint_reexports(&mut self, exported_ambiguities: FxHashSet>) { for module in self.arenas.local_modules().iter() { for (key, resolution) in self.resolutions(*module).borrow().iter() { let resolution = resolution.borrow(); diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index c2b3451d4a1d2..87963295c2d89 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1775,9 +1775,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let exported_ambiguities = self.tcx.sess.time("compute_effective_visibilities", || { EffectiveVisibilitiesVisitor::compute_effective_visibilities(self, krate) }); - self.tcx.sess.time("check_hidden_glob_reexports", || { - self.check_hidden_glob_reexports(exported_ambiguities) - }); + self.tcx.sess.time("lint_reexports", || self.lint_reexports(exported_ambiguities)); self.tcx .sess .time("finalize_macro_resolutions", || self.finalize_macro_resolutions(krate)); From 8b868fa534a8b660a5b8051a5c883d4c15c5b70e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Ml=C3=A1dek?= Date: Sat, 12 Jul 2025 16:44:34 +0200 Subject: [PATCH 22/34] Implement resolver warnings about reexporting private dependencies --- compiler/rustc_lint/messages.ftl | 3 ++ compiler/rustc_lint/src/early/diagnostics.rs | 3 ++ compiler/rustc_lint/src/lints.rs | 8 ++++ compiler/rustc_lint_defs/src/lib.rs | 5 ++ compiler/rustc_resolve/src/imports.rs | 25 +++++++++- tests/ui/privacy/pub-priv-dep/pub-priv1.rs | 14 +++--- .../ui/privacy/pub-priv-dep/pub-priv1.stderr | 46 +++++++++++++++++-- 7 files changed, 90 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 8d9f2385b710f..183db43ca7656 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -744,6 +744,9 @@ lint_redundant_semicolons_suggestion = remove {$multiple_semicolons -> *[false] this semicolon } +lint_reexport_private_dependency = + {$kind} `{$name}` from private dependency '{$krate}' is re-exported + lint_remove_mut_from_pattern = remove `mut` from the parameter lint_removed_lint = lint `{$name}` has been removed: {$reason} diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 653559009ccca..f0fbf5bc81e9b 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -351,6 +351,9 @@ pub fn decorate_builtin_lint( } .decorate_lint(diag); } + BuiltinLintDiag::ReexportPrivateDependency { name, kind, krate } => { + lints::ReexportPrivateDependency { name, kind, krate }.decorate_lint(diag); + } BuiltinLintDiag::UnusedQualifications { removal_span } => { lints::UnusedQualifications { removal_span }.decorate_lint(diag); } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 21148833eaf72..fc3c107325987 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3080,6 +3080,14 @@ pub(crate) struct HiddenGlobReexports { pub namespace: String, } +#[derive(LintDiagnostic)] +#[diag(lint_reexport_private_dependency)] +pub(crate) struct ReexportPrivateDependency { + pub name: String, + pub kind: String, + pub krate: Symbol, +} + #[derive(LintDiagnostic)] #[diag(lint_unnecessary_qualification)] pub(crate) struct UnusedQualifications { diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index cd402c9234fd8..fe068d96b7424 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -739,6 +739,11 @@ pub enum BuiltinLintDiag { /// The local binding that shadows the glob reexport. private_item_span: Span, }, + ReexportPrivateDependency { + name: String, + kind: String, + krate: Symbol, + }, UnusedQualifications { /// The span of the unnecessarily-qualified path to remove. removal_span: Span, diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index b2f6ee6563c56..9e8eac75fa115 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -14,8 +14,8 @@ use rustc_middle::metadata::{ModChild, Reexport}; use rustc_middle::{span_bug, ty}; use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::{ - AMBIGUOUS_GLOB_REEXPORTS, HIDDEN_GLOB_REEXPORTS, PUB_USE_OF_PRIVATE_EXTERN_CRATE, - REDUNDANT_IMPORTS, UNUSED_IMPORTS, + AMBIGUOUS_GLOB_REEXPORTS, EXPORTED_PRIVATE_DEPENDENCIES, HIDDEN_GLOB_REEXPORTS, + PUB_USE_OF_PRIVATE_EXTERN_CRATE, REDUNDANT_IMPORTS, UNUSED_IMPORTS, }; use rustc_session::parse::feature_err; use rustc_span::edit_distance::find_best_match_for_name; @@ -696,6 +696,27 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } } + + if let NameBindingKind::Import { import, .. } = binding.kind + && let Some(binding_id) = import.id() + && let import_def_id = self.local_def_id(binding_id) + && self.effective_visibilities.is_exported(import_def_id) + && let Res::Def(reexported_kind, reexported_def_id) = binding.res() + && !matches!(reexported_kind, DefKind::Ctor(..)) + && !reexported_def_id.is_local() + && self.tcx.is_private_dep(reexported_def_id.krate) + { + self.lint_buffer.buffer_lint( + EXPORTED_PRIVATE_DEPENDENCIES, + binding_id, + binding.span, + BuiltinLintDiag::ReexportPrivateDependency { + kind: binding.res().descr().to_string(), + name: key.ident.name.to_string(), + krate: self.tcx.crate_name(reexported_def_id.krate), + }, + ); + } } } } diff --git a/tests/ui/privacy/pub-priv-dep/pub-priv1.rs b/tests/ui/privacy/pub-priv-dep/pub-priv1.rs index 877029f3de37a..192ca0db8bd41 100644 --- a/tests/ui/privacy/pub-priv-dep/pub-priv1.rs +++ b/tests/ui/privacy/pub-priv-dep/pub-priv1.rs @@ -9,10 +9,10 @@ #![deny(exported_private_dependencies)] // This crate is a private dependency -// FIXME: This should trigger. pub extern crate priv_dep; +//~^ ERROR crate `priv_dep` from private dependency 'priv_dep' is re-exported // This crate is a public dependency -extern crate pub_dep; +pub extern crate pub_dep; // This crate is a private dependency extern crate pm; @@ -91,16 +91,16 @@ pub struct AllowedPrivType { pub allowed: OtherType, } -// FIXME: This should trigger. pub use priv_dep::m; -// FIXME: This should trigger. +//~^ ERROR macro `m` from private dependency 'priv_dep' is re-exported pub use pm::fn_like; -// FIXME: This should trigger. +//~^ ERROR macro `fn_like` from private dependency 'pm' is re-exported pub use pm::PmDerive; -// FIXME: This should trigger. +//~^ ERROR macro `PmDerive` from private dependency 'pm' is re-exported pub use pm::pm_attr; +//~^ ERROR macro `pm_attr` from private dependency 'pm' is re-exported -// FIXME: This should trigger. pub use priv_dep::E::V1; +//~^ ERROR variant `V1` from private dependency 'priv_dep' is re-exported fn main() {} diff --git a/tests/ui/privacy/pub-priv-dep/pub-priv1.stderr b/tests/ui/privacy/pub-priv-dep/pub-priv1.stderr index adfe13424cdf3..9da47827be4b1 100644 --- a/tests/ui/privacy/pub-priv-dep/pub-priv1.stderr +++ b/tests/ui/privacy/pub-priv-dep/pub-priv1.stderr @@ -1,8 +1,8 @@ -error: type `OtherType` from private dependency 'priv_dep' in public interface - --> $DIR/pub-priv1.rs:29:5 +error: crate `priv_dep` from private dependency 'priv_dep' is re-exported + --> $DIR/pub-priv1.rs:12:1 | -LL | pub field: OtherType, - | ^^^^^^^^^^^^^^^^^^^^ +LL | pub extern crate priv_dep; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here --> $DIR/pub-priv1.rs:9:9 @@ -10,6 +10,42 @@ note: the lint level is defined here LL | #![deny(exported_private_dependencies)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: macro `m` from private dependency 'priv_dep' is re-exported + --> $DIR/pub-priv1.rs:94:9 + | +LL | pub use priv_dep::m; + | ^^^^^^^^^^^ + +error: macro `fn_like` from private dependency 'pm' is re-exported + --> $DIR/pub-priv1.rs:96:9 + | +LL | pub use pm::fn_like; + | ^^^^^^^^^^^ + +error: derive macro `PmDerive` from private dependency 'pm' is re-exported + --> $DIR/pub-priv1.rs:98:9 + | +LL | pub use pm::PmDerive; + | ^^^^^^^^^^^^ + +error: attribute macro `pm_attr` from private dependency 'pm' is re-exported + --> $DIR/pub-priv1.rs:100:9 + | +LL | pub use pm::pm_attr; + | ^^^^^^^^^^^ + +error: variant `V1` from private dependency 'priv_dep' is re-exported + --> $DIR/pub-priv1.rs:103:9 + | +LL | pub use priv_dep::E::V1; + | ^^^^^^^^^^^^^^^ + +error: type `OtherType` from private dependency 'priv_dep' in public interface + --> $DIR/pub-priv1.rs:29:5 + | +LL | pub field: OtherType, + | ^^^^^^^^^^^^^^^^^^^^ + error: type `OtherType` from private dependency 'priv_dep' in public interface --> $DIR/pub-priv1.rs:36:5 | @@ -90,5 +126,5 @@ LL | impl PubTraitOnPrivate for OtherType {} | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 14 previous errors +error: aborting due to 20 previous errors From d627e252ae5f1d282f3c2ca6c0a0e24154552e8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Ml=C3=A1dek?= Date: Sat, 12 Jul 2025 23:07:41 +0200 Subject: [PATCH 23/34] make `std_detect` public dependency of `std` --- library/std/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 62ece4b696199..ca2fb2f49a37f 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -23,7 +23,7 @@ unwind = { path = "../unwind" } hashbrown = { version = "0.15", default-features = false, features = [ 'rustc-dep-of-std', ] } -std_detect = { path = "../stdarch/crates/std_detect", default-features = false, features = [ +std_detect = { path = "../stdarch/crates/std_detect", public = true, default-features = false, features = [ 'rustc-dep-of-std', ] } From 4b421d44e20351426cf4ded11248bd1d0af40b5f Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 15 Jul 2025 13:33:32 +0000 Subject: [PATCH 24/34] Fix handling of SCRIPT_ARG in docker images Instead of making this a build parameter, pass the SCRIPT as an environment variable. To this purpose, normalize on always referring to a script in `/scripts`. For i686-gnu-nopt-2 I had to create a separate script, because Docker seems to be really terrible at command line argument parsing, so it's not possible to pass an environment variable that contains whitespace. --- .../host-aarch64/aarch64-gnu-llvm-19/Dockerfile | 8 +++----- .../host-x86_64/dist-x86_64-linux/Dockerfile | 4 +--- src/ci/docker/host-x86_64/i686-gnu-nopt/Dockerfile | 4 ++-- src/ci/docker/host-x86_64/i686-gnu/Dockerfile | 3 +-- .../host-x86_64/x86_64-gnu-llvm-19/Dockerfile | 14 ++++++-------- .../host-x86_64/x86_64-gnu-llvm-20/Dockerfile | 14 ++++++-------- src/ci/docker/run.sh | 12 ++++-------- src/ci/docker/scripts/i686-gnu-nopt-2.sh | 6 ++++++ src/ci/docker/scripts/x86_64-gnu-llvm2.sh | 2 +- src/ci/github-actions/jobs.yml | 6 ++---- 10 files changed, 32 insertions(+), 41 deletions(-) create mode 100755 src/ci/docker/scripts/i686-gnu-nopt-2.sh diff --git a/src/ci/docker/host-aarch64/aarch64-gnu-llvm-19/Dockerfile b/src/ci/docker/host-aarch64/aarch64-gnu-llvm-19/Dockerfile index 2f9d0010573ab..e73fbe506f788 100644 --- a/src/ci/docker/host-aarch64/aarch64-gnu-llvm-19/Dockerfile +++ b/src/ci/docker/host-aarch64/aarch64-gnu-llvm-19/Dockerfile @@ -50,9 +50,7 @@ ENV RUST_CONFIGURE_ARGS \ COPY scripts/shared.sh /scripts/ -ARG SCRIPT_ARG +COPY scripts/stage_2_test_set1.sh /scripts/ +COPY scripts/stage_2_test_set2.sh /scripts/ -COPY scripts/stage_2_test_set1.sh /tmp/ -COPY scripts/stage_2_test_set2.sh /tmp/ - -ENV SCRIPT "/tmp/${SCRIPT_ARG}" +ENV SCRIPT "Must specify DOCKER_SCRIPT for this image" diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile index 44f6a8d2a155a..01f19eac1d2fa 100644 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile @@ -96,12 +96,10 @@ ENV RUST_CONFIGURE_ARGS \ --set rust.lto=thin \ --set rust.codegen-units=1 -ARG SCRIPT_ARG - COPY host-x86_64/dist-x86_64-linux/dist.sh /scripts/ COPY host-x86_64/dist-x86_64-linux/dist-alt.sh /scripts/ -ENV SCRIPT /scripts/${SCRIPT_ARG} +ENV SCRIPT "Must specify DOCKER_SCRIPT for this image" ENV CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=clang diff --git a/src/ci/docker/host-x86_64/i686-gnu-nopt/Dockerfile b/src/ci/docker/host-x86_64/i686-gnu-nopt/Dockerfile index 58e66fd637a29..be3df9d4036db 100644 --- a/src/ci/docker/host-x86_64/i686-gnu-nopt/Dockerfile +++ b/src/ci/docker/host-x86_64/i686-gnu-nopt/Dockerfile @@ -23,7 +23,7 @@ COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu --disable-optimize-tests -ARG SCRIPT_ARG COPY scripts/stage_2_test_set1.sh /scripts/ COPY scripts/stage_2_test_set2.sh /scripts/ -ENV SCRIPT ${SCRIPT_ARG} +COPY scripts/i686-gnu-nopt-2.sh /scripts/ +ENV SCRIPT "Must specify DOCKER_SCRIPT for this image" diff --git a/src/ci/docker/host-x86_64/i686-gnu/Dockerfile b/src/ci/docker/host-x86_64/i686-gnu/Dockerfile index a715f7182d2a6..00cd24b89db8a 100644 --- a/src/ci/docker/host-x86_64/i686-gnu/Dockerfile +++ b/src/ci/docker/host-x86_64/i686-gnu/Dockerfile @@ -24,7 +24,6 @@ COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu -ARG SCRIPT_ARG COPY scripts/stage_2_test_set1.sh /scripts/ COPY scripts/stage_2_test_set2.sh /scripts/ -ENV SCRIPT /scripts/${SCRIPT_ARG} +ENV SCRIPT "Must specify DOCKER_SCRIPT for this image" diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile index c09be047c6a80..5cba7c564f164 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile @@ -57,12 +57,10 @@ ENV RUST_CONFIGURE_ARGS \ COPY scripts/shared.sh /scripts/ -ARG SCRIPT_ARG +COPY scripts/x86_64-gnu-llvm.sh /scripts/ +COPY scripts/x86_64-gnu-llvm2.sh /scripts/ +COPY scripts/x86_64-gnu-llvm3.sh /scripts/ +COPY scripts/stage_2_test_set1.sh /scripts/ +COPY scripts/stage_2_test_set2.sh /scripts/ -COPY scripts/x86_64-gnu-llvm.sh /tmp/ -COPY scripts/x86_64-gnu-llvm2.sh /tmp/ -COPY scripts/x86_64-gnu-llvm3.sh /tmp/ -COPY scripts/stage_2_test_set1.sh /tmp/ -COPY scripts/stage_2_test_set2.sh /tmp/ - -ENV SCRIPT "/tmp/${SCRIPT_ARG}" +ENV SCRIPT "Must specify DOCKER_SCRIPT for this image" diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-20/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-20/Dockerfile index 83a3bfb37a54b..92c2631000f9e 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-20/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-20/Dockerfile @@ -57,12 +57,10 @@ ENV RUST_CONFIGURE_ARGS \ COPY scripts/shared.sh /scripts/ -ARG SCRIPT_ARG +COPY scripts/x86_64-gnu-llvm.sh /scripts/ +COPY scripts/x86_64-gnu-llvm2.sh /scripts/ +COPY scripts/x86_64-gnu-llvm3.sh /scripts/ +COPY scripts/stage_2_test_set1.sh /scripts/ +COPY scripts/stage_2_test_set2.sh /scripts/ -COPY scripts/x86_64-gnu-llvm.sh /tmp/ -COPY scripts/x86_64-gnu-llvm2.sh /tmp/ -COPY scripts/x86_64-gnu-llvm3.sh /tmp/ -COPY scripts/stage_2_test_set1.sh /tmp/ -COPY scripts/stage_2_test_set2.sh /tmp/ - -ENV SCRIPT "/tmp/${SCRIPT_ARG}" +ENV SCRIPT "Must specify DOCKER_SCRIPT for this image" diff --git a/src/ci/docker/run.sh b/src/ci/docker/run.sh index da7d084d48d8f..044f5a8fff322 100755 --- a/src/ci/docker/run.sh +++ b/src/ci/docker/run.sh @@ -114,14 +114,6 @@ if [ -f "$docker_dir/$image/Dockerfile" ]; then "$context" ) - # If the environment variable DOCKER_SCRIPT is defined, - # set the build argument SCRIPT_ARG to DOCKER_SCRIPT. - # In this way, we run the script defined in CI, - # instead of the one defined in the Dockerfile. - if [ -n "${DOCKER_SCRIPT+x}" ]; then - build_args+=("--build-arg" "SCRIPT_ARG=${DOCKER_SCRIPT}") - fi - GHCR_BUILDKIT_IMAGE="ghcr.io/rust-lang/buildkit:buildx-stable-1" # On non-CI jobs, we try to download a pre-built image from the rust-lang-ci # ghcr.io registry. If it is not possible, we fall back to building the image @@ -341,6 +333,10 @@ if [ "$ENABLE_GCC_CODEGEN" = "1" ]; then echo "Setting extra environment values for docker: $extra_env" fi +if [ -n "${DOCKER_SCRIPT}" ]; then + extra_env="$extra_env --env SCRIPT=\"/scripts/${DOCKER_SCRIPT}\"" +fi + docker \ run \ --workdir /checkout/obj \ diff --git a/src/ci/docker/scripts/i686-gnu-nopt-2.sh b/src/ci/docker/scripts/i686-gnu-nopt-2.sh new file mode 100755 index 0000000000000..4c171739dafc0 --- /dev/null +++ b/src/ci/docker/scripts/i686-gnu-nopt-2.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +set -ex + +python3 ../x.py test --stage 1 --set rust.optimize=false library/std && +/scripts/stage_2_test_set2.sh diff --git a/src/ci/docker/scripts/x86_64-gnu-llvm2.sh b/src/ci/docker/scripts/x86_64-gnu-llvm2.sh index fe5382aaa48cc..0060b9bfd23b3 100755 --- a/src/ci/docker/scripts/x86_64-gnu-llvm2.sh +++ b/src/ci/docker/scripts/x86_64-gnu-llvm2.sh @@ -4,7 +4,7 @@ set -ex ##### Test stage 2 ##### -/tmp/stage_2_test_set1.sh +/scripts/stage_2_test_set1.sh # Run the `mir-opt` tests again but this time for a 32-bit target. # This enforces that tests using `// EMIT_MIR_FOR_EACH_BIT_WIDTH` have diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 445fc0dd018cf..6c5e37b51ef23 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -315,16 +315,14 @@ auto: - name: i686-gnu-nopt-1 env: IMAGE: i686-gnu-nopt - DOCKER_SCRIPT: /scripts/stage_2_test_set1.sh + DOCKER_SCRIPT: stage_2_test_set1.sh <<: *job-linux-4c # Skip tests that run in i686-gnu-nopt-1 - name: i686-gnu-nopt-2 env: IMAGE: i686-gnu-nopt - DOCKER_SCRIPT: >- - python3 ../x.py test --stage 1 --set rust.optimize=false library/std && - /scripts/stage_2_test_set2.sh + DOCKER_SCRIPT: i686-gnu-nopt-2.sh <<: *job-linux-4c - name: pr-check-1 From 3f2dc2bd1a2e0120b868911497ddbd8e43f3a9fa Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Tue, 8 Jul 2025 00:20:57 +0800 Subject: [PATCH 25/34] add `const_make_global`; err for `const_allocate` ptrs if didn't call Co-Authored-By: Ralf Jung Co-Authored-By: Oli Scherer --- compiler/rustc_const_eval/messages.ftl | 11 ++++ .../rustc_const_eval/src/const_eval/error.rs | 32 +++++++++- .../src/const_eval/eval_queries.rs | 2 +- .../src/const_eval/machine.rs | 19 ++++-- compiler/rustc_const_eval/src/errors.rs | 8 +++ .../rustc_const_eval/src/interpret/intern.rs | 58 +++++++++++++++++-- .../rustc_const_eval/src/interpret/memory.rs | 44 ++++++++++++++ .../src/util/check_validity_requirement.rs | 10 ++-- .../rustc_hir_analysis/src/check/intrinsic.rs | 3 + .../rustc_middle/src/mir/interpret/error.rs | 2 +- compiler/rustc_span/src/symbol.rs | 1 + library/alloc/src/boxed/thin.rs | 7 ++- library/core/src/intrinsics/mod.rs | 9 +++ .../heap/alloc_intrinsic_nontransient.rs | 4 +- .../heap/alloc_intrinsic_uninit.32bit.stderr | 2 +- .../heap/alloc_intrinsic_uninit.64bit.stderr | 2 +- .../const-eval/heap/alloc_intrinsic_uninit.rs | 5 +- .../heap/alloc_intrinsic_untyped.rs | 1 + .../heap/alloc_intrinsic_untyped.stderr | 10 +++- .../const-eval/heap/dealloc_intrinsic.rs | 2 +- .../const-eval/heap/make-global-dangling.rs | 16 +++++ .../heap/make-global-dangling.stderr | 15 +++++ .../const-eval/heap/make-global-other.rs | 13 +++++ .../const-eval/heap/make-global-other.stderr | 9 +++ .../const-eval/heap/make-global-twice.rs | 16 +++++ .../const-eval/heap/make-global-twice.stderr | 9 +++ .../ui/consts/const-eval/heap/make-global.rs | 21 +++++++ .../heap/ptr_made_global_mutated.rs | 14 +++++ .../heap/ptr_made_global_mutated.stderr | 9 +++ .../const-eval/heap/ptr_not_made_global.rs | 19 ++++++ .../heap/ptr_not_made_global.stderr | 18 ++++++ 31 files changed, 361 insertions(+), 30 deletions(-) create mode 100644 tests/ui/consts/const-eval/heap/make-global-dangling.rs create mode 100644 tests/ui/consts/const-eval/heap/make-global-dangling.stderr create mode 100644 tests/ui/consts/const-eval/heap/make-global-other.rs create mode 100644 tests/ui/consts/const-eval/heap/make-global-other.stderr create mode 100644 tests/ui/consts/const-eval/heap/make-global-twice.rs create mode 100644 tests/ui/consts/const-eval/heap/make-global-twice.stderr create mode 100644 tests/ui/consts/const-eval/heap/make-global.rs create mode 100644 tests/ui/consts/const-eval/heap/ptr_made_global_mutated.rs create mode 100644 tests/ui/consts/const-eval/heap/ptr_made_global_mutated.stderr create mode 100644 tests/ui/consts/const-eval/heap/ptr_not_made_global.rs create mode 100644 tests/ui/consts/const-eval/heap/ptr_not_made_global.stderr diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index b767ca9a3c251..0463788401189 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -56,6 +56,17 @@ const_eval_const_context = {$kind -> *[other] {""} } +const_eval_const_heap_ptr_in_final = encountered `const_allocate` pointer in final value that was not made global + .note = use `const_make_global` to make allocated pointers immutable before returning + +const_eval_const_make_global_ptr_already_made_global = attempting to call `const_make_global` twice on the same allocation {$alloc} + +const_eval_const_make_global_ptr_is_non_heap = pointer passed to `const_make_global` does not point to a heap allocation: {$ptr} + +const_eval_const_make_global_with_dangling_ptr = pointer passed to `const_make_global` is dangling: {$ptr} + +const_eval_const_make_global_with_offset = making {$ptr} global which does not point to the beginning of an object + const_eval_copy_nonoverlapping_overlapping = `copy_nonoverlapping` called on overlapping ranges diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index 08fc03d9c4647..5b07b7c13c1a2 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -2,7 +2,7 @@ use std::mem; use rustc_errors::{Diag, DiagArgName, DiagArgValue, DiagMessage, IntoDiagArg}; use rustc_middle::mir::AssertKind; -use rustc_middle::mir::interpret::{Provenance, ReportedErrorInfo}; +use rustc_middle::mir::interpret::{AllocId, Provenance, ReportedErrorInfo}; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::LayoutError; use rustc_middle::ty::{ConstInt, TyCtxt}; @@ -22,8 +22,22 @@ pub enum ConstEvalErrKind { ModifiedGlobal, RecursiveStatic, AssertFailure(AssertKind), - Panic { msg: Symbol, line: u32, col: u32, file: Symbol }, + Panic { + msg: Symbol, + line: u32, + col: u32, + file: Symbol, + }, WriteThroughImmutablePointer, + /// Called `const_make_global` twice. + ConstMakeGlobalPtrAlreadyMadeGlobal(AllocId), + /// Called `const_make_global` on a non-heap pointer. + ConstMakeGlobalPtrIsNonHeap(String), + /// Called `const_make_global` on a dangling pointer. + ConstMakeGlobalWithDanglingPtr(String), + /// Called `const_make_global` on a pointer that does not start at the + /// beginning of an object. + ConstMakeGlobalWithOffset(String), } impl MachineStopType for ConstEvalErrKind { @@ -38,6 +52,12 @@ impl MachineStopType for ConstEvalErrKind { RecursiveStatic => const_eval_recursive_static, AssertFailure(x) => x.diagnostic_message(), WriteThroughImmutablePointer => const_eval_write_through_immutable_pointer, + ConstMakeGlobalPtrAlreadyMadeGlobal { .. } => { + const_eval_const_make_global_ptr_already_made_global + } + ConstMakeGlobalPtrIsNonHeap(_) => const_eval_const_make_global_ptr_is_non_heap, + ConstMakeGlobalWithDanglingPtr(_) => const_eval_const_make_global_with_dangling_ptr, + ConstMakeGlobalWithOffset(_) => const_eval_const_make_global_with_offset, } } fn add_args(self: Box, adder: &mut dyn FnMut(DiagArgName, DiagArgValue)) { @@ -51,6 +71,14 @@ impl MachineStopType for ConstEvalErrKind { Panic { msg, .. } => { adder("msg".into(), msg.into_diag_arg(&mut None)); } + ConstMakeGlobalPtrIsNonHeap(ptr) + | ConstMakeGlobalWithOffset(ptr) + | ConstMakeGlobalWithDanglingPtr(ptr) => { + adder("ptr".into(), ptr.into_diag_arg(&mut None)); + } + ConstMakeGlobalPtrAlreadyMadeGlobal(alloc) => { + adder("alloc".into(), alloc.into_diag_arg(&mut None)); + } } } } diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 4bd4b49300900..32209ff436791 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -93,7 +93,7 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>( // Since evaluation had no errors, validate the resulting constant. const_validate_mplace(ecx, &ret, cid)?; - // Only report this after validation, as validaiton produces much better diagnostics. + // Only report this after validation, as validation produces much better diagnostics. // FIXME: ensure validation always reports this and stop making interning care about it. match intern_result { diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 52fc898192ade..4e952b043fbca 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -169,13 +169,15 @@ pub type CompileTimeInterpCx<'tcx> = InterpCx<'tcx, CompileTimeMachine<'tcx>>; #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum MemoryKind { - Heap, + Heap { was_made_global: bool }, } impl fmt::Display for MemoryKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - MemoryKind::Heap => write!(f, "heap allocation"), + MemoryKind::Heap { was_made_global } => { + write!(f, "heap allocation{}", if *was_made_global { " (made global)" } else { "" }) + } } } } @@ -184,7 +186,7 @@ impl interpret::MayLeak for MemoryKind { #[inline(always)] fn may_leak(self) -> bool { match self { - MemoryKind::Heap => false, + MemoryKind::Heap { was_made_global } => was_made_global, } } } @@ -420,7 +422,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { let ptr = ecx.allocate_ptr( Size::from_bytes(size), align, - interpret::MemoryKind::Machine(MemoryKind::Heap), + interpret::MemoryKind::Machine(MemoryKind::Heap { was_made_global: false }), AllocInit::Uninit, )?; ecx.write_pointer(ptr, dest)?; @@ -453,10 +455,17 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { ecx.deallocate_ptr( ptr, Some((size, align)), - interpret::MemoryKind::Machine(MemoryKind::Heap), + interpret::MemoryKind::Machine(MemoryKind::Heap { was_made_global: false }), )?; } } + + sym::const_make_global => { + let ptr = ecx.read_pointer(&args[0])?; + ecx.make_const_heap_ptr_global(ptr)?; + ecx.write_pointer(ptr, dest)?; + } + // The intrinsic represents whether the value is known to the optimizer (LLVM). // We're not doing any optimizations here, so there is no optimizer that could know the value. // (We know the value here in the machine of course, but this is the runtime of that code, diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 49cd713874869..b6a64035261e5 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -43,6 +43,14 @@ pub(crate) struct MutablePtrInFinal { pub kind: InternKind, } +#[derive(Diagnostic)] +#[diag(const_eval_const_heap_ptr_in_final)] +#[note] +pub(crate) struct ConstHeapPtrInFinal { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(const_eval_unstable_in_stable_exposed)] pub(crate) struct UnstableInStableExposed { diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index f0f958d069ee8..85524949053c9 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -31,7 +31,7 @@ use super::{ }; use crate::const_eval; use crate::const_eval::DummyMachine; -use crate::errors::NestedStaticInThreadLocal; +use crate::errors::{ConstHeapPtrInFinal, NestedStaticInThreadLocal}; pub trait CompileTimeMachine<'tcx, T> = Machine< 'tcx, @@ -55,6 +55,35 @@ impl HasStaticRootDefId for const_eval::CompileTimeMachine<'_> { } } +pub enum DisallowInternReason { + ConstHeap, +} + +/// A trait for controlling whether memory allocated in the interpreter can be interned. +/// +/// This prevents us from interning `const_allocate` pointers that have not been made +/// global through `const_make_global`. +pub trait CanIntern { + fn disallows_intern(&self) -> Option; +} + +impl CanIntern for const_eval::MemoryKind { + fn disallows_intern(&self) -> Option { + match self { + const_eval::MemoryKind::Heap { was_made_global: false } => { + Some(DisallowInternReason::ConstHeap) + } + const_eval::MemoryKind::Heap { was_made_global: true } => None, + } + } +} + +impl CanIntern for ! { + fn disallows_intern(&self) -> Option { + *self + } +} + /// Intern an allocation. Returns `Err` if the allocation does not exist in the local memory. /// /// `mutability` can be used to force immutable interning: if it is `Mutability::Not`, the @@ -62,7 +91,7 @@ impl HasStaticRootDefId for const_eval::CompileTimeMachine<'_> { /// already mutable (as a sanity check). /// /// Returns an iterator over all relocations referred to by this allocation. -fn intern_shallow<'tcx, T, M: CompileTimeMachine<'tcx, T>>( +fn intern_shallow<'tcx, T: CanIntern, M: CompileTimeMachine<'tcx, T>>( ecx: &mut InterpCx<'tcx, M>, alloc_id: AllocId, mutability: Mutability, @@ -71,9 +100,26 @@ fn intern_shallow<'tcx, T, M: CompileTimeMachine<'tcx, T>>( trace!("intern_shallow {:?}", alloc_id); // remove allocation // FIXME(#120456) - is `swap_remove` correct? - let Some((_kind, mut alloc)) = ecx.memory.alloc_map.swap_remove(&alloc_id) else { + let Some((kind, mut alloc)) = ecx.memory.alloc_map.swap_remove(&alloc_id) else { return Err(()); }; + + match kind { + MemoryKind::Machine(x) if let Some(reason) = x.disallows_intern() => match reason { + // attempting to intern a `const_allocate`d pointer that was not made global via + // `const_make_global`. We emit an error here but don't return an `Err`. The `Err` + // is for pointers that we can't intern at all (i.e. dangling pointers). We still + // (recursively) intern this pointer because we don't have to worry about the + // additional paperwork involved with _not_ interning it, such as storing it in + // the dead memory map and having to deal with additional "dangling pointer" + // messages if someone tries to store the non-made-global ptr in the final value. + DisallowInternReason::ConstHeap => { + ecx.tcx.dcx().emit_err(ConstHeapPtrInFinal { span: ecx.tcx.span }); + } + }, + MemoryKind::Machine(_) | MemoryKind::Stack | MemoryKind::CallerLocation => {} + } + // Set allocation mutability as appropriate. This is used by LLVM to put things into // read-only memory, and also by Miri when evaluating other globals that // access this one. @@ -99,7 +145,7 @@ fn intern_shallow<'tcx, T, M: CompileTimeMachine<'tcx, T>>( } else { ecx.tcx.set_alloc_id_memory(alloc_id, alloc); } - Ok(alloc.0.0.provenance().ptrs().iter().map(|&(_, prov)| prov)) + Ok(alloc.inner().provenance().ptrs().iter().map(|&(_, prov)| prov)) } /// Creates a new `DefId` and feeds all the right queries to make this `DefId` @@ -181,7 +227,7 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval } InternKind::Static(Mutability::Not) => { ( - // Outermost allocation is mutable if `!Freeze`. + // Outermost allocation is mutable if `!Freeze` i.e. contains interior mutable types. if ret.layout.ty.is_freeze(*ecx.tcx, ecx.typing_env) { Mutability::Not } else { @@ -321,7 +367,7 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval /// Intern `ret`. This function assumes that `ret` references no other allocation. #[instrument(level = "debug", skip(ecx))] -pub fn intern_const_alloc_for_constprop<'tcx, T, M: CompileTimeMachine<'tcx, T>>( +pub fn intern_const_alloc_for_constprop<'tcx, T: CanIntern, M: CompileTimeMachine<'tcx, T>>( ecx: &mut InterpCx<'tcx, M>, alloc_id: AllocId, ) -> InterpResult<'tcx, ()> { diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 6414821e21d02..50578e13a7a86 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -26,6 +26,7 @@ use super::{ Misalignment, Pointer, PointerArithmetic, Provenance, Scalar, alloc_range, err_ub, err_ub_custom, interp_ok, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format, }; +use crate::const_eval::ConstEvalErrKind; use crate::fluent_generated as fluent; #[derive(Debug, PartialEq, Copy, Clone)] @@ -311,6 +312,49 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { interp_ok(new_ptr) } + /// mark the `const_allocate`d pointer immutable so we can intern it. + pub fn make_const_heap_ptr_global( + &mut self, + ptr: Pointer>, + ) -> InterpResult<'tcx> + where + M: Machine<'tcx, MemoryKind = crate::const_eval::MemoryKind>, + { + let (alloc_id, offset, _) = self.ptr_get_alloc_id(ptr, 0)?; + if offset.bytes() != 0 { + return Err(ConstEvalErrKind::ConstMakeGlobalWithOffset(format!("{ptr:?}"))).into(); + } + + let not_local_heap = + matches!(self.tcx.try_get_global_alloc(alloc_id), Some(GlobalAlloc::Memory(_))); + + if not_local_heap { + return Err(ConstEvalErrKind::ConstMakeGlobalPtrIsNonHeap(format!("{ptr:?}"))).into(); + } + + let (kind, alloc) = self.memory.alloc_map.get_mut_or(alloc_id, || { + Err(ConstEvalErrKind::ConstMakeGlobalWithDanglingPtr(format!("{ptr:?}"))) + })?; + + alloc.mutability = Mutability::Not; + + match kind { + MemoryKind::Stack | MemoryKind::CallerLocation => { + return Err(ConstEvalErrKind::ConstMakeGlobalPtrIsNonHeap(format!("{ptr:?}"))) + .into(); + } + MemoryKind::Machine(crate::const_eval::MemoryKind::Heap { was_made_global }) => { + if *was_made_global { + return Err(ConstEvalErrKind::ConstMakeGlobalPtrAlreadyMadeGlobal(alloc_id)) + .into(); + } + *was_made_global = true; + } + } + + interp_ok(()) + } + #[instrument(skip(self), level = "debug")] pub fn deallocate_ptr( &mut self, diff --git a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs index 4ca39bbc68ec2..1c2167a50fd26 100644 --- a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs +++ b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs @@ -52,9 +52,8 @@ fn check_validity_requirement_strict<'tcx>( let mut cx = InterpCx::new(cx.tcx(), DUMMY_SP, cx.typing_env, machine); - let allocated = cx - .allocate(ty, MemoryKind::Machine(crate::const_eval::MemoryKind::Heap)) - .expect("OOM: failed to allocate for uninit check"); + let allocated = + cx.allocate(ty, MemoryKind::Stack).expect("OOM: failed to allocate for uninit check"); if kind == ValidityRequirement::Zero { cx.write_bytes_ptr( @@ -185,7 +184,10 @@ pub(crate) fn validate_scalar_in_layout<'tcx>( bug!("could not compute layout of {scalar:?}:{ty:?}") }; let allocated = cx - .allocate(layout, MemoryKind::Machine(crate::const_eval::MemoryKind::Heap)) + .allocate( + layout, + MemoryKind::Machine(crate::const_eval::MemoryKind::Heap { was_made_global: false }), + ) .expect("OOM: failed to allocate for uninit check"); cx.write_scalar(scalar, &allocated).unwrap(); diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index dcab6ef1c5a5f..6e5fe3823ab51 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -422,6 +422,9 @@ pub(crate) fn check_intrinsic_type( vec![Ty::new_mut_ptr(tcx, tcx.types.u8), tcx.types.usize, tcx.types.usize], tcx.types.unit, ), + sym::const_make_global => { + (0, 0, vec![Ty::new_mut_ptr(tcx, tcx.types.u8)], Ty::new_imm_ptr(tcx, tcx.types.u8)) + } sym::ptr_offset_from => ( 1, diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 71e0c943fbbb2..3e68afbfabd04 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -257,7 +257,7 @@ pub enum InvalidProgramInfo<'tcx> { /// Details of why a pointer had to be in-bounds. #[derive(Debug, Copy, Clone)] pub enum CheckInAllocMsg { - /// We are access memory. + /// We are accessing memory. MemoryAccess, /// We are doing pointer arithmetic. InboundsPointerArithmetic, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 8b12edf426c1b..599ef9aba2abb 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -715,6 +715,7 @@ symbols! { const_indexing, const_let, const_loop, + const_make_global, const_mut_refs, const_panic, const_panic_fmt, diff --git a/library/alloc/src/boxed/thin.rs b/library/alloc/src/boxed/thin.rs index 21425b9846e42..1cce36606d2c0 100644 --- a/library/alloc/src/boxed/thin.rs +++ b/library/alloc/src/boxed/thin.rs @@ -5,7 +5,7 @@ use core::error::Error; use core::fmt::{self, Debug, Display, Formatter}; #[cfg(not(no_global_oom_handling))] -use core::intrinsics::const_allocate; +use core::intrinsics::{const_allocate, const_make_global}; use core::marker::PhantomData; #[cfg(not(no_global_oom_handling))] use core::marker::Unsize; @@ -340,9 +340,10 @@ impl WithHeader { alloc.add(metadata_offset).cast(); // SAFETY: `*metadata_ptr` is within the allocation. metadata_ptr.write(ptr::metadata::(ptr::dangling::() as *const Dyn)); - + // SAFETY: valid heap allocation + const_make_global(alloc); // SAFETY: we have just written the metadata. - &*(metadata_ptr) + &*metadata_ptr } }; diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index c2e4b751c65f3..038c705d5b509 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2533,6 +2533,15 @@ pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) // Runtime NOP } +#[rustc_const_unstable(feature = "const_heap", issue = "79597")] +#[rustc_nounwind] +#[rustc_intrinsic] +#[miri::intrinsic_fallback_is_spec] +pub const unsafe fn const_make_global(ptr: *mut u8) -> *const u8 { + // const eval overrides this function; at runtime, it is a NOP. + ptr +} + /// Returns whether we should perform contract-checking at runtime. /// /// This is meant to be similar to the ub_checks intrinsic, in terms diff --git a/tests/ui/consts/const-eval/heap/alloc_intrinsic_nontransient.rs b/tests/ui/consts/const-eval/heap/alloc_intrinsic_nontransient.rs index 83ed496ac2c25..fa49990abfd81 100644 --- a/tests/ui/consts/const-eval/heap/alloc_intrinsic_nontransient.rs +++ b/tests/ui/consts/const-eval/heap/alloc_intrinsic_nontransient.rs @@ -8,11 +8,11 @@ const FOO_RAW: *const i32 = foo(); const fn foo() -> &'static i32 { let t = unsafe { - let i = intrinsics::const_allocate(4, 4) as * mut i32; + let i = intrinsics::const_allocate(4, 4) as *mut i32; *i = 20; i }; - unsafe { &*t } + unsafe { &*(intrinsics::const_make_global(t as *mut u8) as *mut i32) } } fn main() { assert_eq!(*FOO, 20); diff --git a/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.32bit.stderr b/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.32bit.stderr index 11ed0841a0039..a8c7ee93971ec 100644 --- a/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.32bit.stderr +++ b/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.32bit.stderr @@ -1,7 +1,7 @@ error[E0080]: constructing invalid value at .: encountered uninitialized memory, but expected an integer --> $DIR/alloc_intrinsic_uninit.rs:7:1 | -LL | const BAR: &i32 = unsafe { &*(intrinsics::const_allocate(4, 4) as *mut i32) }; +LL | const BAR: &i32 = unsafe { | ^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. diff --git a/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.64bit.stderr b/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.64bit.stderr index 691bde87d2f21..47e1c22cc2c12 100644 --- a/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.64bit.stderr +++ b/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.64bit.stderr @@ -1,7 +1,7 @@ error[E0080]: constructing invalid value at .: encountered uninitialized memory, but expected an integer --> $DIR/alloc_intrinsic_uninit.rs:7:1 | -LL | const BAR: &i32 = unsafe { &*(intrinsics::const_allocate(4, 4) as *mut i32) }; +LL | const BAR: &i32 = unsafe { | ^^^^^^^^^^^^^^^ it is undefined behavior to use this value | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. diff --git a/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.rs b/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.rs index ffc35ca1ddceb..2736cdeed8c8c 100644 --- a/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.rs +++ b/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.rs @@ -4,6 +4,7 @@ #![feature(const_heap)] use std::intrinsics; -const BAR: &i32 = unsafe { &*(intrinsics::const_allocate(4, 4) as *mut i32) }; -//~^ ERROR: uninitialized memory +const BAR: &i32 = unsafe { //~ ERROR: uninitialized memory + &*(intrinsics::const_make_global(intrinsics::const_allocate(4, 4)) as *mut i32) +}; fn main() {} diff --git a/tests/ui/consts/const-eval/heap/alloc_intrinsic_untyped.rs b/tests/ui/consts/const-eval/heap/alloc_intrinsic_untyped.rs index 26cb69e458b61..201fa71502298 100644 --- a/tests/ui/consts/const-eval/heap/alloc_intrinsic_untyped.rs +++ b/tests/ui/consts/const-eval/heap/alloc_intrinsic_untyped.rs @@ -7,5 +7,6 @@ use std::intrinsics; const BAR: *mut i32 = unsafe { intrinsics::const_allocate(4, 4) as *mut i32 }; //~^ error: mutable pointer in final value of constant +//~| error: encountered `const_allocate` pointer in final value that was not made global fn main() {} diff --git a/tests/ui/consts/const-eval/heap/alloc_intrinsic_untyped.stderr b/tests/ui/consts/const-eval/heap/alloc_intrinsic_untyped.stderr index 0dc49dc3cd864..e69d27e48c815 100644 --- a/tests/ui/consts/const-eval/heap/alloc_intrinsic_untyped.stderr +++ b/tests/ui/consts/const-eval/heap/alloc_intrinsic_untyped.stderr @@ -1,8 +1,16 @@ +error: encountered `const_allocate` pointer in final value that was not made global + --> $DIR/alloc_intrinsic_untyped.rs:8:1 + | +LL | const BAR: *mut i32 = unsafe { intrinsics::const_allocate(4, 4) as *mut i32 }; + | ^^^^^^^^^^^^^^^^^^^ + | + = note: use `const_make_global` to make allocated pointers immutable before returning + error: encountered mutable pointer in final value of constant --> $DIR/alloc_intrinsic_untyped.rs:8:1 | LL | const BAR: *mut i32 = unsafe { intrinsics::const_allocate(4, 4) as *mut i32 }; | ^^^^^^^^^^^^^^^^^^^ -error: aborting due to 1 previous error +error: aborting due to 2 previous errors diff --git a/tests/ui/consts/const-eval/heap/dealloc_intrinsic.rs b/tests/ui/consts/const-eval/heap/dealloc_intrinsic.rs index 3cc035c66d33f..515e12d2b7715 100644 --- a/tests/ui/consts/const-eval/heap/dealloc_intrinsic.rs +++ b/tests/ui/consts/const-eval/heap/dealloc_intrinsic.rs @@ -12,7 +12,7 @@ const _X: () = unsafe { const Y: &u32 = unsafe { let ptr = intrinsics::const_allocate(4, 4) as *mut u32; *ptr = 42; - &*ptr + &*(intrinsics::const_make_global(ptr as *mut u8) as *const u32) }; const Z: &u32 = &42; diff --git a/tests/ui/consts/const-eval/heap/make-global-dangling.rs b/tests/ui/consts/const-eval/heap/make-global-dangling.rs new file mode 100644 index 0000000000000..4099e6e13ed87 --- /dev/null +++ b/tests/ui/consts/const-eval/heap/make-global-dangling.rs @@ -0,0 +1,16 @@ +#![feature(core_intrinsics)] +#![feature(const_heap)] + +use std::intrinsics; + +const Y: &u32 = unsafe { + &*(intrinsics::const_make_global(std::ptr::null_mut()) as *const u32) + //~^ error: pointer not dereferenceable +}; + +const Z: &u32 = unsafe { + &*(intrinsics::const_make_global(std::ptr::dangling_mut()) as *const u32) + //~^ error: pointer not dereferenceable +}; + +fn main() {} diff --git a/tests/ui/consts/const-eval/heap/make-global-dangling.stderr b/tests/ui/consts/const-eval/heap/make-global-dangling.stderr new file mode 100644 index 0000000000000..4a5f59b985513 --- /dev/null +++ b/tests/ui/consts/const-eval/heap/make-global-dangling.stderr @@ -0,0 +1,15 @@ +error[E0080]: pointer not dereferenceable: pointer must point to some allocation, but got null pointer + --> $DIR/make-global-dangling.rs:7:8 + | +LL | &*(intrinsics::const_make_global(std::ptr::null_mut()) as *const u32) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `Y` failed here + +error[E0080]: pointer not dereferenceable: pointer must point to some allocation, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) + --> $DIR/make-global-dangling.rs:12:8 + | +LL | &*(intrinsics::const_make_global(std::ptr::dangling_mut()) as *const u32) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `Z` failed here + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-eval/heap/make-global-other.rs b/tests/ui/consts/const-eval/heap/make-global-other.rs new file mode 100644 index 0000000000000..dd5d27cc968cb --- /dev/null +++ b/tests/ui/consts/const-eval/heap/make-global-other.rs @@ -0,0 +1,13 @@ +#![feature(core_intrinsics)] +#![feature(const_heap)] + +use std::intrinsics; + +const X: &i32 = &0; + +const Y: &i32 = unsafe { + &*(intrinsics::const_make_global(X as *const i32 as *mut u8) as *const i32) + //~^ error: pointer passed to `const_make_global` does not point to a heap allocation: ALLOC0 +}; + +fn main() {} diff --git a/tests/ui/consts/const-eval/heap/make-global-other.stderr b/tests/ui/consts/const-eval/heap/make-global-other.stderr new file mode 100644 index 0000000000000..572230afe3b4c --- /dev/null +++ b/tests/ui/consts/const-eval/heap/make-global-other.stderr @@ -0,0 +1,9 @@ +error[E0080]: pointer passed to `const_make_global` does not point to a heap allocation: ALLOC0 + --> $DIR/make-global-other.rs:9:8 + | +LL | &*(intrinsics::const_make_global(X as *const i32 as *mut u8) as *const i32) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `Y` failed here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-eval/heap/make-global-twice.rs b/tests/ui/consts/const-eval/heap/make-global-twice.rs new file mode 100644 index 0000000000000..36d779ad034a2 --- /dev/null +++ b/tests/ui/consts/const-eval/heap/make-global-twice.rs @@ -0,0 +1,16 @@ +#![feature(core_intrinsics)] +#![feature(const_heap)] + +use std::intrinsics; + +const Y: &i32 = unsafe { + let ptr = intrinsics::const_allocate(4, 4); + let i = ptr as *mut i32; + *i = 20; + intrinsics::const_make_global(ptr); + intrinsics::const_make_global(ptr); + //~^ error: attempting to call `const_make_global` twice on the same allocation ALLOC0 + &*i +}; + +fn main() {} diff --git a/tests/ui/consts/const-eval/heap/make-global-twice.stderr b/tests/ui/consts/const-eval/heap/make-global-twice.stderr new file mode 100644 index 0000000000000..35d2385d9e07f --- /dev/null +++ b/tests/ui/consts/const-eval/heap/make-global-twice.stderr @@ -0,0 +1,9 @@ +error[E0080]: attempting to call `const_make_global` twice on the same allocation ALLOC0 + --> $DIR/make-global-twice.rs:11:5 + | +LL | intrinsics::const_make_global(ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `Y` failed here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-eval/heap/make-global.rs b/tests/ui/consts/const-eval/heap/make-global.rs new file mode 100644 index 0000000000000..b26fe2434ab8b --- /dev/null +++ b/tests/ui/consts/const-eval/heap/make-global.rs @@ -0,0 +1,21 @@ +//@ run-pass +#![feature(core_intrinsics)] +#![feature(const_heap)] +use std::intrinsics; + +const FOO: &i32 = foo(); +const FOO_RAW: *const i32 = foo(); + +const fn foo() -> &'static i32 { + unsafe { + let ptr = intrinsics::const_allocate(4, 4); + let t = ptr as *mut i32; + *t = 20; + intrinsics::const_make_global(ptr); + &*t + } +} +fn main() { + assert_eq!(*FOO, 20); + assert_eq!(unsafe { *FOO_RAW }, 20); +} diff --git a/tests/ui/consts/const-eval/heap/ptr_made_global_mutated.rs b/tests/ui/consts/const-eval/heap/ptr_made_global_mutated.rs new file mode 100644 index 0000000000000..75f2d87d4d05d --- /dev/null +++ b/tests/ui/consts/const-eval/heap/ptr_made_global_mutated.rs @@ -0,0 +1,14 @@ +#![feature(core_intrinsics)] +#![feature(const_heap)] +use std::intrinsics; + +const A: &u8 = unsafe { + let ptr = intrinsics::const_allocate(1, 1); + *ptr = 1; + let ptr: *const u8 = intrinsics::const_make_global(ptr); + *(ptr as *mut u8) = 2; + //~^ error: writing to ALLOC0 which is read-only + &*ptr +}; + +fn main() {} diff --git a/tests/ui/consts/const-eval/heap/ptr_made_global_mutated.stderr b/tests/ui/consts/const-eval/heap/ptr_made_global_mutated.stderr new file mode 100644 index 0000000000000..15af44b74d511 --- /dev/null +++ b/tests/ui/consts/const-eval/heap/ptr_made_global_mutated.stderr @@ -0,0 +1,9 @@ +error[E0080]: writing to ALLOC0 which is read-only + --> $DIR/ptr_made_global_mutated.rs:9:5 + | +LL | *(ptr as *mut u8) = 2; + | ^^^^^^^^^^^^^^^^^^^^^ evaluation of `A` failed here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-eval/heap/ptr_not_made_global.rs b/tests/ui/consts/const-eval/heap/ptr_not_made_global.rs new file mode 100644 index 0000000000000..4b0499d41065f --- /dev/null +++ b/tests/ui/consts/const-eval/heap/ptr_not_made_global.rs @@ -0,0 +1,19 @@ +#![feature(core_intrinsics)] +#![feature(const_heap)] +use std::intrinsics; + +const FOO: &i32 = foo(); +//~^ error: encountered `const_allocate` pointer in final value that was not made global +const FOO_RAW: *const i32 = foo(); +//~^ error: encountered `const_allocate` pointer in final value that was not made global + +const fn foo() -> &'static i32 { + let t = unsafe { + let i = intrinsics::const_allocate(4, 4) as *mut i32; + *i = 20; + i + }; + unsafe { &*t } +} + +fn main() {} diff --git a/tests/ui/consts/const-eval/heap/ptr_not_made_global.stderr b/tests/ui/consts/const-eval/heap/ptr_not_made_global.stderr new file mode 100644 index 0000000000000..569a8be3e8ced --- /dev/null +++ b/tests/ui/consts/const-eval/heap/ptr_not_made_global.stderr @@ -0,0 +1,18 @@ +error: encountered `const_allocate` pointer in final value that was not made global + --> $DIR/ptr_not_made_global.rs:5:1 + | +LL | const FOO: &i32 = foo(); + | ^^^^^^^^^^^^^^^ + | + = note: use `const_make_global` to make allocated pointers immutable before returning + +error: encountered `const_allocate` pointer in final value that was not made global + --> $DIR/ptr_not_made_global.rs:7:1 + | +LL | const FOO_RAW: *const i32 = foo(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: use `const_make_global` to make allocated pointers immutable before returning + +error: aborting due to 2 previous errors + From 44b38ca45edc4bd826f4ef5e3fb2221cbd1649cd Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Thu, 10 Jul 2025 17:54:55 +0800 Subject: [PATCH 26/34] format pointer later instead of eagerly converting to string --- .../rustc_const_eval/src/const_eval/error.rs | 12 ++++++------ .../rustc_const_eval/src/interpret/memory.rs | 18 +++++++++--------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index 5b07b7c13c1a2..3e880d0200133 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -11,8 +11,8 @@ use rustc_span::{Span, Symbol}; use super::CompileTimeMachine; use crate::errors::{self, FrameNote, ReportErrorExt}; use crate::interpret::{ - ErrorHandled, Frame, InterpErrorInfo, InterpErrorKind, MachineStopType, err_inval, - err_machine_stop, + CtfeProvenance, ErrorHandled, Frame, InterpErrorInfo, InterpErrorKind, MachineStopType, + Pointer, err_inval, err_machine_stop, }; /// The CTFE machine has some custom error kinds. @@ -32,12 +32,12 @@ pub enum ConstEvalErrKind { /// Called `const_make_global` twice. ConstMakeGlobalPtrAlreadyMadeGlobal(AllocId), /// Called `const_make_global` on a non-heap pointer. - ConstMakeGlobalPtrIsNonHeap(String), + ConstMakeGlobalPtrIsNonHeap(Pointer>), /// Called `const_make_global` on a dangling pointer. - ConstMakeGlobalWithDanglingPtr(String), + ConstMakeGlobalWithDanglingPtr(Pointer>), /// Called `const_make_global` on a pointer that does not start at the /// beginning of an object. - ConstMakeGlobalWithOffset(String), + ConstMakeGlobalWithOffset(Pointer>), } impl MachineStopType for ConstEvalErrKind { @@ -74,7 +74,7 @@ impl MachineStopType for ConstEvalErrKind { ConstMakeGlobalPtrIsNonHeap(ptr) | ConstMakeGlobalWithOffset(ptr) | ConstMakeGlobalWithDanglingPtr(ptr) => { - adder("ptr".into(), ptr.into_diag_arg(&mut None)); + adder("ptr".into(), format!("{ptr:?}").into_diag_arg(&mut None)); } ConstMakeGlobalPtrAlreadyMadeGlobal(alloc) => { adder("alloc".into(), alloc.into_diag_arg(&mut None)); diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 50578e13a7a86..08682dd193d16 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -315,33 +315,33 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// mark the `const_allocate`d pointer immutable so we can intern it. pub fn make_const_heap_ptr_global( &mut self, - ptr: Pointer>, + ptr: Pointer>, ) -> InterpResult<'tcx> where - M: Machine<'tcx, MemoryKind = crate::const_eval::MemoryKind>, + M: Machine<'tcx, MemoryKind = crate::const_eval::MemoryKind, Provenance = CtfeProvenance>, { let (alloc_id, offset, _) = self.ptr_get_alloc_id(ptr, 0)?; if offset.bytes() != 0 { - return Err(ConstEvalErrKind::ConstMakeGlobalWithOffset(format!("{ptr:?}"))).into(); + return Err(ConstEvalErrKind::ConstMakeGlobalWithOffset(ptr)).into(); } let not_local_heap = matches!(self.tcx.try_get_global_alloc(alloc_id), Some(GlobalAlloc::Memory(_))); if not_local_heap { - return Err(ConstEvalErrKind::ConstMakeGlobalPtrIsNonHeap(format!("{ptr:?}"))).into(); + return Err(ConstEvalErrKind::ConstMakeGlobalPtrIsNonHeap(ptr)).into(); } - let (kind, alloc) = self.memory.alloc_map.get_mut_or(alloc_id, || { - Err(ConstEvalErrKind::ConstMakeGlobalWithDanglingPtr(format!("{ptr:?}"))) - })?; + let (kind, alloc) = self + .memory + .alloc_map + .get_mut_or(alloc_id, || Err(ConstEvalErrKind::ConstMakeGlobalWithDanglingPtr(ptr)))?; alloc.mutability = Mutability::Not; match kind { MemoryKind::Stack | MemoryKind::CallerLocation => { - return Err(ConstEvalErrKind::ConstMakeGlobalPtrIsNonHeap(format!("{ptr:?}"))) - .into(); + return Err(ConstEvalErrKind::ConstMakeGlobalPtrIsNonHeap(ptr)).into(); } MemoryKind::Machine(crate::const_eval::MemoryKind::Heap { was_made_global }) => { if *was_made_global { From fd48b7b8dd911229b635f7969e6213b5af337b7d Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Sun, 13 Jul 2025 15:11:31 +0800 Subject: [PATCH 27/34] Comment more code and make tests clearer Co-Authored-By: Ralf Jung --- .../rustc_const_eval/src/const_eval/machine.rs | 6 +++++- .../rustc_const_eval/src/interpret/intern.rs | 13 ++++++------- .../rustc_const_eval/src/interpret/memory.rs | 16 +++++++++------- .../src/util/check_validity_requirement.rs | 11 +++++------ .../heap/alloc_intrinsic_nontransient.rs | 2 +- .../const-eval/heap/alloc_intrinsic_uninit.rs | 3 ++- .../const-eval/heap/make-global-dangling.rs | 2 ++ .../const-eval/heap/make-global-dangling.stderr | 4 ++-- .../consts/const-eval/heap/make-global-other.rs | 2 ++ .../const-eval/heap/make-global-other.stderr | 2 +- .../consts/const-eval/heap/make-global-twice.rs | 2 ++ .../const-eval/heap/make-global-twice.stderr | 2 +- .../const-eval/heap/ptr_made_global_mutated.rs | 1 + .../heap/ptr_made_global_mutated.stderr | 2 +- .../const-eval/heap/ptr_not_made_global.rs | 5 +++++ .../const-eval/heap/ptr_not_made_global.stderr | 4 ++-- ...sic_untyped.rs => ptr_not_made_global_mut.rs} | 0 ...ped.stderr => ptr_not_made_global_mut.stderr} | 4 ++-- 18 files changed, 49 insertions(+), 32 deletions(-) rename tests/ui/consts/const-eval/heap/{alloc_intrinsic_untyped.rs => ptr_not_made_global_mut.rs} (100%) rename tests/ui/consts/const-eval/heap/{alloc_intrinsic_untyped.stderr => ptr_not_made_global_mut.stderr} (85%) diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 4e952b043fbca..0a6bdef2225ed 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -169,7 +169,11 @@ pub type CompileTimeInterpCx<'tcx> = InterpCx<'tcx, CompileTimeMachine<'tcx>>; #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum MemoryKind { - Heap { was_made_global: bool }, + Heap { + /// Indicates whether `make_global` was called on this allocation. + /// If this is `true`, the allocation must be immutable. + was_made_global: bool, + }, } impl fmt::Display for MemoryKind { diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index 85524949053c9..b8fcdc9b7ef03 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -106,13 +106,12 @@ fn intern_shallow<'tcx, T: CanIntern, M: CompileTimeMachine<'tcx, T>>( match kind { MemoryKind::Machine(x) if let Some(reason) = x.disallows_intern() => match reason { - // attempting to intern a `const_allocate`d pointer that was not made global via - // `const_make_global`. We emit an error here but don't return an `Err`. The `Err` - // is for pointers that we can't intern at all (i.e. dangling pointers). We still - // (recursively) intern this pointer because we don't have to worry about the - // additional paperwork involved with _not_ interning it, such as storing it in - // the dead memory map and having to deal with additional "dangling pointer" - // messages if someone tries to store the non-made-global ptr in the final value. + // Attempting to intern a `const_allocate`d pointer that was not made global via + // `const_make_global`. This is an error, but if we return `Err` now, things + // become inconsistent because we already removed the allocation from `alloc_map`. + // So instead we just emit an error and then continue interning as usual. + // We *could* do this check before removing the allocation from `alloc_map`, but then + // it would be much harder to ensure that we only error once for each allocation. DisallowInternReason::ConstHeap => { ecx.tcx.dcx().emit_err(ConstHeapPtrInFinal { span: ecx.tcx.span }); } diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 08682dd193d16..5f9f43a320b59 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -312,7 +312,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { interp_ok(new_ptr) } - /// mark the `const_allocate`d pointer immutable so we can intern it. + /// Mark the `const_allocate`d allocation `ptr` points to as immutable so we can intern it. pub fn make_const_heap_ptr_global( &mut self, ptr: Pointer>, @@ -325,20 +325,19 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { return Err(ConstEvalErrKind::ConstMakeGlobalWithOffset(ptr)).into(); } - let not_local_heap = - matches!(self.tcx.try_get_global_alloc(alloc_id), Some(GlobalAlloc::Memory(_))); - - if not_local_heap { + if matches!(self.tcx.try_get_global_alloc(alloc_id), Some(_)) { + // This points to something outside the current interpreter. return Err(ConstEvalErrKind::ConstMakeGlobalPtrIsNonHeap(ptr)).into(); } + // If we can't find it in `alloc_map` it must be dangling (because we don't use + // `extra_fn_ptr_map` in const-eval). let (kind, alloc) = self .memory .alloc_map .get_mut_or(alloc_id, || Err(ConstEvalErrKind::ConstMakeGlobalWithDanglingPtr(ptr)))?; - alloc.mutability = Mutability::Not; - + // Ensure this is actually a *heap* allocation, and record it as made-global. match kind { MemoryKind::Stack | MemoryKind::CallerLocation => { return Err(ConstEvalErrKind::ConstMakeGlobalPtrIsNonHeap(ptr)).into(); @@ -352,6 +351,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } + // Prevent further mutation, this is now an immutable global. + alloc.mutability = Mutability::Not; + interp_ok(()) } diff --git a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs index 1c2167a50fd26..b1f2959875051 100644 --- a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs +++ b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs @@ -52,6 +52,7 @@ fn check_validity_requirement_strict<'tcx>( let mut cx = InterpCx::new(cx.tcx(), DUMMY_SP, cx.typing_env, machine); + // It doesn't really matter which `MemoryKind` we use here, `Stack` is the least wrong. let allocated = cx.allocate(ty, MemoryKind::Stack).expect("OOM: failed to allocate for uninit check"); @@ -183,12 +184,10 @@ pub(crate) fn validate_scalar_in_layout<'tcx>( let Ok(layout) = cx.layout_of(ty) else { bug!("could not compute layout of {scalar:?}:{ty:?}") }; - let allocated = cx - .allocate( - layout, - MemoryKind::Machine(crate::const_eval::MemoryKind::Heap { was_made_global: false }), - ) - .expect("OOM: failed to allocate for uninit check"); + + // It doesn't really matter which `MemoryKind` we use here, `Stack` is the least wrong. + let allocated = + cx.allocate(layout, MemoryKind::Stack).expect("OOM: failed to allocate for uninit check"); cx.write_scalar(scalar, &allocated).unwrap(); diff --git a/tests/ui/consts/const-eval/heap/alloc_intrinsic_nontransient.rs b/tests/ui/consts/const-eval/heap/alloc_intrinsic_nontransient.rs index fa49990abfd81..af24e463b5059 100644 --- a/tests/ui/consts/const-eval/heap/alloc_intrinsic_nontransient.rs +++ b/tests/ui/consts/const-eval/heap/alloc_intrinsic_nontransient.rs @@ -12,7 +12,7 @@ const fn foo() -> &'static i32 { *i = 20; i }; - unsafe { &*(intrinsics::const_make_global(t as *mut u8) as *mut i32) } + unsafe { &*(intrinsics::const_make_global(t as *mut u8) as *const i32) } } fn main() { assert_eq!(*FOO, 20); diff --git a/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.rs b/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.rs index 2736cdeed8c8c..c54115de2045d 100644 --- a/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.rs +++ b/tests/ui/consts/const-eval/heap/alloc_intrinsic_uninit.rs @@ -5,6 +5,7 @@ use std::intrinsics; const BAR: &i32 = unsafe { //~ ERROR: uninitialized memory - &*(intrinsics::const_make_global(intrinsics::const_allocate(4, 4)) as *mut i32) + // Make the pointer immutable to avoid errors related to mutable pointers in constants. + &*(intrinsics::const_make_global(intrinsics::const_allocate(4, 4)) as *const i32) }; fn main() {} diff --git a/tests/ui/consts/const-eval/heap/make-global-dangling.rs b/tests/ui/consts/const-eval/heap/make-global-dangling.rs index 4099e6e13ed87..f4c5929bc416d 100644 --- a/tests/ui/consts/const-eval/heap/make-global-dangling.rs +++ b/tests/ui/consts/const-eval/heap/make-global-dangling.rs @@ -1,3 +1,5 @@ +// Ensure that we can't call `const_make_global` on dangling pointers. + #![feature(core_intrinsics)] #![feature(const_heap)] diff --git a/tests/ui/consts/const-eval/heap/make-global-dangling.stderr b/tests/ui/consts/const-eval/heap/make-global-dangling.stderr index 4a5f59b985513..48c2796d0bf21 100644 --- a/tests/ui/consts/const-eval/heap/make-global-dangling.stderr +++ b/tests/ui/consts/const-eval/heap/make-global-dangling.stderr @@ -1,11 +1,11 @@ error[E0080]: pointer not dereferenceable: pointer must point to some allocation, but got null pointer - --> $DIR/make-global-dangling.rs:7:8 + --> $DIR/make-global-dangling.rs:9:8 | LL | &*(intrinsics::const_make_global(std::ptr::null_mut()) as *const u32) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `Y` failed here error[E0080]: pointer not dereferenceable: pointer must point to some allocation, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) - --> $DIR/make-global-dangling.rs:12:8 + --> $DIR/make-global-dangling.rs:14:8 | LL | &*(intrinsics::const_make_global(std::ptr::dangling_mut()) as *const u32) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `Z` failed here diff --git a/tests/ui/consts/const-eval/heap/make-global-other.rs b/tests/ui/consts/const-eval/heap/make-global-other.rs index dd5d27cc968cb..4e2a59de13edc 100644 --- a/tests/ui/consts/const-eval/heap/make-global-other.rs +++ b/tests/ui/consts/const-eval/heap/make-global-other.rs @@ -1,3 +1,5 @@ +// Ensure that we can't call `const_make_global` on pointers not in the current interpreter. + #![feature(core_intrinsics)] #![feature(const_heap)] diff --git a/tests/ui/consts/const-eval/heap/make-global-other.stderr b/tests/ui/consts/const-eval/heap/make-global-other.stderr index 572230afe3b4c..ed0d768cf379f 100644 --- a/tests/ui/consts/const-eval/heap/make-global-other.stderr +++ b/tests/ui/consts/const-eval/heap/make-global-other.stderr @@ -1,5 +1,5 @@ error[E0080]: pointer passed to `const_make_global` does not point to a heap allocation: ALLOC0 - --> $DIR/make-global-other.rs:9:8 + --> $DIR/make-global-other.rs:11:8 | LL | &*(intrinsics::const_make_global(X as *const i32 as *mut u8) as *const i32) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `Y` failed here diff --git a/tests/ui/consts/const-eval/heap/make-global-twice.rs b/tests/ui/consts/const-eval/heap/make-global-twice.rs index 36d779ad034a2..0cd617cea3e6e 100644 --- a/tests/ui/consts/const-eval/heap/make-global-twice.rs +++ b/tests/ui/consts/const-eval/heap/make-global-twice.rs @@ -1,3 +1,5 @@ +// Ensure that we can't call `const_make_global` twice. + #![feature(core_intrinsics)] #![feature(const_heap)] diff --git a/tests/ui/consts/const-eval/heap/make-global-twice.stderr b/tests/ui/consts/const-eval/heap/make-global-twice.stderr index 35d2385d9e07f..95cdb9694d8b9 100644 --- a/tests/ui/consts/const-eval/heap/make-global-twice.stderr +++ b/tests/ui/consts/const-eval/heap/make-global-twice.stderr @@ -1,5 +1,5 @@ error[E0080]: attempting to call `const_make_global` twice on the same allocation ALLOC0 - --> $DIR/make-global-twice.rs:11:5 + --> $DIR/make-global-twice.rs:13:5 | LL | intrinsics::const_make_global(ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `Y` failed here diff --git a/tests/ui/consts/const-eval/heap/ptr_made_global_mutated.rs b/tests/ui/consts/const-eval/heap/ptr_made_global_mutated.rs index 75f2d87d4d05d..bea2a5949c261 100644 --- a/tests/ui/consts/const-eval/heap/ptr_made_global_mutated.rs +++ b/tests/ui/consts/const-eval/heap/ptr_made_global_mutated.rs @@ -1,3 +1,4 @@ +// Ensure that once an allocation is "made global", we can no longer mutate it. #![feature(core_intrinsics)] #![feature(const_heap)] use std::intrinsics; diff --git a/tests/ui/consts/const-eval/heap/ptr_made_global_mutated.stderr b/tests/ui/consts/const-eval/heap/ptr_made_global_mutated.stderr index 15af44b74d511..0e88ea77d1ca0 100644 --- a/tests/ui/consts/const-eval/heap/ptr_made_global_mutated.stderr +++ b/tests/ui/consts/const-eval/heap/ptr_made_global_mutated.stderr @@ -1,5 +1,5 @@ error[E0080]: writing to ALLOC0 which is read-only - --> $DIR/ptr_made_global_mutated.rs:9:5 + --> $DIR/ptr_made_global_mutated.rs:10:5 | LL | *(ptr as *mut u8) = 2; | ^^^^^^^^^^^^^^^^^^^^^ evaluation of `A` failed here diff --git a/tests/ui/consts/const-eval/heap/ptr_not_made_global.rs b/tests/ui/consts/const-eval/heap/ptr_not_made_global.rs index 4b0499d41065f..6980e92c21896 100644 --- a/tests/ui/consts/const-eval/heap/ptr_not_made_global.rs +++ b/tests/ui/consts/const-eval/heap/ptr_not_made_global.rs @@ -1,3 +1,8 @@ +// Ensure that we reject interning `const_allocate`d allocations in the final value of constants +// if they have not been made global through `const_make_global`. The pointers are made *immutable* +// to focus the test on the missing `make_global`; `ptr_not_made_global_mut.rs` covers the case +// where the pointer remains mutable. + #![feature(core_intrinsics)] #![feature(const_heap)] use std::intrinsics; diff --git a/tests/ui/consts/const-eval/heap/ptr_not_made_global.stderr b/tests/ui/consts/const-eval/heap/ptr_not_made_global.stderr index 569a8be3e8ced..cb2bb1e8cd852 100644 --- a/tests/ui/consts/const-eval/heap/ptr_not_made_global.stderr +++ b/tests/ui/consts/const-eval/heap/ptr_not_made_global.stderr @@ -1,5 +1,5 @@ error: encountered `const_allocate` pointer in final value that was not made global - --> $DIR/ptr_not_made_global.rs:5:1 + --> $DIR/ptr_not_made_global.rs:10:1 | LL | const FOO: &i32 = foo(); | ^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | const FOO: &i32 = foo(); = note: use `const_make_global` to make allocated pointers immutable before returning error: encountered `const_allocate` pointer in final value that was not made global - --> $DIR/ptr_not_made_global.rs:7:1 + --> $DIR/ptr_not_made_global.rs:12:1 | LL | const FOO_RAW: *const i32 = foo(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/const-eval/heap/alloc_intrinsic_untyped.rs b/tests/ui/consts/const-eval/heap/ptr_not_made_global_mut.rs similarity index 100% rename from tests/ui/consts/const-eval/heap/alloc_intrinsic_untyped.rs rename to tests/ui/consts/const-eval/heap/ptr_not_made_global_mut.rs diff --git a/tests/ui/consts/const-eval/heap/alloc_intrinsic_untyped.stderr b/tests/ui/consts/const-eval/heap/ptr_not_made_global_mut.stderr similarity index 85% rename from tests/ui/consts/const-eval/heap/alloc_intrinsic_untyped.stderr rename to tests/ui/consts/const-eval/heap/ptr_not_made_global_mut.stderr index e69d27e48c815..eae79d24797a9 100644 --- a/tests/ui/consts/const-eval/heap/alloc_intrinsic_untyped.stderr +++ b/tests/ui/consts/const-eval/heap/ptr_not_made_global_mut.stderr @@ -1,5 +1,5 @@ error: encountered `const_allocate` pointer in final value that was not made global - --> $DIR/alloc_intrinsic_untyped.rs:8:1 + --> $DIR/ptr_not_made_global_mut.rs:8:1 | LL | const BAR: *mut i32 = unsafe { intrinsics::const_allocate(4, 4) as *mut i32 }; | ^^^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | const BAR: *mut i32 = unsafe { intrinsics::const_allocate(4, 4) as *mut i32 = note: use `const_make_global` to make allocated pointers immutable before returning error: encountered mutable pointer in final value of constant - --> $DIR/alloc_intrinsic_untyped.rs:8:1 + --> $DIR/ptr_not_made_global_mut.rs:8:1 | LL | const BAR: *mut i32 = unsafe { intrinsics::const_allocate(4, 4) as *mut i32 }; | ^^^^^^^^^^^^^^^^^^^ From bbb114ef6198b1d2de4898f838b4ae3183c2db90 Mon Sep 17 00:00:00 2001 From: binarycat Date: Sat, 12 Jul 2025 13:16:34 -0500 Subject: [PATCH 28/34] ci cleanup: rustdoc-gui-test now installs browser-ui-test this removes the need for --unsafe-perm in the Dockerfile. --- src/build_helper/src/lib.rs | 1 + src/build_helper/src/npm.rs | 30 ++++++ .../host-x86_64/x86_64-gnu-miri/Dockerfile | 8 -- .../host-x86_64/x86_64-gnu-tools/Dockerfile | 11 --- .../x86_64-gnu-tools/browser-ui-test.version | 1 - src/tools/rustdoc-gui-test/src/main.rs | 93 +++---------------- 6 files changed, 44 insertions(+), 100 deletions(-) create mode 100644 src/build_helper/src/npm.rs delete mode 100644 src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version diff --git a/src/build_helper/src/lib.rs b/src/build_helper/src/lib.rs index 05de8fd2d42f1..266eedc624582 100644 --- a/src/build_helper/src/lib.rs +++ b/src/build_helper/src/lib.rs @@ -5,6 +5,7 @@ pub mod drop_bomb; pub mod fs; pub mod git; pub mod metrics; +pub mod npm; pub mod stage0_parser; pub mod targets; pub mod util; diff --git a/src/build_helper/src/npm.rs b/src/build_helper/src/npm.rs new file mode 100644 index 0000000000000..dedef40978dde --- /dev/null +++ b/src/build_helper/src/npm.rs @@ -0,0 +1,30 @@ +use std::error::Error; +use std::path::{Path, PathBuf}; +use std::process::Command; +use std::{fs, io}; + +/// Install an exact package version, and return the path of `node_modules`. +pub fn install_one( + out_dir: &Path, + npm_bin: &Path, + pkg_name: &str, + pkg_version: &str, +) -> Result { + let nm_path = out_dir.join("node_modules"); + let _ = fs::create_dir(&nm_path); + let mut child = Command::new(npm_bin) + .arg("install") + .arg("--audit=false") + .arg("--fund=false") + .arg(format!("{pkg_name}@{pkg_version}")) + .current_dir(out_dir) + .spawn()?; + let exit_status = child.wait()?; + if !exit_status.success() { + eprintln!("npm install did not exit successfully"); + return Err(io::Error::other(Box::::from(format!( + "npm install returned exit code {exit_status}" + )))); + } + Ok(nm_path) +} diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-miri/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-miri/Dockerfile index b937bc3e678d3..ad2ee85c7bb51 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-miri/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-miri/Dockerfile @@ -46,12 +46,4 @@ ENV HOST_TARGET x86_64-unknown-linux-gnu COPY scripts/shared.sh /scripts/ -# For now, we need to use `--unsafe-perm=true` to go around an issue when npm tries -# to create a new folder. For reference: -# https://github.com/puppeteer/puppeteer/issues/375 -# -# We also specify the version in case we need to update it to go around cache limitations. -# -# The `browser-ui-test.version` file is also used by bootstrap to emit warnings in case -# the local version of the package is different than the one used by the CI. ENV SCRIPT /tmp/check-miri.sh ../x.py diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile index e770c58bd9cf3..95357d229374c 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile @@ -76,8 +76,6 @@ COPY scripts/nodejs.sh /scripts/ RUN sh /scripts/nodejs.sh /node ENV PATH="/node/bin:${PATH}" -COPY host-x86_64/x86_64-gnu-tools/browser-ui-test.version /tmp/ - ENV RUST_CONFIGURE_ARGS \ --build=x86_64-unknown-linux-gnu \ --save-toolstates=/tmp/toolstate/toolstates.json \ @@ -91,15 +89,6 @@ ENV HOST_TARGET x86_64-unknown-linux-gnu COPY scripts/shared.sh /scripts/ -# For now, we need to use `--unsafe-perm=true` to go around an issue when npm tries -# to create a new folder. For reference: -# https://github.com/puppeteer/puppeteer/issues/375 -# -# We also specify the version in case we need to update it to go around cache limitations. -# -# The `browser-ui-test.version` file is also used by bootstrap to emit warnings in case -# the local version of the package is different than the one used by the CI. ENV SCRIPT /tmp/checktools.sh ../x.py && \ - npm install browser-ui-test@$(head -n 1 /tmp/browser-ui-test.version) --unsafe-perm=true && \ python3 ../x.py check compiletest --set build.compiletest-use-stage0-libtest=true && \ python3 ../x.py test tests/rustdoc-gui --stage 2 --test-args "'--jobs 1'" diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version deleted file mode 100644 index b9f8e558df4d7..0000000000000 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version +++ /dev/null @@ -1 +0,0 @@ -0.21.1 \ No newline at end of file diff --git a/src/tools/rustdoc-gui-test/src/main.rs b/src/tools/rustdoc-gui-test/src/main.rs index 6461f38f527b4..bcec950a404f0 100644 --- a/src/tools/rustdoc-gui-test/src/main.rs +++ b/src/tools/rustdoc-gui-test/src/main.rs @@ -1,63 +1,15 @@ +use std::env; use std::path::{Path, PathBuf}; use std::process::Command; use std::sync::Arc; -use std::{env, fs}; +use build_helper::npm; use build_helper::util::try_run; use compiletest::directives::TestProps; use config::Config; mod config; -fn get_browser_ui_test_version_inner(npm: &Path, global: bool) -> Option { - let mut command = Command::new(&npm); - command.arg("list").arg("--parseable").arg("--long").arg("--depth=0"); - if global { - command.arg("--global"); - } - let lines = match command.output() { - Ok(output) => String::from_utf8_lossy(&output.stdout).into_owned(), - Err(e) => { - eprintln!( - "path to npm can be wrong, provided path: {npm:?}. Try to set npm path \ - in bootstrap.toml in [build.npm]", - ); - panic!("{:?}", e) - } - }; - lines - .lines() - .find_map(|l| l.rsplit(':').next()?.strip_prefix("browser-ui-test@")) - .map(|v| v.to_owned()) -} - -fn get_browser_ui_test_version(npm: &Path) -> Option { - get_browser_ui_test_version_inner(npm, false) - .or_else(|| get_browser_ui_test_version_inner(npm, true)) -} - -fn compare_browser_ui_test_version(installed_version: &str, src: &Path) { - match fs::read_to_string( - src.join("src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version"), - ) { - Ok(v) => { - if v.trim() != installed_version { - eprintln!( - "⚠️ Installed version of browser-ui-test (`{}`) is different than the \ - one used in the CI (`{}`)", - installed_version, v - ); - eprintln!( - "You can install this version using `npm update browser-ui-test` or by using \ - `npm install browser-ui-test@{}`", - v, - ); - } - } - Err(e) => eprintln!("Couldn't find the CI browser-ui-test version: {:?}", e), - } -} - fn find_librs>(path: P) -> Option { for entry in walkdir::WalkDir::new(path) { let entry = entry.ok()?; @@ -71,27 +23,6 @@ fn find_librs>(path: P) -> Option { fn main() -> Result<(), ()> { let config = Arc::new(Config::from_args(env::args().collect())); - // The goal here is to check if the necessary packages are installed, and if not, we - // panic. - match get_browser_ui_test_version(&config.npm) { - Some(version) => { - // We also check the version currently used in CI and emit a warning if it's not the - // same one. - compare_browser_ui_test_version(&version, &config.rust_src); - } - None => { - eprintln!( - r#" -error: rustdoc-gui test suite cannot be run because npm `browser-ui-test` dependency is missing. - -If you want to install the `browser-ui-test` dependency, run `npm install browser-ui-test` -"#, - ); - - panic!("Cannot run rustdoc-gui tests"); - } - } - let src_path = config.rust_src.join("tests/rustdoc-gui/src"); for entry in src_path.read_dir().expect("read_dir call failed") { if let Ok(entry) = entry { @@ -138,16 +69,12 @@ If you want to install the `browser-ui-test` dependency, run `npm install browse } } - let mut command = Command::new(&config.nodejs); + // FIXME(binarycat): once we get package.json in version control, this should be updated to install via that instead + let local_node_modules = + npm::install_one(&config.out_dir, &config.npm, "browser-ui-test", "0.21.1") + .expect("unable to install browser-ui-test"); - if let Ok(current_dir) = env::current_dir() { - let local_node_modules = current_dir.join("node_modules"); - if local_node_modules.exists() { - // Link the local node_modules if exists. - // This is useful when we run rustdoc-gui-test from outside of the source root. - env::set_var("NODE_PATH", local_node_modules); - } - } + let mut command = Command::new(&config.nodejs); command .arg(config.rust_src.join("src/tools/rustdoc-gui/tester.js")) @@ -158,6 +85,12 @@ If you want to install the `browser-ui-test` dependency, run `npm install browse .arg("--tests-folder") .arg(config.rust_src.join("tests/rustdoc-gui")); + if local_node_modules.exists() { + // Link the local node_modules if exists. + // This is useful when we run rustdoc-gui-test from outside of the source root. + command.env("NODE_PATH", local_node_modules); + } + for file in &config.goml_files { command.arg("--file").arg(file); } From f1d84468c87a4e8ea96e603d21c460004bdeb24f Mon Sep 17 00:00:00 2001 From: "Martin Ombura Jr." <8682597+martinomburajr@users.noreply.github.com> Date: Tue, 15 Jul 2025 20:07:03 -0700 Subject: [PATCH 29/34] Update poison.rs Typo in word "below" --- library/std/src/sync/poison.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/sync/poison.rs b/library/std/src/sync/poison.rs index 571f0d14248e1..0c05f152ef84c 100644 --- a/library/std/src/sync/poison.rs +++ b/library/std/src/sync/poison.rs @@ -10,7 +10,7 @@ //! (some invariant is not being upheld). //! //! The specifics of how this "poisoned" state affects other threads -//! depend on the primitive. See [#Overview] bellow. +//! depend on the primitive. See [#Overview] below. //! //! For the alternative implementations that do not employ poisoning, //! see `std::sync::nonpoisoning`. From 8d64937dc25eb2b01596a3581ec2660d8e81b9b2 Mon Sep 17 00:00:00 2001 From: David Wood Date: Wed, 16 Jul 2025 10:56:32 +0000 Subject: [PATCH 30/34] trait_sel: `MetaSized` always holds temporarily As a temporary measure while a proper fix for `tests/ui/sized-hierarchy/incomplete-inference-issue-143992.rs` is implemented, make `MetaSized` obligations always hold. In effect, temporarily reverting the `sized_hierarchy` feature. This is a small change that can be backported. --- .../rustc_trait_selection/src/traits/util.rs | 7 +++ ...st-size_of_val-align_of_val-extern-type.rs | 4 +- ...ize_of_val-align_of_val-extern-type.stderr | 38 +++----------- tests/ui/extern/extern-types-size_of_val.rs | 4 +- .../ui/extern/extern-types-size_of_val.stderr | 39 -------------- tests/ui/extern/extern-types-unsized.rs | 2 - tests/ui/extern/extern-types-unsized.stderr | 30 +---------- tests/ui/extern/unsized-extern-derefmove.rs | 4 -- .../ui/extern/unsized-extern-derefmove.stderr | 52 +------------------ .../layout/unconstrained-param-ice-137308.rs | 1 - .../unconstrained-param-ice-137308.stderr | 11 +--- tests/ui/nll/issue-50716.rs | 2 +- tests/ui/nll/issue-50716.stderr | 18 +------ ...omplete-inference-issue-143992.next.stderr | 17 ++++++ .../incomplete-inference-issue-143992.rs | 29 +++++++++++ .../sized-hierarchy/overflow.current.stderr | 45 ---------------- tests/ui/sized-hierarchy/overflow.rs | 6 ++- .../resolve-impl-before-constrain-check.rs | 1 - ...resolve-impl-before-constrain-check.stderr | 11 +--- 19 files changed, 77 insertions(+), 244 deletions(-) delete mode 100644 tests/ui/extern/extern-types-size_of_val.stderr create mode 100644 tests/ui/sized-hierarchy/incomplete-inference-issue-143992.next.stderr create mode 100644 tests/ui/sized-hierarchy/incomplete-inference-issue-143992.rs delete mode 100644 tests/ui/sized-hierarchy/overflow.current.stderr diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index 141454bfe3754..00e31021eec0d 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -378,6 +378,13 @@ pub fn sizedness_fast_path<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tc _ => return false, }; + // FIXME(sized_hierarchy): this temporarily reverts the `sized_hierarchy` feature + // while a proper fix for `tests/ui/sized-hierarchy/incomplete-inference-issue-143992.rs` + // is pending a proper fix + if !tcx.features().sized_hierarchy() && matches!(sizedness, SizedTraitKind::MetaSized) { + return true; + } + if trait_pred.self_ty().has_trivial_sizedness(tcx, sizedness) { debug!("fast path -- trivial sizedness"); return true; diff --git a/tests/ui/consts/const-size_of_val-align_of_val-extern-type.rs b/tests/ui/consts/const-size_of_val-align_of_val-extern-type.rs index 2372d1c3e3d7e..fd3ed8f18265e 100644 --- a/tests/ui/consts/const-size_of_val-align_of_val-extern-type.rs +++ b/tests/ui/consts/const-size_of_val-align_of_val-extern-type.rs @@ -8,8 +8,8 @@ extern "C" { } const _SIZE: usize = unsafe { size_of_val(&4 as *const i32 as *const Opaque) }; -//~^ ERROR the size for values of type `Opaque` cannot be known +//~^ ERROR `extern type` does not have known layout const _ALIGN: usize = unsafe { align_of_val(&4 as *const i32 as *const Opaque) }; -//~^ ERROR the size for values of type `Opaque` cannot be known +//~^ ERROR `extern type` does not have known layout fn main() {} diff --git a/tests/ui/consts/const-size_of_val-align_of_val-extern-type.stderr b/tests/ui/consts/const-size_of_val-align_of_val-extern-type.stderr index 825b9e941584c..23f7aaf538edc 100644 --- a/tests/ui/consts/const-size_of_val-align_of_val-extern-type.stderr +++ b/tests/ui/consts/const-size_of_val-align_of_val-extern-type.stderr @@ -1,39 +1,15 @@ -error[E0277]: the size for values of type `Opaque` cannot be known - --> $DIR/const-size_of_val-align_of_val-extern-type.rs:10:43 +error[E0080]: `extern type` does not have known layout + --> $DIR/const-size_of_val-align_of_val-extern-type.rs:10:31 | LL | const _SIZE: usize = unsafe { size_of_val(&4 as *const i32 as *const Opaque) }; - | ----------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `MetaSized` is not implemented for `Opaque` - | | - | required by a bound introduced by this call - | - = note: the trait bound `Opaque: MetaSized` is not satisfied -note: required by a bound in `std::intrinsics::size_of_val` - --> $SRC_DIR/core/src/intrinsics/mod.rs:LL:COL -help: consider borrowing here - | -LL | const _SIZE: usize = unsafe { size_of_val(&(&4 as *const i32 as *const Opaque)) }; - | ++ + -LL | const _SIZE: usize = unsafe { size_of_val(&mut (&4 as *const i32 as *const Opaque)) }; - | ++++++ + + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `_SIZE` failed here -error[E0277]: the size for values of type `Opaque` cannot be known - --> $DIR/const-size_of_val-align_of_val-extern-type.rs:12:45 +error[E0080]: `extern type` does not have known layout + --> $DIR/const-size_of_val-align_of_val-extern-type.rs:12:32 | LL | const _ALIGN: usize = unsafe { align_of_val(&4 as *const i32 as *const Opaque) }; - | ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `MetaSized` is not implemented for `Opaque` - | | - | required by a bound introduced by this call - | - = note: the trait bound `Opaque: MetaSized` is not satisfied -note: required by a bound in `std::intrinsics::align_of_val` - --> $SRC_DIR/core/src/intrinsics/mod.rs:LL:COL -help: consider borrowing here - | -LL | const _ALIGN: usize = unsafe { align_of_val(&(&4 as *const i32 as *const Opaque)) }; - | ++ + -LL | const _ALIGN: usize = unsafe { align_of_val(&mut (&4 as *const i32 as *const Opaque)) }; - | ++++++ + + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `_ALIGN` failed here error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/extern/extern-types-size_of_val.rs b/tests/ui/extern/extern-types-size_of_val.rs index 3ff51b9b6b0df..875ae9a535a7a 100644 --- a/tests/ui/extern/extern-types-size_of_val.rs +++ b/tests/ui/extern/extern-types-size_of_val.rs @@ -1,4 +1,4 @@ -//@ check-fail +//@ check-pass #![feature(extern_types)] use std::mem::{align_of_val, size_of_val}; @@ -11,7 +11,5 @@ fn main() { let x: &A = unsafe { &*(1usize as *const A) }; size_of_val(x); - //~^ ERROR the size for values of type `A` cannot be known align_of_val(x); - //~^ ERROR the size for values of type `A` cannot be known } diff --git a/tests/ui/extern/extern-types-size_of_val.stderr b/tests/ui/extern/extern-types-size_of_val.stderr deleted file mode 100644 index 8678c6c3d6036..0000000000000 --- a/tests/ui/extern/extern-types-size_of_val.stderr +++ /dev/null @@ -1,39 +0,0 @@ -error[E0277]: the size for values of type `A` cannot be known - --> $DIR/extern-types-size_of_val.rs:13:17 - | -LL | size_of_val(x); - | ----------- ^ the trait `MetaSized` is not implemented for `A` - | | - | required by a bound introduced by this call - | - = note: the trait bound `A: MetaSized` is not satisfied -note: required by a bound in `std::mem::size_of_val` - --> $SRC_DIR/core/src/mem/mod.rs:LL:COL -help: consider borrowing here - | -LL | size_of_val(&x); - | + -LL | size_of_val(&mut x); - | ++++ - -error[E0277]: the size for values of type `A` cannot be known - --> $DIR/extern-types-size_of_val.rs:15:18 - | -LL | align_of_val(x); - | ------------ ^ the trait `MetaSized` is not implemented for `A` - | | - | required by a bound introduced by this call - | - = note: the trait bound `A: MetaSized` is not satisfied -note: required by a bound in `std::mem::align_of_val` - --> $SRC_DIR/core/src/mem/mod.rs:LL:COL -help: consider borrowing here - | -LL | align_of_val(&x); - | + -LL | align_of_val(&mut x); - | ++++ - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/extern/extern-types-unsized.rs b/tests/ui/extern/extern-types-unsized.rs index 46cdc24e08328..94a222a7e7e01 100644 --- a/tests/ui/extern/extern-types-unsized.rs +++ b/tests/ui/extern/extern-types-unsized.rs @@ -27,9 +27,7 @@ fn main() { assert_sized::>(); //~^ ERROR the size for values of type - //~| ERROR the size for values of type assert_sized::>>(); //~^ ERROR the size for values of type - //~| ERROR the size for values of type } diff --git a/tests/ui/extern/extern-types-unsized.stderr b/tests/ui/extern/extern-types-unsized.stderr index 43dd9800d6d30..a587d4dda55c8 100644 --- a/tests/ui/extern/extern-types-unsized.stderr +++ b/tests/ui/extern/extern-types-unsized.stderr @@ -59,21 +59,8 @@ help: consider relaxing the implicit `Sized` restriction LL | fn assert_sized() {} | ++++++++ -error[E0277]: the size for values of type `A` cannot be known - --> $DIR/extern-types-unsized.rs:28:20 - | -LL | assert_sized::>(); - | ^^^^^^ doesn't have a known size - | - = help: the trait `MetaSized` is not implemented for `A` -note: required by a bound in `Bar` - --> $DIR/extern-types-unsized.rs:14:12 - | -LL | struct Bar { - | ^ required by this bound in `Bar` - error[E0277]: the size for values of type `A` cannot be known at compilation time - --> $DIR/extern-types-unsized.rs:32:20 + --> $DIR/extern-types-unsized.rs:31:20 | LL | assert_sized::>>(); | ^^^^^^^^^^^ doesn't have a size known at compile-time @@ -94,19 +81,6 @@ help: consider relaxing the implicit `Sized` restriction LL | fn assert_sized() {} | ++++++++ -error[E0277]: the size for values of type `A` cannot be known - --> $DIR/extern-types-unsized.rs:32:20 - | -LL | assert_sized::>>(); - | ^^^^^^^^^^^ doesn't have a known size - | - = help: the trait `MetaSized` is not implemented for `A` -note: required by a bound in `Bar` - --> $DIR/extern-types-unsized.rs:14:12 - | -LL | struct Bar { - | ^ required by this bound in `Bar` - -error: aborting due to 6 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/extern/unsized-extern-derefmove.rs b/tests/ui/extern/unsized-extern-derefmove.rs index c02375266ab47..39597a12fe117 100644 --- a/tests/ui/extern/unsized-extern-derefmove.rs +++ b/tests/ui/extern/unsized-extern-derefmove.rs @@ -7,14 +7,10 @@ extern "C" { } unsafe fn make_device() -> Box { -//~^ ERROR the size for values of type `Device` cannot be known Box::from_raw(0 as *mut _) -//~^ ERROR the size for values of type `Device` cannot be known -//~| ERROR the size for values of type `Device` cannot be known } fn main() { let d: Device = unsafe { *make_device() }; //~^ ERROR the size for values of type `Device` cannot be known -//~| ERROR the size for values of type `Device` cannot be known } diff --git a/tests/ui/extern/unsized-extern-derefmove.stderr b/tests/ui/extern/unsized-extern-derefmove.stderr index a9efc2e66e3b0..c43184d94e177 100644 --- a/tests/ui/extern/unsized-extern-derefmove.stderr +++ b/tests/ui/extern/unsized-extern-derefmove.stderr @@ -1,43 +1,5 @@ -error[E0277]: the size for values of type `Device` cannot be known - --> $DIR/unsized-extern-derefmove.rs:9:28 - | -LL | unsafe fn make_device() -> Box { - | ^^^^^^^^^^^ doesn't have a known size - | - = help: the trait `MetaSized` is not implemented for `Device` -note: required by a bound in `Box` - --> $SRC_DIR/alloc/src/boxed.rs:LL:COL - -error[E0277]: the size for values of type `Device` cannot be known - --> $DIR/unsized-extern-derefmove.rs:11:19 - | -LL | Box::from_raw(0 as *mut _) - | ------------- ^^^^^^^^^^^ the trait `MetaSized` is not implemented for `Device` - | | - | required by a bound introduced by this call - | - = note: the trait bound `Device: MetaSized` is not satisfied -note: required by a bound in `Box::::from_raw` - --> $SRC_DIR/alloc/src/boxed.rs:LL:COL -help: consider borrowing here - | -LL | Box::from_raw(&(0 as *mut _)) - | ++ + -LL | Box::from_raw(&mut (0 as *mut _)) - | ++++++ + - -error[E0277]: the size for values of type `Device` cannot be known - --> $DIR/unsized-extern-derefmove.rs:11:5 - | -LL | Box::from_raw(0 as *mut _) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a known size - | - = help: the trait `MetaSized` is not implemented for `Device` -note: required by a bound in `Box` - --> $SRC_DIR/alloc/src/boxed.rs:LL:COL - error[E0277]: the size for values of type `Device` cannot be known at compilation time - --> $DIR/unsized-extern-derefmove.rs:17:9 + --> $DIR/unsized-extern-derefmove.rs:14:9 | LL | let d: Device = unsafe { *make_device() }; | ^ doesn't have a size known at compile-time @@ -49,16 +11,6 @@ help: consider borrowing here LL | let d: &Device = unsafe { *make_device() }; | + -error[E0277]: the size for values of type `Device` cannot be known - --> $DIR/unsized-extern-derefmove.rs:17:31 - | -LL | let d: Device = unsafe { *make_device() }; - | ^^^^^^^^^^^^^ doesn't have a known size - | - = help: the trait `MetaSized` is not implemented for `Device` -note: required by a bound in `Box` - --> $SRC_DIR/alloc/src/boxed.rs:LL:COL - -error: aborting due to 5 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/layout/unconstrained-param-ice-137308.rs b/tests/ui/layout/unconstrained-param-ice-137308.rs index 03b7e7599601a..d05e6e1fd3f33 100644 --- a/tests/ui/layout/unconstrained-param-ice-137308.rs +++ b/tests/ui/layout/unconstrained-param-ice-137308.rs @@ -17,4 +17,3 @@ impl A for u8 { //~ ERROR: the type parameter `C` is not constrained #[rustc_layout(debug)] struct S([u8; ::B]); //~^ ERROR: the type has an unknown layout -//~| ERROR: type annotations needed diff --git a/tests/ui/layout/unconstrained-param-ice-137308.stderr b/tests/ui/layout/unconstrained-param-ice-137308.stderr index 82cd1217c4903..615c131eb9045 100644 --- a/tests/ui/layout/unconstrained-param-ice-137308.stderr +++ b/tests/ui/layout/unconstrained-param-ice-137308.stderr @@ -4,19 +4,12 @@ error[E0207]: the type parameter `C` is not constrained by the impl trait, self LL | impl A for u8 { | ^ unconstrained type parameter -error[E0282]: type annotations needed - --> $DIR/unconstrained-param-ice-137308.rs:18:16 - | -LL | struct S([u8; ::B]); - | ^^ cannot infer type for type parameter `C` - error: the type has an unknown layout --> $DIR/unconstrained-param-ice-137308.rs:18:1 | LL | struct S([u8; ::B]); | ^^^^^^^^ -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0207, E0282. -For more information about an error, try `rustc --explain E0207`. +For more information about this error, try `rustc --explain E0207`. diff --git a/tests/ui/nll/issue-50716.rs b/tests/ui/nll/issue-50716.rs index 76c6fc5e7b925..96168ebeaa165 100644 --- a/tests/ui/nll/issue-50716.rs +++ b/tests/ui/nll/issue-50716.rs @@ -5,7 +5,7 @@ trait A { type X: ?Sized; } -fn foo<'a, T: 'static>(s: Box<<&'a T as A>::X>) //~ ERROR +fn foo<'a, T: 'static>(s: Box<<&'a T as A>::X>) where for<'b> &'b T: A, <&'static T as A>::X: Sized diff --git a/tests/ui/nll/issue-50716.stderr b/tests/ui/nll/issue-50716.stderr index edd7fd765dade..536f88085ded3 100644 --- a/tests/ui/nll/issue-50716.stderr +++ b/tests/ui/nll/issue-50716.stderr @@ -1,18 +1,3 @@ -error[E0308]: mismatched types - --> $DIR/issue-50716.rs:8:27 - | -LL | fn foo<'a, T: 'static>(s: Box<<&'a T as A>::X>) - | ^^^^^^^^^^^^^^^^^^^^ lifetime mismatch - | - = note: expected trait `<<&'a T as A>::X as MetaSized>` - found trait `<<&'static T as A>::X as MetaSized>` -note: the lifetime `'a` as defined here... - --> $DIR/issue-50716.rs:8:8 - | -LL | fn foo<'a, T: 'static>(s: Box<<&'a T as A>::X>) - | ^^ - = note: ...does not necessarily outlive the static lifetime - error: lifetime may not live long enough --> $DIR/issue-50716.rs:13:14 | @@ -22,6 +7,5 @@ LL | fn foo<'a, T: 'static>(s: Box<<&'a T as A>::X>) LL | let _x = *s; | ^^ proving this value is `Sized` requires that `'a` must outlive `'static` -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/sized-hierarchy/incomplete-inference-issue-143992.next.stderr b/tests/ui/sized-hierarchy/incomplete-inference-issue-143992.next.stderr new file mode 100644 index 0000000000000..cf56f42afc8a3 --- /dev/null +++ b/tests/ui/sized-hierarchy/incomplete-inference-issue-143992.next.stderr @@ -0,0 +1,17 @@ +error[E0308]: mismatched types + --> $DIR/incomplete-inference-issue-143992.rs:27:28 + | +LL | let _x = T::Assoc::new(()); + | ------------- ^^ expected `[u32; 1]`, found `()` + | | + | arguments to this function are incorrect + | +note: associated function defined here + --> $DIR/incomplete-inference-issue-143992.rs:18:8 + | +LL | fn new(r: R) -> R { + | ^^^ ---- + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/sized-hierarchy/incomplete-inference-issue-143992.rs b/tests/ui/sized-hierarchy/incomplete-inference-issue-143992.rs new file mode 100644 index 0000000000000..3e3e1dc50e58b --- /dev/null +++ b/tests/ui/sized-hierarchy/incomplete-inference-issue-143992.rs @@ -0,0 +1,29 @@ +//@ compile-flags: --crate-type=lib +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[current] check-pass +//@[next] compile-flags: -Znext-solver +//@[next] check-fail + +// Test that we avoid incomplete inference when normalizing. Without this, +// `Trait`'s implicit `MetaSized` supertrait requires proving `T::Assoc<_>: MetaSized` +// before checking the `new` arguments, resulting in eagerly constraining the inference +// var to `u32`. This is undesirable and would breaking code. + +pub trait Trait { + type Assoc: OtherTrait; +} + +pub trait OtherTrait { + fn new(r: R) -> R { + r + } +} + +pub fn function() +where + T::Assoc<[u32; 1]>: Clone, +{ + let _x = T::Assoc::new(()); +//[next]~^ ERROR mismatched types +} diff --git a/tests/ui/sized-hierarchy/overflow.current.stderr b/tests/ui/sized-hierarchy/overflow.current.stderr deleted file mode 100644 index e90548aa78c6f..0000000000000 --- a/tests/ui/sized-hierarchy/overflow.current.stderr +++ /dev/null @@ -1,45 +0,0 @@ -error[E0275]: overflow evaluating the requirement `Element: MetaSized` - --> $DIR/overflow.rs:16:16 - | -LL | struct Element(> as ParseTokens>::Output); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: required for `Box` to implement `ParseTokens` - --> $DIR/overflow.rs:12:31 - | -LL | impl ParseTokens for Box { - | - ^^^^^^^^^^^ ^^^^^^ - | | - | unsatisfied trait bound introduced here - = note: 1 redundant requirement hidden - = note: required for `Box>` to implement `ParseTokens` - -error[E0275]: overflow evaluating the requirement `Box: ParseTokens` - --> $DIR/overflow.rs:18:22 - | -LL | impl ParseTokens for Element { - | ^^^^^^^ - | -note: required for `Box>` to implement `ParseTokens` - --> $DIR/overflow.rs:12:31 - | -LL | impl ParseTokens for Box { - | ----------- ^^^^^^^^^^^ ^^^^^^ - | | - | unsatisfied trait bound introduced here -note: required because it appears within the type `Element` - --> $DIR/overflow.rs:16:8 - | -LL | struct Element(> as ParseTokens>::Output); - | ^^^^^^^ -note: required by a bound in `ParseTokens` - --> $DIR/overflow.rs:9:1 - | -LL | / trait ParseTokens { -LL | | type Output; -LL | | } - | |_^ required by this bound in `ParseTokens` - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/sized-hierarchy/overflow.rs b/tests/ui/sized-hierarchy/overflow.rs index e1af4885e5396..f8e5dd5d4029b 100644 --- a/tests/ui/sized-hierarchy/overflow.rs +++ b/tests/ui/sized-hierarchy/overflow.rs @@ -1,9 +1,13 @@ //@ compile-flags: --crate-type=lib //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) +//@[current] check-pass //@[next] check-pass //@[next] compile-flags: -Znext-solver +// FIXME(sized_hierarchy): this is expected to fail in the old solver when there +// isn't a temporary revert of the `sized_hierarchy` feature + use std::marker::PhantomData; trait ParseTokens { @@ -14,8 +18,6 @@ impl ParseTokens for Box { } struct Element(> as ParseTokens>::Output); -//[current]~^ ERROR overflow evaluating impl ParseTokens for Element { -//[current]~^ ERROR overflow evaluating type Output = (); } diff --git a/tests/ui/traits/resolve-impl-before-constrain-check.rs b/tests/ui/traits/resolve-impl-before-constrain-check.rs index 50d1a87455139..87f9c241e402b 100644 --- a/tests/ui/traits/resolve-impl-before-constrain-check.rs +++ b/tests/ui/traits/resolve-impl-before-constrain-check.rs @@ -15,7 +15,6 @@ use foo::*; fn test() -> impl Sized { <() as Callable>::call() -//~^ ERROR: type annotations needed } fn main() {} diff --git a/tests/ui/traits/resolve-impl-before-constrain-check.stderr b/tests/ui/traits/resolve-impl-before-constrain-check.stderr index 13fbfdb855cbf..e8e569ba625ee 100644 --- a/tests/ui/traits/resolve-impl-before-constrain-check.stderr +++ b/tests/ui/traits/resolve-impl-before-constrain-check.stderr @@ -4,13 +4,6 @@ error[E0207]: the type parameter `V` is not constrained by the impl trait, self LL | impl Callable for () { | ^ unconstrained type parameter -error[E0282]: type annotations needed - --> $DIR/resolve-impl-before-constrain-check.rs:17:6 - | -LL | <() as Callable>::call() - | ^^ cannot infer type for type parameter `V` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0207, E0282. -For more information about an error, try `rustc --explain E0207`. +For more information about this error, try `rustc --explain E0207`. From 8f854d9cb2d108a2d4f980ccb4d5909e214e6ef0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 16 Jul 2025 15:11:11 +0200 Subject: [PATCH 31/34] const heap: fix ICE on forgotten make_global --- .../src/const_eval/eval_queries.rs | 11 ++- .../rustc_const_eval/src/interpret/intern.rs | 74 +++++++++---------- .../rustc_const_eval/src/interpret/mod.rs | 2 +- .../src/interpret/validity.rs | 10 ++- tests/crashes/const_mut_ref_check_bypass.rs | 11 --- .../heap/ptr_not_made_global_mut.rs | 9 +-- .../heap/ptr_not_made_global_mut.stderr | 8 +- 7 files changed, 58 insertions(+), 67 deletions(-) delete mode 100644 tests/crashes/const_mut_ref_check_bypass.rs diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 32209ff436791..ce72d59b8b0e4 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -18,7 +18,7 @@ use tracing::{debug, instrument, trace}; use super::{CanAccessMutGlobal, CompileTimeInterpCx, CompileTimeMachine}; use crate::const_eval::CheckAlignment; use crate::interpret::{ - CtfeValidationMode, GlobalId, Immediate, InternKind, InternResult, InterpCx, InterpErrorKind, + CtfeValidationMode, GlobalId, Immediate, InternError, InternKind, InterpCx, InterpErrorKind, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, ReturnContinuation, create_static_alloc, intern_const_alloc_recursive, interp_ok, throw_exhaust, }; @@ -98,20 +98,25 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>( match intern_result { Ok(()) => {} - Err(InternResult::FoundDanglingPointer) => { + Err(InternError::DanglingPointer) => { throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error( ecx.tcx .dcx() .emit_err(errors::DanglingPtrInFinal { span: ecx.tcx.span, kind: intern_kind }), ))); } - Err(InternResult::FoundBadMutablePointer) => { + Err(InternError::BadMutablePointer) => { throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error( ecx.tcx .dcx() .emit_err(errors::MutablePtrInFinal { span: ecx.tcx.span, kind: intern_kind }), ))); } + Err(InternError::ConstAllocNotGlobal) => { + throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error( + ecx.tcx.dcx().emit_err(errors::ConstHeapPtrInFinal { span: ecx.tcx.span }), + ))); + } } interp_ok(R::make_result(ret, ecx)) diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index b8fcdc9b7ef03..f25bb222bcfcc 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -26,12 +26,9 @@ use rustc_middle::ty::layout::TyAndLayout; use rustc_span::def_id::LocalDefId; use tracing::{instrument, trace}; -use super::{ - AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy, err_ub, interp_ok, -}; -use crate::const_eval; +use super::{AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy, interp_ok}; use crate::const_eval::DummyMachine; -use crate::errors::{ConstHeapPtrInFinal, NestedStaticInThreadLocal}; +use crate::{const_eval, errors}; pub trait CompileTimeMachine<'tcx, T> = Machine< 'tcx, @@ -63,7 +60,7 @@ pub enum DisallowInternReason { /// /// This prevents us from interning `const_allocate` pointers that have not been made /// global through `const_make_global`. -pub trait CanIntern { +pub trait CanIntern: Copy { fn disallows_intern(&self) -> Option; } @@ -96,24 +93,22 @@ fn intern_shallow<'tcx, T: CanIntern, M: CompileTimeMachine<'tcx, T>>( alloc_id: AllocId, mutability: Mutability, disambiguator: Option<&mut DisambiguatorState>, -) -> Result + 'tcx, ()> { +) -> Result + 'tcx, InternError> { trace!("intern_shallow {:?}", alloc_id); // remove allocation // FIXME(#120456) - is `swap_remove` correct? let Some((kind, mut alloc)) = ecx.memory.alloc_map.swap_remove(&alloc_id) else { - return Err(()); + return Err(InternError::DanglingPointer); }; match kind { MemoryKind::Machine(x) if let Some(reason) = x.disallows_intern() => match reason { - // Attempting to intern a `const_allocate`d pointer that was not made global via - // `const_make_global`. This is an error, but if we return `Err` now, things - // become inconsistent because we already removed the allocation from `alloc_map`. - // So instead we just emit an error and then continue interning as usual. - // We *could* do this check before removing the allocation from `alloc_map`, but then - // it would be much harder to ensure that we only error once for each allocation. DisallowInternReason::ConstHeap => { - ecx.tcx.dcx().emit_err(ConstHeapPtrInFinal { span: ecx.tcx.span }); + // Attempting to intern a `const_allocate`d pointer that was not made global via + // `const_make_global`. We want to error here, but we have to first put the + // allocation back into the `alloc_map` to keep things in a consistent state. + ecx.memory.alloc_map.insert(alloc_id, (kind, alloc)); + return Err(InternError::ConstAllocNotGlobal); } }, MemoryKind::Machine(_) | MemoryKind::Stack | MemoryKind::CallerLocation => {} @@ -170,7 +165,7 @@ fn intern_as_new_static<'tcx>( tcx.set_nested_alloc_id_static(alloc_id, feed.def_id()); if tcx.is_thread_local_static(static_id.into()) { - tcx.dcx().emit_err(NestedStaticInThreadLocal { span: tcx.def_span(static_id) }); + tcx.dcx().emit_err(errors::NestedStaticInThreadLocal { span: tcx.def_span(static_id) }); } // These do not inherit the codegen attrs of the parent static allocation, since @@ -196,9 +191,10 @@ pub enum InternKind { } #[derive(Debug)] -pub enum InternResult { - FoundBadMutablePointer, - FoundDanglingPointer, +pub enum InternError { + BadMutablePointer, + DanglingPointer, + ConstAllocNotGlobal, } /// Intern `ret` and everything it references. @@ -212,7 +208,7 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval ecx: &mut InterpCx<'tcx, M>, intern_kind: InternKind, ret: &MPlaceTy<'tcx>, -) -> Result<(), InternResult> { +) -> Result<(), InternError> { let mut disambiguator = DisambiguatorState::new(); // We are interning recursively, and for mutability we are distinguishing the "root" allocation @@ -269,6 +265,7 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval // We want to first report "dangling" and then "mutable", so we need to delay reporting these // errors. let mut result = Ok(()); + let mut found_bad_mutable_ptr = false; // Keep interning as long as there are things to intern. // We show errors if there are dangling pointers, or mutable pointers in immutable contexts @@ -323,18 +320,7 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval // when there is memory there that someone might expect to be mutable, but we make it immutable. let dangling = !is_already_global && !ecx.memory.alloc_map.contains_key(&alloc_id); if !dangling { - // Found a mutable pointer inside a const where inner allocations should be - // immutable. - if !ecx.tcx.sess.opts.unstable_opts.unleash_the_miri_inside_of_you { - span_bug!( - ecx.tcx.span, - "the static const safety checks accepted a mutable pointer they should not have accepted" - ); - } - // Prefer dangling pointer errors over mutable pointer errors - if result.is_ok() { - result = Err(InternResult::FoundBadMutablePointer); - } + found_bad_mutable_ptr = true; } } if ecx.tcx.try_get_global_alloc(alloc_id).is_some() { @@ -355,12 +341,25 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval just_interned.insert(alloc_id); match intern_shallow(ecx, alloc_id, inner_mutability, Some(&mut disambiguator)) { Ok(nested) => todo.extend(nested), - Err(()) => { - ecx.tcx.dcx().delayed_bug("found dangling pointer during const interning"); - result = Err(InternResult::FoundDanglingPointer); + Err(err) => { + ecx.tcx.dcx().delayed_bug("error during const interning"); + result = Err(err); } } } + if found_bad_mutable_ptr && result.is_ok() { + // We found a mutable pointer inside a const where inner allocations should be immutable, + // and there was no other error. This should usually never happen! However, this can happen + // in unleash-miri mode, so report it as a normal error then. + if ecx.tcx.sess.opts.unstable_opts.unleash_the_miri_inside_of_you { + result = Err(InternError::BadMutablePointer); + } else { + span_bug!( + ecx.tcx.span, + "the static const safety checks accepted a mutable pointer they should not have accepted" + ); + } + } result } @@ -375,10 +374,7 @@ pub fn intern_const_alloc_for_constprop<'tcx, T: CanIntern, M: CompileTimeMachin return interp_ok(()); } // Move allocation to `tcx`. - if let Some(_) = intern_shallow(ecx, alloc_id, Mutability::Not, None) - .map_err(|()| err_ub!(DeadLocal))? - .next() - { + if let Some(_) = intern_shallow(ecx, alloc_id, Mutability::Not, None).unwrap().next() { // We are not doing recursive interning, so we don't currently support provenance. // (If this assertion ever triggers, we should just implement a // proper recursive interning loop -- or just call `intern_const_alloc_recursive`. diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs index 2fc372dd01978..2f365ec77b33e 100644 --- a/compiler/rustc_const_eval/src/interpret/mod.rs +++ b/compiler/rustc_const_eval/src/interpret/mod.rs @@ -26,7 +26,7 @@ pub use self::call::FnArg; pub use self::eval_context::{InterpCx, format_interp_error}; use self::eval_context::{from_known_layout, mir_assign_valid_types}; pub use self::intern::{ - HasStaticRootDefId, InternKind, InternResult, intern_const_alloc_for_constprop, + HasStaticRootDefId, InternError, InternKind, intern_const_alloc_for_constprop, intern_const_alloc_recursive, }; pub use self::machine::{AllocMap, Machine, MayLeak, ReturnAction, compile_time_machine}; diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index fc44490c96d37..62f591ceaa910 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -558,7 +558,15 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { { // Everything should be already interned. let Some(global_alloc) = self.ecx.tcx.try_get_global_alloc(alloc_id) else { - assert!(self.ecx.memory.alloc_map.get(alloc_id).is_none()); + if self.ecx.memory.alloc_map.contains_key(&alloc_id) { + // This can happen when interning didn't complete due to, e.g. + // missing `make_global`. This must mean other errors are already + // being reported. + self.ecx.tcx.dcx().delayed_bug( + "interning did not complete, there should be an error", + ); + return interp_ok(()); + } // We can't have *any* references to non-existing allocations in const-eval // as the rest of rustc isn't happy with them... so we throw an error, even // though for zero-sized references this isn't really UB. diff --git a/tests/crashes/const_mut_ref_check_bypass.rs b/tests/crashes/const_mut_ref_check_bypass.rs deleted file mode 100644 index 6234536c72beb..0000000000000 --- a/tests/crashes/const_mut_ref_check_bypass.rs +++ /dev/null @@ -1,11 +0,0 @@ -// Version of `tests/ui/consts/const-eval/heap/alloc_intrinsic_untyped.rs` without the flag that -// suppresses the ICE. -//@ known-bug: #129233 -#![feature(core_intrinsics)] -#![feature(const_heap)] -#![feature(const_mut_refs)] -use std::intrinsics; - -const BAR: *mut i32 = unsafe { intrinsics::const_allocate(4, 4) as *mut i32 }; - -fn main() {} diff --git a/tests/ui/consts/const-eval/heap/ptr_not_made_global_mut.rs b/tests/ui/consts/const-eval/heap/ptr_not_made_global_mut.rs index 201fa71502298..e44a3f7a23c2c 100644 --- a/tests/ui/consts/const-eval/heap/ptr_not_made_global_mut.rs +++ b/tests/ui/consts/const-eval/heap/ptr_not_made_global_mut.rs @@ -1,12 +1,11 @@ -// We unleash Miri here since this test demonstrates code that bypasses the checks against interning -// mutable pointers, which currently ICEs. Unleashing Miri silences the ICE. -//@ compile-flags: -Zunleash-the-miri-inside-of-you +// Ensure that we reject interning `const_allocate`d allocations in the final value of constants +// if they have not been made global through `const_make_global`. This covers the case where the +// pointer is even still mutable, which used to ICE. #![feature(core_intrinsics)] #![feature(const_heap)] use std::intrinsics; const BAR: *mut i32 = unsafe { intrinsics::const_allocate(4, 4) as *mut i32 }; -//~^ error: mutable pointer in final value of constant -//~| error: encountered `const_allocate` pointer in final value that was not made global +//~^ error: encountered `const_allocate` pointer in final value that was not made global fn main() {} diff --git a/tests/ui/consts/const-eval/heap/ptr_not_made_global_mut.stderr b/tests/ui/consts/const-eval/heap/ptr_not_made_global_mut.stderr index eae79d24797a9..2445ce633d6ff 100644 --- a/tests/ui/consts/const-eval/heap/ptr_not_made_global_mut.stderr +++ b/tests/ui/consts/const-eval/heap/ptr_not_made_global_mut.stderr @@ -6,11 +6,5 @@ LL | const BAR: *mut i32 = unsafe { intrinsics::const_allocate(4, 4) as *mut i32 | = note: use `const_make_global` to make allocated pointers immutable before returning -error: encountered mutable pointer in final value of constant - --> $DIR/ptr_not_made_global_mut.rs:8:1 - | -LL | const BAR: *mut i32 = unsafe { intrinsics::const_allocate(4, 4) as *mut i32 }; - | ^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error From 0bf0860a0ef285fe3e4eb3b176006c14d8ca9d8d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 16 Jul 2025 15:19:17 +0200 Subject: [PATCH 32/34] simplfy memory kind handling during interning --- .../src/const_eval/dummy_machine.rs | 1 - .../src/const_eval/machine.rs | 2 - .../rustc_const_eval/src/interpret/intern.rs | 49 ++++--------------- .../rustc_const_eval/src/interpret/machine.rs | 1 + 4 files changed, 11 insertions(+), 42 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs index b6e2682af367b..438aed41b8be3 100644 --- a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs @@ -49,7 +49,6 @@ impl HasStaticRootDefId for DummyMachine { impl<'tcx> interpret::Machine<'tcx> for DummyMachine { interpret::compile_time_machine!(<'tcx>); - type MemoryKind = !; const PANIC_ON_ALLOC_FAIL: bool = true; // We want to just eval random consts in the program, so `eval_mir_const` can fail. diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 0a6bdef2225ed..70ad57b2b11b7 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -320,8 +320,6 @@ impl<'tcx> CompileTimeMachine<'tcx> { impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { compile_time_machine!(<'tcx>); - type MemoryKind = MemoryKind; - const PANIC_ON_ALLOC_FAIL: bool = false; // will be raised as a proper error #[inline(always)] diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index f25bb222bcfcc..bb59b9f54186c 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -30,14 +30,14 @@ use super::{AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceT use crate::const_eval::DummyMachine; use crate::{const_eval, errors}; -pub trait CompileTimeMachine<'tcx, T> = Machine< +pub trait CompileTimeMachine<'tcx> = Machine< 'tcx, - MemoryKind = T, + MemoryKind = const_eval::MemoryKind, Provenance = CtfeProvenance, ExtraFnVal = !, FrameExtra = (), AllocExtra = (), - MemoryMap = FxIndexMap, Allocation)>, + MemoryMap = FxIndexMap, Allocation)>, > + HasStaticRootDefId; pub trait HasStaticRootDefId { @@ -52,35 +52,6 @@ impl HasStaticRootDefId for const_eval::CompileTimeMachine<'_> { } } -pub enum DisallowInternReason { - ConstHeap, -} - -/// A trait for controlling whether memory allocated in the interpreter can be interned. -/// -/// This prevents us from interning `const_allocate` pointers that have not been made -/// global through `const_make_global`. -pub trait CanIntern: Copy { - fn disallows_intern(&self) -> Option; -} - -impl CanIntern for const_eval::MemoryKind { - fn disallows_intern(&self) -> Option { - match self { - const_eval::MemoryKind::Heap { was_made_global: false } => { - Some(DisallowInternReason::ConstHeap) - } - const_eval::MemoryKind::Heap { was_made_global: true } => None, - } - } -} - -impl CanIntern for ! { - fn disallows_intern(&self) -> Option { - *self - } -} - /// Intern an allocation. Returns `Err` if the allocation does not exist in the local memory. /// /// `mutability` can be used to force immutable interning: if it is `Mutability::Not`, the @@ -88,7 +59,7 @@ impl CanIntern for ! { /// already mutable (as a sanity check). /// /// Returns an iterator over all relocations referred to by this allocation. -fn intern_shallow<'tcx, T: CanIntern, M: CompileTimeMachine<'tcx, T>>( +fn intern_shallow<'tcx, M: CompileTimeMachine<'tcx>>( ecx: &mut InterpCx<'tcx, M>, alloc_id: AllocId, mutability: Mutability, @@ -102,16 +73,16 @@ fn intern_shallow<'tcx, T: CanIntern, M: CompileTimeMachine<'tcx, T>>( }; match kind { - MemoryKind::Machine(x) if let Some(reason) = x.disallows_intern() => match reason { - DisallowInternReason::ConstHeap => { + MemoryKind::Machine(const_eval::MemoryKind::Heap { was_made_global }) => { + if !was_made_global { // Attempting to intern a `const_allocate`d pointer that was not made global via // `const_make_global`. We want to error here, but we have to first put the // allocation back into the `alloc_map` to keep things in a consistent state. ecx.memory.alloc_map.insert(alloc_id, (kind, alloc)); return Err(InternError::ConstAllocNotGlobal); } - }, - MemoryKind::Machine(_) | MemoryKind::Stack | MemoryKind::CallerLocation => {} + } + MemoryKind::Stack | MemoryKind::CallerLocation => {} } // Set allocation mutability as appropriate. This is used by LLVM to put things into @@ -204,7 +175,7 @@ pub enum InternError { /// /// For `InternKind::Static` the root allocation will not be interned, but must be handled by the caller. #[instrument(level = "debug", skip(ecx))] -pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval::MemoryKind>>( +pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx>>( ecx: &mut InterpCx<'tcx, M>, intern_kind: InternKind, ret: &MPlaceTy<'tcx>, @@ -365,7 +336,7 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval /// Intern `ret`. This function assumes that `ret` references no other allocation. #[instrument(level = "debug", skip(ecx))] -pub fn intern_const_alloc_for_constprop<'tcx, T: CanIntern, M: CompileTimeMachine<'tcx, T>>( +pub fn intern_const_alloc_for_constprop<'tcx, M: CompileTimeMachine<'tcx>>( ecx: &mut InterpCx<'tcx, M>, alloc_id: AllocId, ) -> InterpResult<'tcx, ()> { diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index d150ed69250e5..e981f3973ae63 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -649,6 +649,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) { type ExtraFnVal = !; + type MemoryKind = $crate::const_eval::MemoryKind; type MemoryMap = rustc_data_structures::fx::FxIndexMap, Allocation)>; const GLOBAL_KIND: Option = None; // no copying of globals from `tcx` to machine memory From fb7aa9e4fdb88a4833274303899b9801ef924100 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 27 May 2025 15:51:47 +1000 Subject: [PATCH 33/34] Improve path segment joining. There are many places that join path segments with `::` to produce a string. A lot of these use `join("::")`. Many in rustdoc use `join_with_double_colon`, and a few use `.joined("..")`. One in Clippy uses `itertools::join`. A couple of them look for `kw::PathRoot` in the first segment, which can be important. This commit introduces `rustc_ast::join_path_{syms,ident}` to do the joining for everyone. `rustc_ast` is as good a location for these as any, being the earliest-running of the several crates with a `Path` type. Two functions are needed because `Ident` printing is more complex than simple `Symbol` printing. The commit also removes `join_with_double_colon`, and `estimate_item_path_byte_length` with it. There are still a handful of places that join strings with "::" that are unchanged. They are not that important: some of them are in tests, and some of them first split a path around "::" and then rejoin with "::". This fixes one test case where `{{root}}` shows up in an error message. --- compiler/rustc_ast/src/ast.rs | 55 ++++++++++++++++++- .../rustc_builtin_macros/src/source_util.rs | 4 +- compiler/rustc_builtin_macros/src/test.rs | 9 +-- compiler/rustc_hir/src/hir.rs | 4 +- .../src/method/prelude_edition_lints.rs | 11 ++-- compiler/rustc_passes/src/check_attr.rs | 6 +- compiler/rustc_resolve/src/diagnostics.rs | 29 +++++----- compiler/rustc_resolve/src/rustdoc.rs | 10 ++-- src/librustdoc/clean/utils.rs | 11 +--- src/librustdoc/formats/cache.rs | 4 +- src/librustdoc/html/format.rs | 23 ++------ src/librustdoc/html/render/context.rs | 4 +- src/librustdoc/html/render/mod.rs | 9 +-- src/librustdoc/html/render/print_item.rs | 9 +-- src/librustdoc/html/render/search_index.rs | 14 ++--- src/librustdoc/html/render/write_shared.rs | 4 +- src/librustdoc/html/url_parts_builder.rs | 12 +--- .../methods/from_iter_instead_of_collect.rs | 3 +- src/tools/clippy/clippy_utils/src/lib.rs | 17 +++--- tests/ui/asm/naked-invalid-attr.stderr | 2 +- 20 files changed, 130 insertions(+), 110 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 3c576316f6230..fa542a810dcea 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -18,7 +18,7 @@ //! - [`Attribute`]: Metadata associated with item. //! - [`UnOp`], [`BinOp`], and [`BinOpKind`]: Unary and binary operators. -use std::borrow::Cow; +use std::borrow::{Borrow, Cow}; use std::{cmp, fmt}; pub use GenericArgs::*; @@ -155,6 +155,59 @@ impl Path { } } +/// Joins multiple symbols with "::" into a path, e.g. "a::b::c". If the first +/// segment is `kw::PathRoot` it will be printed as empty, e.g. "::b::c". +/// +/// The generics on the `path` argument mean it can accept many forms, such as: +/// - `&[Symbol]` +/// - `Vec` +/// - `Vec<&Symbol>` +/// - `impl Iterator` +/// - `impl Iterator` +/// +/// Panics if `path` is empty or a segment after the first is `kw::PathRoot`. +pub fn join_path_syms(path: impl IntoIterator>) -> String { + // This is a guess at the needed capacity that works well in practice. It is slightly faster + // than (a) starting with an empty string, or (b) computing the exact capacity required. + // `8` works well because it's about the right size and jemalloc's size classes are all + // multiples of 8. + let mut iter = path.into_iter(); + let len_hint = iter.size_hint().1.unwrap_or(1); + let mut s = String::with_capacity(len_hint * 8); + + let first_sym = *iter.next().unwrap().borrow(); + if first_sym != kw::PathRoot { + s.push_str(first_sym.as_str()); + } + for sym in iter { + let sym = *sym.borrow(); + debug_assert_ne!(sym, kw::PathRoot); + s.push_str("::"); + s.push_str(sym.as_str()); + } + s +} + +/// Like `join_path_syms`, but for `Ident`s. This function is necessary because +/// `Ident::to_string` does more than just print the symbol in the `name` field. +pub fn join_path_idents(path: impl IntoIterator>) -> String { + let mut iter = path.into_iter(); + let len_hint = iter.size_hint().1.unwrap_or(1); + let mut s = String::with_capacity(len_hint * 8); + + let first_ident = *iter.next().unwrap().borrow(); + if first_ident.name != kw::PathRoot { + s.push_str(&first_ident.to_string()); + } + for ident in iter { + let ident = *ident.borrow(); + debug_assert_ne!(ident.name, kw::PathRoot); + s.push_str("::"); + s.push_str(&ident.to_string()); + } + s +} + /// A segment of a path: an identifier, an optional lifetime, and a set of types. /// /// E.g., `std`, `String` or `Box`. diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index cebfffa1e16cb..ecfd46a84ec3d 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -4,8 +4,8 @@ use std::sync::Arc; use rustc_ast as ast; use rustc_ast::ptr::P; -use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; +use rustc_ast::{join_path_idents, token}; use rustc_ast_pretty::pprust; use rustc_expand::base::{ DummyResult, ExpandResult, ExtCtxt, MacEager, MacResult, MacroExpanderResult, resolve_path, @@ -100,7 +100,7 @@ pub(crate) fn expand_mod( let sp = cx.with_def_site_ctxt(sp); check_zero_tts(cx, sp, tts, "module_path!"); let mod_path = &cx.current_expansion.module.mod_path; - let string = mod_path.iter().map(|x| x.to_string()).collect::>().join("::"); + let string = join_path_idents(mod_path); ExpandResult::Ready(MacEager::expr(cx.expr_str(sp, Symbol::intern(&string)))) } diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index b067578794b30..ba3d8368b2a08 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -5,7 +5,7 @@ use std::assert_matches::assert_matches; use std::iter; use rustc_ast::ptr::P; -use rustc_ast::{self as ast, GenericParamKind, attr}; +use rustc_ast::{self as ast, GenericParamKind, attr, join_path_idents}; use rustc_ast_pretty::pprust; use rustc_errors::{Applicability, Diag, Level}; use rustc_expand::base::*; @@ -446,12 +446,7 @@ fn get_location_info(cx: &ExtCtxt<'_>, fn_: &ast::Fn) -> (Symbol, usize, usize, } fn item_path(mod_path: &[Ident], item_ident: &Ident) -> String { - mod_path - .iter() - .chain(iter::once(item_ident)) - .map(|x| x.to_string()) - .collect::>() - .join("::") + join_path_idents(mod_path.iter().chain(iter::once(item_ident))) } enum ShouldPanic { diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 159518a8d8716..ace2259aec387 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -7,7 +7,7 @@ use rustc_ast::token::CommentKind; use rustc_ast::util::parser::ExprPrecedence; use rustc_ast::{ self as ast, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label, LitIntType, - LitKind, TraitObjectSyntax, UintTy, UnsafeBinderCastKind, + LitKind, TraitObjectSyntax, UintTy, UnsafeBinderCastKind, join_path_idents, }; pub use rustc_ast::{ AssignOp, AssignOpKind, AttrId, AttrStyle, BinOp, BinOpKind, BindingMode, BorrowKind, @@ -1168,7 +1168,7 @@ impl AttrPath { impl fmt::Display for AttrPath { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.segments.iter().map(|i| i.to_string()).collect::>().join("::")) + write!(f, "{}", join_path_idents(&self.segments)) } } diff --git a/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs b/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs index 0037d5ac042bc..38413cca633c9 100644 --- a/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs +++ b/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs @@ -2,6 +2,7 @@ use std::fmt::Write; use hir::def_id::DefId; use hir::{HirId, ItemKind}; +use rustc_ast::join_path_idents; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER}; @@ -383,13 +384,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // All that is left is `_`! We need to use the full path. It doesn't matter which one we // pick, so just take the first one. match import_items[0].kind { - ItemKind::Use(path, _) => Some( - path.segments - .iter() - .map(|segment| segment.ident.to_string()) - .collect::>() - .join("::"), - ), + ItemKind::Use(path, _) => { + Some(join_path_idents(path.segments.iter().map(|seg| seg.ident))) + } _ => { span_bug!(span, "unexpected item kind, expected a use: {:?}", import_items[0].kind); } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 2009ddc1e2d59..5b2b9c152aa7c 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -10,7 +10,7 @@ use std::collections::hash_map::Entry; use std::slice; use rustc_abi::{Align, ExternAbi, Size}; -use rustc_ast::{AttrStyle, LitKind, MetaItemInner, MetaItemKind, ast}; +use rustc_ast::{AttrStyle, LitKind, MetaItemInner, MetaItemKind, ast, join_path_syms}; use rustc_attr_data_structures::{AttributeKind, InlineAttr, ReprAttr, find_attr}; use rustc_attr_parsing::{AttributeParser, Late}; use rustc_data_structures::fx::FxHashMap; @@ -678,9 +678,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { allowed_target: Target, ) { if target != allowed_target { - let path = attr.path(); - let path: Vec<_> = path.iter().map(|s| s.as_str()).collect(); - let attr_name = path.join("::"); + let attr_name = join_path_syms(attr.path()); self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 11e4a729ae239..5a162b5d2b901 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1,6 +1,8 @@ use rustc_ast::ptr::P; use rustc_ast::visit::{self, Visitor}; -use rustc_ast::{self as ast, CRATE_NODE_ID, Crate, ItemKind, ModKind, NodeId, Path}; +use rustc_ast::{ + self as ast, CRATE_NODE_ID, Crate, ItemKind, ModKind, NodeId, Path, join_path_idents, +}; use rustc_ast_pretty::pprust; use rustc_attr_data_structures::{ self as attr, AttributeKind, CfgEntry, Stability, StrippedCfgItem, find_attr, @@ -2018,7 +2020,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } - let mut sugg_paths = vec![]; + let mut sugg_paths: Vec<(Vec, bool)> = vec![]; if let Some(mut def_id) = res.opt_def_id() { // We can't use `def_path_str` in resolve. let mut path = vec![def_id]; @@ -2031,16 +2033,16 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } // We will only suggest importing directly if it is accessible through that path. - let path_names: Option> = path + let path_names: Option> = path .iter() .rev() .map(|def_id| { - self.tcx.opt_item_name(*def_id).map(|n| { - if def_id.is_top_level_module() { - "crate".to_string() + self.tcx.opt_item_name(*def_id).map(|name| { + Ident::with_dummy_span(if def_id.is_top_level_module() { + kw::Crate } else { - n.to_string() - } + name + }) }) }) .collect(); @@ -2084,13 +2086,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { match binding.kind { NameBindingKind::Import { import, .. } => { for segment in import.module_path.iter().skip(1) { - path.push(segment.ident.to_string()); + path.push(segment.ident); } sugg_paths.push(( - path.iter() - .cloned() - .chain(vec![ident.to_string()].into_iter()) - .collect::>(), + path.iter().cloned().chain(std::iter::once(ident)).collect::>(), true, // re-export )); } @@ -2126,7 +2125,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { err.subdiagnostic(note); } // We prioritize shorter paths, non-core imports and direct imports over the alternatives. - sugg_paths.sort_by_key(|(p, reexport)| (p.len(), p[0] == "core", *reexport)); + sugg_paths.sort_by_key(|(p, reexport)| (p.len(), p[0].name == sym::core, *reexport)); for (sugg, reexport) in sugg_paths { if not_publicly_reexported { break; @@ -2136,7 +2135,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // `tests/ui/imports/issue-55884-2.rs` continue; } - let path = sugg.join("::"); + let path = join_path_idents(sugg); let sugg = if reexport { errors::ImportIdent::ThroughReExport { span: dedup_span, ident, path } } else { diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs index f61cd1f0adfb8..24e15ded94fef 100644 --- a/compiler/rustc_resolve/src/rustdoc.rs +++ b/compiler/rustc_resolve/src/rustdoc.rs @@ -7,6 +7,7 @@ use pulldown_cmark::{ }; use rustc_ast as ast; use rustc_ast::attr::AttributeExt; +use rustc_ast::join_path_syms; use rustc_ast::util::comments::beautify_doc_string; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::unord::UnordSet; @@ -259,7 +260,7 @@ pub fn main_body_opts() -> Options { | Options::ENABLE_SMART_PUNCTUATION } -fn strip_generics_from_path_segment(segment: Vec) -> Result { +fn strip_generics_from_path_segment(segment: Vec) -> Result { let mut stripped_segment = String::new(); let mut param_depth = 0; @@ -284,7 +285,7 @@ fn strip_generics_from_path_segment(segment: Vec) -> Result>` Err(MalformedGenerics::UnbalancedAngleBrackets) @@ -346,9 +347,8 @@ pub fn strip_generics_from_path(path_str: &str) -> Result, MalformedGen debug!("path_str: {path_str:?}\nstripped segments: {stripped_segments:?}"); - let stripped_path = stripped_segments.join("::"); - - if !stripped_path.is_empty() { + if !stripped_segments.is_empty() { + let stripped_path = join_path_syms(stripped_segments); Ok(stripped_path.into()) } else { Err(MalformedGenerics::MissingType) diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index bf3f7607274df..fd1b17b64765e 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -3,6 +3,7 @@ use std::fmt::{self, Display, Write as _}; use std::sync::LazyLock as Lazy; use std::{ascii, mem}; +use rustc_ast::join_path_idents; use rustc_ast::tokenstream::TokenTree; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; @@ -24,7 +25,7 @@ use crate::clean::{ clean_middle_ty, inline, }; use crate::core::DocContext; -use crate::display::{Joined as _, MaybeDisplay as _}; +use crate::display::Joined as _; #[cfg(test)] mod tests; @@ -251,13 +252,7 @@ pub(crate) fn qpath_to_string(p: &hir::QPath<'_>) -> String { hir::QPath::LangItem(lang_item, ..) => return lang_item.name().to_string(), }; - fmt::from_fn(|f| { - segments - .iter() - .map(|seg| (seg.ident.name != kw::PathRoot).then_some(seg.ident).maybe_display()) - .joined("::", f) - }) - .to_string() + join_path_idents(segments.iter().map(|seg| seg.ident)) } pub(crate) fn build_deref_target_impls( diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 4989bd718c9f9..5191120ebdb0f 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -1,5 +1,6 @@ use std::mem; +use rustc_ast::join_path_syms; use rustc_attr_data_structures::StabilityLevel; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet}; @@ -13,7 +14,6 @@ use crate::core::DocContext; use crate::fold::DocFolder; use crate::formats::Impl; use crate::formats::item_type::ItemType; -use crate::html::format::join_with_double_colon; use crate::html::markdown::short_markdown_summary; use crate::html::render::IndexItem; use crate::html::render::search_index::get_function_type_for_search; @@ -558,7 +558,7 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It clean::ItemKind::ImportItem(import) => import.source.did.unwrap_or(item_def_id), _ => item_def_id, }; - let path = join_with_double_colon(parent_path); + let path = join_path_syms(parent_path); let impl_id = if let Some(ParentStackItem::Impl { item_id, .. }) = cache.parent_stack.last() { item_id.as_def_id() } else { diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index bcb3e57c84428..b16485107a02e 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -14,6 +14,7 @@ use std::slice; use itertools::{Either, Itertools}; use rustc_abi::ExternAbi; +use rustc_ast::join_path_syms; use rustc_attr_data_structures::{ConstStability, StabilityLevel, StableSince}; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; @@ -25,7 +26,7 @@ use rustc_span::symbol::kw; use rustc_span::{Symbol, sym}; use tracing::{debug, trace}; -use super::url_parts_builder::{UrlPartsBuilder, estimate_item_path_byte_length}; +use super::url_parts_builder::UrlPartsBuilder; use crate::clean::types::ExternalLocation; use crate::clean::utils::find_nearest_parent_module; use crate::clean::{self, ExternalCrate, PrimitiveType}; @@ -369,18 +370,6 @@ pub(crate) enum HrefError { NotInExternalCache, } -// Panics if `syms` is empty. -pub(crate) fn join_with_double_colon(syms: &[Symbol]) -> String { - let mut s = String::with_capacity(estimate_item_path_byte_length(syms.len())); - // NOTE: using `Joined::joined` here causes a noticeable perf regression - s.push_str(syms[0].as_str()); - for sym in &syms[1..] { - s.push_str("::"); - s.push_str(sym.as_str()); - } - s -} - /// This function is to get the external macro path because they are not in the cache used in /// `href_with_root_path`. fn generate_macro_def_id_path( @@ -672,7 +661,7 @@ pub(crate) fn link_tooltip( write!(f, "{}", cx.tcx().item_name(id))?; } else if !fqp.is_empty() { write!(f, "{shortty} ")?; - fqp.iter().joined("::", f)?; + write!(f, "{}", join_path_syms(fqp))?; } Ok(()) }) @@ -703,7 +692,7 @@ fn resolved_path( write!( f, "{path}::{anchor}", - path = join_with_double_colon(&fqp[..fqp.len() - 1]), + path = join_path_syms(&fqp[..fqp.len() - 1]), anchor = print_anchor(did, *fqp.last().unwrap(), cx) ) } else { @@ -835,7 +824,7 @@ pub(crate) fn print_anchor(did: DefId, text: Symbol, cx: &Context<'_>) -> impl D write!( f, r#"{text}"#, - path = join_with_double_colon(&fqp), + path = join_path_syms(fqp), text = EscapeBodyText(text.as_str()), ) } else { @@ -1095,7 +1084,7 @@ impl clean::QPathData { title=\"type {path}::{name}\">{name}", shortty = ItemType::AssocType, name = assoc.name, - path = join_with_double_colon(&path), + path = join_path_syms(path), ) } else { write!(f, "{}", assoc.name) diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 3b4dae841ee7f..7b814701a732e 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -6,6 +6,7 @@ use std::path::{Path, PathBuf}; use std::sync::mpsc::{Receiver, channel}; use askama::Template; +use rustc_ast::join_path_syms; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir::def_id::{DefIdMap, LOCAL_CRATE}; use rustc_middle::ty::TyCtxt; @@ -27,7 +28,6 @@ use crate::formats::FormatRenderer; use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; use crate::html::escape::Escape; -use crate::html::format::join_with_double_colon; use crate::html::markdown::{self, ErrorCodes, IdMap, plain_text_summary}; use crate::html::render::write_shared::write_shared; use crate::html::url_parts_builder::UrlPartsBuilder; @@ -211,7 +211,7 @@ impl<'tcx> Context<'tcx> { title.push_str(" in "); } // No need to include the namespace for primitive types and keywords - title.push_str(&join_with_double_colon(&self.current)); + title.push_str(&join_path_syms(&self.current)); }; title.push_str(" - Rust"); let tyname = it.type_(); diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 06de4944d97d2..d8c37137bbc73 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -49,6 +49,7 @@ use std::{fs, str}; use askama::Template; use itertools::Either; +use rustc_ast::join_path_syms; use rustc_attr_data_structures::{ ConstStability, DeprecatedSince, Deprecation, RustcVersion, StabilityLevel, StableSince, }; @@ -74,9 +75,9 @@ use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; use crate::html::escape::Escape; use crate::html::format::{ - Ending, HrefError, PrintWithSpace, href, join_with_double_colon, print_abi_with_space, - print_constness_with_space, print_default_space, print_generic_bounds, print_where_clause, - visibility_print_with_space, write_str, + Ending, HrefError, PrintWithSpace, href, print_abi_with_space, print_constness_with_space, + print_default_space, print_generic_bounds, print_where_clause, visibility_print_with_space, + write_str, }; use crate::html::markdown::{ HeadingOffset, IdMap, Markdown, MarkdownItemInfo, MarkdownSummaryLine, @@ -2555,7 +2556,7 @@ fn collect_paths_for_type(first_ty: &clean::Type, cache: &Cache) -> Vec let fqp = cache.exact_paths.get(&did).or_else(get_extern); if let Some(path) = fqp { - out.push(join_with_double_colon(path)); + out.push(join_path_syms(path)); } }; diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 667d39e9bc2f6..5fbda4797cc9f 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -4,6 +4,7 @@ use std::iter; use askama::Template; use rustc_abi::VariantIdx; +use rustc_ast::join_path_syms; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_hir as hir; use rustc_hir::def::CtorKind; @@ -30,8 +31,8 @@ use crate::formats::Impl; use crate::formats::item_type::ItemType; use crate::html::escape::{Escape, EscapeBodyTextWithWbr}; use crate::html::format::{ - Ending, PrintWithSpace, join_with_double_colon, print_abi_with_space, - print_constness_with_space, print_where_clause, visibility_print_with_space, + Ending, PrintWithSpace, print_abi_with_space, print_constness_with_space, print_where_clause, + visibility_print_with_space, }; use crate::html::markdown::{HeadingOffset, MarkdownSummaryLine}; use crate::html::render::{document_full, document_item_info}; @@ -1424,7 +1425,7 @@ fn item_type_alias(cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) -> iter::repeat_n("..", cx.current.len()).chain(iter::once("type.impl")).collect(); js_src_path.extend(target_fqp[..target_fqp.len() - 1].iter().copied()); js_src_path.push_fmt(format_args!("{target_type}.{}.js", target_fqp.last().unwrap())); - let self_path = fmt::from_fn(|f| self_fqp.iter().joined("::", f)); + let self_path = join_path_syms(self_fqp); write!( w, "", @@ -2256,7 +2257,7 @@ pub(crate) fn compare_names(left: &str, right: &str) -> Ordering { } pub(super) fn full_path(cx: &Context<'_>, item: &clean::Item) -> String { - let mut s = join_with_double_colon(&cx.current); + let mut s = join_path_syms(&cx.current); s.push_str("::"); s.push_str(item.name.unwrap().as_str()); s diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index aff8684ee3a09..80a59fa218c6e 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -4,6 +4,7 @@ use std::collections::hash_map::Entry; use std::collections::{BTreeMap, VecDeque}; use encode::{bitmap_to_string, write_vlqhex_to_string}; +use rustc_ast::join_path_syms; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::DefId; @@ -17,7 +18,6 @@ use crate::clean::types::{Function, Generics, ItemId, Type, WherePredicate}; use crate::clean::{self, utils}; use crate::formats::cache::{Cache, OrphanImplItem}; use crate::formats::item_type::ItemType; -use crate::html::format::join_with_double_colon; use crate::html::markdown::short_markdown_summary; use crate::html::render::ordered_json::OrderedJson; use crate::html::render::{self, IndexItem, IndexItemFunctionType, RenderType, RenderTypeId}; @@ -78,7 +78,7 @@ pub(crate) fn build_index( ty: item.type_(), defid: item.item_id.as_def_id(), name: item.name.unwrap(), - path: join_with_double_colon(&fqp[..fqp.len() - 1]), + path: join_path_syms(&fqp[..fqp.len() - 1]), desc, parent: Some(parent), parent_idx: None, @@ -416,7 +416,7 @@ pub(crate) fn build_index( if fqp.len() < 2 { return None; } - join_with_double_colon(&fqp[..fqp.len() - 1]) + join_path_syms(&fqp[..fqp.len() - 1]) }; if path == item.path { return None; @@ -427,10 +427,10 @@ pub(crate) fn build_index( let i = >::try_into(parent_idx).unwrap(); item.path = { let p = &crate_paths[i].1; - join_with_double_colon(&p[..p.len() - 1]) + join_path_syms(&p[..p.len() - 1]) }; item.exact_path = - crate_paths[i].2.as_ref().map(|xp| join_with_double_colon(&xp[..xp.len() - 1])); + crate_paths[i].2.as_ref().map(|xp| join_path_syms(&xp[..xp.len() - 1])); } // Omit the parent path if it is same to that of the prior item. @@ -549,11 +549,11 @@ pub(crate) fn build_index( }); continue; } - let full_path = join_with_double_colon(&path[..path.len() - 1]); + let full_path = join_path_syms(&path[..path.len() - 1]); let full_exact_path = exact .as_ref() .filter(|exact| exact.last() == path.last() && exact.len() >= 2) - .map(|exact| join_with_double_colon(&exact[..exact.len() - 1])); + .map(|exact| join_path_syms(&exact[..exact.len() - 1])); let exact_path = extra_paths.len() + self.items.len(); let exact_path = full_exact_path.as_ref().map(|full_exact_path| match extra_paths .entry(full_exact_path.clone()) diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index 0078671fcc5a1..1f691392b1716 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -26,6 +26,7 @@ use std::{fmt, fs}; use indexmap::IndexMap; use regex::Regex; +use rustc_ast::join_path_syms; use rustc_data_structures::flock; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_middle::ty::TyCtxt; @@ -43,7 +44,6 @@ use crate::docfs::PathError; use crate::error::Error; use crate::formats::Impl; use crate::formats::item_type::ItemType; -use crate::html::format::join_with_double_colon; use crate::html::layout; use crate::html::render::ordered_json::{EscapedJson, OrderedJson}; use crate::html::render::search_index::{SerializedSearchIndex, build_index}; @@ -608,7 +608,7 @@ impl TypeAliasPart { for &(type_alias_fqp, type_alias_item) in type_aliases { cx.id_map.borrow_mut().clear(); cx.deref_id_map.borrow_mut().clear(); - let type_alias_fqp = join_with_double_colon(&type_alias_fqp); + let type_alias_fqp = join_path_syms(type_alias_fqp); if let Some(ret) = &mut ret { ret.aliases.push(type_alias_fqp); } else { diff --git a/src/librustdoc/html/url_parts_builder.rs b/src/librustdoc/html/url_parts_builder.rs index 9a53382744154..705fa498e8d36 100644 --- a/src/librustdoc/html/url_parts_builder.rs +++ b/src/librustdoc/html/url_parts_builder.rs @@ -117,7 +117,7 @@ impl UrlPartsBuilder { /// This is just a guess at the average length of a URL part, /// used for [`String::with_capacity`] calls in the [`FromIterator`] -/// and [`Extend`] impls, and for [estimating item path lengths]. +/// and [`Extend`] impls. /// /// The value `8` was chosen for two main reasons: /// @@ -125,18 +125,8 @@ impl UrlPartsBuilder { /// * jemalloc's size classes are all multiples of eight, /// which means that the amount of memory it allocates will often match /// the amount requested, avoiding wasted bytes. -/// -/// [estimating item path lengths]: estimate_item_path_byte_length const AVG_PART_LENGTH: usize = 8; -/// Estimate the number of bytes in an item's path, based on how many segments it has. -/// -/// **Note:** This is only to be used with, e.g., [`String::with_capacity()`]; -/// the return value is just a rough estimate. -pub(crate) const fn estimate_item_path_byte_length(segment_count: usize) -> usize { - AVG_PART_LENGTH * segment_count -} - impl<'a> FromIterator<&'a str> for UrlPartsBuilder { fn from_iter>(iter: T) -> Self { let iter = iter.into_iter(); diff --git a/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs index 045363058d198..d664eaaac704f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs @@ -4,6 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::implements_trait; use clippy_utils::{is_path_diagnostic_item, sugg}; +use rustc_ast::join_path_idents; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{self as hir, Expr, ExprKind, GenericArg, QPath, TyKind}; @@ -47,7 +48,7 @@ fn build_full_type(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, app: &mut Applica && let QPath::Resolved(None, ty_path) = &ty_qpath && let Res::Def(_, ty_did) = ty_path.res { - let mut ty_str = itertools::join(ty_path.segments.iter().map(|s| s.ident), "::"); + let mut ty_str = join_path_idents(ty_path.segments.iter().map(|seg| seg.ident)); let mut first = true; let mut append = |arg: &str| { write!(&mut ty_str, "{}{arg}", [", ", "<"][usize::from(first)]).unwrap(); diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index ad8c816e204f8..ff1ee663f9bf2 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -89,6 +89,7 @@ use std::sync::{Mutex, MutexGuard, OnceLock}; use itertools::Itertools; use rustc_abi::Integer; +use rustc_ast::join_path_syms; use rustc_ast::ast::{self, LitKind, RangeLimits}; use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_data_structures::fx::FxHashMap; @@ -3245,8 +3246,8 @@ fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> St // a::b::c ::d::sym refers to // e::f::sym:: :: // result should be super::super::super::super::e::f - if let DefPathData::TypeNs(s) = l { - path.push(s.to_string()); + if let DefPathData::TypeNs(sym) = l { + path.push(sym); } if let DefPathData::TypeNs(_) = r { go_up_by += 1; @@ -3256,7 +3257,7 @@ fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> St // a::b::sym:: :: refers to // c::d::e ::f::sym // when looking at `f` - Left(DefPathData::TypeNs(sym)) => path.push(sym.to_string()), + Left(DefPathData::TypeNs(sym)) => path.push(sym), // consider: // a::b::c ::d::sym refers to // e::f::sym:: :: @@ -3268,17 +3269,17 @@ fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> St if go_up_by > max_super { // `super` chain would be too long, just use the absolute path instead - once(String::from("crate")) - .chain(to.data.iter().filter_map(|el| { + join_path_syms( + once(kw::Crate).chain(to.data.iter().filter_map(|el| { if let DefPathData::TypeNs(sym) = el.data { - Some(sym.to_string()) + Some(sym) } else { None } })) - .join("::") + ) } else { - repeat_n(String::from("super"), go_up_by).chain(path).join("::") + join_path_syms(repeat_n(kw::Super, go_up_by).chain(path)) } } diff --git a/tests/ui/asm/naked-invalid-attr.stderr b/tests/ui/asm/naked-invalid-attr.stderr index 915b54b3fc236..2571c8fa9896d 100644 --- a/tests/ui/asm/naked-invalid-attr.stderr +++ b/tests/ui/asm/naked-invalid-attr.stderr @@ -8,7 +8,7 @@ error[E0736]: attribute incompatible with `#[unsafe(naked)]` --> $DIR/naked-invalid-attr.rs:56:3 | LL | #[::a] - | ^^^ the `{{root}}::a` attribute is incompatible with `#[unsafe(naked)]` + | ^^^ the `::a` attribute is incompatible with `#[unsafe(naked)]` ... LL | #[unsafe(naked)] | ---------------- function marked with `#[unsafe(naked)]` here From 6414352bff4f92db7efb177c4b3263060379ca60 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 20 Jun 2025 16:59:02 +1000 Subject: [PATCH 34/34] Use `join_path_syms` in one more place. This one is a bit marginal, because the segments are a mix of symbols and strings. --- compiler/rustc_hir/src/definitions.rs | 20 +++++++------------ compiler/rustc_middle/src/ty/print/pretty.rs | 2 +- .../src/error_reporting/infer/mod.rs | 11 +++++----- 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_hir/src/definitions.rs b/compiler/rustc_hir/src/definitions.rs index f93b9e5af5345..8aec11a2e81e9 100644 --- a/compiler/rustc_hir/src/definitions.rs +++ b/compiler/rustc_hir/src/definitions.rs @@ -181,32 +181,26 @@ pub struct DisambiguatedDefPathData { } impl DisambiguatedDefPathData { - pub fn fmt_maybe_verbose(&self, writer: &mut impl Write, verbose: bool) -> fmt::Result { + pub fn as_sym(&self, verbose: bool) -> Symbol { match self.data.name() { DefPathDataName::Named(name) => { if verbose && self.disambiguator != 0 { - write!(writer, "{}#{}", name, self.disambiguator) + Symbol::intern(&format!("{}#{}", name, self.disambiguator)) } else { - writer.write_str(name.as_str()) + name } } DefPathDataName::Anon { namespace } => { if let DefPathData::AnonAssocTy(method) = self.data { - write!(writer, "{}::{{{}#{}}}", method, namespace, self.disambiguator) + Symbol::intern(&format!("{}::{{{}#{}}}", method, namespace, self.disambiguator)) } else { - write!(writer, "{{{}#{}}}", namespace, self.disambiguator) + Symbol::intern(&format!("{{{}#{}}}", namespace, self.disambiguator)) } } } } } -impl fmt::Display for DisambiguatedDefPathData { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.fmt_maybe_verbose(f, true) - } -} - #[derive(Clone, Debug, Encodable, Decodable)] pub struct DefPath { /// The path leading from the crate root to the item. @@ -250,7 +244,7 @@ impl DefPath { let mut s = String::with_capacity(self.data.len() * 16); for component in &self.data { - write!(s, "::{component}").unwrap(); + write!(s, "::{}", component.as_sym(true)).unwrap(); } s @@ -266,7 +260,7 @@ impl DefPath { for component in &self.data { s.extend(opt_delimiter); opt_delimiter = Some('-'); - write!(s, "{component}").unwrap(); + write!(s, "{}", component.as_sym(true)).unwrap(); } s diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 8f0f9b21dc163..b7088bd003af0 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -2430,7 +2430,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { } let verbose = self.should_print_verbose(); - disambiguated_data.fmt_maybe_verbose(self, verbose)?; + write!(self, "{}", disambiguated_data.as_sym(verbose))?; self.empty_path = false; diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index bc464b099e291..b9acadc406e9c 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -51,6 +51,7 @@ use std::path::PathBuf; use std::{cmp, fmt, iter}; use rustc_abi::ExternAbi; +use rustc_ast::join_path_syms; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::{ Applicability, Diag, DiagStyledString, IntoDiagArg, MultiSpan, StringPart, pluralize, @@ -73,7 +74,7 @@ use rustc_middle::ty::{ TypeVisitableExt, }; use rustc_span::def_id::LOCAL_CRATE; -use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Pos, Span, sym}; +use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Pos, Span, Symbol, sym}; use tracing::{debug, instrument}; use crate::error_reporting::TypeErrCtxt; @@ -225,7 +226,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { struct AbsolutePathPrinter<'tcx> { tcx: TyCtxt<'tcx>, - segments: Vec, + segments: Vec, } impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { @@ -253,7 +254,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> { - self.segments = vec![self.tcx.crate_name(cnum).to_string()]; + self.segments = vec![self.tcx.crate_name(cnum)]; Ok(()) } fn path_qualified( @@ -279,7 +280,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { disambiguated_data: &DisambiguatedDefPathData, ) -> Result<(), PrintError> { print_prefix(self)?; - self.segments.push(disambiguated_data.to_string()); + self.segments.push(disambiguated_data.as_sym(true)); Ok(()) } fn path_generic_args( @@ -314,7 +315,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // known" by the same name, we use the "absolute path" which uses the original // crate name instead. let (expected, found) = if expected_str == found_str { - (expected_abs.join("::"), found_abs.join("::")) + (join_path_syms(&expected_abs), join_path_syms(&found_abs)) } else { (expected_str.clone(), found_str.clone()) };