From 2f4813fa0ad1f046ac22659b136c0439787fb1e7 Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Wed, 20 Aug 2025 00:25:31 +0300 Subject: [PATCH 01/12] Stabilize `vec_deque_pop_if` --- library/alloc/src/collections/vec_deque/mod.rs | 10 ++++------ library/alloctests/tests/lib.rs | 1 - 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index ac619a42d356d..07ed72c5f2ea1 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -1831,7 +1831,6 @@ impl VecDeque { /// # Examples /// /// ``` - /// #![feature(vec_deque_pop_if)] /// use std::collections::VecDeque; /// /// let mut deque: VecDeque = vec![0, 1, 2, 3, 4].into(); @@ -1841,7 +1840,7 @@ impl VecDeque { /// assert_eq!(deque, [1, 2, 3, 4]); /// assert_eq!(deque.pop_front_if(pred), None); /// ``` - #[unstable(feature = "vec_deque_pop_if", issue = "135889")] + #[stable(feature = "vec_deque_pop_if", since = "CURRENT_RUSTC_VERSION")] pub fn pop_front_if(&mut self, predicate: impl FnOnce(&mut T) -> bool) -> Option { let first = self.front_mut()?; if predicate(first) { self.pop_front() } else { None } @@ -1854,7 +1853,6 @@ impl VecDeque { /// # Examples /// /// ``` - /// #![feature(vec_deque_pop_if)] /// use std::collections::VecDeque; /// /// let mut deque: VecDeque = vec![0, 1, 2, 3, 4].into(); @@ -1864,10 +1862,10 @@ impl VecDeque { /// assert_eq!(deque, [0, 1, 2, 3]); /// assert_eq!(deque.pop_back_if(pred), None); /// ``` - #[unstable(feature = "vec_deque_pop_if", issue = "135889")] + #[stable(feature = "vec_deque_pop_if", since = "CURRENT_RUSTC_VERSION")] pub fn pop_back_if(&mut self, predicate: impl FnOnce(&mut T) -> bool) -> Option { - let first = self.back_mut()?; - if predicate(first) { self.pop_back() } else { None } + let last = self.back_mut()?; + if predicate(last) { self.pop_back() } else { None } } /// Prepends an element to the deque. diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index f94f92397bb18..79b4622a0382e 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -37,7 +37,6 @@ #![feature(str_as_str)] #![feature(strict_provenance_lints)] #![feature(string_replace_in_place)] -#![feature(vec_deque_pop_if)] #![feature(vec_deque_truncate_front)] #![feature(unique_rc_arc)] #![feature(macro_metavar_expr_concat)] From acd029484577c064949d537894d9b2265a28cba0 Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Fri, 17 Oct 2025 12:10:51 +0000 Subject: [PATCH 02/12] btree: cleanup difference, intersection, is_subset --- library/alloc/src/collections/btree/set.rs | 179 +++++++++--------- library/alloctests/Cargo.toml | 2 +- tests/run-make/alloc-no-oom-handling/rmake.rs | 2 +- tests/run-make/alloc-no-rc/rmake.rs | 2 +- tests/run-make/alloc-no-sync/rmake.rs | 2 +- 5 files changed, 90 insertions(+), 97 deletions(-) diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index 6e6996bcbd69b..cb3e14252f8a3 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -427,39 +427,35 @@ impl BTreeSet { where T: Ord, { - let (self_min, self_max) = - if let (Some(self_min), Some(self_max)) = (self.first(), self.last()) { - (self_min, self_max) - } else { - return Difference { inner: DifferenceInner::Iterate(self.iter()) }; - }; - let (other_min, other_max) = - if let (Some(other_min), Some(other_max)) = (other.first(), other.last()) { - (other_min, other_max) - } else { - return Difference { inner: DifferenceInner::Iterate(self.iter()) }; - }; - Difference { - inner: match (self_min.cmp(other_max), self_max.cmp(other_min)) { - (Greater, _) | (_, Less) => DifferenceInner::Iterate(self.iter()), - (Equal, _) => { - let mut self_iter = self.iter(); - self_iter.next(); - DifferenceInner::Iterate(self_iter) - } - (_, Equal) => { - let mut self_iter = self.iter(); - self_iter.next_back(); - DifferenceInner::Iterate(self_iter) - } - _ if self.len() <= other.len() / ITER_PERFORMANCE_TIPPING_SIZE_DIFF => { - DifferenceInner::Search { self_iter: self.iter(), other_set: other } - } - _ => DifferenceInner::Stitch { - self_iter: self.iter(), - other_iter: other.iter().peekable(), + if let Some(self_min) = self.first() + && let Some(self_max) = self.last() + && let Some(other_min) = other.first() + && let Some(other_max) = other.last() + { + Difference { + inner: match (self_min.cmp(other_max), self_max.cmp(other_min)) { + (Greater, _) | (_, Less) => DifferenceInner::Iterate(self.iter()), + (Equal, _) => { + let mut self_iter = self.iter(); + self_iter.next(); + DifferenceInner::Iterate(self_iter) + } + (_, Equal) => { + let mut self_iter = self.iter(); + self_iter.next_back(); + DifferenceInner::Iterate(self_iter) + } + _ if self.len() <= other.len() / ITER_PERFORMANCE_TIPPING_SIZE_DIFF => { + DifferenceInner::Search { self_iter: self.iter(), other_set: other } + } + _ => DifferenceInner::Stitch { + self_iter: self.iter(), + other_iter: other.iter().peekable(), + }, }, - }, + } + } else { + Difference { inner: DifferenceInner::Iterate(self.iter()) } } } @@ -519,31 +515,27 @@ impl BTreeSet { where T: Ord, { - let (self_min, self_max) = - if let (Some(self_min), Some(self_max)) = (self.first(), self.last()) { - (self_min, self_max) - } else { - return Intersection { inner: IntersectionInner::Answer(None) }; - }; - let (other_min, other_max) = - if let (Some(other_min), Some(other_max)) = (other.first(), other.last()) { - (other_min, other_max) - } else { - return Intersection { inner: IntersectionInner::Answer(None) }; - }; - Intersection { - inner: match (self_min.cmp(other_max), self_max.cmp(other_min)) { - (Greater, _) | (_, Less) => IntersectionInner::Answer(None), - (Equal, _) => IntersectionInner::Answer(Some(self_min)), - (_, Equal) => IntersectionInner::Answer(Some(self_max)), - _ if self.len() <= other.len() / ITER_PERFORMANCE_TIPPING_SIZE_DIFF => { - IntersectionInner::Search { small_iter: self.iter(), large_set: other } - } - _ if other.len() <= self.len() / ITER_PERFORMANCE_TIPPING_SIZE_DIFF => { - IntersectionInner::Search { small_iter: other.iter(), large_set: self } - } - _ => IntersectionInner::Stitch { a: self.iter(), b: other.iter() }, - }, + if let Some(self_min) = self.first() + && let Some(self_max) = self.last() + && let Some(other_min) = other.first() + && let Some(other_max) = other.last() + { + Intersection { + inner: match (self_min.cmp(other_max), self_max.cmp(other_min)) { + (Greater, _) | (_, Less) => IntersectionInner::Answer(None), + (Equal, _) => IntersectionInner::Answer(Some(self_min)), + (_, Equal) => IntersectionInner::Answer(Some(self_max)), + _ if self.len() <= other.len() / ITER_PERFORMANCE_TIPPING_SIZE_DIFF => { + IntersectionInner::Search { small_iter: self.iter(), large_set: other } + } + _ if other.len() <= self.len() / ITER_PERFORMANCE_TIPPING_SIZE_DIFF => { + IntersectionInner::Search { small_iter: other.iter(), large_set: self } + } + _ => IntersectionInner::Stitch { a: self.iter(), b: other.iter() }, + }, + } + } else { + Intersection { inner: IntersectionInner::Answer(None) } } } @@ -694,55 +686,56 @@ impl BTreeSet { // Same result as self.difference(other).next().is_none() // but the code below is faster (hugely in some cases). if self.len() > other.len() { - return false; + return false; // self has more elements than other } - let (self_min, self_max) = - if let (Some(self_min), Some(self_max)) = (self.first(), self.last()) { - (self_min, self_max) - } else { - return true; // self is empty - }; - let (other_min, other_max) = - if let (Some(other_min), Some(other_max)) = (other.first(), other.last()) { - (other_min, other_max) - } else { - return false; // other is empty - }; + let (Some(self_min), Some(self_max)) = (self.first(), self.last()) else { + return true; // self is empty + }; + let (Some(other_min), Some(other_max)) = (other.first(), other.last()) else { + return false; // other is empty + }; let mut self_iter = self.iter(); match self_min.cmp(other_min) { - Less => return false, + Less => return false, // other does not contain self_min Equal => { - self_iter.next(); + self_iter.next(); // self_min is contained in other, so remove it from consideration + // other_min is now not in self_iter (used below) } - Greater => (), - } + Greater => {} // other_min is not in self_iter (used below) + }; + match self_max.cmp(other_max) { - Greater => return false, + Greater => return false, // other does not contain self_max Equal => { - self_iter.next_back(); + self_iter.next_back(); // self_max is contained in other, so remove it from consideration + // other_max is now not in self_iter (used below) } - Less => (), - } + Less => {} // other_max is not in self_iter (used below) + }; if self_iter.len() <= other.len() / ITER_PERFORMANCE_TIPPING_SIZE_DIFF { - for next in self_iter { - if !other.contains(next) { - return false; - } - } + self_iter.all(|e| other.contains(e)) } else { let mut other_iter = other.iter(); - other_iter.next(); - other_iter.next_back(); - let mut self_next = self_iter.next(); - while let Some(self1) = self_next { - match other_iter.next().map_or(Less, |other1| self1.cmp(other1)) { - Less => return false, - Equal => self_next = self_iter.next(), - Greater => (), - } + { + // remove other_min and other_max as they are not in self_iter (see above) + other_iter.next(); + other_iter.next_back(); } + // custom `self_iter.all(|e| other.contains(e))` + self_iter.all(|self1| { + while let Some(other1) = other_iter.next() { + match other1.cmp(self1) { + // happens up to `ITER_PERFORMANCE_TIPPING_SIZE_DIFF * self.len() - 1` times + Less => continue, // skip over elements that are smaller + // happens `self.len()` times + Equal => return true, // self1 is in other + // happens only once + Greater => return false, // self1 is not in other + } + } + false + }) } - true } /// Returns `true` if the set is a superset of another, diff --git a/library/alloctests/Cargo.toml b/library/alloctests/Cargo.toml index 07c45d1b82484..3b522bf80a217 100644 --- a/library/alloctests/Cargo.toml +++ b/library/alloctests/Cargo.toml @@ -6,7 +6,7 @@ repository = "https://github.com/rust-lang/rust.git" description = "Tests for the Rust Allocation Library" autotests = false autobenches = false -edition = "2021" +edition = "2024" [lib] path = "lib.rs" diff --git a/tests/run-make/alloc-no-oom-handling/rmake.rs b/tests/run-make/alloc-no-oom-handling/rmake.rs index 89a6636d9a0cc..94002eca5736f 100644 --- a/tests/run-make/alloc-no-oom-handling/rmake.rs +++ b/tests/run-make/alloc-no-oom-handling/rmake.rs @@ -6,7 +6,7 @@ use run_make_support::{rustc, source_root}; fn main() { rustc() - .edition("2021") + .edition("2024") .arg("-Dwarnings") .crate_type("rlib") .input(source_root().join("library/alloc/src/lib.rs")) diff --git a/tests/run-make/alloc-no-rc/rmake.rs b/tests/run-make/alloc-no-rc/rmake.rs index 12171c2148f1d..8d8cd4990bc65 100644 --- a/tests/run-make/alloc-no-rc/rmake.rs +++ b/tests/run-make/alloc-no-rc/rmake.rs @@ -6,7 +6,7 @@ use run_make_support::{rustc, source_root}; fn main() { rustc() - .edition("2021") + .edition("2024") .arg("-Dwarnings") .crate_type("rlib") .input(source_root().join("library/alloc/src/lib.rs")) diff --git a/tests/run-make/alloc-no-sync/rmake.rs b/tests/run-make/alloc-no-sync/rmake.rs index 29f204f30673e..a096d3941d466 100644 --- a/tests/run-make/alloc-no-sync/rmake.rs +++ b/tests/run-make/alloc-no-sync/rmake.rs @@ -6,7 +6,7 @@ use run_make_support::{rustc, source_root}; fn main() { rustc() - .edition("2021") + .edition("2024") .arg("-Dwarnings") .crate_type("rlib") .input(source_root().join("library/alloc/src/lib.rs")) From 360b38cceb1d20cffb00057be2078cdf7fa0b25a Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Sun, 31 Aug 2025 19:49:40 -0700 Subject: [PATCH 03/12] Fix device code generation, to account for an implicit dyn_ptr argument. --- compiler/rustc_codegen_llvm/src/back/lto.rs | 3 +- compiler/rustc_codegen_llvm/src/back/write.rs | 70 ++++++++++++++++++- .../src/builder/gpu_offload.rs | 3 + compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 6 ++ compiler/rustc_codegen_llvm/src/type_.rs | 5 ++ compiler/rustc_codegen_ssa/src/back/write.rs | 2 + .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 24 +++++++ compiler/rustc_target/src/callconv/mod.rs | 1 + compiler/rustc_target/src/spec/json.rs | 3 + compiler/rustc_target/src/spec/mod.rs | 8 +++ .../src/spec/targets/amdgcn_amd_amdhsa.rs | 3 + .../src/spec/targets/nvptx64_nvidia_cuda.rs | 3 + 12 files changed, 129 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 02b50fa8a6971..b820b992105fd 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -616,7 +616,8 @@ pub(crate) fn run_pass_manager( write::llvm_optimize(cgcx, dcx, module, None, config, opt_level, opt_stage, stage); } - if enable_gpu && !thin { + // Here we only handle the GPU host (=cpu) code. + if enable_gpu && !thin && !cgcx.target_is_like_gpu { let cx = SimpleCx::new(module.module_llvm.llmod(), &module.module_llvm.llcx, cgcx.pointer_size); crate::builder::gpu_offload::handle_gpu_code(cgcx, &cx); diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index b582d587d9f8a..5b71d6b6ba8e0 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -43,7 +43,7 @@ use crate::errors::{ use crate::llvm::diagnostic::OptimizationDiagnosticKind::*; use crate::llvm::{self, DiagnosticInfo}; use crate::type_::llvm_type_ptr; -use crate::{LlvmCodegenBackend, ModuleLlvm, base, common, llvm_util}; +use crate::{LlvmCodegenBackend, ModuleLlvm, SimpleCx, base, common, llvm_util}; pub(crate) fn llvm_err<'a>(dcx: DiagCtxtHandle<'_>, err: LlvmError<'a>) -> ! { match llvm::last_error() { @@ -645,6 +645,74 @@ pub(crate) unsafe fn llvm_optimize( None }; + fn handle_offload<'ll>(cx: &'ll SimpleCx<'_>, old_fn: &llvm::Value) { + let old_fn_ty = cx.get_type_of_global(old_fn); + let old_param_types = cx.func_params_types(old_fn_ty); + let old_param_count = old_param_types.len(); + if old_param_count == 0 { + return; + } + + let first_param = llvm::get_param(old_fn, 0); + let c_name = llvm::get_value_name(first_param); + let first_arg_name = str::from_utf8(&c_name).unwrap(); + // We might call llvm_optimize (and thus this code) multiple times on the same IR, + // but we shouldn't add this helper ptr multiple times. + // FIXME(offload): This could break if the user calls his first argument `dyn_ptr`. + if first_arg_name == "dyn_ptr" { + return; + } + + // Create the new parameter list, with ptr as the first argument + let mut new_param_types = Vec::with_capacity(old_param_count as usize + 1); + new_param_types.push(cx.type_ptr()); + new_param_types.extend(old_param_types); + + // Create the new function type + let ret_ty = unsafe { llvm::LLVMGetReturnType(old_fn_ty) }; + let new_fn_ty = cx.type_func(&new_param_types, ret_ty); + + // Create the new function, with a temporary .offload name to avoid a name collision. + let old_fn_name = String::from_utf8(llvm::get_value_name(old_fn)).unwrap(); + let new_fn_name = format!("{}.offload", &old_fn_name); + let new_fn = cx.add_func(&new_fn_name, new_fn_ty); + let a0 = llvm::get_param(new_fn, 0); + llvm::set_value_name(a0, CString::new("dyn_ptr").unwrap().as_bytes()); + + // Here we map the old arguments to the new arguments, with an offset of 1 to make sure + // that we don't use the newly added `%dyn_ptr`. + unsafe { + llvm::LLVMRustOffloadMapper(cx.llmod(), old_fn, new_fn); + } + + llvm::set_linkage(new_fn, llvm::get_linkage(old_fn)); + llvm::set_visibility(new_fn, llvm::get_visibility(old_fn)); + + // Replace all uses of old_fn with new_fn (RAUW) + unsafe { + llvm::LLVMReplaceAllUsesWith(old_fn, new_fn); + } + let name = llvm::get_value_name(old_fn); + unsafe { + llvm::LLVMDeleteFunction(old_fn); + } + // Now we can re-use the old name, without name collision. + llvm::set_value_name(new_fn, &name); + } + + if cgcx.target_is_like_gpu && config.offload.contains(&config::Offload::Enable) { + let cx = + SimpleCx::new(module.module_llvm.llmod(), module.module_llvm.llcx, cgcx.pointer_size); + // For now we only support up to 10 kernels named kernel_0 ... kernel_9, a follow-up PR is + // introducing a proper offload intrinsic to solve this limitation. + for num in 0..9 { + let name = format!("kernel_{num}"); + if let Some(kernel) = cx.get_function(&name) { + handle_offload(&cx, kernel); + } + } + } + let mut llvm_profiler = cgcx .prof .llvm_recording_enabled() diff --git a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs index 3d55064ea1304..5c2f8f700627e 100644 --- a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs +++ b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs @@ -19,6 +19,9 @@ pub(crate) fn handle_gpu_code<'ll>( let mut memtransfer_types = vec![]; let mut region_ids = vec![]; let offload_entry_ty = TgtOffloadEntry::new_decl(&cx); + // This is a temporary hack, we only search for kernel_0 to kernel_9 functions. + // There is a draft PR in progress which will introduce a proper offload intrinsic to remove + // this limitation. for num in 0..9 { let kernel = cx.get_function(&format!("kernel_{num}")); if let Some(kernel) = kernel { diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 9a391d57d6fb4..74d268ad5dd2e 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1127,6 +1127,11 @@ unsafe extern "C" { // Operations on functions pub(crate) fn LLVMSetFunctionCallConv(Fn: &Value, CC: c_uint); + pub(crate) fn LLVMAddFunction<'a>( + Mod: &'a Module, + Name: *const c_char, + FunctionTy: &'a Type, + ) -> &'a Value; pub(crate) fn LLVMDeleteFunction(Fn: &Value); // Operations about llvm intrinsics @@ -2017,6 +2022,7 @@ unsafe extern "C" { ) -> &Attribute; // Operations on functions + pub(crate) fn LLVMRustOffloadMapper<'a>(M: &'a Module, Fn: &'a Value, Fn: &'a Value); pub(crate) fn LLVMRustGetOrInsertFunction<'a>( M: &'a Module, Name: *const c_char, diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index 81bb70c958790..55f053f4fad3f 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -68,6 +68,11 @@ impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { unsafe { llvm::LLVMVectorType(ty, len as c_uint) } } + pub(crate) fn add_func(&self, name: &str, ty: &'ll Type) -> &'ll Value { + let name = SmallCStr::new(name); + unsafe { llvm::LLVMAddFunction(self.llmod(), name.as_ptr(), ty) } + } + pub(crate) fn func_params_types(&self, ty: &'ll Type) -> Vec<&'ll Type> { unsafe { let n_args = llvm::LLVMCountParamTypes(ty) as usize; diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 368a2e307bb27..edaf65bdb9222 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -342,6 +342,7 @@ pub struct CodegenContext { pub target_arch: String, pub target_is_like_darwin: bool, pub target_is_like_aix: bool, + pub target_is_like_gpu: bool, pub split_debuginfo: rustc_target::spec::SplitDebuginfo, pub split_dwarf_kind: rustc_session::config::SplitDwarfKind, pub pointer_size: Size, @@ -1309,6 +1310,7 @@ fn start_executing_work( target_arch: tcx.sess.target.arch.to_string(), target_is_like_darwin: tcx.sess.target.is_like_darwin, target_is_like_aix: tcx.sess.target.is_like_aix, + target_is_like_gpu: tcx.sess.target.is_like_gpu, split_debuginfo: tcx.sess.split_debuginfo(), split_dwarf_kind: tcx.sess.opts.unstable_opts.split_dwarf_kind, parallel: backend.supports_parallel() && !sess.opts.unstable_opts.no_parallel_backend, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 2d87ea232eea2..df811ddd8d4fc 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -35,6 +35,8 @@ #include "llvm/Support/Signals.h" #include "llvm/Support/Timer.h" #include "llvm/Support/ToolOutputFile.h" +#include "llvm/Transforms/Utils/Cloning.h" +#include "llvm/Transforms/Utils/ValueMapper.h" #include // for raw `write` in the bad-alloc handler @@ -142,6 +144,28 @@ extern "C" void LLVMRustPrintStatistics(RustStringRef OutBuf) { llvm::PrintStatistics(OS); } +extern "C" void LLVMRustOffloadMapper(LLVMModuleRef M, LLVMValueRef OldFn, + LLVMValueRef NewFn) { + llvm::Module *module = llvm::unwrap(M); + llvm::Function *oldFn = llvm::unwrap(OldFn); + llvm::Function *newFn = llvm::unwrap(NewFn); + + // Map old arguments to new arguments. We skip the first dyn_ptr argument, + // since it can't be used directly by user code. + llvm::ValueToValueMapTy vmap; + auto newArgIt = newFn->arg_begin(); + newArgIt->setName("dyn_ptr"); + ++newArgIt; // skip %dyn_ptr + for (auto &oldArg : oldFn->args()) { + vmap[&oldArg] = &*newArgIt++; + } + + llvm::SmallVector returns; + llvm::CloneFunctionInto(newFn, oldFn, vmap, + llvm::CloneFunctionChangeType::LocalChangesOnly, + returns); +} + extern "C" LLVMValueRef LLVMRustGetNamedValue(LLVMModuleRef M, const char *Name, size_t NameLen) { return wrap(unwrap(M)->getNamedValue(StringRef(Name, NameLen))); diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index 43e1ca3ef9cee..147b17b24bb57 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -578,6 +578,7 @@ impl RiscvInterruptKind { /// /// The signature represented by this type may not match the MIR function signature. /// Certain attributes, like `#[track_caller]` can introduce additional arguments, which are present in [`FnAbi`], but not in `FnSig`. +/// The std::offload module also adds an addition dyn_ptr argument to the GpuKernel ABI. /// While this difference is rarely relevant, it should still be kept in mind. /// /// I will do my best to describe this structure, but these diff --git a/compiler/rustc_target/src/spec/json.rs b/compiler/rustc_target/src/spec/json.rs index c25628c3939db..563ba0c4131ae 100644 --- a/compiler/rustc_target/src/spec/json.rs +++ b/compiler/rustc_target/src/spec/json.rs @@ -147,6 +147,7 @@ impl Target { forward!(is_like_darwin); forward!(is_like_solaris); forward!(is_like_windows); + forward!(is_like_gpu); forward!(is_like_msvc); forward!(is_like_wasm); forward!(is_like_android); @@ -337,6 +338,7 @@ impl ToJson for Target { target_option_val!(is_like_darwin); target_option_val!(is_like_solaris); target_option_val!(is_like_windows); + target_option_val!(is_like_gpu); target_option_val!(is_like_msvc); target_option_val!(is_like_wasm); target_option_val!(is_like_android); @@ -556,6 +558,7 @@ struct TargetSpecJson { is_like_darwin: Option, is_like_solaris: Option, is_like_windows: Option, + is_like_gpu: Option, is_like_msvc: Option, is_like_wasm: Option, is_like_android: Option, diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 74048d351802a..5d8ef47efe31b 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -2180,6 +2180,8 @@ pub struct TargetOptions { /// Also indicates whether to use Apple-specific ABI changes, such as extending function /// parameters to 32-bits. pub is_like_darwin: bool, + /// Whether the target is a GPU (e.g. NVIDIA, AMD, Intel). + pub is_like_gpu: bool, /// Whether the target toolchain is like Solaris's. /// Only useful for compiling against Illumos/Solaris, /// as they have a different set of linker flags. Defaults to false. @@ -2583,6 +2585,7 @@ impl Default for TargetOptions { abi_return_struct_as_int: false, is_like_aix: false, is_like_darwin: false, + is_like_gpu: false, is_like_solaris: false, is_like_windows: false, is_like_msvc: false, @@ -2748,6 +2751,11 @@ impl Target { self.os == "solaris" || self.os == "illumos", "`is_like_solaris` must be set if and only if `os` is `solaris` or `illumos`" ); + check_eq!( + self.is_like_gpu, + self.arch == Arch::Nvptx64 || self.arch == Arch::AmdGpu, + "`is_like_gpu` must be set if and only if `target` is `nvptx64` or `amdgcn`" + ); check_eq!( self.is_like_windows, self.os == "windows" || self.os == "uefi" || self.os == "cygwin", diff --git a/compiler/rustc_target/src/spec/targets/amdgcn_amd_amdhsa.rs b/compiler/rustc_target/src/spec/targets/amdgcn_amd_amdhsa.rs index 07772c7573377..d80a3ffd0c7fd 100644 --- a/compiler/rustc_target/src/spec/targets/amdgcn_amd_amdhsa.rs +++ b/compiler/rustc_target/src/spec/targets/amdgcn_amd_amdhsa.rs @@ -34,6 +34,9 @@ pub(crate) fn target() -> Target { no_builtins: true, simd_types_indirect: false, + // Clearly a GPU + is_like_gpu: true, + // Allow `cdylib` crate type. dynamic_linking: true, only_cdylib: true, diff --git a/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs b/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs index ac2d31a0d61aa..5bbf40b5fadd7 100644 --- a/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs +++ b/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs @@ -42,6 +42,9 @@ pub(crate) fn target() -> Target { // Let the `ptx-linker` to handle LLVM lowering into MC / assembly. obj_is_bitcode: true, + // Clearly a GPU + is_like_gpu: true, + // Convenient and predicable naming scheme. dll_prefix: "".into(), dll_suffix: ".ptx".into(), From ff5440e3fa03ca14787d76cd835f84c1d138ae95 Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 6 Nov 2025 17:34:48 +0800 Subject: [PATCH 04/12] Fix incorrect precedence caused by range expression --- compiler/rustc_hir_typeck/src/expr.rs | 8 ++++++++ .../feature-gate-new_range.stderr | 12 ++++++------ .../into-convert-range-issue-148344.fixed | 15 +++++++++++++++ .../into-convert-range-issue-148344.rs | 15 +++++++++++++++ .../into-convert-range-issue-148344.stderr | 18 ++++++++++++++++++ 5 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 tests/ui/suggestions/into-convert-range-issue-148344.fixed create mode 100644 tests/ui/suggestions/into-convert-range-issue-148344.rs create mode 100644 tests/ui/suggestions/into-convert-range-issue-148344.stderr diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 78b16ffee81cf..e533ee78cc824 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -77,6 +77,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } false }; + + // Special case: range expressions are desugared to struct literals in HIR, + // so they would normally return `Unambiguous` precedence in expr.precedence. + // we should return `Range` precedence for correct parenthesization in suggestions. + if is_range_literal(expr) { + return ExprPrecedence::Range; + } + expr.precedence(&has_attr) } diff --git a/tests/ui/feature-gates/feature-gate-new_range.stderr b/tests/ui/feature-gates/feature-gate-new_range.stderr index c4241390418b6..b7f70d30bfa0b 100644 --- a/tests/ui/feature-gates/feature-gate-new_range.stderr +++ b/tests/ui/feature-gates/feature-gate-new_range.stderr @@ -10,8 +10,8 @@ LL | let a: core::range::RangeFrom = 1..; found struct `std::ops::RangeFrom<{integer}>` help: call `Into::into` on this expression to convert `std::ops::RangeFrom<{integer}>` into `std::range::RangeFrom` | -LL | let a: core::range::RangeFrom = 1...into(); - | +++++++ +LL | let a: core::range::RangeFrom = (1..).into(); + | + ++++++++ error[E0308]: mismatched types --> $DIR/feature-gate-new_range.rs:6:37 @@ -25,8 +25,8 @@ LL | let b: core::range::Range = 2..3; found struct `std::ops::Range<{integer}>` help: call `Into::into` on this expression to convert `std::ops::Range<{integer}>` into `std::range::Range` | -LL | let b: core::range::Range = 2..3.into(); - | +++++++ +LL | let b: core::range::Range = (2..3).into(); + | + ++++++++ error[E0308]: mismatched types --> $DIR/feature-gate-new_range.rs:8:46 @@ -40,8 +40,8 @@ LL | let c: core::range::RangeInclusive = 4..=5; found struct `std::ops::RangeInclusive<{integer}>` help: call `Into::into` on this expression to convert `std::ops::RangeInclusive<{integer}>` into `std::range::RangeInclusive` | -LL | let c: core::range::RangeInclusive = 4..=5.into(); - | +++++++ +LL | let c: core::range::RangeInclusive = (4..=5).into(); + | + ++++++++ error: aborting due to 3 previous errors diff --git a/tests/ui/suggestions/into-convert-range-issue-148344.fixed b/tests/ui/suggestions/into-convert-range-issue-148344.fixed new file mode 100644 index 0000000000000..5bf0e8e3063bf --- /dev/null +++ b/tests/ui/suggestions/into-convert-range-issue-148344.fixed @@ -0,0 +1,15 @@ +//@ run-rustfix +use std::ops::Range; + +struct Strange; +impl From> for Strange { + fn from(_: Range) -> Self { + Self + } +} + +fn main() { + let _: Strange = (0..10).into(); + //~^ ERROR mismatched types + //~| HELP call `Into::into` on this expression +} diff --git a/tests/ui/suggestions/into-convert-range-issue-148344.rs b/tests/ui/suggestions/into-convert-range-issue-148344.rs new file mode 100644 index 0000000000000..aa1df2d4103ac --- /dev/null +++ b/tests/ui/suggestions/into-convert-range-issue-148344.rs @@ -0,0 +1,15 @@ +//@ run-rustfix +use std::ops::Range; + +struct Strange; +impl From> for Strange { + fn from(_: Range) -> Self { + Self + } +} + +fn main() { + let _: Strange = 0..10; + //~^ ERROR mismatched types + //~| HELP call `Into::into` on this expression +} diff --git a/tests/ui/suggestions/into-convert-range-issue-148344.stderr b/tests/ui/suggestions/into-convert-range-issue-148344.stderr new file mode 100644 index 0000000000000..ce5342f5789b9 --- /dev/null +++ b/tests/ui/suggestions/into-convert-range-issue-148344.stderr @@ -0,0 +1,18 @@ +error[E0308]: mismatched types + --> $DIR/into-convert-range-issue-148344.rs:12:22 + | +LL | let _: Strange = 0..10; + | ------- ^^^^^ expected `Strange`, found `Range<{integer}>` + | | + | expected due to this + | + = note: expected struct `Strange` + found struct `std::ops::Range<{integer}>` +help: call `Into::into` on this expression to convert `std::ops::Range<{integer}>` into `Strange` + | +LL | let _: Strange = (0..10).into(); + | + ++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. From d956fa10ce7cf10eeb5faa523f0f740dcc1f3766 Mon Sep 17 00:00:00 2001 From: Shun Sakai Date: Wed, 5 Nov 2025 19:19:36 +0900 Subject: [PATCH 05/12] style: Update doctests for `highest_one` and `lowest_one` Use binary literals instead of hex literals. --- library/core/src/num/int_macros.rs | 16 ++++++++-------- library/core/src/num/nonzero.rs | 12 ++++++------ library/core/src/num/uint_macros.rs | 16 ++++++++-------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 16f85c71403ab..7d395eb780346 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -217,10 +217,10 @@ macro_rules! int_impl { /// ``` /// #![feature(int_lowest_highest_one)] /// - #[doc = concat!("assert_eq!(0x0_", stringify!($SelfT), ".highest_one(), None);")] - #[doc = concat!("assert_eq!(0x1_", stringify!($SelfT), ".highest_one(), Some(0));")] - #[doc = concat!("assert_eq!(0x10_", stringify!($SelfT), ".highest_one(), Some(4));")] - #[doc = concat!("assert_eq!(0x1f_", stringify!($SelfT), ".highest_one(), Some(4));")] + #[doc = concat!("assert_eq!(0b0_", stringify!($SelfT), ".highest_one(), None);")] + #[doc = concat!("assert_eq!(0b1_", stringify!($SelfT), ".highest_one(), Some(0));")] + #[doc = concat!("assert_eq!(0b1_0000_", stringify!($SelfT), ".highest_one(), Some(4));")] + #[doc = concat!("assert_eq!(0b1_1111_", stringify!($SelfT), ".highest_one(), Some(4));")] /// ``` #[unstable(feature = "int_lowest_highest_one", issue = "145203")] #[must_use = "this returns the result of the operation, \ @@ -238,10 +238,10 @@ macro_rules! int_impl { /// ``` /// #![feature(int_lowest_highest_one)] /// - #[doc = concat!("assert_eq!(0x0_", stringify!($SelfT), ".lowest_one(), None);")] - #[doc = concat!("assert_eq!(0x1_", stringify!($SelfT), ".lowest_one(), Some(0));")] - #[doc = concat!("assert_eq!(0x10_", stringify!($SelfT), ".lowest_one(), Some(4));")] - #[doc = concat!("assert_eq!(0x1f_", stringify!($SelfT), ".lowest_one(), Some(0));")] + #[doc = concat!("assert_eq!(0b0_", stringify!($SelfT), ".lowest_one(), None);")] + #[doc = concat!("assert_eq!(0b1_", stringify!($SelfT), ".lowest_one(), Some(0));")] + #[doc = concat!("assert_eq!(0b1_0000_", stringify!($SelfT), ".lowest_one(), Some(4));")] + #[doc = concat!("assert_eq!(0b1_1111_", stringify!($SelfT), ".lowest_one(), Some(0));")] /// ``` #[unstable(feature = "int_lowest_highest_one", issue = "145203")] #[must_use = "this returns the result of the operation, \ diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index efb0665b7f461..92bca0eebfd93 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -708,9 +708,9 @@ macro_rules! nonzero_integer { /// # use core::num::NonZero; /// # fn main() { test().unwrap(); } /// # fn test() -> Option<()> { - #[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::new(0x1)?.highest_one(), 0);")] - #[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::new(0x10)?.highest_one(), 4);")] - #[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::new(0x1f)?.highest_one(), 4);")] + #[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::new(0b1)?.highest_one(), 0);")] + #[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::new(0b1_0000)?.highest_one(), 4);")] + #[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::new(0b1_1111)?.highest_one(), 4);")] /// # Some(()) /// # } /// ``` @@ -732,9 +732,9 @@ macro_rules! nonzero_integer { /// # use core::num::NonZero; /// # fn main() { test().unwrap(); } /// # fn test() -> Option<()> { - #[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::new(0x1)?.lowest_one(), 0);")] - #[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::new(0x10)?.lowest_one(), 4);")] - #[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::new(0x1f)?.lowest_one(), 0);")] + #[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::new(0b1)?.lowest_one(), 0);")] + #[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::new(0b1_0000)?.lowest_one(), 4);")] + #[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::new(0b1_1111)?.lowest_one(), 0);")] /// # Some(()) /// # } /// ``` diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 1efc551d670ab..2996e7b00da4e 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -272,10 +272,10 @@ macro_rules! uint_impl { /// ``` /// #![feature(int_lowest_highest_one)] /// - #[doc = concat!("assert_eq!(0x0_", stringify!($SelfT), ".highest_one(), None);")] - #[doc = concat!("assert_eq!(0x1_", stringify!($SelfT), ".highest_one(), Some(0));")] - #[doc = concat!("assert_eq!(0x10_", stringify!($SelfT), ".highest_one(), Some(4));")] - #[doc = concat!("assert_eq!(0x1f_", stringify!($SelfT), ".highest_one(), Some(4));")] + #[doc = concat!("assert_eq!(0b0_", stringify!($SelfT), ".highest_one(), None);")] + #[doc = concat!("assert_eq!(0b1_", stringify!($SelfT), ".highest_one(), Some(0));")] + #[doc = concat!("assert_eq!(0b1_0000_", stringify!($SelfT), ".highest_one(), Some(4));")] + #[doc = concat!("assert_eq!(0b1_1111_", stringify!($SelfT), ".highest_one(), Some(4));")] /// ``` #[unstable(feature = "int_lowest_highest_one", issue = "145203")] #[must_use = "this returns the result of the operation, \ @@ -296,10 +296,10 @@ macro_rules! uint_impl { /// ``` /// #![feature(int_lowest_highest_one)] /// - #[doc = concat!("assert_eq!(0x0_", stringify!($SelfT), ".lowest_one(), None);")] - #[doc = concat!("assert_eq!(0x1_", stringify!($SelfT), ".lowest_one(), Some(0));")] - #[doc = concat!("assert_eq!(0x10_", stringify!($SelfT), ".lowest_one(), Some(4));")] - #[doc = concat!("assert_eq!(0x1f_", stringify!($SelfT), ".lowest_one(), Some(0));")] + #[doc = concat!("assert_eq!(0b0_", stringify!($SelfT), ".lowest_one(), None);")] + #[doc = concat!("assert_eq!(0b1_", stringify!($SelfT), ".lowest_one(), Some(0));")] + #[doc = concat!("assert_eq!(0b1_0000_", stringify!($SelfT), ".lowest_one(), Some(4));")] + #[doc = concat!("assert_eq!(0b1_1111_", stringify!($SelfT), ".lowest_one(), Some(0));")] /// ``` #[unstable(feature = "int_lowest_highest_one", issue = "145203")] #[must_use = "this returns the result of the operation, \ From 72cec248e86458a22d0423aeca0a0becbeb47a01 Mon Sep 17 00:00:00 2001 From: Tom Fryers Date: Thu, 6 Nov 2025 10:59:46 +0000 Subject: [PATCH 06/12] Fix mismatched brackets in generated .dir-locals.el This caused Emacs to throw errors when opening files. Introduced in 3fe3edbcdee. --- src/bootstrap/src/core/build_steps/setup.rs | 2 +- src/etc/rust_analyzer_eglot.el | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs index 4b0080f1c80a4..380c774b143c6 100644 --- a/src/bootstrap/src/core/build_steps/setup.rs +++ b/src/bootstrap/src/core/build_steps/setup.rs @@ -587,7 +587,7 @@ Select which editor you would like to set up [default: None]: "; "631c837b0e98ae35fd48b0e5f743b1ca60adadf2d0a2b23566ba25df372cf1a9", "080955765db84bb6cbf178879f489c4e2369397626a6ecb3debedb94a9d0b3ce", "f501475c6654187091c924ae26187fa5791d74d4a8ab3fb61fbbe4c0275aade1", - "e260553b71e4773c30a63c4b23b42b279fc73e72f95b775c47b7b7c511c51595", + "54bc48fe1996177f5eef86d7231b33978e6d8b737cb0a899e622b7e975c95308", ], EditorKind::Helix => &[ "2d3069b8cf1b977e5d4023965eb6199597755e6c96c185ed5f2854f98b83d233", diff --git a/src/etc/rust_analyzer_eglot.el b/src/etc/rust_analyzer_eglot.el index e5abf67235a5d..d33760007c325 100644 --- a/src/etc/rust_analyzer_eglot.el +++ b/src/etc/rust_analyzer_eglot.el @@ -28,7 +28,7 @@ "--build-dir" "build-rust-analyzer" "--json-output" - "--compile-time-deps"])] + "--compile-time-deps"]) :sysrootSrc "./library" :extraEnv (:RUSTC_BOOTSTRAP "1")) :rustc ( :source "./Cargo.toml" ))))))) From e77e5d1bc1c23d16fce69f19ffbd06a77c8c1835 Mon Sep 17 00:00:00 2001 From: Vyacheslav <91011801+vyacheslavhere@users.noreply.github.com> Date: Thu, 6 Nov 2025 17:17:03 +0500 Subject: [PATCH 07/12] fix dev guide link in rustc_query_system/dep_graph/README.MD --- compiler/rustc_query_system/src/dep_graph/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/rustc_query_system/src/dep_graph/README.md b/compiler/rustc_query_system/src/dep_graph/README.md index b9d91cd35a8dd..3dd107f2feabe 100644 --- a/compiler/rustc_query_system/src/dep_graph/README.md +++ b/compiler/rustc_query_system/src/dep_graph/README.md @@ -1,4 +1,3 @@ -To learn more about how dependency tracking works in rustc, see the [rustc -guide]. +To learn more about how dependency tracking works in rustc, see the [rustc dev guide]. [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/query.html From 893f0d2cfff0d62ddb954ee9ec3a3f90f285ece4 Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Thu, 6 Nov 2025 15:22:30 +0100 Subject: [PATCH 08/12] core docs: add notes about availability of `Atomic*::from_mut_slice` --- library/core/src/sync/atomic.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 30a42d4eb5e64..a3ceac89ef128 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -1558,6 +1558,8 @@ impl AtomicPtr { /// Gets atomic access to a pointer. /// + /// **Note:** This function is only available on targets where `AtomicPtr` has the same alignment as `*const T` + /// /// # Examples /// /// ``` @@ -1625,6 +1627,8 @@ impl AtomicPtr { /// Gets atomic access to a slice of pointers. /// + /// **Note:** This function is only available on targets where `AtomicPtr` has the same alignment as `*const T` + /// /// # Examples /// /// ```ignore-wasm @@ -2804,6 +2808,14 @@ macro_rules! atomic_int { #[doc = concat!("Get atomic access to a `&mut [", stringify!($int_type), "]` slice.")] /// + #[doc = if_8_bit! { + $int_type, + no = [ + "**Note:** This function is only available on targets where `", + stringify!($atomic_type), "` has the same alignment as `", stringify!($int_type), "`." + ], + }] + /// /// # Examples /// /// ```ignore-wasm From 3edd25f04962dd37bd4d0f460883d0c16ff480d4 Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 6 Nov 2025 10:47:47 +0800 Subject: [PATCH 09/12] Add typo suggestion for a misspelt Cargo environment variable --- compiler/rustc_builtin_macros/messages.ftl | 1 + compiler/rustc_builtin_macros/src/env.rs | 53 +++++++++++++++++ compiler/rustc_builtin_macros/src/errors.rs | 8 +++ .../env-cargo-var-typo-issue-148439.rs | 50 ++++++++++++++++ .../env-cargo-var-typo-issue-148439.stderr | 58 +++++++++++++++++++ 5 files changed, 170 insertions(+) create mode 100644 tests/ui/env-macro/env-cargo-var-typo-issue-148439.rs create mode 100644 tests/ui/env-macro/env-cargo-var-typo-issue-148439.stderr diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 5e498bf98fb55..c9dd92ff2f243 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -156,6 +156,7 @@ builtin_macros_duplicate_macro_attribute = duplicated attribute builtin_macros_env_not_defined = environment variable `{$var}` not defined at compile time .cargo = Cargo sets build script variables at run time. Use `std::env::var({$var_expr})` instead + .cargo_typo = there is a similar Cargo environment variable: `{$suggested_var}` .custom = use `std::env::var({$var_expr})` to read the variable at run time builtin_macros_env_not_unicode = environment variable `{$var}` is not a valid Unicode string diff --git a/compiler/rustc_builtin_macros/src/env.rs b/compiler/rustc_builtin_macros/src/env.rs index f3ac932e1b7e9..af78db156a221 100644 --- a/compiler/rustc_builtin_macros/src/env.rs +++ b/compiler/rustc_builtin_macros/src/env.rs @@ -10,6 +10,7 @@ use rustc_ast::token::{self, LitKind}; use rustc_ast::tokenstream::TokenStream; use rustc_ast::{ExprKind, GenericArg, Mutability}; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult}; +use rustc_span::edit_distance::edit_distance; use rustc_span::{Ident, Span, Symbol, kw, sym}; use thin_vec::thin_vec; @@ -144,6 +145,12 @@ pub(crate) fn expand_env<'cx>( if let Some(msg_from_user) = custom_msg { cx.dcx() .emit_err(errors::EnvNotDefinedWithUserMessage { span, msg_from_user }) + } else if let Some(suggested_var) = find_similar_cargo_var(var.as_str()) { + cx.dcx().emit_err(errors::EnvNotDefined::CargoEnvVarTypo { + span, + var: *symbol, + suggested_var: Symbol::intern(suggested_var), + }) } else if is_cargo_env_var(var.as_str()) { cx.dcx().emit_err(errors::EnvNotDefined::CargoEnvVar { span, @@ -176,3 +183,49 @@ fn is_cargo_env_var(var: &str) -> bool { || var.starts_with("DEP_") || matches!(var, "OUT_DIR" | "OPT_LEVEL" | "PROFILE" | "HOST" | "TARGET") } + +const KNOWN_CARGO_VARS: &[&str] = &[ + // List of known Cargo environment variables that are set for crates (not build scripts, OUT_DIR etc). + // See: https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates + "CARGO_PKG_VERSION", + "CARGO_PKG_VERSION_MAJOR", + "CARGO_PKG_VERSION_MINOR", + "CARGO_PKG_VERSION_PATCH", + "CARGO_PKG_VERSION_PRE", + "CARGO_PKG_AUTHORS", + "CARGO_PKG_NAME", + "CARGO_PKG_DESCRIPTION", + "CARGO_PKG_HOMEPAGE", + "CARGO_PKG_REPOSITORY", + "CARGO_PKG_LICENSE", + "CARGO_PKG_LICENSE_FILE", + "CARGO_PKG_RUST_VERSION", + "CARGO_PKG_README", + "CARGO_MANIFEST_DIR", + "CARGO_MANIFEST_PATH", + "CARGO_CRATE_NAME", + "CARGO_BIN_NAME", + "CARGO_PRIMARY_PACKAGE", +]; + +fn find_similar_cargo_var(var: &str) -> Option<&'static str> { + if !var.starts_with("CARGO_") { + return None; + } + + let lookup_len = var.chars().count(); + let max_dist = std::cmp::max(lookup_len, 3) / 3; + let mut best_match = None; + let mut best_distance = usize::MAX; + + for &known_var in KNOWN_CARGO_VARS { + if let Some(distance) = edit_distance(var, known_var, max_dist) { + if distance < best_distance { + best_distance = distance; + best_match = Some(known_var); + } + } + } + + best_match +} diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index d6ffbb5a4101d..dd6a5a20ccebc 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -535,6 +535,14 @@ pub(crate) enum EnvNotDefined<'a> { var_expr: &'a rustc_ast::Expr, }, #[diag(builtin_macros_env_not_defined)] + #[help(builtin_macros_cargo_typo)] + CargoEnvVarTypo { + #[primary_span] + span: Span, + var: Symbol, + suggested_var: Symbol, + }, + #[diag(builtin_macros_env_not_defined)] #[help(builtin_macros_custom)] CustomEnvVar { #[primary_span] diff --git a/tests/ui/env-macro/env-cargo-var-typo-issue-148439.rs b/tests/ui/env-macro/env-cargo-var-typo-issue-148439.rs new file mode 100644 index 0000000000000..f859decd09ecb --- /dev/null +++ b/tests/ui/env-macro/env-cargo-var-typo-issue-148439.rs @@ -0,0 +1,50 @@ +//@ edition: 2021 + +// Regression test for issue #148439 +// Ensure that when using misspelled Cargo environment variables in env!(), + +fn test_cargo_package_version() { + let _ = env!("CARGO_PACKAGE_VERSION"); + //~^ ERROR environment variable `CARGO_PACKAGE_VERSION` not defined at compile time + //~| HELP there is a similar Cargo environment variable: `CARGO_PKG_VERSION` +} + +fn test_cargo_package_name() { + let _ = env!("CARGO_PACKAGE_NAME"); + //~^ ERROR environment variable `CARGO_PACKAGE_NAME` not defined at compile time + //~| HELP there is a similar Cargo environment variable: `CARGO_PKG_NAME` +} + +fn test_cargo_package_authors() { + let _ = env!("CARGO_PACKAGE_AUTHORS"); + //~^ ERROR environment variable `CARGO_PACKAGE_AUTHORS` not defined at compile time + //~| HELP there is a similar Cargo environment variable: `CARGO_PKG_AUTHORS` +} + +fn test_cargo_manifest_directory() { + let _ = env!("CARGO_MANIFEST_DIRECTORY"); + //~^ ERROR environment variable `CARGO_MANIFEST_DIRECTORY` not defined at compile time + //~| HELP there is a similar Cargo environment variable: `CARGO_MANIFEST_DIR` +} + +fn test_cargo_pkg_version_typo() { + let _ = env!("CARGO_PKG_VERSIO"); + //~^ ERROR environment variable `CARGO_PKG_VERSIO` not defined at compile time + //~| HELP there is a similar Cargo environment variable: `CARGO_PKG_VERSION` +} + +fn test_non_cargo_var() { + // Non-Cargo variable should get different help message + let _ = env!("MY_CUSTOM_VAR"); + //~^ ERROR environment variable `MY_CUSTOM_VAR` not defined at compile time + //~| HELP use `std::env::var("MY_CUSTOM_VAR")` to read the variable at run time +} + +fn test_cargo_unknown_var() { + // Cargo-prefixed but not similar to any known variable + let _ = env!("CARGO_SOMETHING_TOTALLY_UNKNOWN"); + //~^ ERROR environment variable `CARGO_SOMETHING_TOTALLY_UNKNOWN` not defined at compile time + //~| HELP Cargo sets build script variables at run time. Use `std::env::var("CARGO_SOMETHING_TOTALLY_UNKNOWN")` instead +} + +fn main() {} diff --git a/tests/ui/env-macro/env-cargo-var-typo-issue-148439.stderr b/tests/ui/env-macro/env-cargo-var-typo-issue-148439.stderr new file mode 100644 index 0000000000000..e16c4d9a1f4c2 --- /dev/null +++ b/tests/ui/env-macro/env-cargo-var-typo-issue-148439.stderr @@ -0,0 +1,58 @@ +error: environment variable `CARGO_PACKAGE_VERSION` not defined at compile time + --> $DIR/env-cargo-var-typo-issue-148439.rs:7:13 + | +LL | let _ = env!("CARGO_PACKAGE_VERSION"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: there is a similar Cargo environment variable: `CARGO_PKG_VERSION` + +error: environment variable `CARGO_PACKAGE_NAME` not defined at compile time + --> $DIR/env-cargo-var-typo-issue-148439.rs:13:13 + | +LL | let _ = env!("CARGO_PACKAGE_NAME"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: there is a similar Cargo environment variable: `CARGO_PKG_NAME` + +error: environment variable `CARGO_PACKAGE_AUTHORS` not defined at compile time + --> $DIR/env-cargo-var-typo-issue-148439.rs:19:13 + | +LL | let _ = env!("CARGO_PACKAGE_AUTHORS"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: there is a similar Cargo environment variable: `CARGO_PKG_AUTHORS` + +error: environment variable `CARGO_MANIFEST_DIRECTORY` not defined at compile time + --> $DIR/env-cargo-var-typo-issue-148439.rs:25:13 + | +LL | let _ = env!("CARGO_MANIFEST_DIRECTORY"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: there is a similar Cargo environment variable: `CARGO_MANIFEST_DIR` + +error: environment variable `CARGO_PKG_VERSIO` not defined at compile time + --> $DIR/env-cargo-var-typo-issue-148439.rs:31:13 + | +LL | let _ = env!("CARGO_PKG_VERSIO"); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: there is a similar Cargo environment variable: `CARGO_PKG_VERSION` + +error: environment variable `MY_CUSTOM_VAR` not defined at compile time + --> $DIR/env-cargo-var-typo-issue-148439.rs:38:13 + | +LL | let _ = env!("MY_CUSTOM_VAR"); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `std::env::var("MY_CUSTOM_VAR")` to read the variable at run time + +error: environment variable `CARGO_SOMETHING_TOTALLY_UNKNOWN` not defined at compile time + --> $DIR/env-cargo-var-typo-issue-148439.rs:45:13 + | +LL | let _ = env!("CARGO_SOMETHING_TOTALLY_UNKNOWN"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: Cargo sets build script variables at run time. Use `std::env::var("CARGO_SOMETHING_TOTALLY_UNKNOWN")` instead + +error: aborting due to 7 previous errors + From eec36b390e26a73e03b6750acbd4458e4ba38df0 Mon Sep 17 00:00:00 2001 From: Emily Albini Date: Thu, 6 Nov 2025 19:09:12 +0100 Subject: [PATCH 10/12] update release notes --- RELEASES.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index 74b0d4424c163..b14cc499b46d0 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,11 @@ +Version 1.91.1 (2025-11-10) +=========================== + + + +- [Enable file locking support in illumos](https://github.com/rust-lang/rust/pull/148322). This fixes Cargo not locking the build directory on illumos. +- [Fix `wasm_import_module` attribute cross-crate](https://github.com/rust-lang/rust/pull/148363). This fixes linker errors on WASM targets. + Version 1.91.0 (2025-10-30) ========================== From b975c570fab6e04ab81aca6db357456a2964761c Mon Sep 17 00:00:00 2001 From: Dmitry Marakasov Date: Fri, 7 Nov 2025 00:58:04 +0300 Subject: [PATCH 11/12] Sync str::rsplit_once example with str::split_once --- library/core/src/str/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 37dc401ed0098..45308c4b3e9c5 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -1953,6 +1953,7 @@ impl str { /// /// ``` /// assert_eq!("cfg".rsplit_once('='), None); + /// assert_eq!("cfg=".rsplit_once('='), Some(("cfg", ""))); /// assert_eq!("cfg=foo".rsplit_once('='), Some(("cfg", "foo"))); /// assert_eq!("cfg=foo=bar".rsplit_once('='), Some(("cfg=foo", "bar"))); /// ``` From 62ccf14c143d9141173993f3b2a468a30938617e Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Mon, 6 Oct 2025 20:44:50 +0000 Subject: [PATCH 12/12] add check if macro from expansion --- .../src/error_reporting/traits/suggestions.rs | 5 +++++ .../macros/macro-expansion-empty-span-147255.rs | 10 ++++++++++ .../macro-expansion-empty-span-147255.stderr | 15 +++++++++++++++ 3 files changed, 30 insertions(+) create mode 100644 tests/ui/macros/macro-expansion-empty-span-147255.rs create mode 100644 tests/ui/macros/macro-expansion-empty-span-147255.stderr diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 37e622102e70f..75d7d32d25f84 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -942,6 +942,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err.span_label(block.span, "this empty block is missing a tail expression"); return; }; + // FIXME expr and stmt have the same span if expr comes from expansion + // cc: https://github.com/rust-lang/rust/pull/147416#discussion_r2499407523 + if stmt.span.from_expansion() { + return; + } let hir::StmtKind::Semi(tail_expr) = stmt.kind else { return; }; diff --git a/tests/ui/macros/macro-expansion-empty-span-147255.rs b/tests/ui/macros/macro-expansion-empty-span-147255.rs new file mode 100644 index 0000000000000..311cf5bf9b1bd --- /dev/null +++ b/tests/ui/macros/macro-expansion-empty-span-147255.rs @@ -0,0 +1,10 @@ +//! Regression test for + +fn main() { + let mut x = 4; + let x_str = { + format!("{}", x); + //() + }; + println!("{}", x_str); //~ ERROR `()` doesn't implement `std::fmt::Display` +} diff --git a/tests/ui/macros/macro-expansion-empty-span-147255.stderr b/tests/ui/macros/macro-expansion-empty-span-147255.stderr new file mode 100644 index 0000000000000..99396622b34eb --- /dev/null +++ b/tests/ui/macros/macro-expansion-empty-span-147255.stderr @@ -0,0 +1,15 @@ +error[E0277]: `()` doesn't implement `std::fmt::Display` + --> $DIR/macro-expansion-empty-span-147255.rs:9:20 + | +LL | println!("{}", x_str); + | -- ^^^^^ `()` cannot be formatted with the default formatter + | | + | required by this formatting parameter + | + = help: the trait `std::fmt::Display` is not implemented for `()` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead + = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`.