From 2233d3969bc61c30d6d058dc037d83a1c5ac3641 Mon Sep 17 00:00:00 2001 From: Noratrieb <48135649+Noratrieb@users.noreply.github.com> Date: Sun, 2 Nov 2025 21:19:29 +0100 Subject: [PATCH 01/23] Mangle personality symbol Previously, std-internal symbols (like `__rust_alloc` or `rust_panic`) were changed to now be mangled via a v0 scheme with the compiler version hash (`_RNvCs4CIB29Id3dw_7___rustc10rust_panic`) to make it possible to have multiple staticlibs (as long as they have different version hashes) in the same program without symbol conflicts. But the personality function remained unmangled, because LLVM hardcodes that name. I made a change in LLVM that makes LLVM now check the suffix instead of an exact match, so we can mangle the personality function with this scheme. This removes the last completely unmangled symbol from Rust staticlibs! (though the mangling is still quite weak, so it's still not as nice as it would ideally be). Before, on a trivial staticlib: ``` readelf -s liblib.a | rg GLOBAL | rg -v " UND | HIDDEN " | rg -v ' _ZN' 8: 0000000000000000 5 FUNC GLOBAL DEFAULT 3 _RNvCseCSg29WUqSe_7___rustc12___rust_alloc 10: 0000000000000000 5 FUNC GLOBAL DEFAULT 5 _RNvCseCSg29WUqSe_7___rustc14___rust_dealloc 12: 0000000000000000 5 FUNC GLOBAL DEFAULT 7 _RNvCseCSg29WUqSe_7___rustc14___rust_realloc 14: 0000000000000000 5 FUNC GLOBAL DEFAULT 9 _RNvCseCSg29WUqSe_7___rustc19___rust_alloc_zeroed 16: 0000000000000000 3 FUNC GLOBAL DEFAULT 11 _RNvCseCSg29WUqSe_7___rustc42___rust_alloc_error_handler_should_panic_v2 17: 0000000000000000 1 FUNC GLOBAL DEFAULT 12 _RNvCseCSg29WUqSe_7___rustc35___rust_no_alloc_shim_is_unstable_v2 3383: 0000000000000000 111 FUNC GLOBAL DEFAULT 5 _RNvCseCSg29WUqSe_7___rustc10rust_panic 3387: 0000000000000000 93 FUNC GLOBAL DEFAULT 7 _RNvCseCSg29WUqSe_7___rustc11___rdl_alloc 3390: 0000000000000000 10 FUNC GLOBAL DEFAULT 9 _RNvCseCSg29WUqSe_7___rustc12___rust_abort 3391: 0000000000000000 6 FUNC GLOBAL DEFAULT 11 _RNvCseCSg29WUqSe_7___rustc13___rdl_dealloc 3393: 0000000000000000 167 FUNC GLOBAL DEFAULT 13 _RNvCseCSg29WUqSe_7___rustc13___rdl_realloc 3396: 0000000000000000 203 FUNC GLOBAL DEFAULT 15 _RNvCseCSg29WUqSe_7___rustc17___rust_drop_panic 3399: 0000000000000000 29 FUNC GLOBAL DEFAULT 18 _RNvCseCSg29WUqSe_7___rustc17rust_begin_unwind 3401: 0000000000000000 140 FUNC GLOBAL DEFAULT 20 _RNvCseCSg29WUqSe_7___rustc18___rdl_alloc_zeroed 3404: 0000000000000000 203 FUNC GLOBAL DEFAULT 22 _RNvCseCSg29WUqSe_7___rustc24___rust_foreign_exception 3405: 0000000000000000 19 FUNC GLOBAL DEFAULT 25 _RNvCseCSg29WUqSe_7___rustc26___rust_alloc_error_handler 4410: 0000000000000000 1471 FUNC GLOBAL DEFAULT 2752 rust_eh_personality 25: 0000000000000000 161 FUNC GLOBAL DEFAULT 5 _RNvCseCSg29WUqSe_7___rustc18___rust_start_panic 32: 0000000000000000 82 FUNC GLOBAL DEFAULT 8 _RNvCseCSg29WUqSe_7___rustc20___rust_panic_cleanup 249: 0000000000000000 182 FUNC GLOBAL DEFAULT 5 _RNvCseCSg29WUqSe_7___rustc25___rdl_alloc_error_handler 3: 0000000000000000 57 FUNC GLOBAL DEFAULT 3 _RNvCseCSg29WUqSe_7___rustc17___rust_probestack ``` After: ``` 8: 0000000000000000 5 FUNC GLOBAL DEFAULT 3 _RNvCs4CIB29Id3dw_7___rustc12___rust_alloc 10: 0000000000000000 5 FUNC GLOBAL DEFAULT 5 _RNvCs4CIB29Id3dw_7___rustc14___rust_dealloc 12: 0000000000000000 5 FUNC GLOBAL DEFAULT 7 _RNvCs4CIB29Id3dw_7___rustc14___rust_realloc 14: 0000000000000000 5 FUNC GLOBAL DEFAULT 9 _RNvCs4CIB29Id3dw_7___rustc19___rust_alloc_zeroed 16: 0000000000000000 3 FUNC GLOBAL DEFAULT 11 _RNvCs4CIB29Id3dw_7___rustc42___rust_alloc_error_handler_should_panic_v2 17: 0000000000000000 1 FUNC GLOBAL DEFAULT 12 _RNvCs4CIB29Id3dw_7___rustc35___rust_no_alloc_shim_is_unstable_v2 422: 0000000000000000 19 FUNC GLOBAL DEFAULT 5 _RNvCs4CIB29Id3dw_7___rustc26___rust_alloc_error_handler 429: 0000000000000000 308 FUNC GLOBAL DEFAULT 5 _RNvCs4CIB29Id3dw_7___rustc19rust_eh_personality 349: 0000000000000000 10 FUNC GLOBAL DEFAULT 5 _RNvCs4CIB29Id3dw_7___rustc12___rust_abort 164: 0000000000000000 93 FUNC GLOBAL DEFAULT 5 _RNvCs4CIB29Id3dw_7___rustc11___rdl_alloc 167: 0000000000000000 11 FUNC GLOBAL DEFAULT 7 _RNvCs4CIB29Id3dw_7___rustc13___rdl_dealloc 169: 0000000000000000 167 FUNC GLOBAL DEFAULT 9 _RNvCs4CIB29Id3dw_7___rustc13___rdl_realloc 172: 0000000000000000 140 FUNC GLOBAL DEFAULT 11 _RNvCs4CIB29Id3dw_7___rustc18___rdl_alloc_zeroed 688: 0000000000000000 106 FUNC GLOBAL DEFAULT 5 _RNvCs4CIB29Id3dw_7___rustc10rust_panic 693: 0000000000000000 207 FUNC GLOBAL DEFAULT 7 _RNvCs4CIB29Id3dw_7___rustc17___rust_drop_panic 696: 0000000000000000 29 FUNC GLOBAL DEFAULT 10 _RNvCs4CIB29Id3dw_7___rustc17rust_begin_unwind 698: 0000000000000000 207 FUNC GLOBAL DEFAULT 12 _RNvCs4CIB29Id3dw_7___rustc24___rust_foreign_exception 26: 0000000000000000 161 FUNC GLOBAL DEFAULT 5 _RNvCs4CIB29Id3dw_7___rustc18___rust_start_panic 33: 0000000000000000 82 FUNC GLOBAL DEFAULT 8 _RNvCs4CIB29Id3dw_7___rustc20___rust_panic_cleanup 88: 0000000000000000 182 FUNC GLOBAL DEFAULT 5 _RNvCs4CIB29Id3dw_7___rustc25___rdl_alloc_error_handler 3: 0000000000000000 57 FUNC GLOBAL DEFAULT 3 _RNvCs4CIB29Id3dw_7___rustc17___rust_probestack ``` Co-Authored-By: bjorn3 <17426603+bjorn3@users.noreply.github.com> --- .../src/debuginfo/unwind.rs | 6 +-- .../rustc_codegen_cranelift/src/driver/aot.rs | 6 ++- .../rustc_codegen_cranelift/src/driver/jit.rs | 6 ++- .../src/unwind_module.rs | 4 +- compiler/rustc_codegen_gcc/src/context.rs | 4 +- compiler/rustc_codegen_gcc/src/declare.rs | 2 +- compiler/rustc_codegen_gcc/src/lib.rs | 12 ++++- compiler/rustc_codegen_llvm/src/back/lto.rs | 2 +- compiler/rustc_codegen_llvm/src/context.rs | 9 +++- compiler/rustc_codegen_ssa/src/back/write.rs | 2 + compiler/rustc_symbol_mangling/src/lib.rs | 2 +- compiler/rustc_symbol_mangling/src/v0.rs | 6 ++- tests/codegen-llvm/gdb_debug_script_load.rs | 2 +- tests/run-make/no-alloc-shim/foo.rs | 3 +- tests/run-make/symbols-all-mangled/rmake.rs | 46 +++++++++---------- .../raw-dylib/elf/glibc-x86_64.rs | 3 +- .../auxiliary/panic-runtime-abort.rs | 4 +- .../auxiliary/panic-runtime-unwind.rs | 4 +- .../auxiliary/panic-runtime-unwind2.rs | 4 +- tests/ui/panic-runtime/incompatible-type.rs | 3 +- tests/ui/sanitizer/dataflow-abilist.txt | 1 - 21 files changed, 80 insertions(+), 51 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs index ad0a15bf7f6e5..18eb85289e520 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs @@ -25,7 +25,7 @@ pub(crate) struct UnwindContext { } impl UnwindContext { - pub(crate) fn new(module: &mut dyn Module, pic_eh_frame: bool) -> Self { + pub(crate) fn new(module: &mut dyn Module, eh_personality: &str, pic_eh_frame: bool) -> Self { let endian = match module.isa().endianness() { Endianness::Little => RunTimeEndian::Little, Endianness::Big => RunTimeEndian::Big, @@ -70,7 +70,7 @@ impl UnwindContext { // FIXME use eh_personality lang item instead let personality = module .declare_function( - "rust_eh_personality", + eh_personality, Linkage::Import, &Signature { params: vec![ @@ -89,7 +89,7 @@ impl UnwindContext { // Use indirection here to support PIC the case where rust_eh_personality is defined in // another DSO. let personality_ref = module - .declare_data("DW.ref.rust_eh_personality", Linkage::Local, false, false) + .declare_data(&format!("DW.ref.{eh_personality}"), Linkage::Local, false, false) .unwrap(); let mut personality_ref_data = DataDescription::new(); diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index a62dff641cfec..06a7f28a7d830 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -62,7 +62,11 @@ fn make_module(tcx: TyCtxt<'_>, cgu_name: &str) -> AotModule { tcx.sess.opts.unstable_opts.function_sections.unwrap_or(default_function_sections), ); - let module = UnwindModule::new(ObjectModule::new(builder), true); + let module = UnwindModule::new( + ObjectModule::new(builder), + &rustc_symbol_mangling::eh_personality_symbol(tcx), + true, + ); let producer = crate::debuginfo::producer(tcx.sess); let global_asm_config = GlobalAsmConfig::new(tcx.sess); diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs index 8bf1cd9bf85bc..9864cefbf9368 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs @@ -25,7 +25,11 @@ fn create_jit_module( let mut jit_builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names()); crate::compiler_builtins::register_functions_for_jit(&mut jit_builder); jit_builder.symbol_lookup_fn(dep_symbol_lookup_fn(tcx.sess, crate_info.clone())); - let mut jit_module = UnwindModule::new(JITModule::new(jit_builder), false); + let mut jit_module = UnwindModule::new( + JITModule::new(jit_builder), + &rustc_symbol_mangling::eh_personality_symbol(tcx), + false, + ); let cx = DebugContext::new(tcx, jit_module.isa(), false, "dummy_cgu_name"); diff --git a/compiler/rustc_codegen_cranelift/src/unwind_module.rs b/compiler/rustc_codegen_cranelift/src/unwind_module.rs index b4eb939cf2560..d79960f418128 100644 --- a/compiler/rustc_codegen_cranelift/src/unwind_module.rs +++ b/compiler/rustc_codegen_cranelift/src/unwind_module.rs @@ -17,8 +17,8 @@ pub(crate) struct UnwindModule { } impl UnwindModule { - pub(crate) fn new(mut module: T, pic_eh_frame: bool) -> Self { - let unwind_context = UnwindContext::new(&mut module, pic_eh_frame); + pub(crate) fn new(mut module: T, eh_personality: &str, pic_eh_frame: bool) -> Self { + let unwind_context = UnwindContext::new(&mut module, eh_personality, pic_eh_frame); UnwindModule { module, unwind_context } } } diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 5f342e8b2dc33..3607a5053bdfa 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -516,10 +516,12 @@ impl<'gcc, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { self.declare_fn(symbol_name, fn_abi) } _ => { + let rust_name; let name = if wants_msvc_seh(&self.sess().target) { "__CxxFrameHandler3" } else { - "rust_eh_personality" + rust_name = rustc_symbol_mangling::eh_personality_symbol(tcx); + &rust_name }; self.declare_func(name, self.type_i32(), &[], true) } diff --git a/compiler/rustc_codegen_gcc/src/declare.rs b/compiler/rustc_codegen_gcc/src/declare.rs index 32bb7c3aa349e..a0c73bed0c7ed 100644 --- a/compiler/rustc_codegen_gcc/src/declare.rs +++ b/compiler/rustc_codegen_gcc/src/declare.rs @@ -187,7 +187,7 @@ fn declare_raw_fn<'gcc>( cx.functions.borrow_mut().insert(name.to_string(), func); #[cfg(feature = "master")] - if name == "rust_eh_personality" { + if name.ends_with("rust_eh_personality") { // NOTE: GCC will sometimes change the personality function set on a function from // rust_eh_personality to __gcc_personality_v0 as an optimization. // As such, we need to create a weak alias from __gcc_personality_v0 to diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index b82d05dc211aa..9be81fafce7ac 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -154,6 +154,14 @@ impl GccCodegenBackend { fn config(&self) -> &BackendConfig { self.config.as_ref().expect("target info not initialized") } + + #[cfg(feature = "master")] + fn set_personality_function(&self, tcx: TyCtxt<'_>) { + let personality_symbol = rustc_symbol_mangling::eh_personality_symbol(tcx); + gccjit::set_global_personality_function_name( + &CString::new(personality_symbol).expect("symbol name shouldn't contain NUL"), + ); + } } fn load_libgccjit_if_needed(libgccjit_target_lib_file: &Path) { @@ -236,8 +244,6 @@ impl CodegenBackend for GccCodegenBackend { target_info: Arc::new(IntoDynSyncSend(context.get_target_info())), lto_supported: gccjit::is_lto_supported(), }); - - gccjit::set_global_personality_function_name(c"rust_eh_personality"); } #[cfg(not(feature = "master"))] @@ -273,6 +279,8 @@ impl CodegenBackend for GccCodegenBackend { } fn codegen_crate(&self, tcx: TyCtxt<'_>) -> Box { + #[cfg(feature = "master")] + self.set_personality_function(tcx); Box::new(codegen_crate(self.clone(), tcx)) } diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index d52e34d30fb7d..67311f1153e75 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -82,7 +82,7 @@ fn prepare_lto( symbols_below_threshold.push(c"__llvm_profile_counter_bias".to_owned()); // LTO seems to discard this otherwise under certain circumstances. - symbols_below_threshold.push(c"rust_eh_personality".to_owned()); + symbols_below_threshold.push(CString::new(cgcx.rust_eh_personality_symbol.clone()).unwrap()); // If we're performing LTO for the entire crate graph, then for each of our // upstream dependencies, find the corresponding rlib and load the bitcode diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index d64c8de6ed7aa..ffcca98acf17e 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -1031,7 +1031,14 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { tcx.sess.pointer_authentication_functions(), ), _ => { - let name = name.unwrap_or("rust_eh_personality"); + let mangled_symbol; + let name = match name { + Some(name) => name, + None => { + mangled_symbol = rustc_symbol_mangling::eh_personality_symbol(tcx); + mangled_symbol.as_str() + } + }; if let Some(llfn) = self.get_declared_value(name) { llfn } else { diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 04be7956e8278..18968f0f89960 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 split_debuginfo: rustc_target::spec::SplitDebuginfo, pub split_dwarf_kind: rustc_session::config::SplitDwarfKind, pub pointer_size: Size, + pub rust_eh_personality_symbol: String, /// LLVM optimizations for which we want to print remarks. pub remark: Passes, @@ -1284,6 +1285,7 @@ fn start_executing_work( 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, + rust_eh_personality_symbol: rustc_symbol_mangling::eh_personality_symbol(tcx), split_debuginfo: tcx.sess.split_debuginfo(), split_dwarf_kind: tcx.sess.opts.unstable_opts.split_dwarf_kind, parallel, diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index 93db093f630bd..9c465b409ca85 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -104,7 +104,7 @@ mod v0; pub mod test; -pub use v0::{mangle_cgu, mangle_internal_symbol}; +pub use v0::{eh_personality_symbol, mangle_cgu, mangle_internal_symbol}; /// Offload kernels need custom v0 symbol treatment because the host /// and device compilation passes run with different `stable_crate_id`s diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index fb9f242f60085..2b9a346eec912 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -127,10 +127,12 @@ pub fn mangle_cgu<'tcx>(tcx: TyCtxt<'tcx>, krate: CrateNum, cgu_name: Either(tcx: TyCtxt<'tcx>) -> String { + mangle_internal_symbol(tcx, "rust_eh_personality") +} + pub fn mangle_internal_symbol<'tcx>(tcx: TyCtxt<'tcx>, item_name: &str) -> String { match item_name { - // rust_eh_personality must not be renamed as LLVM hard-codes the name - "rust_eh_personality" => return item_name.to_owned(), // Apple availability symbols need to not be mangled to be usable by // C/Objective-C code. "__isPlatformVersionAtLeast" | "__isOSVersionAtLeast" => return item_name.to_owned(), diff --git a/tests/codegen-llvm/gdb_debug_script_load.rs b/tests/codegen-llvm/gdb_debug_script_load.rs index 3e92eba10b121..044d8577df6a9 100644 --- a/tests/codegen-llvm/gdb_debug_script_load.rs +++ b/tests/codegen-llvm/gdb_debug_script_load.rs @@ -14,7 +14,7 @@ fn panic_handler(_: &core::panic::PanicInfo) -> ! { loop {} } -#[no_mangle] +#[lang = "eh_personality"] extern "C" fn rust_eh_personality() { loop {} } diff --git a/tests/run-make/no-alloc-shim/foo.rs b/tests/run-make/no-alloc-shim/foo.rs index a22307f41b39e..6604574d0fa5f 100644 --- a/tests/run-make/no-alloc-shim/foo.rs +++ b/tests/run-make/no-alloc-shim/foo.rs @@ -1,3 +1,4 @@ +#![feature(lang_items)] #![feature(rustc_attrs)] #![no_std] #![no_main] @@ -11,7 +12,7 @@ fn panic_handler(_: &core::panic::PanicInfo) -> ! { loop {} } -#[no_mangle] +#[lang = "eh_personality"] extern "C" fn rust_eh_personality( _version: i32, _actions: i32, diff --git a/tests/run-make/symbols-all-mangled/rmake.rs b/tests/run-make/symbols-all-mangled/rmake.rs index 2cf579758002a..99281d8be3bbf 100644 --- a/tests/run-make/symbols-all-mangled/rmake.rs +++ b/tests/run-make/symbols-all-mangled/rmake.rs @@ -19,13 +19,31 @@ fn main() { symbols_check(&exe_name); } +fn is_symbol_ok(sym: &str) -> bool { + let sym = strip_underscore_if_apple(sym); + // There are debuginfo helper symbols that get prefixed with DW.ref. + let sym = sym.strip_prefix("DW.ref.").unwrap_or(sym); + + if sym.starts_with("_ZN") || sym.starts_with("_R") { + return true; // Correctly mangled + } + + if sym.contains(".llvm.") { + // Starting in LLVM 21 we get various implementation-detail functions which + // contain .llvm. that are not a problem. + return true; + } + + false +} + fn symbols_check_archive(path: &str) { let binary_data = rfs::read(path); let file = object::read::archive::ArchiveFile::parse(&*binary_data).unwrap(); for symbol in file.symbols().unwrap().unwrap() { let symbol = symbol.unwrap(); - let name = strip_underscore_if_apple(std::str::from_utf8(symbol.name()).unwrap()); - if name.starts_with("_ZN") || name.starts_with("_R") { + let name = std::str::from_utf8(symbol.name()).unwrap(); + if is_symbol_ok(name) { continue; // Correctly mangled } @@ -35,16 +53,6 @@ fn symbols_check_archive(path: &str) { continue; // All compiler-builtins symbols must remain unmangled } - if name.contains("rust_eh_personality") { - continue; // Unfortunately LLVM doesn't allow us to mangle this symbol - } - - if name.contains(".llvm.") { - // Starting in LLVM 21 we get various implementation-detail functions which - // contain .llvm. that are not a problem. - continue; - } - panic!("Unmangled symbol found in {path}: {name}"); } } @@ -59,8 +67,8 @@ fn symbols_check(path: &str) { if symbol.is_weak() { continue; // Likely an intrinsic from compiler-builtins } - let name = strip_underscore_if_apple(symbol.name().unwrap()); - if name.starts_with("_ZN") || name.starts_with("_R") { + let name = symbol.name().unwrap(); + if is_symbol_ok(name) { continue; // Correctly mangled } @@ -71,16 +79,6 @@ fn symbols_check(path: &str) { continue; } - if name.contains("rust_eh_personality") { - continue; // Unfortunately LLVM doesn't allow us to mangle this symbol - } - - if name.contains(".llvm.") { - // Starting in LLVM 21 we get various implementation-detail functions which - // contain .llvm. that are not a problem. - continue; - } - panic!("Unmangled symbol found in {path}: {name}"); } } diff --git a/tests/ui/linkage-attr/raw-dylib/elf/glibc-x86_64.rs b/tests/ui/linkage-attr/raw-dylib/elf/glibc-x86_64.rs index 62d352facd178..150dcfbf6e922 100644 --- a/tests/ui/linkage-attr/raw-dylib/elf/glibc-x86_64.rs +++ b/tests/ui/linkage-attr/raw-dylib/elf/glibc-x86_64.rs @@ -6,6 +6,7 @@ //@ ignore-backends: gcc #![allow(incomplete_features)] +#![feature(lang_items)] #![feature(raw_dylib_elf)] #![no_std] #![no_main] @@ -69,7 +70,7 @@ fn panic_handler(_: &core::panic::PanicInfo<'_>) -> ! { exit(1); } -#[unsafe(no_mangle)] +#[lang = "eh_personality"] extern "C" fn rust_eh_personality( _version: i32, _actions: i32, diff --git a/tests/ui/panic-runtime/auxiliary/panic-runtime-abort.rs b/tests/ui/panic-runtime/auxiliary/panic-runtime-abort.rs index 36db32f281a4f..cd748c86d3966 100644 --- a/tests/ui/panic-runtime/auxiliary/panic-runtime-abort.rs +++ b/tests/ui/panic-runtime/auxiliary/panic-runtime-abort.rs @@ -2,8 +2,8 @@ //@ no-prefer-dynamic #![feature(panic_runtime)] +#![feature(rustc_attrs)] #![crate_type = "rlib"] - #![no_std] #![panic_runtime] @@ -13,5 +13,5 @@ pub extern "C" fn __rust_maybe_catch_panic() {} #[no_mangle] pub extern "C" fn __rust_start_panic() {} -#[no_mangle] +#[rustc_std_internal_symbol] pub extern "C" fn rust_eh_personality() {} diff --git a/tests/ui/panic-runtime/auxiliary/panic-runtime-unwind.rs b/tests/ui/panic-runtime/auxiliary/panic-runtime-unwind.rs index aea42d1f103fb..b7e062164b985 100644 --- a/tests/ui/panic-runtime/auxiliary/panic-runtime-unwind.rs +++ b/tests/ui/panic-runtime/auxiliary/panic-runtime-unwind.rs @@ -2,8 +2,8 @@ //@ no-prefer-dynamic #![feature(panic_runtime)] +#![feature(rustc_attrs)] #![crate_type = "rlib"] - #![no_std] #![panic_runtime] @@ -13,5 +13,5 @@ pub extern "C" fn __rust_maybe_catch_panic() {} #[no_mangle] pub extern "C" fn __rust_start_panic() {} -#[no_mangle] +#[rustc_std_internal_symbol] pub extern "C" fn rust_eh_personality() {} diff --git a/tests/ui/panic-runtime/auxiliary/panic-runtime-unwind2.rs b/tests/ui/panic-runtime/auxiliary/panic-runtime-unwind2.rs index aea42d1f103fb..b7e062164b985 100644 --- a/tests/ui/panic-runtime/auxiliary/panic-runtime-unwind2.rs +++ b/tests/ui/panic-runtime/auxiliary/panic-runtime-unwind2.rs @@ -2,8 +2,8 @@ //@ no-prefer-dynamic #![feature(panic_runtime)] +#![feature(rustc_attrs)] #![crate_type = "rlib"] - #![no_std] #![panic_runtime] @@ -13,5 +13,5 @@ pub extern "C" fn __rust_maybe_catch_panic() {} #[no_mangle] pub extern "C" fn __rust_start_panic() {} -#[no_mangle] +#[rustc_std_internal_symbol] pub extern "C" fn rust_eh_personality() {} diff --git a/tests/ui/panic-runtime/incompatible-type.rs b/tests/ui/panic-runtime/incompatible-type.rs index f82c23d68c2c7..ad8760a5fdab7 100644 --- a/tests/ui/panic-runtime/incompatible-type.rs +++ b/tests/ui/panic-runtime/incompatible-type.rs @@ -7,6 +7,7 @@ //@ compile-flags: --crate-type=lib -Ccodegen-units=1 #![no_std] #![panic_runtime] +#![feature(lang_items)] #![feature(panic_runtime)] #![feature(rustc_attrs)] @@ -20,7 +21,7 @@ pub fn test(_: DropMe) { unreachable!(); } -#[rustc_std_internal_symbol] +#[lang = "eh_personality"] pub unsafe extern "C" fn rust_eh_personality( _version: i32, _actions: i32, diff --git a/tests/ui/sanitizer/dataflow-abilist.txt b/tests/ui/sanitizer/dataflow-abilist.txt index 1fa78ebd55939..0e9b643c98254 100644 --- a/tests/ui/sanitizer/dataflow-abilist.txt +++ b/tests/ui/sanitizer/dataflow-abilist.txt @@ -494,5 +494,4 @@ fun:_ZN4core*=uninstrumented fun:_R*4core*=uninstrumented fun:_ZN3std*=uninstrumented fun:_R*3std*=uninstrumented -fun:rust_eh_personality=uninstrumented fun:_R*__rustc*=uninstrumented From 2e2cd549242fd53a1529340fdcaf68759811af74 Mon Sep 17 00:00:00 2001 From: ozankenangungor Date: Mon, 7 Sep 2026 02:23:55 +0300 Subject: [PATCH 02/23] Improve diagnostics for non-well-formed associated items --- .../traits/fulfillment_errors.rs | 33 ++++- .../src/error_reporting/traits/suggestions.rs | 59 +++++++++ .../src/solve/fulfill/derive_errors.rs | 39 ++++-- ...item-own-bound-issue-162368.current.stderr | 17 +++ ...oc-item-own-bound-issue-162368.next.stderr | 27 ++++ .../assoc-item-own-bound-issue-162368.rs | 17 +++ ...-item-own-bound-where-clause-suggestion.rs | 67 ++++++++++ ...m-own-bound-where-clause-suggestion.stderr | 119 ++++++++++++++++++ ...rivial-unsized-projection-2.bad_new.stderr | 34 +++-- .../ui/traits/trivial-unsized-projection-2.rs | 3 +- .../trivial-unsized-projection.bad_new.stderr | 29 +++-- tests/ui/traits/trivial-unsized-projection.rs | 3 +- 12 files changed, 410 insertions(+), 37 deletions(-) create mode 100644 tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.current.stderr create mode 100644 tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.next.stderr create mode 100644 tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.rs create mode 100644 tests/ui/traits/next-solver/assoc-item-own-bound-where-clause-suggestion.rs create mode 100644 tests/ui/traits/next-solver/assoc-item-own-bound-where-clause-suggestion.stderr diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 47d1f1b22c090..10ea471f18fc1 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -175,6 +175,31 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let Err(guar) = leaf_trait_predicate.error_reported() { return guar; } + let impl_assoc_type_self_trait_bound_param = + if self.is_explicit_self_trait_bound_on_impl_assoc_type( + obligation.cause.body_def_id, + obligation.cause.code(), + leaf_trait_predicate.def_id(), + ) && let ty::Param(param_ty) = + *leaf_trait_predicate.skip_binder().self_ty().kind() + { + Some(param_ty) + } else { + None + }; + + let is_explicit_self_sized_bound_on_impl_assoc_type = + impl_assoc_type_self_trait_bound_param.is_some() + && self + .tcx + .is_lang_item(leaf_trait_predicate.def_id(), LangItem::Sized); + + if let Some(param_ty) = impl_assoc_type_self_trait_bound_param { + span = param_ty.span_from_generics( + self.tcx, + obligation.cause.body_def_id.to_def_id(), + ); + } // Silence redundant errors on binding access that are already // reported on the binding definition (#56607). if let Err(guar) = self.fn_arg_obligation(&obligation) { @@ -393,9 +418,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { _ => DUMMY_SP, }; if let Some(s) = label { - // If it has a custom `#[rustc_on_unimplemented]` - // error message, let's display it as the label! - err.span_label(span, s); + // Preserve custom `#[rustc_on_unimplemented]` labels for other traits; + // the explicit `Self: Sized` associated-type case has targeted labeling. + if !is_explicit_self_sized_bound_on_impl_assoc_type { + err.span_label(span, s); + } if !matches!(leaf_trait_predicate.skip_binder().self_ty().kind(), ty::Param(_)) // When the self type is a type param We don't need to "the trait // `std::marker::Sized` is not implemented for `T`" as we will point 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 5a17d65b0c0d6..f7f0a137ef18b 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -3834,6 +3834,38 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); true } + /// Checks for an explicit `Self: Trait` bound on an associated type impl. + pub(super) fn is_explicit_self_trait_bound_on_impl_assoc_type( + &self, + body_def_id: LocalDefId, + cause_code: &ObligationCauseCode<'tcx>, + trait_def_id: DefId, + ) -> bool { + let ObligationCauseCode::WhereClause(item_def_id, span) = *cause_code.peel_derives() else { + return false; + }; + let is_impl_trait_assoc_type = if let Some(assoc_item) = + self.tcx.opt_associated_item(body_def_id.to_def_id()) + && let Some(trait_item_def_id) = assoc_item.trait_item_def_id() + { + trait_item_def_id == item_def_id + && matches!(assoc_item.kind, ty::AssocKind::Type { .. }) + } else { + false + }; + + if !is_impl_trait_assoc_type { + return false; + } + + self.tcx.clauses_of(item_def_id).instantiate_own_identity().any(|(clause, clause_span)| { + clause_span == span + && clause.skip_norm_wip().as_trait_clause().is_some_and(|trait_pred| { + trait_pred.def_id() == trait_def_id + && trait_pred.skip_binder().self_ty().is_self_param() + }) + }) + } pub(crate) fn note_obligation_cause_code( &self, @@ -4022,6 +4054,32 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { this = "the implicit `Sized` requirement on this type parameter"; } + let is_explicit_self_sized_bound_on_impl_assoc_type = self + .is_explicit_self_trait_bound_on_impl_assoc_type( + body_def_id, + cause_code, + def_id, + ); + + if is_explicit_self_sized_bound_on_impl_assoc_type + && let Some(generics) = tcx.hir_get_generics(body_def_id) + && !generics.where_clause_span.from_expansion() + && generics.where_clause_span.desugaring_kind().is_none() + { + let tail_span = generics.tail_span_for_predicate_suggestion(); + if tail_span.can_be_used_for_suggestions() { + err.span_suggestion_verbose( + tail_span, + "consider adding `where Self: Sized`", + format!( + "{} Self: Sized", + generics.add_where_or_trailing_comma() + ), + Applicability::MachineApplicable, + ); + } + } + if let Some(hir::Node::TraitItem(hir::TraitItem { generics, kind: hir::TraitItemKind::Type(bounds, None), @@ -4031,6 +4089,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { && !bounds.iter() .filter_map(|bound| bound.trait_ref()) .any(|tr| tr.trait_def_id().is_some_and(|def_id| tcx.is_lang_item(def_id, LangItem::Sized))) + && !is_explicit_self_sized_bound_on_impl_assoc_type { let (span, separator) = if let [.., last] = bounds { (last.span().shrink_to_hi(), " +") diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs index 992c521261b5c..634a17dc372ef 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -350,19 +350,42 @@ impl<'tcx> BestObligation<'tcx> { alias: ty::AliasTerm<'tcx>, ) -> ControlFlow> { let tcx = goal.infcx().tcx; - let obligation = Obligation::new( - tcx, - self.obligation.cause.clone(), - goal.goal().param_env, - alias.trait_ref(tcx), - ); + + let trait_ref = alias.trait_ref(tcx); + let obligation = + Obligation::new(tcx, self.obligation.cause.clone(), goal.goal().param_env, trait_ref); + self.with_derived_obligation(obligation, |this| { goal.infcx().visit_proof_tree_at_depth( - goal.goal().with(tcx, alias.trait_ref(tcx)), + goal.goal().with(tcx, trait_ref), goal.depth() + 1, this, ) - }) + })?; + + let def_id = alias.expect_projection_def_id(); + + for (clause, span) in tcx.clauses_of(def_id).instantiate_own(tcx, alias.args) { + let clause = clause.skip_norm_wip(); + + let cause = ObligationCause::new( + self.obligation.cause.span, + self.obligation.cause.body_def_id, + ObligationCauseCode::WhereClause(def_id, span), + ); + + let obligation = Obligation::new(tcx, cause, goal.goal().param_env, clause); + + self.with_derived_obligation(obligation, |this| { + goal.infcx().visit_proof_tree_at_depth( + goal.goal().with(tcx, clause), + goal.depth() + 1, + this, + ) + })?; + } + + ControlFlow::Continue(()) } /// If we have no candidates, then it's likely that there is a diff --git a/tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.current.stderr b/tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.current.stderr new file mode 100644 index 0000000000000..00378f33a5597 --- /dev/null +++ b/tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.current.stderr @@ -0,0 +1,17 @@ +error[E0277]: the size for values of type `T` cannot be known at compilation time + --> $DIR/assoc-item-own-bound-issue-162368.rs:13:18 + | +LL | impl Trait for T { + | - this type parameter needs to be `Sized` +LL | type Assoc = i32; + | ^^^ doesn't have a size known at compile-time + | +help: consider removing the `?Sized` bound to make the type parameter `Sized` + | +LL - impl Trait for T { +LL + impl Trait for T { + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.next.stderr b/tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.next.stderr new file mode 100644 index 0000000000000..c2a9e40177178 --- /dev/null +++ b/tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.next.stderr @@ -0,0 +1,27 @@ +error[E0277]: the size for values of type `T` cannot be known at compilation time + --> $DIR/assoc-item-own-bound-issue-162368.rs:12:6 + | +LL | impl Trait for T { + | ^ this type parameter needs to be `Sized` + | +note: required by a bound in `Trait::Assoc` + --> $DIR/assoc-item-own-bound-issue-162368.rs:8:15 + | +LL | type Assoc + | ----- required by a bound in this associated type +LL | where +LL | Self: Sized; + | ^^^^^ required by this bound in `Trait::Assoc` +help: consider removing the `?Sized` bound to make the type parameter `Sized` + | +LL - impl Trait for T { +LL + impl Trait for T { + | +help: consider adding `where Self: Sized` + | +LL | type Assoc = i32 where Self: Sized; + | +++++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.rs b/tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.rs new file mode 100644 index 0000000000000..b6356c9b72d76 --- /dev/null +++ b/tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.rs @@ -0,0 +1,17 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +trait Trait { + type Assoc + where + Self: Sized; +} + +//[next]~v ERROR the size for values of type `T` cannot be known at compilation time +impl Trait for T { + type Assoc = i32; + //[current]~^ ERROR the size for values of type `T` cannot be known at compilation time +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/assoc-item-own-bound-where-clause-suggestion.rs b/tests/ui/traits/next-solver/assoc-item-own-bound-where-clause-suggestion.rs new file mode 100644 index 0000000000000..f9553fbee821d --- /dev/null +++ b/tests/ui/traits/next-solver/assoc-item-own-bound-where-clause-suggestion.rs @@ -0,0 +1,67 @@ +//@ compile-flags: -Znext-solver + +// Case B: implementation associated type already has an existing where-clause +trait TraitWithBound { + type Assoc + where + Self: Sized, + Self: 'static; +} + +impl TraitWithBound for T { + //~^ ERROR the size for values of type `T` cannot be known at compilation time + type Assoc = i32 + where + Self: 'static; +} + +// Case C: normal associated-type implicit-Sized diagnostic (suppression is not global) +trait NormalAssoc { + type Assoc; +} + +impl NormalAssoc for () { + type Assoc = [u8]; + //~^ ERROR the size for values of type `[u8]` cannot be known at compilation time +} + +// Case D: projection/non-impl use-site (targeted suggestion does not leak) +trait NonImplTrait { + type Assoc + where + Self: Sized; +} + +fn non_impl_use() { + let _: ::Assoc; + //~^ ERROR the size for values of type `T` cannot be known at compilation time + //~| ERROR the trait bound `T: NonImplTrait` is not satisfied +} + +// Case E: explicit associated-type bound with a trait other than `Sized` +trait Marker {} + +trait TraitWithMarker { + type Assoc + where + Self: Marker; +} + +impl TraitWithMarker for T { + //~^ ERROR the trait bound `T: Marker` is not satisfied + type Assoc = i32; +} + +// Case F: associated-type bound with a custom on-unimplemented diagnostic +trait TraitWithSend { + type Assoc + where + Self: Send; +} + +impl TraitWithSend for T { + //~^ ERROR `T` cannot be sent between threads safely + type Assoc = i32; +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/assoc-item-own-bound-where-clause-suggestion.stderr b/tests/ui/traits/next-solver/assoc-item-own-bound-where-clause-suggestion.stderr new file mode 100644 index 0000000000000..dcca095ce7717 --- /dev/null +++ b/tests/ui/traits/next-solver/assoc-item-own-bound-where-clause-suggestion.stderr @@ -0,0 +1,119 @@ +error[E0277]: the size for values of type `T` cannot be known at compilation time + --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:11:6 + | +LL | impl TraitWithBound for T { + | ^ this type parameter needs to be `Sized` + | +note: required by a bound in `TraitWithBound::Assoc` + --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:7:15 + | +LL | type Assoc + | ----- required by a bound in this associated type +LL | where +LL | Self: Sized, + | ^^^^^ required by this bound in `TraitWithBound::Assoc` +help: consider removing the `?Sized` bound to make the type parameter `Sized` + | +LL - impl TraitWithBound for T { +LL + impl TraitWithBound for T { + | +help: consider adding `where Self: Sized` + | +LL | Self: 'static, Self: Sized; + | +++++++++++++ + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:24:18 + | +LL | type Assoc = [u8]; + | ^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `NormalAssoc::Assoc` + --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:20:5 + | +LL | type Assoc; + | ^^^^^^^^^^^ required by this bound in `NormalAssoc::Assoc` +help: consider relaxing the implicit `Sized` restriction + | +LL | type Assoc: ?Sized; + | ++++++++ + +error[E0277]: the trait bound `T: Marker` is not satisfied + --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:50:6 + | +LL | impl TraitWithMarker for T { + | ^ the trait `Marker` is not implemented for `T` + | +note: required by a bound in `TraitWithMarker::Assoc` + --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:47:15 + | +LL | type Assoc + | ----- required by a bound in this associated type +LL | where +LL | Self: Marker; + | ^^^^^^ required by this bound in `TraitWithMarker::Assoc` +help: consider restricting type parameter `T` with trait `Marker` + | +LL | impl TraitWithMarker for T { + | ++++++++ + +error[E0277]: `T` cannot be sent between threads safely + --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:62:6 + | +LL | impl TraitWithSend for T { + | ^ `T` cannot be sent between threads safely + | +note: required by a bound in `TraitWithSend::Assoc` + --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:59:15 + | +LL | type Assoc + | ----- required by a bound in this associated type +LL | where +LL | Self: Send; + | ^^^^ required by this bound in `TraitWithSend::Assoc` +help: consider restricting type parameter `T` with trait `Send` + | +LL | impl TraitWithSend for T { + | +++++++++++++++++++ + +error[E0277]: the trait bound `T: NonImplTrait` is not satisfied + --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:36:13 + | +LL | let _: ::Assoc; + | ^ the trait `NonImplTrait` is not implemented for `T` + | +help: consider further restricting type parameter `T` with trait `NonImplTrait` + | +LL | fn non_impl_use() { + | ++++++++++++++ + +error[E0277]: the size for values of type `T` cannot be known at compilation time + --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:36:13 + | +LL | fn non_impl_use() { + | - this type parameter needs to be `Sized` +LL | let _: ::Assoc; + | ^ doesn't have a size known at compile-time + | +note: required by a bound in `NonImplTrait::Assoc` + --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:32:15 + | +LL | type Assoc + | ----- required by a bound in this associated type +LL | where +LL | Self: Sized; + | ^^^^^ required by this bound in `NonImplTrait::Assoc` +help: consider removing the `?Sized` bound to make the type parameter `Sized` + | +LL - fn non_impl_use() { +LL + fn non_impl_use() { + | +help: consider relaxing the implicit `Sized` restriction + | +LL | type Assoc: ?Sized + | ++++++++ + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/trivial-unsized-projection-2.bad_new.stderr b/tests/ui/traits/trivial-unsized-projection-2.bad_new.stderr index a58cebabf75d8..b378a7406ecf5 100644 --- a/tests/ui/traits/trivial-unsized-projection-2.bad_new.stderr +++ b/tests/ui/traits/trivial-unsized-projection-2.bad_new.stderr @@ -1,9 +1,3 @@ -error[E0271]: type mismatch resolving `::Assert == _` - --> $DIR/trivial-unsized-projection-2.rs:22:12 - | -LL | const FOO: ::Assert = todo!(); - | ^^^^^^^^^^^^^^^^^^^^^ types differ - error[E0277]: the size for values of type `[()]` cannot be known at compilation time --> $DIR/trivial-unsized-projection-2.rs:22:12 | @@ -29,13 +23,31 @@ help: consider relaxing the implicit `Sized` restriction LL | type Assert: ?Sized | ++++++++ -error[E0271]: type mismatch resolving `::Assert == _` +error[E0277]: the size for values of type `[()]` cannot be known at compilation time --> $DIR/trivial-unsized-projection-2.rs:22:36 | LL | const FOO: ::Assert = todo!(); - | ^^^^^^^ types differ + | ^^^^^^^ doesn't have a size known at compile-time + | + = help: within `Tail`, the trait `Sized` is not implemented for `[()]` +note: required because it appears within the type `Tail` + --> $DIR/trivial-unsized-projection-2.rs:17:8 + | +LL | struct Tail([()]); + | ^^^^ +note: required by a bound in `Bad::Assert` + --> $DIR/trivial-unsized-projection-2.rs:14:15 + | +LL | type Assert + | ------ required by a bound in this associated type +LL | where +LL | Self: Sized; + | ^^^^^ required by this bound in `Bad::Assert` +help: consider relaxing the implicit `Sized` restriction + | +LL | type Assert: ?Sized + | ++++++++ -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0271, E0277. -For more information about an error, try `rustc --explain E0271`. +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/trivial-unsized-projection-2.rs b/tests/ui/traits/trivial-unsized-projection-2.rs index 175a2712d25b5..af4e12f6f9008 100644 --- a/tests/ui/traits/trivial-unsized-projection-2.rs +++ b/tests/ui/traits/trivial-unsized-projection-2.rs @@ -23,8 +23,7 @@ const FOO: ::Assert = todo!(); //[bad]~^ ERROR the size for values of type `[()]` cannot be known at compilation time //[bad]~| ERROR the size for values of type `[()]` cannot be known at compilation time //[bad_new]~^^^ ERROR the size for values of type `[()]` cannot be known at compilation time -//[bad_new]~| ERROR: type mismatch resolving `::Assert == _` -//[bad_new]~| ERROR: type mismatch resolving `::Assert == _` +//[bad_new]~| ERROR the size for values of type `[()]` cannot be known at compilation time #[cfg(any(good, good_new))] // Well-formed in trivially false param-env diff --git a/tests/ui/traits/trivial-unsized-projection.bad_new.stderr b/tests/ui/traits/trivial-unsized-projection.bad_new.stderr index a055181542d4d..d5d829285305a 100644 --- a/tests/ui/traits/trivial-unsized-projection.bad_new.stderr +++ b/tests/ui/traits/trivial-unsized-projection.bad_new.stderr @@ -1,9 +1,3 @@ -error[E0271]: type mismatch resolving `<[()] as Bad>::Assert == _` - --> $DIR/trivial-unsized-projection.rs:20:12 - | -LL | const FOO: <[()] as Bad>::Assert = todo!(); - | ^^^^^^^^^^^^^^^^^^^^^ types differ - error[E0277]: the size for values of type `[()]` cannot be known at compilation time --> $DIR/trivial-unsized-projection.rs:20:12 | @@ -24,13 +18,26 @@ help: consider relaxing the implicit `Sized` restriction LL | type Assert: ?Sized | ++++++++ -error[E0271]: type mismatch resolving `<[()] as Bad>::Assert == _` +error[E0277]: the size for values of type `[()]` cannot be known at compilation time --> $DIR/trivial-unsized-projection.rs:20:36 | LL | const FOO: <[()] as Bad>::Assert = todo!(); - | ^^^^^^^ types differ + | ^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[()]` +note: required by a bound in `Bad::Assert` + --> $DIR/trivial-unsized-projection.rs:14:15 + | +LL | type Assert + | ------ required by a bound in this associated type +LL | where +LL | Self: Sized; + | ^^^^^ required by this bound in `Bad::Assert` +help: consider relaxing the implicit `Sized` restriction + | +LL | type Assert: ?Sized + | ++++++++ -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0271, E0277. -For more information about an error, try `rustc --explain E0271`. +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/trivial-unsized-projection.rs b/tests/ui/traits/trivial-unsized-projection.rs index 8b62356785a35..62ff25fb7ac0a 100644 --- a/tests/ui/traits/trivial-unsized-projection.rs +++ b/tests/ui/traits/trivial-unsized-projection.rs @@ -21,8 +21,7 @@ const FOO: <[()] as Bad>::Assert = todo!(); //[bad]~^ ERROR the size for values of type `[()]` cannot be known at compilation time //[bad]~| ERROR the size for values of type `[()]` cannot be known at compilation time //[bad_new]~^^^ ERROR the size for values of type `[()]` cannot be known at compilation time -//[bad_new]~| ERROR type mismatch resolving `<[()] as Bad>::Assert == _` -//[bad_new]~| ERROR type mismatch resolving `<[()] as Bad>::Assert == _` +//[bad_new]~| ERROR the size for values of type `[()]` cannot be known at compilation time #[cfg(any(good, good_new))] // Well-formed in trivially false param-env From 29d1ae1dc6f260517cfbed020aff70afb315e9df Mon Sep 17 00:00:00 2001 From: ozankenangungor Date: Sat, 10 Oct 2026 07:23:35 +0300 Subject: [PATCH 03/23] Handle explicit associated item own bounds generally Remove the `where Self: Sized` suggestion and the Sized-specific label suppression so an explicit `Self: Sized` clause is reported like any other `Self: Trait` bound, and only suggest relaxing the implicit `Sized` restriction when the failing obligation is that implicit restriction rather than an explicit where clause of the item. Also rename the regression tests and add a case for an implicit `Sized` failure on an associated item with other explicit where clauses. --- .../traits/fulfillment_errors.rs | 47 +++++++--- .../src/error_reporting/traits/suggestions.rs | 66 ++----------- .../assoc_type_bounds_sized_used.stderr | 4 - .../assoc-item-own-bound-diagnostics.rs | 92 +++++++++++++++++++ ...> assoc-item-own-bound-diagnostics.stderr} | 64 ++++++++----- ...-item-own-bound-where-clause-suggestion.rs | 67 -------------- ...rr => assoc-item-own-bound.current.stderr} | 2 +- ...tderr => assoc-item-own-bound.next.stderr} | 15 ++- ...ssue-162368.rs => assoc-item-own-bound.rs} | 5 + .../trivial-unsized-projection-2.bad.stderr | 8 -- ...rivial-unsized-projection-2.bad_new.stderr | 14 +-- .../trivial-unsized-projection.bad.stderr | 8 -- .../trivial-unsized-projection.bad_new.stderr | 10 +- 13 files changed, 192 insertions(+), 210 deletions(-) create mode 100644 tests/ui/traits/next-solver/assoc-item-own-bound-diagnostics.rs rename tests/ui/traits/next-solver/{assoc-item-own-bound-where-clause-suggestion.stderr => assoc-item-own-bound-diagnostics.stderr} (68%) delete mode 100644 tests/ui/traits/next-solver/assoc-item-own-bound-where-clause-suggestion.rs rename tests/ui/traits/next-solver/{assoc-item-own-bound-issue-162368.current.stderr => assoc-item-own-bound.current.stderr} (90%) rename tests/ui/traits/next-solver/{assoc-item-own-bound-issue-162368.next.stderr => assoc-item-own-bound.next.stderr} (61%) rename tests/ui/traits/next-solver/{assoc-item-own-bound-issue-162368.rs => assoc-item-own-bound.rs} (61%) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 10ea471f18fc1..000a4c1cdec43 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -57,6 +57,39 @@ use crate::traits::{ }; impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { + /// Checks for an explicit `Self: Trait` bound on an associated type impl. + fn is_explicit_self_trait_bound_on_impl_assoc_type( + &self, + body_def_id: LocalDefId, + cause_code: &ObligationCauseCode<'tcx>, + trait_def_id: DefId, + ) -> bool { + let ObligationCauseCode::WhereClause(item_def_id, span) = *cause_code.peel_derives() else { + return false; + }; + let is_impl_trait_assoc_type = if let Some(assoc_item) = + self.tcx.opt_associated_item(body_def_id.to_def_id()) + && let Some(trait_item_def_id) = assoc_item.trait_item_def_id() + { + trait_item_def_id == item_def_id + && matches!(assoc_item.kind, ty::AssocKind::Type { .. }) + } else { + false + }; + + if !is_impl_trait_assoc_type { + return false; + } + + self.tcx.clauses_of(item_def_id).instantiate_own_identity().any(|(clause, clause_span)| { + clause_span == span + && clause.skip_norm_wip().as_trait_clause().is_some_and(|trait_pred| { + trait_pred.def_id() == trait_def_id + && trait_pred.skip_binder().self_ty().is_self_param() + }) + }) + } + /// The `root_obligation` parameter should be the `root_obligation` field /// from a `FulfillmentError`. If no `FulfillmentError` is available, /// then it should be the same as `obligation`. @@ -188,12 +221,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { None }; - let is_explicit_self_sized_bound_on_impl_assoc_type = - impl_assoc_type_self_trait_bound_param.is_some() - && self - .tcx - .is_lang_item(leaf_trait_predicate.def_id(), LangItem::Sized); - if let Some(param_ty) = impl_assoc_type_self_trait_bound_param { span = param_ty.span_from_generics( self.tcx, @@ -418,11 +445,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { _ => DUMMY_SP, }; if let Some(s) = label { - // Preserve custom `#[rustc_on_unimplemented]` labels for other traits; - // the explicit `Self: Sized` associated-type case has targeted labeling. - if !is_explicit_self_sized_bound_on_impl_assoc_type { - err.span_label(span, s); - } + // If it has a custom `#[rustc_on_unimplemented]` + // error message, let's display it as the label! + err.span_label(span, s); if !matches!(leaf_trait_predicate.skip_binder().self_ty().kind(), ty::Param(_)) // When the self type is a type param We don't need to "the trait // `std::marker::Sized` is not implemented for `T`" as we will point 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 f7f0a137ef18b..33db9f71522a7 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -3834,38 +3834,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); true } - /// Checks for an explicit `Self: Trait` bound on an associated type impl. - pub(super) fn is_explicit_self_trait_bound_on_impl_assoc_type( - &self, - body_def_id: LocalDefId, - cause_code: &ObligationCauseCode<'tcx>, - trait_def_id: DefId, - ) -> bool { - let ObligationCauseCode::WhereClause(item_def_id, span) = *cause_code.peel_derives() else { - return false; - }; - let is_impl_trait_assoc_type = if let Some(assoc_item) = - self.tcx.opt_associated_item(body_def_id.to_def_id()) - && let Some(trait_item_def_id) = assoc_item.trait_item_def_id() - { - trait_item_def_id == item_def_id - && matches!(assoc_item.kind, ty::AssocKind::Type { .. }) - } else { - false - }; - - if !is_impl_trait_assoc_type { - return false; - } - - self.tcx.clauses_of(item_def_id).instantiate_own_identity().any(|(clause, clause_span)| { - clause_span == span - && clause.skip_norm_wip().as_trait_clause().is_some_and(|trait_pred| { - trait_pred.def_id() == trait_def_id - && trait_pred.skip_binder().self_ty().is_self_param() - }) - }) - } pub(crate) fn note_obligation_cause_code( &self, @@ -4054,32 +4022,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { this = "the implicit `Sized` requirement on this type parameter"; } - let is_explicit_self_sized_bound_on_impl_assoc_type = self - .is_explicit_self_trait_bound_on_impl_assoc_type( - body_def_id, - cause_code, - def_id, - ); - - if is_explicit_self_sized_bound_on_impl_assoc_type - && let Some(generics) = tcx.hir_get_generics(body_def_id) - && !generics.where_clause_span.from_expansion() - && generics.where_clause_span.desugaring_kind().is_none() - { - let tail_span = generics.tail_span_for_predicate_suggestion(); - if tail_span.can_be_used_for_suggestions() { - err.span_suggestion_verbose( - tail_span, - "consider adding `where Self: Sized`", - format!( - "{} Self: Sized", - generics.add_where_or_trailing_comma() - ), - Applicability::MachineApplicable, - ); - } - } - if let Some(hir::Node::TraitItem(hir::TraitItem { generics, kind: hir::TraitItemKind::Type(bounds, None), @@ -4089,7 +4031,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { && !bounds.iter() .filter_map(|bound| bound.trait_ref()) .any(|tr| tr.trait_def_id().is_some_and(|def_id| tcx.is_lang_item(def_id, LangItem::Sized))) - && !is_explicit_self_sized_bound_on_impl_assoc_type + // Only suggest relaxing the implicit `Sized` restriction if the + // failing obligation is that restriction. Relaxing it cannot + // satisfy an explicit where clause of the item, e.g. + // `type Assoc where Self: Sized;`. + && !generics.predicates.iter().any(|pred| { + pred.kind.in_where_clause() && pred.span.contains(span) + }) { let (span, separator) = if let [.., last] = bounds { (last.span().shrink_to_hi(), " +") diff --git a/tests/ui/dyn-compatibility/assoc_type_bounds_sized_used.stderr b/tests/ui/dyn-compatibility/assoc_type_bounds_sized_used.stderr index 9eb4e5bf5ef05..d33536e57ebe5 100644 --- a/tests/ui/dyn-compatibility/assoc_type_bounds_sized_used.stderr +++ b/tests/ui/dyn-compatibility/assoc_type_bounds_sized_used.stderr @@ -19,10 +19,6 @@ help: consider removing the `?Sized` bound to make the type parameter `Sized` LL - fn bop() { LL + fn bop() { | -help: consider relaxing the implicit `Sized` restriction - | -LL | type Bar: Default + ?Sized - | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/traits/next-solver/assoc-item-own-bound-diagnostics.rs b/tests/ui/traits/next-solver/assoc-item-own-bound-diagnostics.rs new file mode 100644 index 0000000000000..20cfbfb6fcffe --- /dev/null +++ b/tests/ui/traits/next-solver/assoc-item-own-bound-diagnostics.rs @@ -0,0 +1,92 @@ +//@ compile-flags: -Znext-solver + +// Tests the diagnostics for associated-item own clauses (e.g. `type Assoc +// where Self: Sized;`) that are not satisfied by an impl. The recovered error +// should point at the type that fails the clause and only suggest fixes that +// would actually satisfy it. + +// The impl associated type already carries other where clauses, but is +// missing the `Self: Sized` clause required by the trait item. Relaxing the +// implicit `Sized` restriction of the associated type would not satisfy that +// clause. +trait TraitWithBound { + type Assoc + where + Self: Sized, + Self: 'static; +} + +impl TraitWithBound for T { + //~^ ERROR the size for values of type `T` cannot be known at compilation time + type Assoc = i32 + where + Self: 'static; +} + +// The value of the impl associated type does not satisfy the implicit `Sized` +// restriction of the trait item. Relaxing that restriction is the right fix, +// so the suggestion must still be emitted even though the trait item carries +// other explicit where clauses. +trait TraitWithOtherClause { + type Assoc + where + Self: 'static; +} + +impl TraitWithOtherClause for () { + type Assoc = [u8]; + //~^ ERROR the size for values of type `[u8]` cannot be known at compilation time +} + +// A trait item without any explicit where clauses: the ordinary +// implicit-`Sized` diagnostic is unchanged. +trait NormalAssoc { + type Assoc; +} + +impl NormalAssoc for () { + type Assoc = [u8]; + //~^ ERROR the size for values of type `[u8]` cannot be known at compilation time +} + +// A projection use site outside of an impl: the recovery also applies there. +trait NonImplTrait { + type Assoc + where + Self: Sized; +} + +fn non_impl_use() { + let _: ::Assoc; + //~^ ERROR the size for values of type `T` cannot be known at compilation time + //~| ERROR the trait bound `T: NonImplTrait` is not satisfied +} + +// Own clauses with traits other than `Sized` are handled the same way. +trait Marker {} + +trait TraitWithMarker { + type Assoc + where + Self: Marker; +} + +impl TraitWithMarker for T { + //~^ ERROR the trait bound `T: Marker` is not satisfied + type Assoc = i32; +} + +// Own clauses on traits with a custom `#[rustc_on_unimplemented]` diagnostic +// keep their custom label. +trait TraitWithSend { + type Assoc + where + Self: Send; +} + +impl TraitWithSend for T { + //~^ ERROR `T` cannot be sent between threads safely + type Assoc = i32; +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/assoc-item-own-bound-where-clause-suggestion.stderr b/tests/ui/traits/next-solver/assoc-item-own-bound-diagnostics.stderr similarity index 68% rename from tests/ui/traits/next-solver/assoc-item-own-bound-where-clause-suggestion.stderr rename to tests/ui/traits/next-solver/assoc-item-own-bound-diagnostics.stderr index dcca095ce7717..26e62981a6b4e 100644 --- a/tests/ui/traits/next-solver/assoc-item-own-bound-where-clause-suggestion.stderr +++ b/tests/ui/traits/next-solver/assoc-item-own-bound-diagnostics.stderr @@ -1,14 +1,17 @@ error[E0277]: the size for values of type `T` cannot be known at compilation time - --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:11:6 + --> $DIR/assoc-item-own-bound-diagnostics.rs:19:6 | LL | impl TraitWithBound for T { - | ^ this type parameter needs to be `Sized` + | ^ + | | + | doesn't have a size known at compile-time + | this type parameter needs to be `Sized` | note: required by a bound in `TraitWithBound::Assoc` - --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:7:15 + --> $DIR/assoc-item-own-bound-diagnostics.rs:15:15 | +LL | trait TraitWithBound { LL | type Assoc - | ----- required by a bound in this associated type LL | where LL | Self: Sized, | ^^^^^ required by this bound in `TraitWithBound::Assoc` @@ -17,21 +20,38 @@ help: consider removing the `?Sized` bound to make the type parameter `Sized` LL - impl TraitWithBound for T { LL + impl TraitWithBound for T { | -help: consider adding `where Self: Sized` + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/assoc-item-own-bound-diagnostics.rs:37:18 + | +LL | type Assoc = [u8]; + | ^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` +note: required by a bound in `TraitWithOtherClause::Assoc` + --> $DIR/assoc-item-own-bound-diagnostics.rs:31:5 + | +LL | trait TraitWithOtherClause { +LL | / type Assoc +LL | | where +LL | | Self: 'static; + | |______________________^ required by this bound in `TraitWithOtherClause::Assoc` +help: consider relaxing the implicit `Sized` restriction | -LL | Self: 'static, Self: Sized; - | +++++++++++++ +LL | type Assoc: ?Sized + | ++++++++ error[E0277]: the size for values of type `[u8]` cannot be known at compilation time - --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:24:18 + --> $DIR/assoc-item-own-bound-diagnostics.rs:48:18 | LL | type Assoc = [u8]; | ^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `[u8]` note: required by a bound in `NormalAssoc::Assoc` - --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:20:5 + --> $DIR/assoc-item-own-bound-diagnostics.rs:44:5 | +LL | trait NormalAssoc { LL | type Assoc; | ^^^^^^^^^^^ required by this bound in `NormalAssoc::Assoc` help: consider relaxing the implicit `Sized` restriction @@ -40,16 +60,16 @@ LL | type Assoc: ?Sized; | ++++++++ error[E0277]: the trait bound `T: Marker` is not satisfied - --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:50:6 + --> $DIR/assoc-item-own-bound-diagnostics.rs:74:6 | LL | impl TraitWithMarker for T { | ^ the trait `Marker` is not implemented for `T` | note: required by a bound in `TraitWithMarker::Assoc` - --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:47:15 + --> $DIR/assoc-item-own-bound-diagnostics.rs:71:15 | +LL | trait TraitWithMarker { LL | type Assoc - | ----- required by a bound in this associated type LL | where LL | Self: Marker; | ^^^^^^ required by this bound in `TraitWithMarker::Assoc` @@ -59,16 +79,16 @@ LL | impl TraitWithMarker for T { | ++++++++ error[E0277]: `T` cannot be sent between threads safely - --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:62:6 + --> $DIR/assoc-item-own-bound-diagnostics.rs:87:6 | LL | impl TraitWithSend for T { | ^ `T` cannot be sent between threads safely | note: required by a bound in `TraitWithSend::Assoc` - --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:59:15 + --> $DIR/assoc-item-own-bound-diagnostics.rs:84:15 | +LL | trait TraitWithSend { LL | type Assoc - | ----- required by a bound in this associated type LL | where LL | Self: Send; | ^^^^ required by this bound in `TraitWithSend::Assoc` @@ -78,7 +98,7 @@ LL | impl TraitWithSend for T { | +++++++++++++++++++ error[E0277]: the trait bound `T: NonImplTrait` is not satisfied - --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:36:13 + --> $DIR/assoc-item-own-bound-diagnostics.rs:60:13 | LL | let _: ::Assoc; | ^ the trait `NonImplTrait` is not implemented for `T` @@ -89,7 +109,7 @@ LL | fn non_impl_use() { | ++++++++++++++ error[E0277]: the size for values of type `T` cannot be known at compilation time - --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:36:13 + --> $DIR/assoc-item-own-bound-diagnostics.rs:60:13 | LL | fn non_impl_use() { | - this type parameter needs to be `Sized` @@ -97,10 +117,10 @@ LL | let _: ::Assoc; | ^ doesn't have a size known at compile-time | note: required by a bound in `NonImplTrait::Assoc` - --> $DIR/assoc-item-own-bound-where-clause-suggestion.rs:32:15 + --> $DIR/assoc-item-own-bound-diagnostics.rs:56:15 | +LL | trait NonImplTrait { LL | type Assoc - | ----- required by a bound in this associated type LL | where LL | Self: Sized; | ^^^^^ required by this bound in `NonImplTrait::Assoc` @@ -109,11 +129,7 @@ help: consider removing the `?Sized` bound to make the type parameter `Sized` LL - fn non_impl_use() { LL + fn non_impl_use() { | -help: consider relaxing the implicit `Sized` restriction - | -LL | type Assoc: ?Sized - | ++++++++ -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/assoc-item-own-bound-where-clause-suggestion.rs b/tests/ui/traits/next-solver/assoc-item-own-bound-where-clause-suggestion.rs deleted file mode 100644 index f9553fbee821d..0000000000000 --- a/tests/ui/traits/next-solver/assoc-item-own-bound-where-clause-suggestion.rs +++ /dev/null @@ -1,67 +0,0 @@ -//@ compile-flags: -Znext-solver - -// Case B: implementation associated type already has an existing where-clause -trait TraitWithBound { - type Assoc - where - Self: Sized, - Self: 'static; -} - -impl TraitWithBound for T { - //~^ ERROR the size for values of type `T` cannot be known at compilation time - type Assoc = i32 - where - Self: 'static; -} - -// Case C: normal associated-type implicit-Sized diagnostic (suppression is not global) -trait NormalAssoc { - type Assoc; -} - -impl NormalAssoc for () { - type Assoc = [u8]; - //~^ ERROR the size for values of type `[u8]` cannot be known at compilation time -} - -// Case D: projection/non-impl use-site (targeted suggestion does not leak) -trait NonImplTrait { - type Assoc - where - Self: Sized; -} - -fn non_impl_use() { - let _: ::Assoc; - //~^ ERROR the size for values of type `T` cannot be known at compilation time - //~| ERROR the trait bound `T: NonImplTrait` is not satisfied -} - -// Case E: explicit associated-type bound with a trait other than `Sized` -trait Marker {} - -trait TraitWithMarker { - type Assoc - where - Self: Marker; -} - -impl TraitWithMarker for T { - //~^ ERROR the trait bound `T: Marker` is not satisfied - type Assoc = i32; -} - -// Case F: associated-type bound with a custom on-unimplemented diagnostic -trait TraitWithSend { - type Assoc - where - Self: Send; -} - -impl TraitWithSend for T { - //~^ ERROR `T` cannot be sent between threads safely - type Assoc = i32; -} - -fn main() {} diff --git a/tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.current.stderr b/tests/ui/traits/next-solver/assoc-item-own-bound.current.stderr similarity index 90% rename from tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.current.stderr rename to tests/ui/traits/next-solver/assoc-item-own-bound.current.stderr index 00378f33a5597..5f60362ef64a0 100644 --- a/tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.current.stderr +++ b/tests/ui/traits/next-solver/assoc-item-own-bound.current.stderr @@ -1,5 +1,5 @@ error[E0277]: the size for values of type `T` cannot be known at compilation time - --> $DIR/assoc-item-own-bound-issue-162368.rs:13:18 + --> $DIR/assoc-item-own-bound.rs:18:18 | LL | impl Trait for T { | - this type parameter needs to be `Sized` diff --git a/tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.next.stderr b/tests/ui/traits/next-solver/assoc-item-own-bound.next.stderr similarity index 61% rename from tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.next.stderr rename to tests/ui/traits/next-solver/assoc-item-own-bound.next.stderr index c2a9e40177178..4831cf72da63f 100644 --- a/tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.next.stderr +++ b/tests/ui/traits/next-solver/assoc-item-own-bound.next.stderr @@ -1,14 +1,17 @@ error[E0277]: the size for values of type `T` cannot be known at compilation time - --> $DIR/assoc-item-own-bound-issue-162368.rs:12:6 + --> $DIR/assoc-item-own-bound.rs:17:6 | LL | impl Trait for T { - | ^ this type parameter needs to be `Sized` + | ^ + | | + | doesn't have a size known at compile-time + | this type parameter needs to be `Sized` | note: required by a bound in `Trait::Assoc` - --> $DIR/assoc-item-own-bound-issue-162368.rs:8:15 + --> $DIR/assoc-item-own-bound.rs:13:15 | +LL | trait Trait { LL | type Assoc - | ----- required by a bound in this associated type LL | where LL | Self: Sized; | ^^^^^ required by this bound in `Trait::Assoc` @@ -17,10 +20,6 @@ help: consider removing the `?Sized` bound to make the type parameter `Sized` LL - impl Trait for T { LL + impl Trait for T { | -help: consider adding `where Self: Sized` - | -LL | type Assoc = i32 where Self: Sized; - | +++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.rs b/tests/ui/traits/next-solver/assoc-item-own-bound.rs similarity index 61% rename from tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.rs rename to tests/ui/traits/next-solver/assoc-item-own-bound.rs index b6356c9b72d76..17275775c704c 100644 --- a/tests/ui/traits/next-solver/assoc-item-own-bound-issue-162368.rs +++ b/tests/ui/traits/next-solver/assoc-item-own-bound.rs @@ -2,6 +2,11 @@ //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver +// Regression test for rust-lang/rust#162368: an impl associated type that does +// not satisfy an explicit own clause of the trait item used to be reported as +// a projection type mismatch (E0271) by the new solver instead of the +// unsatisfied trait bound (E0277). + trait Trait { type Assoc where diff --git a/tests/ui/traits/trivial-unsized-projection-2.bad.stderr b/tests/ui/traits/trivial-unsized-projection-2.bad.stderr index 16f1ceaf7e67f..3b5efa6b73523 100644 --- a/tests/ui/traits/trivial-unsized-projection-2.bad.stderr +++ b/tests/ui/traits/trivial-unsized-projection-2.bad.stderr @@ -18,10 +18,6 @@ LL | type Assert LL | where LL | Self: Sized; | ^^^^^ required by this bound in `Bad::Assert` -help: consider relaxing the implicit `Sized` restriction - | -LL | type Assert: ?Sized - | ++++++++ error[E0277]: the size for values of type `[()]` cannot be known at compilation time --> $DIR/trivial-unsized-projection-2.rs:22:12 @@ -44,10 +40,6 @@ LL | where LL | Self: Sized; | ^^^^^ required by this bound in `Bad::Assert` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -help: consider relaxing the implicit `Sized` restriction - | -LL | type Assert: ?Sized - | ++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/traits/trivial-unsized-projection-2.bad_new.stderr b/tests/ui/traits/trivial-unsized-projection-2.bad_new.stderr index b378a7406ecf5..0eb9e23a0e7ce 100644 --- a/tests/ui/traits/trivial-unsized-projection-2.bad_new.stderr +++ b/tests/ui/traits/trivial-unsized-projection-2.bad_new.stderr @@ -18,10 +18,6 @@ LL | type Assert LL | where LL | Self: Sized; | ^^^^^ required by this bound in `Bad::Assert` -help: consider relaxing the implicit `Sized` restriction - | -LL | type Assert: ?Sized - | ++++++++ error[E0277]: the size for values of type `[()]` cannot be known at compilation time --> $DIR/trivial-unsized-projection-2.rs:22:36 @@ -31,22 +27,18 @@ LL | const FOO: ::Assert = todo!(); | = help: within `Tail`, the trait `Sized` is not implemented for `[()]` note: required because it appears within the type `Tail` - --> $DIR/trivial-unsized-projection-2.rs:17:8 + --> $DIR/trivial-unsized-projection-2.rs:17:13 | LL | struct Tail([()]); - | ^^^^ + | ^^^^ required by this field note: required by a bound in `Bad::Assert` --> $DIR/trivial-unsized-projection-2.rs:14:15 | +LL | trait Bad { LL | type Assert - | ------ required by a bound in this associated type LL | where LL | Self: Sized; | ^^^^^ required by this bound in `Bad::Assert` -help: consider relaxing the implicit `Sized` restriction - | -LL | type Assert: ?Sized - | ++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/traits/trivial-unsized-projection.bad.stderr b/tests/ui/traits/trivial-unsized-projection.bad.stderr index bab0a72587320..d003d8017151c 100644 --- a/tests/ui/traits/trivial-unsized-projection.bad.stderr +++ b/tests/ui/traits/trivial-unsized-projection.bad.stderr @@ -13,10 +13,6 @@ LL | type Assert LL | where LL | Self: Sized; | ^^^^^ required by this bound in `Bad::Assert` -help: consider relaxing the implicit `Sized` restriction - | -LL | type Assert: ?Sized - | ++++++++ error[E0277]: the size for values of type `[()]` cannot be known at compilation time --> $DIR/trivial-unsized-projection.rs:20:12 @@ -34,10 +30,6 @@ LL | where LL | Self: Sized; | ^^^^^ required by this bound in `Bad::Assert` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -help: consider relaxing the implicit `Sized` restriction - | -LL | type Assert: ?Sized - | ++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/traits/trivial-unsized-projection.bad_new.stderr b/tests/ui/traits/trivial-unsized-projection.bad_new.stderr index d5d829285305a..d7d3032c40d9d 100644 --- a/tests/ui/traits/trivial-unsized-projection.bad_new.stderr +++ b/tests/ui/traits/trivial-unsized-projection.bad_new.stderr @@ -13,10 +13,6 @@ LL | type Assert LL | where LL | Self: Sized; | ^^^^^ required by this bound in `Bad::Assert` -help: consider relaxing the implicit `Sized` restriction - | -LL | type Assert: ?Sized - | ++++++++ error[E0277]: the size for values of type `[()]` cannot be known at compilation time --> $DIR/trivial-unsized-projection.rs:20:36 @@ -28,15 +24,11 @@ LL | const FOO: <[()] as Bad>::Assert = todo!(); note: required by a bound in `Bad::Assert` --> $DIR/trivial-unsized-projection.rs:14:15 | +LL | trait Bad { LL | type Assert - | ------ required by a bound in this associated type LL | where LL | Self: Sized; | ^^^^^ required by this bound in `Bad::Assert` -help: consider relaxing the implicit `Sized` restriction - | -LL | type Assert: ?Sized - | ++++++++ error: aborting due to 2 previous errors From 24171c0f68525912e44a25eb850003936bfdcf9b Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Mon, 12 Oct 2026 17:41:37 +0300 Subject: [PATCH 04/23] `incomplete` -> `unstable` for delegation feature --- compiler/rustc_feature/src/unstable.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index a35ea37b3bd5f..c1f4cd4fd6fe3 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -546,7 +546,7 @@ declare_features! ( /// Allows using `#[align(...)]` on function items (unstable, fn_align, "1.53.0", Some(82232)), /// Support delegating implementation of functions to other already implemented functions. - (incomplete, fn_delegation, "1.76.0", Some(118212)), + (unstable, fn_delegation, "1.76.0", Some(118212)), /// Traits for function pointers and items (unstable, fn_static, "1.100.0", Some(148768)), /// Allows using forced keywords `k#fn`. From 35b8ebcf69676f7887902a32dfb1201090c51371 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 12 Oct 2026 19:25:19 +0000 Subject: [PATCH 05/23] Point at assoc item for trait that needs to be implemented --- compiler/rustc_hir_typeck/src/diagnostics.rs | 2 +- .../rustc_hir_typeck/src/method/suggest.rs | 10 +++++++++- .../associated-const-no-item.stderr | 2 ++ .../inherent-impls-self-replacement.stderr | 6 ++++++ .../custom-on-unimplemented-diagnostic.stderr | 2 ++ .../functions-closures/fn-help-with-err.stderr | 3 +++ ...hod-unsatisfied-assoc-type-predicate.stderr | 2 ++ ...fn-ptr-impl-not-general-enough-57936.stderr | 3 +++ .../hrtb-doesnt-borrow-self-1.stderr | 3 +++ .../hrtb-doesnt-borrow-self-2.stderr | 2 ++ ..._inherent_impl_on_rigid_type.current.stderr | 2 ++ ...hod_on_inherent_impl_ref-err.current.stderr | 2 ++ ...method_on_inherent_impl_ref-err.next.stderr | 2 ++ ...ue-21659-show-relevant-trait-impls-3.stderr | 2 ++ .../would-constrain-opaque.current.stderr | 4 ++++ .../no-method-suggested-traits.stderr | 18 ++++++++++++++++++ .../iterator-does-not-need-into-iter.stderr | 2 ++ .../tuple-suggestions-issue-142488.stderr | 4 ++++ tests/ui/methods/vec-autoderef-autoref.stderr | 11 +++++++++++ tests/ui/nll/issue-57362-1.stderr | 2 ++ tests/ui/nll/issue-57362-2.stderr | 3 +++ .../issue-57642-higher-ranked-subtype.stderr | 6 ++++++ ...ult-impl-not-a-candidate-issue-48515.stderr | 2 ++ .../default-impl-not-an-impl.stderr | 2 ++ ...specialization-trait-not-implemented.stderr | 2 ++ ...plicitly-unimplemented-error-message.stderr | 1 + .../trait-upcasting/subtrait-method.stderr | 10 ++++++++++ .../trivial-bounds/trivial-bounds-leak.stderr | 2 ++ .../ui/use/unused-trait-with-method-err.stderr | 2 ++ 29 files changed, 112 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/diagnostics.rs b/compiler/rustc_hir_typeck/src/diagnostics.rs index 0b4a88500aaaa..3f06ce69b7c87 100644 --- a/compiler/rustc_hir_typeck/src/diagnostics.rs +++ b/compiler/rustc_hir_typeck/src/diagnostics.rs @@ -894,7 +894,7 @@ pub(crate) struct NoAssociatedItem<'tcx> { )] pub(crate) struct CandidateTraitNote { #[primary_span] - pub span: Span, + pub span: MultiSpan, pub trait_name: String, pub item_name: Ident, pub action_or_ty: String, diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 53b081962a386..2dbeb64270c4c 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -4735,8 +4735,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if impls_trait(trait_info.def_id) { self.suggest_valid_traits(err, item_name, vec![trait_info.def_id], false); } else { + let mut span: MultiSpan = self.tcx.def_span(trait_info.def_id).into(); + for assoc in self + .tcx + .associated_items(trait_info.def_id) + .filter_by_name_unhygienic(item_name.name) + { + span.push_span_context(self.tcx.def_span(assoc.def_id).shrink_to_lo()); + } err.subdiagnostic(CandidateTraitNote { - span: self.tcx.def_span(trait_info.def_id), + span, trait_name: self.tcx.def_path_str(trait_info.def_id), item_name, action_or_ty: if trait_missing_method { diff --git a/tests/ui/associated-consts/associated-const-no-item.stderr b/tests/ui/associated-consts/associated-const-no-item.stderr index 40cd7791399b0..2cb3a0c33308f 100644 --- a/tests/ui/associated-consts/associated-const-no-item.stderr +++ b/tests/ui/associated-consts/associated-const-no-item.stderr @@ -10,6 +10,8 @@ note: `Foo` defines an item `ID`, perhaps you need to implement it | LL | trait Foo { | ^^^^^^^^^ +LL | const ID: i32; + | error: aborting due to 1 previous error diff --git a/tests/ui/delegation/inherent-impls-self-replacement.stderr b/tests/ui/delegation/inherent-impls-self-replacement.stderr index e017bee8af540..61eb8b363f359 100644 --- a/tests/ui/delegation/inherent-impls-self-replacement.stderr +++ b/tests/ui/delegation/inherent-impls-self-replacement.stderr @@ -109,6 +109,8 @@ note: `Trait` defines an item `get_s`, perhaps you need to implement it | LL | trait Trait: Sized { | ^^^^^^^^^^^^^^^^^^ +LL | fn get_s(self) -> S<(), 123>; + | error[E0599]: no method named `get_s` found for struct `Rc` in the current scope --> $DIR/inherent-impls-self-replacement.rs:49:38 @@ -122,6 +124,8 @@ note: `Trait` defines an item `get_s`, perhaps you need to implement it | LL | trait Trait: Sized { | ^^^^^^^^^^^^^^^^^^ +LL | fn get_s(self) -> S<(), 123>; + | error[E0599]: no method named `get_s` found for struct `Pin>` in the current scope --> $DIR/inherent-impls-self-replacement.rs:51:39 @@ -135,6 +139,8 @@ note: `Trait` defines an item `get_s`, perhaps you need to implement it | LL | trait Trait: Sized { | ^^^^^^^^^^^^^^^^^^ +LL | fn get_s(self) -> S<(), 123>; + | error[E0507]: cannot move out of `*self` which is behind a shared reference --> $DIR/inherent-impls-self-replacement.rs:24:34 diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/custom-on-unimplemented-diagnostic.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/custom-on-unimplemented-diagnostic.stderr index 1ba2c2e222dd0..1f34397c8a939 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/custom-on-unimplemented-diagnostic.stderr +++ b/tests/ui/diagnostic_namespace/on_unimplemented/custom-on-unimplemented-diagnostic.stderr @@ -26,6 +26,8 @@ note: `ProviderExt` defines an item `request`, perhaps you need to implement it | LL | pub trait ProviderExt { | ^^^^^^^^^^^^^^^^^^^^^ +LL | fn request(&self) { + | error: aborting due to 1 previous error diff --git a/tests/ui/functions-closures/fn-help-with-err.stderr b/tests/ui/functions-closures/fn-help-with-err.stderr index 9dc56c3652ac0..0540104d35620 100644 --- a/tests/ui/functions-closures/fn-help-with-err.stderr +++ b/tests/ui/functions-closures/fn-help-with-err.stderr @@ -16,6 +16,9 @@ note: `Bar` defines an item `bar`, perhaps you need to implement it | LL | trait Bar { | ^^^^^^^^^ +LL | +LL | fn bar(&self) {} + | help: use parentheses to call this closure | LL | arc2().bar(); diff --git a/tests/ui/generic-associated-types/method-unsatisfied-assoc-type-predicate.stderr b/tests/ui/generic-associated-types/method-unsatisfied-assoc-type-predicate.stderr index 61512dd46582d..24aabf2e8f898 100644 --- a/tests/ui/generic-associated-types/method-unsatisfied-assoc-type-predicate.stderr +++ b/tests/ui/generic-associated-types/method-unsatisfied-assoc-type-predicate.stderr @@ -20,6 +20,8 @@ note: `M` defines an item `f`, perhaps you need to implement it | LL | trait M { | ^^^^^^^ +LL | fn f(&self) {} + | error: aborting due to 1 previous error diff --git a/tests/ui/higher-ranked/hrtb-fn-ptr-impl-not-general-enough-57936.stderr b/tests/ui/higher-ranked/hrtb-fn-ptr-impl-not-general-enough-57936.stderr index f9862b8ab13a4..a90cedea04081 100644 --- a/tests/ui/higher-ranked/hrtb-fn-ptr-impl-not-general-enough-57936.stderr +++ b/tests/ui/higher-ranked/hrtb-fn-ptr-impl-not-general-enough-57936.stderr @@ -10,6 +10,9 @@ note: `X` defines an item `make_g`, perhaps you need to implement it | LL | trait X { | ^^^^^^^ +LL | type G; +LL | fn make_g() -> Self::G; + | error: implementation of `X` is not general enough --> $DIR/hrtb-fn-ptr-impl-not-general-enough-57936.rs:26:5 diff --git a/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-1.stderr b/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-1.stderr index 21dc5c66adf2b..d3b95365efced 100644 --- a/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-1.stderr +++ b/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-1.stderr @@ -21,6 +21,9 @@ note: `StreamExt` defines an item `filterx`, perhaps you need to implement it | LL | pub trait StreamExt | ^^^^^^^^^^^^^^^^^^^ +... +LL | fn filterx(self, func: F) -> Filter + | error: aborting due to 1 previous error diff --git a/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-2.stderr b/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-2.stderr index 91e65b2b07318..1b69dcb5788ec 100644 --- a/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-2.stderr +++ b/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-2.stderr @@ -21,6 +21,8 @@ note: `StreamExt` defines an item `countx`, perhaps you need to implement it | LL | pub trait StreamExt | ^^^^^^^^^^^^^^^^^^^ +... +LL | fn countx(mut self) -> usize = note: the full name for the type has been written to '$TEST_BUILD_DIR/hrtb-doesnt-borrow-self-2.long-type-$LONG_TYPE_HASH.txt' = note: consider using `--verbose` to print the full type name to the console diff --git a/tests/ui/impl-trait/call_method_on_inherent_impl_on_rigid_type.current.stderr b/tests/ui/impl-trait/call_method_on_inherent_impl_on_rigid_type.current.stderr index 3822bb5c9729e..2b1aca253d173 100644 --- a/tests/ui/impl-trait/call_method_on_inherent_impl_on_rigid_type.current.stderr +++ b/tests/ui/impl-trait/call_method_on_inherent_impl_on_rigid_type.current.stderr @@ -10,6 +10,8 @@ note: `MyDebug` defines an item `my_debug`, perhaps you need to implement it | LL | trait MyDebug { | ^^^^^^^^^^^^^ +LL | fn my_debug(&self); + | error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/call_method_on_inherent_impl_ref-err.current.stderr b/tests/ui/impl-trait/call_method_on_inherent_impl_ref-err.current.stderr index f77c44118966b..6c3ed0efcd9f4 100644 --- a/tests/ui/impl-trait/call_method_on_inherent_impl_ref-err.current.stderr +++ b/tests/ui/impl-trait/call_method_on_inherent_impl_ref-err.current.stderr @@ -13,6 +13,8 @@ note: `MyDebug` defines an item `my_debug`, perhaps you need to implement it | LL | trait MyDebug { | ^^^^^^^^^^^^^ +LL | fn my_debug(&self); + | error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/call_method_on_inherent_impl_ref-err.next.stderr b/tests/ui/impl-trait/call_method_on_inherent_impl_ref-err.next.stderr index 71c588cd3a62f..94985e5e60b4e 100644 --- a/tests/ui/impl-trait/call_method_on_inherent_impl_ref-err.next.stderr +++ b/tests/ui/impl-trait/call_method_on_inherent_impl_ref-err.next.stderr @@ -13,6 +13,8 @@ note: `MyDebug` defines an item `my_debug`, perhaps you need to implement it | LL | trait MyDebug { | ^^^^^^^^^^^^^ +LL | fn my_debug(&self); + | error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/issues/issue-21659-show-relevant-trait-impls-3.stderr b/tests/ui/impl-trait/issues/issue-21659-show-relevant-trait-impls-3.stderr index e5d7183937387..7834db7c5ff3c 100644 --- a/tests/ui/impl-trait/issues/issue-21659-show-relevant-trait-impls-3.stderr +++ b/tests/ui/impl-trait/issues/issue-21659-show-relevant-trait-impls-3.stderr @@ -13,6 +13,8 @@ note: `Foo` defines an item `foo`, perhaps you need to implement it | LL | trait Foo { | ^^^^^^^^^^^^ +LL | fn foo(&self, a: A) -> A { + | error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/method/would-constrain-opaque.current.stderr b/tests/ui/impl-trait/method/would-constrain-opaque.current.stderr index e29297ceea024..1e9a34b381192 100644 --- a/tests/ui/impl-trait/method/would-constrain-opaque.current.stderr +++ b/tests/ui/impl-trait/method/would-constrain-opaque.current.stderr @@ -10,6 +10,8 @@ note: `Trait` defines an item `method`, perhaps you need to implement it | LL | trait Trait: Sized { | ^^^^^^^^^^^^^^^^^^ +LL | fn method(self) {} + | error[E0599]: no method named `method` found for reference `&impl Sized` in the current scope --> $DIR/would-constrain-opaque.rs:32:11 @@ -23,6 +25,8 @@ note: `Trait` defines an item `method`, perhaps you need to implement it | LL | trait Trait: Sized { | ^^^^^^^^^^^^^^^^^^ +LL | fn method(self) {} + | error: aborting due to 2 previous errors diff --git a/tests/ui/impl-trait/no-method-suggested-traits.stderr b/tests/ui/impl-trait/no-method-suggested-traits.stderr index ca383523f9238..1524679f8ba34 100644 --- a/tests/ui/impl-trait/no-method-suggested-traits.stderr +++ b/tests/ui/impl-trait/no-method-suggested-traits.stderr @@ -147,6 +147,9 @@ note: `foo::Bar` defines an item `method2`, perhaps you need to implement it | LL | pub trait Bar { | ^^^^^^^^^^^^^ +... +LL | fn method2(&self) {} + | error[E0599]: no method named `method2` found for struct `Rc<&mut Box<&u64>>` in the current scope --> $DIR/no-method-suggested-traits.rs:50:44 @@ -160,6 +163,9 @@ note: `foo::Bar` defines an item `method2`, perhaps you need to implement it | LL | pub trait Bar { | ^^^^^^^^^^^^^ +... +LL | fn method2(&self) {} + | error[E0599]: no method named `method2` found for struct `no_method_suggested_traits::Foo` in the current scope --> $DIR/no-method-suggested-traits.rs:53:37 @@ -173,6 +179,9 @@ note: `foo::Bar` defines an item `method2`, perhaps you need to implement it | LL | pub trait Bar { | ^^^^^^^^^^^^^ +... +LL | fn method2(&self) {} + | error[E0599]: no method named `method2` found for struct `Rc<&mut Box<&no_method_suggested_traits::Foo>>` in the current scope --> $DIR/no-method-suggested-traits.rs:55:71 @@ -186,6 +195,9 @@ note: `foo::Bar` defines an item `method2`, perhaps you need to implement it | LL | pub trait Bar { | ^^^^^^^^^^^^^ +... +LL | fn method2(&self) {} + | error[E0599]: no method named `method2` found for enum `no_method_suggested_traits::Bar` in the current scope --> $DIR/no-method-suggested-traits.rs:57:40 @@ -199,6 +211,9 @@ note: `foo::Bar` defines an item `method2`, perhaps you need to implement it | LL | pub trait Bar { | ^^^^^^^^^^^^^ +... +LL | fn method2(&self) {} + | error[E0599]: no method named `method2` found for struct `Rc<&mut Box<&no_method_suggested_traits::Bar>>` in the current scope --> $DIR/no-method-suggested-traits.rs:59:74 @@ -212,6 +227,9 @@ note: `foo::Bar` defines an item `method2`, perhaps you need to implement it | LL | pub trait Bar { | ^^^^^^^^^^^^^ +... +LL | fn method2(&self) {} + | error[E0599]: no method named `method3` found for struct `Foo` in the current scope --> $DIR/no-method-suggested-traits.rs:62:9 diff --git a/tests/ui/iterators/iterator-does-not-need-into-iter.stderr b/tests/ui/iterators/iterator-does-not-need-into-iter.stderr index 3d3861e959f82..79810f761ee83 100644 --- a/tests/ui/iterators/iterator-does-not-need-into-iter.stderr +++ b/tests/ui/iterators/iterator-does-not-need-into-iter.stderr @@ -22,6 +22,8 @@ note: `HasMethod` defines an item `foo`, perhaps you need to implement it | LL | trait HasMethod { | ^^^^^^^^^^^^^^^ +LL | fn foo(self); + | error: aborting due to 1 previous error diff --git a/tests/ui/methods/tuple-suggestions-issue-142488.stderr b/tests/ui/methods/tuple-suggestions-issue-142488.stderr index f9363bb216f43..2ef7483770bf5 100644 --- a/tests/ui/methods/tuple-suggestions-issue-142488.stderr +++ b/tests/ui/methods/tuple-suggestions-issue-142488.stderr @@ -33,6 +33,8 @@ note: `Trait` defines an item `meth`, perhaps you need to implement it | LL | trait Trait { | ^^^^^^^^^^^ +LL | fn meth(self); + | error[E0599]: the method `f` exists for tuple `(_,)`, but its trait bounds were not satisfied --> $DIR/tuple-suggestions-issue-142488.rs:46:16 @@ -54,6 +56,8 @@ note: `Tr` defines an item `f`, perhaps you need to implement it | LL | trait Tr { | ^^^^^^^^ +LL | fn f(self) -> (A,) + | error: aborting due to 4 previous errors diff --git a/tests/ui/methods/vec-autoderef-autoref.stderr b/tests/ui/methods/vec-autoderef-autoref.stderr index 61c3bcc5b3b37..ecebce9430fc5 100644 --- a/tests/ui/methods/vec-autoderef-autoref.stderr +++ b/tests/ui/methods/vec-autoderef-autoref.stderr @@ -10,6 +10,8 @@ note: `MyIter` defines an item `test_mut`, perhaps you need to implement it | LL | trait MyIter { | ^^^^^^^^^^^^ +LL | fn test_mut(&mut self); + | help: there is a method `get_mut` with a similar name, but with different arguments --> $SRC_DIR/core/src/slice/mod.rs:LL:COL @@ -25,6 +27,9 @@ note: `MyIter` defines an item `test`, perhaps you need to implement it | LL | trait MyIter { | ^^^^^^^^^^^^ +LL | fn test_mut(&mut self); +LL | fn test(&self); + | error[E0599]: no method named `test` found for array `[{integer}; 1]` in the current scope --> $DIR/vec-autoderef-autoref.rs:9:11 @@ -38,6 +43,9 @@ note: `MyIter` defines an item `test`, perhaps you need to implement it | LL | trait MyIter { | ^^^^^^^^^^^^ +LL | fn test_mut(&mut self); +LL | fn test(&self); + | error[E0599]: no method named `test` found for reference `&[{integer}; 1]` in the current scope --> $DIR/vec-autoderef-autoref.rs:10:12 @@ -51,6 +59,9 @@ note: `MyIter` defines an item `test`, perhaps you need to implement it | LL | trait MyIter { | ^^^^^^^^^^^^ +LL | fn test_mut(&mut self); +LL | fn test(&self); + | error: aborting due to 4 previous errors diff --git a/tests/ui/nll/issue-57362-1.stderr b/tests/ui/nll/issue-57362-1.stderr index 79b1b131afa2c..892b9b7b30141 100644 --- a/tests/ui/nll/issue-57362-1.stderr +++ b/tests/ui/nll/issue-57362-1.stderr @@ -10,6 +10,8 @@ note: `Trait` defines an item `f`, perhaps you need to implement it | LL | trait Trait { | ^^^^^^^^^^^ +LL | fn f(self); + | error: aborting due to 1 previous error diff --git a/tests/ui/nll/issue-57362-2.stderr b/tests/ui/nll/issue-57362-2.stderr index 1b962b16bdc8f..913ae08efb788 100644 --- a/tests/ui/nll/issue-57362-2.stderr +++ b/tests/ui/nll/issue-57362-2.stderr @@ -10,6 +10,9 @@ note: `X` defines an item `make_g`, perhaps you need to implement it | LL | trait X { | ^^^^^^^ +LL | type G; +LL | fn make_g() -> Self::G; + | error: aborting due to 1 previous error diff --git a/tests/ui/nll/issue-57642-higher-ranked-subtype.stderr b/tests/ui/nll/issue-57642-higher-ranked-subtype.stderr index 0a56d1bc72031..dac3e57747ce1 100644 --- a/tests/ui/nll/issue-57642-higher-ranked-subtype.stderr +++ b/tests/ui/nll/issue-57642-higher-ranked-subtype.stderr @@ -10,6 +10,9 @@ note: `X` defines an item `make_g`, perhaps you need to implement it | LL | trait X { | ^^^^^^^ +LL | type G; +LL | fn make_g() -> Self::G; + | error[E0599]: no associated function or constant named `make_f` found for fn pointer `for<'a> fn(&'a ())` in the current scope --> $DIR/issue-57642-higher-ranked-subtype.rs:36:25 @@ -23,6 +26,9 @@ note: `Y` defines an item `make_f`, perhaps you need to implement it | LL | trait Y { | ^^^^^^^ +LL | type F; +LL | fn make_f() -> Self::F; + | error: aborting due to 2 previous errors diff --git a/tests/ui/specialization/default-impl-not-a-candidate-issue-48515.stderr b/tests/ui/specialization/default-impl-not-a-candidate-issue-48515.stderr index 91df5005d7095..7f82ae5e5a5c6 100644 --- a/tests/ui/specialization/default-impl-not-a-candidate-issue-48515.stderr +++ b/tests/ui/specialization/default-impl-not-a-candidate-issue-48515.stderr @@ -56,6 +56,8 @@ note: `Set` defines an item `contains`, perhaps you need to implement it | LL | trait Set { | ^^^^^^^^^^^^ +LL | fn contains(&self, bit: T); + | error: aborting due to 4 previous errors diff --git a/tests/ui/specialization/default-impl-not-an-impl.stderr b/tests/ui/specialization/default-impl-not-an-impl.stderr index cde757b3f98a7..c6dcafdd7bb06 100644 --- a/tests/ui/specialization/default-impl-not-an-impl.stderr +++ b/tests/ui/specialization/default-impl-not-an-impl.stderr @@ -33,6 +33,8 @@ note: `Foo` defines an item `f`, perhaps you need to implement it | LL | trait Foo { | ^^^^^^^^^ +LL | fn f(&self) -> u32; + | error[E0277]: the trait bound `u32: Foo` is not satisfied --> $DIR/default-impl-not-an-impl.rs:65:23 diff --git a/tests/ui/specialization/defaultimpl/specialization-trait-not-implemented.stderr b/tests/ui/specialization/defaultimpl/specialization-trait-not-implemented.stderr index 7cec84b1af6dd..bc3f1e1cce908 100644 --- a/tests/ui/specialization/defaultimpl/specialization-trait-not-implemented.stderr +++ b/tests/ui/specialization/defaultimpl/specialization-trait-not-implemented.stderr @@ -13,6 +13,8 @@ note: `Foo` defines an item `foo_one`, perhaps you need to implement it | LL | trait Foo { | ^^^^^^^^^ +LL | fn foo_one(&self) -> &'static str; + | error: aborting due to 1 previous error diff --git a/tests/ui/traits/negative-impls/explicitly-unimplemented-error-message.stderr b/tests/ui/traits/negative-impls/explicitly-unimplemented-error-message.stderr index b29442d7b8fb2..8bbdd2f75c538 100644 --- a/tests/ui/traits/negative-impls/explicitly-unimplemented-error-message.stderr +++ b/tests/ui/traits/negative-impls/explicitly-unimplemented-error-message.stderr @@ -45,6 +45,7 @@ note: `Foo` defines an item `foo`, perhaps you need to implement it | LL | trait Foo { | ^^^^^^^^^ +LL | fn foo(&self); = note: the trait `FooBar` defines an item `foo`, but is explicitly unimplemented error: aborting due to 4 previous errors diff --git a/tests/ui/traits/trait-upcasting/subtrait-method.stderr b/tests/ui/traits/trait-upcasting/subtrait-method.stderr index a16daa11d6f7a..9bfc3dfde0430 100644 --- a/tests/ui/traits/trait-upcasting/subtrait-method.stderr +++ b/tests/ui/traits/trait-upcasting/subtrait-method.stderr @@ -10,6 +10,8 @@ note: `Baz` defines an item `c`, perhaps you need to implement it | LL | trait Baz: Bar { | ^^^^^^^^^^^^^^ +LL | fn c(&self) -> i32 { + | help: there is a method `a` with a similar name | LL - bar.c(); @@ -28,6 +30,8 @@ note: `Bar` defines an item `b`, perhaps you need to implement it | LL | trait Bar: Foo { | ^^^^^^^^^^^^^^ +LL | fn b(&self) -> i32 { + | help: there is a method `a` with a similar name | LL - foo.b(); @@ -46,6 +50,8 @@ note: `Baz` defines an item `c`, perhaps you need to implement it | LL | trait Baz: Bar { | ^^^^^^^^^^^^^^ +LL | fn c(&self) -> i32 { + | help: there is a method `a` with a similar name | LL - foo.c(); @@ -64,6 +70,8 @@ note: `Bar` defines an item `b`, perhaps you need to implement it | LL | trait Bar: Foo { | ^^^^^^^^^^^^^^ +LL | fn b(&self) -> i32 { + | help: there is a method `a` with a similar name | LL - foo.b(); @@ -82,6 +90,8 @@ note: `Baz` defines an item `c`, perhaps you need to implement it | LL | trait Baz: Bar { | ^^^^^^^^^^^^^^ +LL | fn c(&self) -> i32 { + | help: there is a method `a` with a similar name | LL - foo.c(); diff --git a/tests/ui/trivial-bounds/trivial-bounds-leak.stderr b/tests/ui/trivial-bounds/trivial-bounds-leak.stderr index be472a5076913..b9882c74ad44f 100644 --- a/tests/ui/trivial-bounds/trivial-bounds-leak.stderr +++ b/tests/ui/trivial-bounds/trivial-bounds-leak.stderr @@ -19,6 +19,8 @@ note: `Foo` defines an item `test`, perhaps you need to implement it | LL | pub trait Foo { | ^^^^^^^^^^^^^ +LL | fn test(&self); + | error[E0277]: the trait bound `i32: Foo` is not satisfied --> $DIR/trivial-bounds-leak.rs:25:15 diff --git a/tests/ui/use/unused-trait-with-method-err.stderr b/tests/ui/use/unused-trait-with-method-err.stderr index 7ca4563673bb1..d748fddcf374a 100644 --- a/tests/ui/use/unused-trait-with-method-err.stderr +++ b/tests/ui/use/unused-trait-with-method-err.stderr @@ -13,6 +13,8 @@ note: `Bar` defines an item `uwu`, perhaps you need to implement it | LL | pub trait Bar { | ^^^^^^^^^^^^^ +LL | fn uwu(&self) {} + | error: aborting due to 1 previous error From bee7ba847e2aa00d428c818e875003928ad553fc Mon Sep 17 00:00:00 2001 From: ozankenangungor Date: Mon, 12 Oct 2026 23:48:29 +0300 Subject: [PATCH 06/23] Simplify associated type bound checks --- .../traits/fulfillment_errors.rs | 37 +++++++------------ 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 000a4c1cdec43..fb527466d7fa4 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -67,17 +67,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let ObligationCauseCode::WhereClause(item_def_id, span) = *cause_code.peel_derives() else { return false; }; - let is_impl_trait_assoc_type = if let Some(assoc_item) = - self.tcx.opt_associated_item(body_def_id.to_def_id()) - && let Some(trait_item_def_id) = assoc_item.trait_item_def_id() - { - trait_item_def_id == item_def_id - && matches!(assoc_item.kind, ty::AssocKind::Type { .. }) - } else { - false - }; - if !is_impl_trait_assoc_type { + if !self.tcx.opt_associated_item(body_def_id.to_def_id()).is_some_and(|assoc_item| { + assoc_item.trait_item_def_id().is_some_and(|trait_item_def_id| { + trait_item_def_id == item_def_id + && matches!(assoc_item.kind, ty::AssocKind::Type { .. }) + }) + }) { return false; } @@ -208,20 +204,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let Err(guar) = leaf_trait_predicate.error_reported() { return guar; } - let impl_assoc_type_self_trait_bound_param = - if self.is_explicit_self_trait_bound_on_impl_assoc_type( - obligation.cause.body_def_id, - obligation.cause.code(), - leaf_trait_predicate.def_id(), - ) && let ty::Param(param_ty) = - *leaf_trait_predicate.skip_binder().self_ty().kind() - { - Some(param_ty) - } else { - None - }; - - if let Some(param_ty) = impl_assoc_type_self_trait_bound_param { + if self.is_explicit_self_trait_bound_on_impl_assoc_type( + obligation.cause.body_def_id, + obligation.cause.code(), + leaf_trait_predicate.def_id(), + ) && let ty::Param(param_ty) = + *leaf_trait_predicate.skip_binder().self_ty().kind() + { span = param_ty.span_from_generics( self.tcx, obligation.cause.body_def_id.to_def_id(), From 192d095f71e44f3ffb25ba36b8e645ceba0569a9 Mon Sep 17 00:00:00 2001 From: ozankenangungor Date: Tue, 13 Oct 2026 00:14:38 +0300 Subject: [PATCH 07/23] Document associated type guard --- .../src/error_reporting/traits/fulfillment_errors.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index fb527466d7fa4..b7f3aab985863 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -67,7 +67,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let ObligationCauseCode::WhereClause(item_def_id, span) = *cause_code.peel_derives() else { return false; }; - + // Continue only if there is an impl associated type corresponding to the expected trait item. if !self.tcx.opt_associated_item(body_def_id.to_def_id()).is_some_and(|assoc_item| { assoc_item.trait_item_def_id().is_some_and(|trait_item_def_id| { trait_item_def_id == item_def_id From f8d4856c08ecbdc05e7766b12e809a90ee7ede22 Mon Sep 17 00:00:00 2001 From: Urgau Date: Tue, 13 Oct 2026 20:47:57 +0200 Subject: [PATCH 08/23] Output remapped paths in rustdoc JSON with the `DOCUMENTATION` scope --- src/librustdoc/json/conversions.rs | 21 ++++++++----------- .../remap-path-prefix/debuginfo.rs | 5 +++++ tests/rustdoc-json/remap-path-prefix/diag.rs | 5 +++++ tests/rustdoc-json/remap-path-prefix/doc.rs | 5 +++++ .../rustdoc-json/remap-path-prefix/object.rs | 5 +++++ .../remap-path-prefix/remap-path-prefix.rs | 4 ++++ 6 files changed, 33 insertions(+), 12 deletions(-) create mode 100644 tests/rustdoc-json/remap-path-prefix/debuginfo.rs create mode 100644 tests/rustdoc-json/remap-path-prefix/diag.rs create mode 100644 tests/rustdoc-json/remap-path-prefix/doc.rs create mode 100644 tests/rustdoc-json/remap-path-prefix/object.rs create mode 100644 tests/rustdoc-json/remap-path-prefix/remap-path-prefix.rs diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 556a922f7044c..d23e9a8ca456a 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -17,7 +17,7 @@ use rustc_hir_pretty::PpAnn; use rustc_metadata::rendered_const; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::def_id::ModId; -use rustc_span::{Pos, Symbol, bug, kw, sym}; +use rustc_span::{Pos, RemapPathScopeComponents, Symbol, bug, kw, sym}; use rustdoc_json_types::*; use crate::clean::{self, ItemId}; @@ -191,17 +191,14 @@ impl FromClean for Option { fn from_clean(span: &clean::Span, renderer: &JsonRenderer<'_>) -> Self { match span.filename(renderer.sess()) { rustc_span::FileName::Real(name) => { - if let Some(local_path) = name.into_local_path() { - let hi = span.hi(renderer.sess()); - let lo = span.lo(renderer.sess()); - Some(Span { - filename: local_path, - begin: (lo.line, lo.col.to_usize() + 1), - end: (hi.line, hi.col.to_usize() + 1), - }) - } else { - None - } + let filename = name.path(RemapPathScopeComponents::DOCUMENTATION); + let hi = span.hi(renderer.sess()); + let lo = span.lo(renderer.sess()); + Some(Span { + filename: filename.to_path_buf(), + begin: (lo.line, lo.col.to_usize() + 1), + end: (hi.line, hi.col.to_usize() + 1), + }) } _ => None, } diff --git a/tests/rustdoc-json/remap-path-prefix/debuginfo.rs b/tests/rustdoc-json/remap-path-prefix/debuginfo.rs new file mode 100644 index 0000000000000..d378dca22be58 --- /dev/null +++ b/tests/rustdoc-json/remap-path-prefix/debuginfo.rs @@ -0,0 +1,5 @@ +//@ compile-flags: --remap-path-prefix={{src-base}}=/remapped +//@ compile-flags: --remap-path-scope=debuginfo -Zunstable-options + +//@ is "$.index[?(@.name=='StrLike')].span.filename" $FILE +pub trait StrLike {} diff --git a/tests/rustdoc-json/remap-path-prefix/diag.rs b/tests/rustdoc-json/remap-path-prefix/diag.rs new file mode 100644 index 0000000000000..79b469c40084e --- /dev/null +++ b/tests/rustdoc-json/remap-path-prefix/diag.rs @@ -0,0 +1,5 @@ +//@ compile-flags: --remap-path-prefix={{src-base}}=/remapped +//@ compile-flags: --remap-path-scope=diagnostics -Zunstable-options + +//@ is "$.index[?(@.name=='StrLike')].span.filename" $FILE +pub trait StrLike {} diff --git a/tests/rustdoc-json/remap-path-prefix/doc.rs b/tests/rustdoc-json/remap-path-prefix/doc.rs new file mode 100644 index 0000000000000..11fa449983754 --- /dev/null +++ b/tests/rustdoc-json/remap-path-prefix/doc.rs @@ -0,0 +1,5 @@ +//@ compile-flags: --remap-path-prefix={{src-base}}=/remapped +//@ compile-flags: --remap-path-scope=documentation -Zunstable-options + +//@ is "$.index[?(@.name=='StrLike')].span.filename" \"/remapped/remap-path-prefix/doc.rs\" +pub trait StrLike {} diff --git a/tests/rustdoc-json/remap-path-prefix/object.rs b/tests/rustdoc-json/remap-path-prefix/object.rs new file mode 100644 index 0000000000000..052ad7651eeab --- /dev/null +++ b/tests/rustdoc-json/remap-path-prefix/object.rs @@ -0,0 +1,5 @@ +//@ compile-flags: --remap-path-prefix={{src-base}}=/remapped +//@ compile-flags: --remap-path-scope=object -Zunstable-options + +//@ is "$.index[?(@.name=='StrLike')].span.filename" $FILE +pub trait StrLike {} diff --git a/tests/rustdoc-json/remap-path-prefix/remap-path-prefix.rs b/tests/rustdoc-json/remap-path-prefix/remap-path-prefix.rs new file mode 100644 index 0000000000000..1d0a1fa65fad0 --- /dev/null +++ b/tests/rustdoc-json/remap-path-prefix/remap-path-prefix.rs @@ -0,0 +1,4 @@ +//@ compile-flags: --remap-path-prefix={{src-base}}=/remapped + +//@ is "$.index[?(@.name=='StrLike')].span.filename" \"/remapped/remap-path-prefix/remap-path-prefix.rs\" +pub trait StrLike {} From 0d0f40400243a926ab26953a5b089252c06035fc Mon Sep 17 00:00:00 2001 From: Urgau Date: Tue, 13 Oct 2026 21:54:04 +0200 Subject: [PATCH 09/23] Add README.md and descriptions for the remap-path-prefix JSON tests --- tests/rustdoc-json/README.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 tests/rustdoc-json/README.md diff --git a/tests/rustdoc-json/README.md b/tests/rustdoc-json/README.md new file mode 100644 index 0000000000000..875a149585010 --- /dev/null +++ b/tests/rustdoc-json/README.md @@ -0,0 +1,7 @@ +## `tests/rustdoc-json/remap-path-prefix` + +Tests for `--remap-path-prefix` and `--remap-path-scope` with rustdoc JSON: + - `remap-path-prefix`: only uses bare `--remap-path-prefix`, expects remapped paths + - `doc`: pass `--remap-path-prefix` and the `documentation` scope, expects remapped paths + - `object`: pass `--remap-path-prefix` and the `object` scope, expects un-remapped paths + - `debuginfo`: pass `--remap-path-prefix` and the `debuginfo` scope, expects un-remapped paths From 60ec7daab75c946fada8778a1871718a7c4e721b Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 14 Oct 2026 12:40:44 +1100 Subject: [PATCH 10/23] Move `thin_lto_buffer` to a OnceLock in ModuleLlvm This LTO buffer appears to be an implementation detail of the LLVM backend, as it is not touched by other in-tree backends. Using OnceLock makes it possible to set the buffer through a shared reference. This will be necessary when enforcing scoped access to the other contents of ModuleLlvm. --- .../rustc_codegen_llvm/src/back/llvm_backend.rs | 16 +++++++++++++++- compiler/rustc_codegen_llvm/src/back/lto.rs | 6 +++++- compiler/rustc_codegen_llvm/src/back/write.rs | 8 ++++++-- compiler/rustc_codegen_ssa/src/lib.rs | 16 ++-------------- 4 files changed, 28 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs b/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs index 8780af2f6d595..269540f268e8e 100644 --- a/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs +++ b/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs @@ -5,6 +5,7 @@ use std::any::Any; use std::ffi::CStr; use std::mem::ManuallyDrop; use std::path::PathBuf; +use std::sync::OnceLock; use rustc_ast::expand::allocator::AllocatorMethod; use rustc_codegen_ssa::back::lto::ThinModule; @@ -427,6 +428,12 @@ pub struct ModuleLlvm { // This field is `ManuallyDrop` because it is important that the `TargetMachine` // is disposed prior to the `Context` being disposed otherwise UAFs can occur. pub(crate) tm: ManuallyDrop, + + /// Saved ThinLTO buffer for embedding in the object file. + /// + /// This is currently a `OnceLock` to avoid aliasing problems when writing to it + /// from code that also wants to pass around a shared ref to `ModuleCodegen`. + pub(crate) thin_lto_buffer: OnceLock>, } unsafe impl Send for ModuleLlvm {} @@ -442,6 +449,7 @@ impl ModuleLlvm { llmod_raw, llcx, tm: ManuallyDrop::new(create_target_machine(tcx, mod_name)), + thin_lto_buffer: OnceLock::new(), } } } @@ -455,6 +463,7 @@ impl ModuleLlvm { llmod_raw, llcx, tm: ManuallyDrop::new(create_informational_target_machine(tcx.sess)), + thin_lto_buffer: OnceLock::new(), } } } @@ -472,7 +481,12 @@ impl ModuleLlvm { let llmod_raw = back::lto::parse_module(llcx, name, buffer, dcx); let tm = tm_factory(dcx, TargetMachineFactoryConfig::new(cgcx, name.to_str().unwrap())); - ModuleLlvm { llmod_raw, llcx, tm: ManuallyDrop::new(tm) } + ModuleLlvm { + llmod_raw, + llcx, + tm: ManuallyDrop::new(tm), + thin_lto_buffer: OnceLock::new(), + } } } diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index b171424b46245..90bdd2e42fea6 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -718,7 +718,11 @@ pub(crate) fn optimize_and_codegen_thin_module( let mut module = ModuleCodegen::new_regular(thin_module.name(), module_llvm); // Given that the newly created module lacks a thinlto buffer for embedding, we need to re-add it here. if cgcx.module_config.embed_bitcode() { - module.thin_lto_buffer = Some(thin_module.data().to_vec()); + module + .module_llvm + .thin_lto_buffer + .set(thin_module.data().to_vec()) + .expect("thin_lto_buffer should be unset"); } { let target = &*module.module_llvm.tm; diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index b7e400567b1f3..56b5e45b85a2f 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -937,7 +937,11 @@ pub(crate) fn optimize( }; if let Some(thin_lto_buffer) = thin_lto_buffer { let thin_lto_buffer = thin_lto_buffer.unwrap(); - module.thin_lto_buffer = Some(thin_lto_buffer.data().to_vec()); + module + .module_llvm + .thin_lto_buffer + .set(thin_lto_buffer.data().to_vec()) + .expect("thin_lto_buffer should be unset"); let bc_summary_out = cgcx.output_filenames.temp_path_for_cgu(OutputType::ThinLinkBitcode, &module.name); if let Some(thin_lto_summary_buffer) = thin_lto_summary_buffer @@ -1025,7 +1029,7 @@ pub(crate) fn codegen( let _timer = prof .generic_activity_with_arg("LLVM_module_codegen_embed_bitcode", &*module.name); let thin_bc = - module.thin_lto_buffer.as_deref().expect("cannot find embedded bitcode"); + module.module_llvm.thin_lto_buffer.get().expect("cannot find embedded bitcode"); embed_bitcode(cgcx, llcx, llmod, &thin_bc); } } diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index af37694136d24..1ac9564db0149 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -65,27 +65,15 @@ pub struct ModuleCodegen { pub name: String, pub module_llvm: M, pub kind: ModuleKind, - /// Saving the ThinLTO buffer for embedding in the object file. - pub thin_lto_buffer: Option>, } impl ModuleCodegen { pub fn new_regular(name: impl Into, module: M) -> Self { - Self { - name: name.into(), - module_llvm: module, - kind: ModuleKind::Regular, - thin_lto_buffer: None, - } + Self { name: name.into(), module_llvm: module, kind: ModuleKind::Regular } } pub fn new_allocator(name: impl Into, module: M) -> Self { - Self { - name: name.into(), - module_llvm: module, - kind: ModuleKind::Allocator, - thin_lto_buffer: None, - } + Self { name: name.into(), module_llvm: module, kind: ModuleKind::Allocator } } pub fn into_compiled_module( From d3f7014e7ab83f9e1cabddb9e752a82e912fc431 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 9 Oct 2026 21:03:53 +1100 Subject: [PATCH 11/23] Use a callback to enforce scoped lifetimes in ModuleLlvm Codegen code that needs any of `llcx`/`llmod`/`tm` must now access them through a callback, to ensure that the `'ll` lifetimes are coherent and non-static, and don't accidentally escape. --- .../src/back/llvm_backend.rs | 50 +++++++++--- compiler/rustc_codegen_llvm/src/back/lto.rs | 71 +++++++++-------- compiler/rustc_codegen_llvm/src/back/write.rs | 76 +++++++++++-------- compiler/rustc_codegen_llvm/src/base.rs | 6 +- compiler/rustc_codegen_llvm/src/context.rs | 5 +- 5 files changed, 130 insertions(+), 78 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs b/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs index 269540f268e8e..6d0da50afc312 100644 --- a/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs +++ b/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs @@ -3,6 +3,7 @@ use std::any::Any; use std::ffi::CStr; +use std::marker::PhantomData; use std::mem::ManuallyDrop; use std::path::PathBuf; use std::sync::OnceLock; @@ -60,11 +61,10 @@ impl ExtraBackendMethods for LlvmCodegenBackend { methods: &[AllocatorMethod], ) -> ModuleLlvm { let module_llvm = ModuleLlvm::new_metadata(tcx, module_name); - let cx = - SimpleCx::new(module_llvm.llmod(), &module_llvm.llcx, tcx.data_layout.pointer_size()); - unsafe { - allocator::codegen(tcx, cx, module_name, methods); - } + module_llvm.with_module_refs(|ModuleRefs { llcx, llmod, .. }| { + let cx = SimpleCx::new(llmod, llcx, tcx.data_layout.pointer_size()); + unsafe { allocator::codegen(tcx, cx, module_name, methods) }; + }); module_llvm } fn compile_codegen_unit( @@ -142,7 +142,7 @@ impl WriteBackendMethods for LlvmCodegenBackend { module: &mut ModuleCodegen, config: &ModuleConfig, ) { - back::write::optimize(cgcx, prof, shared_emitter, module, config) + back::write::optimize(cgcx, prof, shared_emitter, module, config); } fn optimize_and_codegen_thin( cgcx: &CodegenContext, @@ -163,7 +163,9 @@ impl WriteBackendMethods for LlvmCodegenBackend { back::write::codegen(cgcx, prof, shared_emitter, module, config) } fn serialize_module(module: Self::Module, is_thin: bool) -> Self::ModuleBuffer { - back::lto::ModuleBuffer::new(module.llmod(), is_thin) + module.with_module_refs(|ModuleRefs { llmod, .. }| { + back::lto::ModuleBuffer::new(llmod, is_thin) + }) } } @@ -422,12 +424,12 @@ impl CodegenBackend for LlvmCodegenBackend { } pub struct ModuleLlvm { - pub(crate) llcx: &'static mut llvm::Context, + llcx: &'static mut llvm::Context, llmod_raw: *const llvm::Module, // This field is `ManuallyDrop` because it is important that the `TargetMachine` // is disposed prior to the `Context` being disposed otherwise UAFs can occur. - pub(crate) tm: ManuallyDrop, + tm: ManuallyDrop, /// Saved ThinLTO buffer for embedding in the object file. /// @@ -490,8 +492,21 @@ impl ModuleLlvm { } } - pub(crate) fn llmod(&self) -> &llvm::Module { - unsafe { &*self.llmod_raw } + /// Passes a [`ModuleRefs`] struct to a callback function, giving it scoped + /// access to the resources owned by this module. + /// + /// Passing the references to a callback ensures that they have coherent + /// `'ll` lifetimes, and that they can't escape the callback. + pub(crate) fn with_module_refs( + &self, + inner_fn: impl for<'ll> FnOnce(ModuleRefs<'ll>) -> R, + ) -> R { + inner_fn(ModuleRefs { + llcx: self.llcx, + llmod: unsafe { &*self.llmod_raw }, + tm: self.tm.raw(), + _ph: PhantomData, + }) } } @@ -503,3 +518,16 @@ impl Drop for ModuleLlvm { } } } + +/// Bundle of references provided by [`ModuleLlvm::with_module_refs`]. +#[derive(Clone, Copy)] +pub(crate) struct ModuleRefs<'ll> { + pub(crate) llcx: &'ll llvm::Context, + pub(crate) llmod: &'ll llvm::Module, + pub(crate) tm: &'ll llvm::TargetMachine, + /// Make sure `'ll` is invariant, even if no fields happen to require that. + /// + /// FIXME(Zalathar): Consider using `PhantomInvariantLifetime` when the API + /// for that is settled. + _ph: PhantomData &'ll ()>, +} diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 90bdd2e42fea6..6d7c406946127 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -24,6 +24,7 @@ use rustc_span::bug; use rustc_structures::SanitizerSet; use tracing::{debug, info}; +use crate::back::llvm_backend::ModuleRefs; use crate::back::write::{ self, CodegenDiagnosticsStage, DiagnosticHandlers, bitcode_section_name, codegen, save_temp_bitcode, @@ -249,7 +250,9 @@ fn fat_lto( .enumerate() .filter(|&(_, module)| module.kind == ModuleKind::Regular) .map(|(i, module)| { - let cost = unsafe { llvm::LLVMRustModuleCost(module.module_llvm.llmod()) }; + let cost = module.module_llvm.with_module_refs(|ModuleRefs { llmod, .. }| unsafe { + llvm::LLVMRustModuleCost(llmod) + }); (cost, i) }) .max(); @@ -269,11 +272,7 @@ fn fat_lto( ModuleCodegen::new_regular(name.into_string().unwrap(), llvm_module) } }; - { - let (llcx, llmod) = { - let llvm = &module.module_llvm; - (&llvm.llcx, llvm.llmod()) - }; + module.module_llvm.with_module_refs(|ModuleRefs { llcx, llmod, .. }| { info!("using {:?} as a base module", module.name); // The linking steps below may produce errors and diagnostics within LLVM @@ -293,7 +292,9 @@ fn fat_lto( // way we know of to do that is to serialize them to a string and them parse // them later. Not great but hey, that's why it's "fat" LTO, right? for module in in_memory { - let buffer = ModuleBuffer::new(module.module_llvm.llmod(), false); + let buffer = module + .module_llvm + .with_module_refs(|ModuleRefs { llmod, .. }| ModuleBuffer::new(llmod, false)); let llmod_id = CString::new(&module.name[..]).unwrap(); serialized_modules.push((SerializedModule::Local(buffer), llmod_id)); } @@ -330,7 +331,7 @@ fn fat_lto( ); } save_temp_bitcode(cgcx, &module, "lto.after-restriction"); - } + }); module } @@ -584,7 +585,7 @@ pub(crate) fn run_pass_manager( cgcx: &CodegenContext, prof: &SelfProfilerRef, dcx: DiagCtxtHandle<'_>, - module: &mut ModuleCodegen, + module: &ModuleCodegen, thin: bool, ) { let _timer = prof.generic_activity_with_arg("LLVM_lto_optimize", &*module.name); @@ -612,11 +613,24 @@ pub(crate) fn run_pass_manager( if enable_ad { write::AutodiffStage::DuringAD } else { write::AutodiffStage::PostAD } }; - unsafe { - write::llvm_optimize( - cgcx, prof, dcx, module, None, None, config, opt_level, opt_stage, stage, - ); - } + let llvm_optimize = |opt_stage, stage| { + module.module_llvm.with_module_refs(|module_refs| unsafe { + write::llvm_optimize( + cgcx, + prof, + dcx, + module_refs, + None, + None, + config, + opt_level, + opt_stage, + stage, + ) + }); + }; + + llvm_optimize(opt_stage, stage); if enable_ad && !thin { let opt_stage = llvm::OptStage::FatLTO; @@ -624,17 +638,15 @@ pub(crate) fn run_pass_manager( if !config.autodiff.contains(&config::AutoDiff::NoPostopt) && config.autodiff_post_passes.as_deref() != Some("") { - unsafe { - write::llvm_optimize( - cgcx, prof, dcx, module, None, None, config, opt_level, opt_stage, stage, - ); - } + llvm_optimize(opt_stage, stage); } // This is the final IR, so people should be able to inspect the optimized autodiff output, // for manual inspection. if config.autodiff.contains(&config::AutoDiff::PrintModFinal) { - unsafe { llvm::LLVMDumpModule(module.module_llvm.llmod()) }; + module.module_llvm.with_module_refs(|ModuleRefs { llmod, .. }| unsafe { + llvm::LLVMDumpModule(llmod) + }); } } @@ -715,7 +727,7 @@ pub(crate) fn optimize_and_codegen_thin_module( // crates but for locally codegened modules we may be able to reuse // that LLVM Context and Module. let module_llvm = ModuleLlvm::parse(cgcx, tm_factory, module_name, thin_module.data(), dcx); - let mut module = ModuleCodegen::new_regular(thin_module.name(), module_llvm); + let module = ModuleCodegen::new_regular(thin_module.name(), module_llvm); // Given that the newly created module lacks a thinlto buffer for embedding, we need to re-add it here. if cgcx.module_config.embed_bitcode() { module @@ -724,9 +736,7 @@ pub(crate) fn optimize_and_codegen_thin_module( .set(thin_module.data().to_vec()) .expect("thin_lto_buffer should be unset"); } - { - let target = &*module.module_llvm.tm; - let llmod = module.module_llvm.llmod(); + module.module_llvm.with_module_refs(|ModuleRefs { llmod, tm, .. }| { save_temp_bitcode(cgcx, &module, "thin-lto-input"); // Up next comes the per-module local analyses that we do for Thin LTO. @@ -739,9 +749,7 @@ pub(crate) fn optimize_and_codegen_thin_module( // bindings we've got (currently `PassWrapper.cpp`) { let _timer = prof.generic_activity_with_arg("LLVM_thin_lto_rename", thin_module.name()); - unsafe { - llvm::LLVMRustPrepareThinLTORename(thin_module.shared.data.0, llmod, target.raw()) - }; + unsafe { llvm::LLVMRustPrepareThinLTORename(thin_module.shared.data.0, llmod, tm) }; save_temp_bitcode(cgcx, &module, "thin-lto-after-rename"); } @@ -767,9 +775,8 @@ pub(crate) fn optimize_and_codegen_thin_module( { let _timer = prof.generic_activity_with_arg("LLVM_thin_lto_import", thin_module.name()); - if unsafe { - !llvm::LLVMRustPrepareThinLTOImport(thin_module.shared.data.0, llmod, target.raw()) - } { + if unsafe { !llvm::LLVMRustPrepareThinLTOImport(thin_module.shared.data.0, llmod, tm) } + { write::llvm_err(dcx, LlvmError::PrepareThinLtoModule); } save_temp_bitcode(cgcx, &module, "thin-lto-after-import"); @@ -782,10 +789,10 @@ pub(crate) fn optimize_and_codegen_thin_module( // little differently. { info!("running thin lto passes over {}", module.name); - run_pass_manager(cgcx, prof, dcx, &mut module, true); + run_pass_manager(cgcx, prof, dcx, &module, true); save_temp_bitcode(cgcx, &module, "thin-lto-after-pm"); } - } + }); codegen(cgcx, prof, shared_emitter, module, &cgcx.module_config) } diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 56b5e45b85a2f..41a04c637b231 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -26,6 +26,7 @@ use rustc_span::{BytePos, DUMMY_SP, InnerSpan, Pos, RemapPathScopeComponents}; use rustc_target::spec::{CodeModel, FloatAbi, RelocModel, SanitizerSet, SplitDebuginfo, TlsModel}; use tracing::{debug, trace}; +use crate::back::llvm_backend::ModuleRefs; use crate::back::lto::{Buffer, ModuleBuffer}; use crate::back::owned_target_machine::OwnedTargetMachine; use crate::back::profiling::{ @@ -302,11 +303,10 @@ pub(crate) fn save_temp_bitcode( } fn write_bitcode_to_file(module: &ModuleLlvm, path: &Path) { - unsafe { - let path = path_to_c_string(&path); - let llmod = module.llmod(); - llvm::LLVMWriteBitcodeToFile(llmod, path.as_ptr()); - } + let path = path_to_c_string(&path); + module.with_module_refs(|ModuleRefs { llmod, .. }| { + unsafe { llvm::LLVMWriteBitcodeToFile(llmod, path.as_ptr()) }; + }); } /// In what context is a diagnostic handler being attached to a codegen unit? @@ -540,11 +540,11 @@ pub(crate) enum AutodiffStage { PostAD, } -pub(crate) unsafe fn llvm_optimize( +pub(crate) unsafe fn llvm_optimize<'ll>( cgcx: &CodegenContext, prof: &SelfProfilerRef, dcx: DiagCtxtHandle<'_>, - module: &ModuleCodegen, + module_refs: ModuleRefs<'ll>, thin_lto_buffer: Option<&mut Option>, thin_lto_summary_buffer: Option<&mut Option>, config: &ModuleConfig, @@ -552,6 +552,8 @@ pub(crate) unsafe fn llvm_optimize( opt_stage: llvm::OptStage, autodiff_stage: AutodiffStage, ) { + let ModuleRefs { llcx, llmod, tm, .. } = module_refs; + // Enzyme: // The whole point of compiler based AD is to differentiate optimized IR instead of unoptimized // source code. However, benchmarks show that optimizations increasing the code size @@ -747,8 +749,7 @@ pub(crate) unsafe fn llvm_optimize( if cgcx.target_is_like_gpu && config.offload.iter().any(|o| matches!(o, config::Offload::Device(_))) { - let cx = - SimpleCx::new(module.module_llvm.llmod(), module.module_llvm.llcx, cgcx.pointer_size); + let cx = SimpleCx::new(llmod, llcx, cgcx.pointer_size); for func in cx.get_functions() { let offload_kernel = "offload-kernel"; if attributes::has_string_attr(func, offload_kernel) { @@ -778,8 +779,8 @@ pub(crate) unsafe fn llvm_optimize( let result = unsafe { llvm::LLVMRustOptimize( - module.module_llvm.llmod(), - &*module.module_llvm.tm.raw(), + llmod, + tm, to_pass_builder_opt_level(opt_level), opt_stage, cgcx.use_linker_plugin_lto, @@ -827,8 +828,8 @@ pub(crate) unsafe fn llvm_optimize( // 1) Bundle device module into offload image device.bin (device TM) let ok = unsafe { llvm::RustOffloadWrapper::get_instance().llvm_rust_bundle_images( - module.module_llvm.llmod(), - module.module_llvm.tm.raw(), + llmod, + tm, device_out_c.as_c_str(), ) }; @@ -860,10 +861,8 @@ pub(crate) unsafe fn llvm_optimize( } let device_bin_c = path_to_c_string(device_pathbuf.as_path()); let ok = unsafe { - llvm::RustOffloadWrapper::get_instance().llvm_rust_offload_wrap_images( - module.module_llvm.llmod(), - device_bin_c.as_c_str(), - ) + llvm::RustOffloadWrapper::get_instance() + .llvm_rust_offload_wrap_images(llmod, device_bin_c.as_c_str()) }; if !ok { dcx.emit_err(crate::diagnostics::OffloadWrapImagesFailed); @@ -873,12 +872,28 @@ pub(crate) unsafe fn llvm_optimize( result.into_result().unwrap_or_else(|()| llvm_err(dcx, LlvmError::RunLlvmPasses)) } -// Unsafe due to LLVM calls. pub(crate) fn optimize( cgcx: &CodegenContext, prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, - module: &mut ModuleCodegen, + module: &ModuleCodegen, + config: &ModuleConfig, +) { + // Pass `module_refs` to an inner function to avoid having to indent the whole thing. + // + // It feels a bit odd to be passing both `module` and `module_refs` here, but it's + // unclear whether trying to avoid that would actually be an improvement. + module.module_llvm.with_module_refs(|module_refs| { + optimize_inner(cgcx, prof, shared_emitter, module, module_refs, config); + }); +} + +fn optimize_inner<'ll>( + cgcx: &CodegenContext, + prof: &SelfProfilerRef, + shared_emitter: &SharedEmitter, + module: &ModuleCodegen, + module_refs: ModuleRefs<'ll>, // Should be the refs from `module.module_llvm` config: &ModuleConfig, ) { let _timer = prof.generic_activity_with_arg("LLVM_module_optimize", &*module.name); @@ -886,9 +901,13 @@ pub(crate) fn optimize( let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); let dcx = dcx.handle(); - let llcx = &*module.module_llvm.llcx; - let _handlers = - DiagnosticHandlers::new(cgcx, shared_emitter, llcx, module, CodegenDiagnosticsStage::Opt); + let _handlers = DiagnosticHandlers::new( + cgcx, + shared_emitter, + module_refs.llcx, + module, + CodegenDiagnosticsStage::Opt, + ); if module.kind == ModuleKind::Regular { save_temp_bitcode(cgcx, module, "no-opt"); @@ -926,7 +945,7 @@ pub(crate) fn optimize( cgcx, prof, dcx, - module, + module_refs, thin_lto_buffer.as_mut(), thin_lto_summary_buffer.as_mut(), config, @@ -978,10 +997,7 @@ pub(crate) fn codegen( let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); let dcx = dcx.handle(); - { - let llmod = module.module_llvm.llmod(); - let llcx = &*module.module_llvm.llcx; - let tm = &*module.module_llvm.tm; + module.module_llvm.with_module_refs(|ModuleRefs { llcx, llmod, tm, .. }| { let _handlers = DiagnosticHandlers::new( cgcx, shared_emitter, @@ -1095,7 +1111,7 @@ pub(crate) fn codegen( }; write_output_file( dcx, - tm.raw(), + tm, config.no_builtins, llmod, &path, @@ -1128,7 +1144,7 @@ pub(crate) fn codegen( write_output_file( dcx, - tm.raw(), + tm, config.no_builtins, llmod, &obj_out, @@ -1155,7 +1171,7 @@ pub(crate) fn codegen( } record_llvm_cgu_instructions_stats(prof, &module.name, llmod); - } + }); // `.dwo` files are only emitted if: // diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 77cb37edec770..87eb157a8892f 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -93,8 +93,8 @@ pub(crate) fn compile_codegen_unit( }); // Instantiate monomorphizations without filling out definitions yet... let llvm_module = ModuleLlvm::new(tcx, cgu_name.as_str()); - { - let mut cx = CodegenCx::new(tcx, cgu, &llvm_module, needs_bitcode); + llvm_module.with_module_refs(|module_refs| { + let mut cx = CodegenCx::new(tcx, cgu, module_refs, needs_bitcode); // Declare and store globals shared by all offload kernels // @@ -213,7 +213,7 @@ pub(crate) fn compile_codegen_unit( if cx.sess().opts.debuginfo != DebugInfo::None { cx.debuginfo_finalize(); } - } + }); ModuleCodegen::new_regular(cgu_name.to_string(), llvm_module) } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 46196ab7d360f..f653f99cc5c29 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -34,6 +34,7 @@ use rustc_target::spec::{ use smallvec::SmallVec; use crate::abi::to_llvm_calling_convention; +use crate::back::llvm_backend::ModuleRefs; use crate::back::write::to_llvm_code_model; use crate::builder::gpu_offload::{OffloadGlobals, OffloadKernelGlobals}; use crate::callee::get_fn; @@ -600,7 +601,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { pub(crate) fn new( tcx: TyCtxt<'tcx>, codegen_unit: &'tcx CodegenUnit<'tcx>, - llvm_module: &'ll crate::ModuleLlvm, + module_refs: ModuleRefs<'ll>, bitcode_needed: bool, ) -> Self { // An interesting part of Windows which MSVC forces our hand on (and @@ -659,7 +660,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { let tls_model = to_llvm_tls_model(tcx.sess.tls_model()); - let (llcx, llmod) = (&*llvm_module.llcx, llvm_module.llmod()); + let ModuleRefs { llcx, llmod, .. } = module_refs; let coverage_cx = tcx.sess.instrument_coverage().then(coverageinfo::CguCoverageContext::new); From d005c6506be92e3f1f4a84ec3394169d0a7ddcb6 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 9 Oct 2026 23:12:20 +1100 Subject: [PATCH 12/23] Avoid `&'static mut` references to an owned `llvm::Context` --- .../src/back/llvm_backend.rs | 38 +++++++++++-------- compiler/rustc_codegen_llvm/src/context.rs | 15 +++++--- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 6 +-- 3 files changed, 35 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs b/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs index 6d0da50afc312..7aa1920ccf81d 100644 --- a/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs +++ b/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs @@ -6,6 +6,7 @@ use std::ffi::CStr; use std::marker::PhantomData; use std::mem::ManuallyDrop; use std::path::PathBuf; +use std::ptr; use std::sync::OnceLock; use rustc_ast::expand::allocator::AllocatorMethod; @@ -424,7 +425,7 @@ impl CodegenBackend for LlvmCodegenBackend { } pub struct ModuleLlvm { - llcx: &'static mut llvm::Context, + llcx_raw: ptr::NonNull, llmod_raw: *const llvm::Module, // This field is `ManuallyDrop` because it is important that the `TargetMachine` @@ -441,15 +442,24 @@ pub struct ModuleLlvm { unsafe impl Send for ModuleLlvm {} unsafe impl Sync for ModuleLlvm {} +/// Shared helper to call [`llvm::LLVMContextCreate`] and then configure +/// [`llvm::LLVMContextSetDiscardValueNames`]. +fn create_context_and_set_fewer_names(fewer_names: bool) -> ptr::NonNull { + unsafe { + let llcx_raw = llvm::LLVMContextCreate(); + llvm::LLVMContextSetDiscardValueNames(llcx_raw.as_ref(), fewer_names.to_llvm_bool()); + llcx_raw + } +} + impl ModuleLlvm { pub(crate) fn new(tcx: TyCtxt<'_>, mod_name: &str) -> Self { unsafe { - let llcx = llvm::LLVMContextCreate(); - llvm::LLVMContextSetDiscardValueNames(llcx, tcx.sess.fewer_names().to_llvm_bool()); - let llmod_raw = context::create_module(tcx, llcx, mod_name) as *const _; + let llcx_raw = create_context_and_set_fewer_names(tcx.sess.fewer_names()); + let llmod_raw = context::create_module(tcx, llcx_raw.as_ref(), mod_name); ModuleLlvm { llmod_raw, - llcx, + llcx_raw, tm: ManuallyDrop::new(create_target_machine(tcx, mod_name)), thin_lto_buffer: OnceLock::new(), } @@ -458,12 +468,11 @@ impl ModuleLlvm { pub(crate) fn new_metadata(tcx: TyCtxt<'_>, mod_name: &str) -> Self { unsafe { - let llcx = llvm::LLVMContextCreate(); - llvm::LLVMContextSetDiscardValueNames(llcx, tcx.sess.fewer_names().to_llvm_bool()); - let llmod_raw = context::create_module(tcx, llcx, mod_name) as *const _; + let llcx_raw = create_context_and_set_fewer_names(tcx.sess.fewer_names()); + let llmod_raw = context::create_module(tcx, llcx_raw.as_ref(), mod_name); ModuleLlvm { llmod_raw, - llcx, + llcx_raw, tm: ManuallyDrop::new(create_informational_target_machine(tcx.sess)), thin_lto_buffer: OnceLock::new(), } @@ -478,14 +487,13 @@ impl ModuleLlvm { dcx: DiagCtxtHandle<'_>, ) -> Self { unsafe { - let llcx = llvm::LLVMContextCreate(); - llvm::LLVMContextSetDiscardValueNames(llcx, cgcx.fewer_names.to_llvm_bool()); - let llmod_raw = back::lto::parse_module(llcx, name, buffer, dcx); + let llcx_raw = create_context_and_set_fewer_names(cgcx.fewer_names); + let llmod_raw = back::lto::parse_module(llcx_raw.as_ref(), name, buffer, dcx); let tm = tm_factory(dcx, TargetMachineFactoryConfig::new(cgcx, name.to_str().unwrap())); ModuleLlvm { llmod_raw, - llcx, + llcx_raw, tm: ManuallyDrop::new(tm), thin_lto_buffer: OnceLock::new(), } @@ -502,7 +510,7 @@ impl ModuleLlvm { inner_fn: impl for<'ll> FnOnce(ModuleRefs<'ll>) -> R, ) -> R { inner_fn(ModuleRefs { - llcx: self.llcx, + llcx: unsafe { self.llcx_raw.as_ref() }, llmod: unsafe { &*self.llmod_raw }, tm: self.tm.raw(), _ph: PhantomData, @@ -514,7 +522,7 @@ impl Drop for ModuleLlvm { fn drop(&mut self) { unsafe { ManuallyDrop::drop(&mut self.tm); - llvm::LLVMContextDispose(&mut *(self.llcx as *mut _)); + llvm::LLVMContextDispose(self.llcx_raw); } } } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index f653f99cc5c29..6e0681e203f1d 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -186,11 +186,16 @@ pub(crate) unsafe fn create_module<'ll>( tcx: TyCtxt<'_>, llcx: &'ll llvm::Context, mod_name: &str, -) -> &'ll llvm::Module { - let sess = tcx.sess; - let mod_name = SmallCStr::new(mod_name); - let llmod = unsafe { llvm::LLVMModuleCreateWithNameInContext(mod_name.as_ptr(), llcx) }; +) -> *const llvm::Module { + let mod_name_cstr = SmallCStr::new(mod_name); + let llmod_raw = + unsafe { llvm::LLVMModuleCreateWithNameInContext(mod_name_cstr.as_ptr(), llcx) }; + init_module(tcx, llcx, unsafe { &*llmod_raw }); + llmod_raw +} +fn init_module<'ll, 'tcx>(tcx: TyCtxt<'tcx>, llcx: &'ll llvm::Context, llmod: &'ll llvm::Module) { + let sess = tcx.sess; let cx = SimpleCx::new(llmod, llcx, tcx.data_layout.pointer_size()); let mut target_data_layout = sess.target.data_layout.to_string(); @@ -593,8 +598,6 @@ pub(crate) unsafe fn create_module<'ll>( }; llvm::add_module_flag_u32(llmod, merge_behavior, key, *value); } - - llmod } impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 1a1af50c2c025..a8ededf305a29 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -887,8 +887,8 @@ pub(crate) type GetSymbolsErrorCallback = unsafe extern "C" fn(*const c_char) -> unsafe extern "C" { // Create and destroy contexts. - pub(crate) fn LLVMContextCreate() -> &'static mut Context; - pub(crate) fn LLVMContextDispose(C: &'static mut Context); + pub(crate) fn LLVMContextCreate() -> ptr::NonNull; + pub(crate) fn LLVMContextDispose(C: ptr::NonNull); pub(crate) fn LLVMContextSetDiscardValueNames(C: &Context, Discard: Bool); pub(crate) fn LLVMGetMDKindIDInContext( C: &Context, @@ -908,7 +908,7 @@ unsafe extern "C" { pub(crate) fn LLVMModuleCreateWithNameInContext( ModuleID: *const c_char, C: &Context, - ) -> &Module; + ) -> *const Module; pub(crate) safe fn LLVMCloneModule(M: &Module) -> &Module; /// Data layout. See Module::getDataLayout. From 86d18170e47363c0030316eb9eb5d0393f719f50 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sat, 10 Oct 2026 17:42:33 +0900 Subject: [PATCH 13/23] add tests for local binding resolution in move expressions --- tests/ui/move-expr/closure-local-bindings.rs | 77 +++++++++++ .../move-expr/closure-local-bindings.stderr | 125 ++++++++++++++++++ .../move-expr/closure-local-uninitialized.rs | 13 ++ .../closure-local-uninitialized.stderr | 12 ++ .../move-initializer-local-bindings.rs | 66 +++++++++ tests/ui/move-expr/name-resolution.rs | 6 +- tests/ui/move-expr/name-resolution.stderr | 12 ++ 7 files changed, 307 insertions(+), 4 deletions(-) create mode 100644 tests/ui/move-expr/closure-local-bindings.rs create mode 100644 tests/ui/move-expr/closure-local-bindings.stderr create mode 100644 tests/ui/move-expr/closure-local-uninitialized.rs create mode 100644 tests/ui/move-expr/closure-local-uninitialized.stderr create mode 100644 tests/ui/move-expr/move-initializer-local-bindings.rs create mode 100644 tests/ui/move-expr/name-resolution.stderr diff --git a/tests/ui/move-expr/closure-local-bindings.rs b/tests/ui/move-expr/closure-local-bindings.rs new file mode 100644 index 0000000000000..878d458cc977a --- /dev/null +++ b/tests/ui/move-expr/closure-local-bindings.rs @@ -0,0 +1,77 @@ +//@ check-fail +//@ edition: 2024 + +#![allow(incomplete_features)] +#![feature(gen_blocks, move_expr)] + +macro_rules! move_value { + ($value:expr) => { move($value) }; +} + +fn main() { + let _ = |x: i32| move(x); + //~^ ERROR cannot use `x` in this move expression + + let _ = |(x, _): (i32, i32)| move(x); + //~^ ERROR cannot use `x` in this move expression + + let x = 1; + let _ = || { + let x = 2; + move(x); + //~^ ERROR cannot use `x` in this move expression + }; + + let _ = || { + for x in [1] { + move(x); + //~^ ERROR cannot use `x` in this move expression + } + }; + + let _ = || { + if let Some(x) = Some(1) { + move(x); + //~^ ERROR cannot use `x` in this move expression + } + }; + + let _ = || { + let x = 1; + || move(move(x)) + //~^ ERROR cannot use `x` in this move expression + }; + + let _ = || || move({ + let x = 1; + move(x) + //~^ ERROR cannot use `x` in this move expression + }); + + let _ = || { + let x = 1; + move_value!(x); + //~^ ERROR cannot use `x` in this move expression + }; + + let _ = async |x: i32| move(x); + //~^ ERROR cannot use `x` in this move expression + + let _ = async { + let x = 1; + move(x); + //~^ ERROR cannot use `x` in this move expression + }; + + let _ = gen { + let x = 1; + yield move(x); + //~^ ERROR cannot use `x` in this move expression + }; + + let _ = || { + let x = 1; + move(|| x) + //~^ ERROR cannot use `x` in this move expression + }; +} diff --git a/tests/ui/move-expr/closure-local-bindings.stderr b/tests/ui/move-expr/closure-local-bindings.stderr new file mode 100644 index 0000000000000..a762eeb5eb1e2 --- /dev/null +++ b/tests/ui/move-expr/closure-local-bindings.stderr @@ -0,0 +1,125 @@ +error: cannot use `x` in this move expression + --> $DIR/closure-local-bindings.rs:12:27 + | +LL | let _ = |x: i32| move(x); + | -------- -----^- + | || | + | || this expression is evaluated outside that scope + | |`x` is defined here + | `x` is defined within this scope + +error: cannot use `x` in this move expression + --> $DIR/closure-local-bindings.rs:15:39 + | +LL | let _ = |(x, _): (i32, i32)| move(x); + | -------------------- -----^- + | | | | + | | | this expression is evaluated outside that scope + | | `x` is defined here + | `x` is defined within this scope + +error: cannot use `x` in this move expression + --> $DIR/closure-local-bindings.rs:21:14 + | +LL | let _ = || { + | -- `x` is defined within this scope +LL | let x = 2; + | - `x` is defined here +LL | move(x); + | -----^- this expression is evaluated outside that scope + +error: cannot use `x` in this move expression + --> $DIR/closure-local-bindings.rs:27:18 + | +LL | let _ = || { + | -- `x` is defined within this scope +LL | for x in [1] { + | - `x` is defined here +LL | move(x); + | -----^- this expression is evaluated outside that scope + +error: cannot use `x` in this move expression + --> $DIR/closure-local-bindings.rs:34:18 + | +LL | let _ = || { + | -- `x` is defined within this scope +LL | if let Some(x) = Some(1) { + | - `x` is defined here +LL | move(x); + | -----^- this expression is evaluated outside that scope + +error: cannot use `x` in this move expression + --> $DIR/closure-local-bindings.rs:41:22 + | +LL | let _ = || { + | -- `x` is defined within this scope +LL | let x = 1; + | - `x` is defined here +LL | || move(move(x)) + | -----^- this expression is evaluated outside that scope + +error: cannot use `x` in this move expression + --> $DIR/closure-local-bindings.rs:47:14 + | +LL | let _ = || || move({ + | -- `x` is defined within this scope +LL | let x = 1; + | - `x` is defined here +LL | move(x) + | -----^- this expression is evaluated outside that scope + +error: cannot use `x` in this move expression + --> $DIR/closure-local-bindings.rs:53:21 + | +LL | ($value:expr) => { move($value) }; + | ------------ this expression is evaluated outside that scope +... +LL | let _ = || { + | -- `x` is defined within this scope +LL | let x = 1; + | - `x` is defined here +LL | move_value!(x); + | ^ + +error: cannot use `x` in this move expression + --> $DIR/closure-local-bindings.rs:57:33 + | +LL | let _ = async |x: i32| move(x); + | -------------- -----^- + | | | | + | | | this expression is evaluated outside that scope + | | `x` is defined here + | `x` is defined within this scope + +error: cannot use `x` in this move expression + --> $DIR/closure-local-bindings.rs:62:14 + | +LL | let _ = async { + | ----- `x` is defined within this scope +LL | let x = 1; + | - `x` is defined here +LL | move(x); + | -----^- this expression is evaluated outside that scope + +error: cannot use `x` in this move expression + --> $DIR/closure-local-bindings.rs:68:20 + | +LL | let _ = gen { + | --- `x` is defined within this scope +LL | let x = 1; + | - `x` is defined here +LL | yield move(x); + | -----^- this expression is evaluated outside that scope + +error: cannot use `x` in this move expression + --> $DIR/closure-local-bindings.rs:74:17 + | +LL | let _ = || { + | -- `x` is defined within this scope +LL | let x = 1; + | - `x` is defined here +LL | move(|| x) + | --------^- this expression is evaluated outside that scope + +error: aborting due to 12 previous errors + diff --git a/tests/ui/move-expr/closure-local-uninitialized.rs b/tests/ui/move-expr/closure-local-uninitialized.rs new file mode 100644 index 0000000000000..bf5fd4364cbe3 --- /dev/null +++ b/tests/ui/move-expr/closure-local-uninitialized.rs @@ -0,0 +1,13 @@ +//@ check-fail +// Regression test for https://github.com/rust-lang/rust/issues/156509 + +#![allow(incomplete_features)] +#![feature(move_expr)] + +fn main() { + let _c = || { + let x; + move(x); + //~^ ERROR cannot use `x` in this move expression + }; +} diff --git a/tests/ui/move-expr/closure-local-uninitialized.stderr b/tests/ui/move-expr/closure-local-uninitialized.stderr new file mode 100644 index 0000000000000..a008c01fc38be --- /dev/null +++ b/tests/ui/move-expr/closure-local-uninitialized.stderr @@ -0,0 +1,12 @@ +error: cannot use `x` in this move expression + --> $DIR/closure-local-uninitialized.rs:10:14 + | +LL | let _c = || { + | -- `x` is defined within this scope +LL | let x; + | - `x` is defined here +LL | move(x); + | -----^- this expression is evaluated outside that scope + +error: aborting due to 1 previous error + diff --git a/tests/ui/move-expr/move-initializer-local-bindings.rs b/tests/ui/move-expr/move-initializer-local-bindings.rs new file mode 100644 index 0000000000000..000c39b5c23d9 --- /dev/null +++ b/tests/ui/move-expr/move-initializer-local-bindings.rs @@ -0,0 +1,66 @@ +//@ run-pass + +#![allow(incomplete_features, unused_variables)] +#![feature(move_expr)] + +fn main() { + let c = || move({ + let x = 3; + x + }); + assert_eq!(c(), 3); + + let c = || move({ + let x = 4; + (|| x)() + }); + assert_eq!(c(), 4); + + let outer = || { + let x = 5; + || move(x) + }; + assert_eq!(outer()(), 5); + + let outer = || { + let x = 6; + || move({ + let y = 7; + || move(y + x) + }) + }; + assert_eq!(outer()()(), 13); + + let c = || move({ + fn identity(x: i32) -> i32 { + x + } + identity(8) + }); + assert_eq!(c(), 8); + + let c = || move(const { 9 }); + assert_eq!(c(), 9); + + let x = 10; + let c = || move(x); + let d = || move(x + 1); + assert_eq!(c(), 10); + assert_eq!(d(), 11); + + let c = || { + let x = 12; + move({ + let x = 13; + x + }) + }; + assert_eq!(c(), 13); + + let c = |x: i32| move({ + let x; + x = 14; + x + }); + assert_eq!(c(0), 14); +} diff --git a/tests/ui/move-expr/name-resolution.rs b/tests/ui/move-expr/name-resolution.rs index 7c0886183eb36..0e4f44ed3b5ec 100644 --- a/tests/ui/move-expr/name-resolution.rs +++ b/tests/ui/move-expr/name-resolution.rs @@ -1,7 +1,4 @@ -//@ check-pass -//@ ignore-test (#155050): currently ICEs instead of reporting a name-resolution error -// FIXME(TaKO8Ki): Remove this ignore once closure-local names in `move(expr)` produce a real -// diagnostic instead of hitting the current `Res::Err` ICE path. +//@ check-fail #![allow(incomplete_features)] #![feature(move_expr)] @@ -9,5 +6,6 @@ fn main() { let _c = || { let x = 3; move(x); + //~^ ERROR cannot use `x` in this move expression }; } diff --git a/tests/ui/move-expr/name-resolution.stderr b/tests/ui/move-expr/name-resolution.stderr new file mode 100644 index 0000000000000..f82e3bc5de3f6 --- /dev/null +++ b/tests/ui/move-expr/name-resolution.stderr @@ -0,0 +1,12 @@ +error: cannot use `x` in this move expression + --> $DIR/name-resolution.rs:8:14 + | +LL | let _c = || { + | -- `x` is defined within this scope +LL | let x = 3; + | - `x` is defined here +LL | move(x); + | -----^- this expression is evaluated outside that scope + +error: aborting due to 1 previous error + From 13466179a09bccfc40e004d970811e141c6c8a69 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sat, 10 Oct 2026 17:42:45 +0900 Subject: [PATCH 14/23] reject out-of-scope local bindings in move expressions --- .../rustc_resolve/src/diagnostics/impls.rs | 11 +++ compiler/rustc_resolve/src/diagnostics/mod.rs | 14 +++ compiler/rustc_resolve/src/ident.rs | 21 +++++ compiler/rustc_resolve/src/late.rs | 87 ++++++++++++++++--- compiler/rustc_resolve/src/lib.rs | 29 +++++-- 5 files changed, 144 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_resolve/src/diagnostics/impls.rs b/compiler/rustc_resolve/src/diagnostics/impls.rs index f1400c83e4295..8ec9dbbd9ee2f 100644 --- a/compiler/rustc_resolve/src/diagnostics/impls.rs +++ b/compiler/rustc_resolve/src/diagnostics/impls.rs @@ -1436,6 +1436,17 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ResolutionError::BindingInNeverPattern => { self.dcx().create_err(diagnostics::BindingInNeverPattern { span }) } + ResolutionError::CannotUseInnerLocalInMoveExpr { + move_expr_span, + ident, + scope_span, + } => self.dcx().create_err(diagnostics::CannotUseInnerLocalInMoveExpr { + span, + ident, + binding_span: ident.span, + move_expr_span, + scope_span, + }), } } diff --git a/compiler/rustc_resolve/src/diagnostics/mod.rs b/compiler/rustc_resolve/src/diagnostics/mod.rs index a629af5d690bc..65ef919e9d0c7 100644 --- a/compiler/rustc_resolve/src/diagnostics/mod.rs +++ b/compiler/rustc_resolve/src/diagnostics/mod.rs @@ -525,6 +525,20 @@ pub(crate) struct BindingInNeverPattern { pub(crate) span: Span, } +#[derive(Diagnostic)] +#[diag("cannot use `{$ident}` in this move expression")] +pub(crate) struct CannotUseInnerLocalInMoveExpr { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident: Ident, + #[label("`{$ident}` is defined here")] + pub(crate) binding_span: Span, + #[label("this expression is evaluated outside that scope")] + pub(crate) move_expr_span: Span, + #[label("`{$ident}` is defined within this scope")] + pub(crate) scope_span: Span, +} + #[derive(Diagnostic)] #[diag("duplicate definitions with name `{$name}`:", code = E0201)] pub(crate) struct TraitImplDuplicate { diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 6a5a73715c840..6b460195b9b5b 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -1511,9 +1511,28 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { for rib in ribs { match rib.kind { + // `first_rib` marks the start of the closure or coroutine scope. + // The initializer is evaluated outside that scope, so bindings defined + // inside it are unavailable, while outer bindings remain accessible. + RibKind::MoveExpr { first_rib, move_expr_span, scope_span } + if rib_index >= first_rib => + { + if let Some(span) = finalize { + self.report_error( + span, + CannotUseInnerLocalInMoveExpr { + move_expr_span, + ident: original_rib_ident_def, + scope_span, + }, + ); + } + return Res::Err; + } RibKind::Normal | RibKind::Block(..) | RibKind::FnOrCoroutine + | RibKind::MoveExpr { .. } | RibKind::Module(..) | RibKind::MacroDefinition(..) | RibKind::ForwardGenericParamBan(_) => { @@ -1610,6 +1629,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { RibKind::Normal | RibKind::Block(..) | RibKind::FnOrCoroutine + | RibKind::MoveExpr { .. } | RibKind::Module(..) | RibKind::MacroDefinition(..) | RibKind::InlineAsmSym @@ -1717,6 +1737,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { RibKind::Normal | RibKind::Block(..) | RibKind::FnOrCoroutine + | RibKind::MoveExpr { .. } | RibKind::Module(..) | RibKind::MacroDefinition(..) | RibKind::InlineAsmSym diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 417b875297ce4..315ace9c0d4cf 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -219,6 +219,12 @@ pub(crate) enum RibKind<'ra> { /// We passed through a function, closure or coroutine signature. Disallow labels. FnOrCoroutine, + MoveExpr { + first_rib: usize, + move_expr_span: Span, + scope_span: Span, + }, + /// We passed through an item scope. Disallow upvars. Item(HasGenericParams, DefKind), @@ -264,6 +270,7 @@ impl RibKind<'_> { RibKind::Normal | RibKind::Block(..) | RibKind::FnOrCoroutine + | RibKind::MoveExpr { .. } | RibKind::ConstantItem(..) | RibKind::Module(_) | RibKind::MacroDefinition(_) @@ -840,6 +847,8 @@ struct LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { /// The current set of local scopes for types and values. ribs: PerNS>>, + move_expr_scopes: Vec>, + /// Previous popped `rib`, only used for diagnostic. last_block_rib: Option>, @@ -901,7 +910,9 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc // Always report errors in items we just entered. let old_ignore = replace(&mut self.in_func_body, false); with_owner(self, item.id, |this| { - this.with_lifetime_rib(LifetimeRibKind::Item, |this| this.resolve_item(item)) + this.with_move_expr_scope(None, |this| { + this.with_lifetime_rib(LifetimeRibKind::Item, |this| this.resolve_item(item)) + }) }); self.in_func_body = old_ignore; self.diag_metadata.current_item = prev; @@ -1617,6 +1628,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { type_ns: vec![Rib::new(start_rib_kind)], macro_ns: vec![Rib::new(start_rib_kind)], }, + move_expr_scopes: Vec::new(), last_block_rib: None, label_ribs: Vec::new(), lifetime_ribs: Vec::new(), @@ -1728,6 +1740,39 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } } + fn with_move_expr_scope( + &mut self, + scope_span: Option, + work: impl FnOnce(&mut Self) -> T, + ) -> T { + if !self.r.features.move_expr() { + return work(self); + } + let scope = scope_span.map(|span| (self.ribs[ValueNS].len(), span)); + self.move_expr_scopes.push(scope); + let result = work(self); + self.move_expr_scopes.pop(); + result + } + + fn with_move_expr_initializer( + &mut self, + move_expr_span: Span, + work: impl FnOnce(&mut Self) -> T, + ) -> T { + let Some(Some((first_rib, scope_span))) = self.move_expr_scopes.last().copied() else { + return work(self); + }; + self.move_expr_scopes.pop(); + let result = self.with_rib( + ValueNS, + RibKind::MoveExpr { first_rib, move_expr_span, scope_span }, + work, + ); + self.move_expr_scopes.push(Some((first_rib, scope_span))); + result + } + fn visit_generic_params(&mut self, params: &'ast [GenericParam], add_self_upper: bool) { // For type parameter defaults, we have to ban access // to following type parameters, as the GenericArgs can only @@ -5566,30 +5611,46 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { ExprKind::Type(ref _type_expr, ref _ty) => { visit::walk_expr(self, expr); } + ExprKind::Move(..) => { + self.with_move_expr_initializer(expr.span, |this| visit::walk_expr(this, expr)); + } // For closures, RibKind::FnOrCoroutine is added in visit_fn ExprKind::Closure(ast::Closure { binder: ClosureBinder::For { ref generic_params, span }, + fn_decl_span, .. }) => { - self.with_generic_param_rib( - generic_params, - RibKind::Normal, - expr.id, - LifetimeBinderKind::Closure, - span, - |this| visit::walk_expr(this, expr), - ); + self.with_move_expr_scope(Some(fn_decl_span), |this| { + this.with_generic_param_rib( + generic_params, + RibKind::Normal, + expr.id, + LifetimeBinderKind::Closure, + span, + |this| visit::walk_expr(this, expr), + ); + }); + } + ExprKind::Closure(ref closure) => { + self.with_move_expr_scope(Some(closure.fn_decl_span), |this| { + visit::walk_expr(this, expr) + }); } - ExprKind::Closure(..) => visit::walk_expr(self, expr), - ExprKind::Gen(..) => { - self.with_label_rib(RibKind::FnOrCoroutine, |this| visit::walk_expr(this, expr)); + ExprKind::Gen(_, _, _, decl_span) => { + self.with_move_expr_scope(Some(decl_span), |this| { + this.with_label_rib(RibKind::FnOrCoroutine, |this| { + visit::walk_expr(this, expr) + }); + }); } ExprKind::Repeat(ref elem, ref ct) => { self.visit_expr(elem); self.resolve_anon_const(ct, AnonConstKind::ConstArg(IsRepeatExpr::Yes)); } ExprKind::ConstBlock(ref ct) => { - self.resolve_anon_const(ct, AnonConstKind::InlineConst); + self.with_move_expr_scope(None, |this| { + this.resolve_anon_const(ct, AnonConstKind::InlineConst); + }); } ExprKind::Index(ref elem, ref idx, _) => { self.resolve_expr(elem, Some(expr)); diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 6182f64f63efd..7a7078deaadc4 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -262,7 +262,10 @@ enum ResolutionError<'ra> { /// Error E0416: identifier is bound more than once in the same pattern. IdentifierBoundMoreThanOnceInSamePattern(Ident), /// Error E0426: use of undeclared label. - UndeclaredLabel { name: Symbol, suggestion: Option }, + UndeclaredLabel { + name: Symbol, + suggestion: Option, + }, /// Error E0433: failed to resolve. FailedToResolve { segment: Symbol, @@ -273,7 +276,9 @@ enum ResolutionError<'ra> { message: String, }, /// Error E0434: can't capture dynamic environment in a fn item. - CannotCaptureDynamicEnvironmentInFnItem { suggest_closure: bool }, + CannotCaptureDynamicEnvironmentInFnItem { + suggest_closure: bool, + }, /// Error E0435: attempt to use a non-constant value in a constant. AttemptToUseNonConstantValueInConstant { ident: Ident, @@ -296,7 +301,9 @@ enum ResolutionError<'ra> { // FIXME(generic_const_parameter_types): This should give custom output specifying it's only // problematic to use *forward declared* parameters when the feature is enabled. /// ERROR E0770: the type of const parameters must not depend on other generic parameters. - ParamInTyOfConstParam { name: Symbol }, + ParamInTyOfConstParam { + name: Symbol, + }, /// cannot use self in const param SelfInConstParam, /// generic parameters must not be used inside const evaluations. @@ -310,11 +317,18 @@ enum ResolutionError<'ra> { /// generic parameters must not be used inside enum discriminants. /// /// This error is emitted even with `generic_const_exprs`. - ParamInEnumDiscriminant { name: Symbol, param_kind: ParamKindInEnumDiscriminant }, + ParamInEnumDiscriminant { + name: Symbol, + param_kind: ParamKindInEnumDiscriminant, + }, /// Error E0735: generic parameters with a default cannot use `Self` ForwardDeclaredSelf(ForwardGenericParamBanReason), /// Error E0767: use of unreachable label - UnreachableLabel { name: Symbol, definition_span: Span, suggestion: Option }, + UnreachableLabel { + name: Symbol, + definition_span: Span, + suggestion: Option, + }, /// Error E0323, E0324, E0325: mismatch between trait item and impl item. TraitImplMismatch { name: Ident, @@ -339,6 +353,11 @@ enum ResolutionError<'ra> { LowercaseSelf, /// A never pattern has a binding. BindingInNeverPattern, + CannotUseInnerLocalInMoveExpr { + move_expr_span: Span, + ident: Ident, + scope_span: Span, + }, } #[derive(Debug)] From 2e4bf49949e08772543289043c5347c379bb27a0 Mon Sep 17 00:00:00 2001 From: Daniel Paoliello Date: Wed, 14 Oct 2026 12:10:42 -0700 Subject: [PATCH 15/23] [coff] Symbol renaming stored the original name, not the undecorated name A previous PR changed `symbol_edit` to store the undecorated Arm64EC symbol in the set of renamed symbols. While the lookup for symbols in that set did use `undecorate_coff_symbol`, it is possible that we had a untyped reference to a symbol (for example `_RNvCsVsdZgbfeYr_7___rustc12___rust_alloc` or `_RINvNtCs1dnTqovEYZ_3std9panicking11begin_panicReEB4_`), which does not get undecorated hence the lookup fails. The fix is to store the original, decorated name in the set, which also saves having to undecorate when looking up the symbol. --- compiler/rustc_codegen_ssa/src/back/symbol_edit.rs | 8 +++----- .../staticlib-rename-internal-symbols-coff/rmake.rs | 2 ++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_edit.rs b/compiler/rustc_codegen_ssa/src/back/symbol_edit.rs index 2ed758112a9fb..8fdbd304c4c2d 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_edit.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_edit.rs @@ -483,8 +483,8 @@ fn coff_collect_impl<'data, Coff: CoffHeader>( } let Ok(name_bytes) = sym.name(strings) else { continue }; let Ok(name) = str::from_utf8(name_bytes) else { continue }; - let name = undecorate_coff_symbol(name, machine, coff_symbol_kind(sym)); - if !exported.contains(name) { + let export_name = undecorate_coff_symbol(name, machine, coff_symbol_kind(sym)); + if !exported.contains(export_name) { out.insert(name.to_string()); } } @@ -522,7 +522,6 @@ fn coff_edit_impl<'data, Coff: CoffHeader>( let Ok(symbols) = header.symbols(data) else { return None }; let strings = symbols.strings(); - let machine = header.machine(); let mut renames = Vec::new(); for (index, sym) in symbols.iter() { @@ -532,8 +531,7 @@ fn coff_edit_impl<'data, Coff: CoffHeader>( } let Ok(name_bytes) = sym.name(strings) else { continue }; let Ok(name) = str::from_utf8(name_bytes) else { continue }; - let check_name = undecorate_coff_symbol(name, machine, coff_symbol_kind(sym)); - if rename.is_some_and(|(rename_set, _)| rename_set.contains(check_name)) { + if rename.is_some_and(|(rename_set, _)| rename_set.contains(name)) { renames.push(RenameEntry { name_field_offset: symbol_bytes_offset + index.0 * sym_size, name: name.to_string(), diff --git a/tests/run-make/staticlib-rename-internal-symbols-coff/rmake.rs b/tests/run-make/staticlib-rename-internal-symbols-coff/rmake.rs index 12ddd353bc24d..9e47a6277622c 100644 --- a/tests/run-make/staticlib-rename-internal-symbols-coff/rmake.rs +++ b/tests/run-make/staticlib-rename-internal-symbols-coff/rmake.rs @@ -120,6 +120,8 @@ fn decorated_exports() -> Vec<&'static str> { "_decorated_stdcall@8", "@decorated_fastcall@8", ] + } else if target().starts_with("arm64ec") { + vec!["#decorated_cdecl", "#decorated_calls_internal"] } else { vec!["decorated_cdecl", "decorated_calls_internal"] }; From dd49010532dc6e671cc6e2c054e70a92b0e8cdf5 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 17 Sep 2026 14:22:49 -0700 Subject: [PATCH 16/23] Test panics and wasm32-wasip3 in CI, not wasip1 This commit migrates the `test-various` job in CI from testing the `wasm32-wasip1` target to testing the `wasm32-wasip3` target. While doing so this additionally runs tests with `-Cpanic=unwind` instead of the historical `-Cpanic=abort` to expand the scope of tests slightly and run `alloc` tests, for example. The purpose of this change is to start migrating towards a wasm testing target which has a more fully-featured `std`, e.g. panics & threads, to run more tests in CI and make it less of a too-different target. This change was originally proposed at rust-lang/compiler-team/1039 which contains some more background details as well. The major notable change in test coverage here is running tests with `-Cpanic=unwind`. WebAssembly targets by default still use `-Cpanic=abort` and this means that large swaths of the test suite do not execute because they require panicking/unwinding in one way or another. CI here is configuring wasm targets to specifically enable unwinding, but only for the testing build. To achieve this a new `bootstrap.toml` option is added which enables configuring the default `panic_strategy` for wasm targets -- an option only intended to be used in testing. Most of this change is adjusting tests for this new testing infrastructure, some old Wasmtime-related TODOs are cleaned up as well with an updated build. Notably the `only-wasm32-wasip1` directive for compiletest is removed and all preexisting tests are migrated to run for any wasm target. The `run-make` suite in particular is updated to handle when the wasm target outputs components and the various ABI details of the wasip3 target. This change notably does not yet start to run `std`'s test suite for the `wasm32-wasip3` target. Too much of that test suite requires threads to be useful to run at this time, and as such running those tests is delayed until a later time when cooperative threading is enabled. Closes 163451 --- bootstrap.example.toml | 6 +++ compiler/rustc_target/src/spec/base/wasm.rs | 23 +++++--- .../src/collections/linked_list/tests.rs | 1 + library/alloc/src/rcs/arc.rs | 8 +-- library/alloctests/tests/sync.rs | 2 + library/test/src/lib.rs | 5 +- src/bootstrap/src/core/build_steps/compile.rs | 6 +++ src/bootstrap/src/core/config/config.rs | 4 ++ src/bootstrap/src/core/config/toml/rust.rs | 2 + src/bootstrap/src/core/session.rs | 21 +++----- .../host-x86_64/dist-various-2/Dockerfile | 2 +- .../host-x86_64/test-pr-check-2/Dockerfile | 6 +-- .../host-x86_64/test-various/Dockerfile | 14 ++--- .../src/directives/directive_names.rs | 1 - src/tools/run-make-support/Cargo.toml | 2 +- src/tools/run-make-support/src/lib.rs | 1 + src/tools/run-make-support/src/wasm.rs | 52 +++++++++++++++++++ tests/assembly-llvm/wasm_exceptions.rs | 2 - tests/assembly-llvm/wasm_legacy_eh.rs | 2 - tests/codegen-llvm/double_panic_wasm.rs | 2 +- tests/codegen-llvm/personality_lifetimes.rs | 1 + tests/codegen-llvm/wasm_custom_section.rs | 6 +-- tests/codegen-llvm/wasm_exceptions.rs | 1 + .../c-link-to-rust-va-list-fn/rmake.rs | 19 ++++--- tests/run-make/complex-abi/rmake.rs | 8 ++- tests/run-make/wasm-custom-section/rmake.rs | 9 ++-- .../wasm-custom-sections-opt/rmake.rs | 7 ++- .../run-make/wasm-export-all-symbols/rmake.rs | 27 +++++----- tests/run-make/wasm-import-module/rmake.rs | 6 +-- tests/run-make/wasm-panic-small/rmake.rs | 15 +++--- tests/run-make/wasm-spurious-import/main.rs | 9 ++++ tests/run-make/wasm-spurious-import/rmake.rs | 8 ++- .../wasm-stringify-ints-small/rmake.rs | 17 +++--- .../wasm-symbols-different-module/rmake.rs | 9 +++- .../run-make/wasm-symbols-not-exported/bar.rs | 11 +++- .../wasm-symbols-not-exported/rmake.rs | 16 +++--- .../run-make/wasm-symbols-not-imported/foo.rs | 9 ++++ .../wasm-symbols-not-imported/rmake.rs | 19 ++++--- tests/ui/backtrace/dylib-dep.rs | 2 +- tests/ui/backtrace/line-tables-only.rs | 2 +- ...internal-unstable-asm-experimental-arch.rs | 2 +- .../short-ice-remove-middle-frames-2.rs | 1 + ...hort-ice-remove-middle-frames-2.run.stderr | 2 +- .../panics/short-ice-remove-middle-frames.rs | 1 + .../short-ice-remove-middle-frames.run.stderr | 2 +- tests/ui/range/issue-54505-no-std.rs | 2 +- .../target-feature/implicit-features-cli.rs | 2 +- tests/ui/target-feature/implicit-features.rs | 2 +- tests/ui/target-feature/wasm-disable-simd.rs | 2 +- tests/ui/target-feature/wasm-relaxed-simd.rs | 2 +- ...sm-link-name-in-foreign-crate-respected.rs | 2 +- 51 files changed, 255 insertions(+), 128 deletions(-) create mode 100644 src/tools/run-make-support/src/wasm.rs diff --git a/bootstrap.example.toml b/bootstrap.example.toml index 34ff4eb623917..846ae9ca314de 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -956,6 +956,12 @@ # want to enable this right now. #rust.wasm-proc-macros = false +# Enables wasm targets to be built with `-Cpanic=unwind` by default instead of +# the current `-Cpanic=abort`. This is currently used in testing the Rust +# compiler itself and is only intended for local development/testing at this +# time. +#rust.wasm-panic-unwind-default = false + # ============================================================================= # Distribution options # diff --git a/compiler/rustc_target/src/spec/base/wasm.rs b/compiler/rustc_target/src/spec/base/wasm.rs index 1596100e629d7..dd5bcff87cde1 100644 --- a/compiler/rustc_target/src/spec/base/wasm.rs +++ b/compiler/rustc_target/src/spec/base/wasm.rs @@ -60,12 +60,23 @@ pub(crate) fn options() -> TargetOptions { max_atomic_width: Some(64), - // Unwinding doesn't work right now, so the whole target unconditionally - // defaults to panic=abort. Note that this is guaranteed to change in - // the future once unwinding is implemented. Don't rely on this as we're - // basically guaranteed to change it once WebAssembly supports - // exceptions. - panic_strategy: PanicStrategy::Abort, + // WebAssembly targets all started out originally as `-Cpanic=abort` + // since that was all that worked. Since then, however, the WebAssembly + // exception-handling proposal has been finalized/implemented and is now + // supported in both LLVM & rustc & libstd & co. The default still + // remains at `-Cpanic=abort` for now for historical compatibility, + // but `bootstrap.toml` can be used to configure a local build to + // use `-Cpanic=unwind` instead. This is used in CI, for example, to + // test wasm targets with `-Cpanic=unwind`. + // + // In the future this might change where preexisting targets migrate, + // but that's not going to be as easy as just changing this, so for now + // this is just a local build configuration option. + panic_strategy: if option_env!("CFG_WASM_PANIC_UNWIND_DEFAULT") == Some("1") { + PanicStrategy::Unwind + } else { + PanicStrategy::Abort + }, // Wasm doesn't have atomics yet, so tell LLVM that we're in a single // threaded model which will legalize atomics to normal operations. diff --git a/library/alloc/src/collections/linked_list/tests.rs b/library/alloc/src/collections/linked_list/tests.rs index a51af6345e205..1fe63070f1a15 100644 --- a/library/alloc/src/collections/linked_list/tests.rs +++ b/library/alloc/src/collections/linked_list/tests.rs @@ -304,6 +304,7 @@ fn test_clone_from() { #[test] #[cfg_attr(target_os = "emscripten", ignore)] +#[cfg_attr(target_os = "wasi", ignore)] fn test_send() { let n = list_from(&[1, 2, 3]); thread::spawn(move || { diff --git a/library/alloc/src/rcs/arc.rs b/library/alloc/src/rcs/arc.rs index 8ae3c094b2910..1a8364e08d76c 100644 --- a/library/alloc/src/rcs/arc.rs +++ b/library/alloc/src/rcs/arc.rs @@ -213,7 +213,7 @@ macro_rules! acquire { /// /// Sharing some immutable data between threads: /// -/// ``` +/// ```ignore-wasm /// use std::sync::Arc; /// use std::thread; /// @@ -232,7 +232,7 @@ macro_rules! acquire { /// /// [`AtomicUsize`]: core::sync::atomic::AtomicUsize "sync::atomic::AtomicUsize" /// -/// ``` +/// ```ignore-wasm /// use std::sync::Arc; /// use std::sync::atomic::{AtomicUsize, Ordering}; /// use std::thread; @@ -1091,7 +1091,7 @@ impl Arc { /// # Examples /// /// Minimal example demonstrating the guarantee that `Arc::into_inner` gives. - /// ``` + /// ```ignore-wasm /// use std::sync::Arc; /// /// let x = Arc::new(3); @@ -1114,7 +1114,7 @@ impl Arc { /// ``` /// /// A more practical example demonstrating the need for `Arc::into_inner`: - /// ``` + /// ```ignore-wasm /// use std::sync::Arc; /// /// // Definition of a simple singly linked list using `Arc`: diff --git a/library/alloctests/tests/sync.rs b/library/alloctests/tests/sync.rs index 5742855768c70..05f90a1a78cce 100644 --- a/library/alloctests/tests/sync.rs +++ b/library/alloctests/tests/sync.rs @@ -58,6 +58,7 @@ impl Drop for AllocCanary<'_> { #[test] #[cfg_attr(target_os = "emscripten", ignore)] +#[cfg_attr(target_os = "wasi", ignore)] fn manually_share_arc() { let v = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; let arc_v = Arc::new(v); @@ -454,6 +455,7 @@ fn test_ptr_eq() { #[test] #[cfg_attr(target_os = "emscripten", ignore)] +#[cfg_attr(target_os = "wasi", ignore)] fn test_weak_count_locked() { let mut a = Arc::new(atomic::AtomicBool::new(false)); let a2 = a.clone(); diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index 7eb632ba69882..e385932bda802 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -616,10 +616,11 @@ pub fn run_test( ) -> Option> { let TestDescAndFn { desc, testfn } = test; - // Emscripten can catch panics but other wasm targets cannot + // Emscripten can catch panics, and test for `panic=unwind` on other wasm + // targets to see if they can catch panics. let ignore_because_no_process_support = desc.should_panic != ShouldPanic::No && (cfg!(target_family = "wasm") || cfg!(target_os = "zkvm")) - && !cfg!(target_os = "emscripten"); + && !(cfg!(target_os = "emscripten") || cfg!(panic = "unwind")); if force_ignore || desc.ignore || ignore_because_no_process_support { let message = CompletedTest::new(id, desc, TrIgnored, None, Vec::new()); diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 3aded58123525..47d7056ad84f7 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1421,6 +1421,12 @@ fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelec cargo.env("JEMALLOC_SYS_WITH_LG_PAGE", "14"); } } + + // Plumb the `rust.wasm-panic-unwind-default` configuration option into the + // `rustc_target` crate. + if builder.config.wasm_panic_unwind_default { + cargo.env("CFG_WASM_PANIC_UNWIND_DEFAULT", "1"); + } } /// Pass down configuration from the LLVM build into the build of diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 1e58d86024874..3d03cc547ab75 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -348,6 +348,8 @@ pub(crate) struct Config { pub exec_ctx: ExecutionContext, pub wasm_proc_macros: bool, + + pub wasm_panic_unwind_default: bool, } impl Config { @@ -624,6 +626,7 @@ impl Config { rustflags: rust_rustflags, stdlib_semver_baseline: rust_stdlib_semver_baseline, wasm_proc_macros, + wasm_panic_unwind_default, } = toml_rust.unwrap_or_default(); let Llvm { @@ -1612,6 +1615,7 @@ NOTE: Please add `--stage 2` to your command line, or if you're sure you want to .unwrap_or(rust_debug == Some(true)), vendor, verbose_tests, + wasm_panic_unwind_default: wasm_panic_unwind_default.unwrap_or(false), wasm_proc_macros: wasm_proc_macros.unwrap_or(false), windows_rc: build_windows_rc.map(PathBuf::from), yarn: build_yarn.map(PathBuf::from), diff --git a/src/bootstrap/src/core/config/toml/rust.rs b/src/bootstrap/src/core/config/toml/rust.rs index 0c88a3d8d0172..7360d8553fb55 100644 --- a/src/bootstrap/src/core/config/toml/rust.rs +++ b/src/bootstrap/src/core/config/toml/rust.rs @@ -81,6 +81,7 @@ define_config! { parallel_frontend_threads: Option = "parallel-frontend-threads", stdlib_semver_baseline: Option = "stdlib-semver-baseline", wasm_proc_macros: Option = "wasm-proc-macros", + wasm_panic_unwind_default: Option = "wasm-panic-unwind-default", } } @@ -400,6 +401,7 @@ pub fn check_incompatible_options_for_ci_rustc( rustflags: _, stdlib_semver_baseline: _, wasm_proc_macros: _, + wasm_panic_unwind_default: _, } = ci_rust_config; // There are two kinds of checks for CI rustc incompatible options: diff --git a/src/bootstrap/src/core/session.rs b/src/bootstrap/src/core/session.rs index 1808f1b8855bc..df6c8b3958736 100644 --- a/src/bootstrap/src/core/session.rs +++ b/src/bootstrap/src/core/session.rs @@ -1197,7 +1197,7 @@ impl Session { } if target.starts_with("wasm") && target.contains("wasi") { - self.default_wasi_runner(target) + self.default_wasi_runner() } else { None } @@ -1206,7 +1206,7 @@ impl Session { /// When a `runner` configuration is not provided and a WASI-looking target /// is being tested this is consulted to prove the environment to see if /// there's a runtime already lying around that seems reasonable to use. - fn default_wasi_runner(&self, target: TargetSelection) -> Option { + fn default_wasi_runner(&self) -> Option { let mut finder = crate::core::sanity::Finder::new(); // Look for Wasmtime, and for its default options be sure to disable @@ -1215,19 +1215,10 @@ impl Session { if let Some(path) = finder.maybe_have("wasmtime") && let Ok(mut path) = path.into_os_string().into_string() { - path.push_str(" run -Wexceptions -C cache=n --dir ."); - // Make sure that tests have access to RUSTC_BOOTSTRAP. This (for example) is - // required for libtest to work on beta/stable channels. - // - // NB: with Wasmtime 20 this can change to `-S inherit-env` to - // inherit the entire environment rather than just this single - // environment variable. - path.push_str(" --env RUSTC_BOOTSTRAP"); - - if target.contains("wasip2") { - path.push_str(" --wasi inherit-network --wasi allow-ip-name-lookup"); - } - + path.push_str( + " run -C cache=n --dir . \ + --wasi inherit-env,inherit-network,allow-ip-name-lookup", + ); return Some(path); } diff --git a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile index d753d16aff66c..6be97366544cf 100644 --- a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile @@ -79,7 +79,7 @@ RUN /tmp/build-fuchsia-toolchain.sh COPY host-x86_64/dist-various-2/build-x86_64-fortanix-unknown-sgx-toolchain.sh /tmp/ RUN /tmp/build-x86_64-fortanix-unknown-sgx-toolchain.sh -RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-34/wasi-sdk-34.0-x86_64-linux.tar.gz | \ +RUN curl -L https://ci-mirrors.rust-lang.org/rustc/wasi-sdk-34.0-x86_64-linux.tar.gz | \ tar -xz ENV WASI_SDK_PATH=/tmp/wasi-sdk-34.0-x86_64-linux diff --git a/src/ci/docker/host-x86_64/test-pr-check-2/Dockerfile b/src/ci/docker/host-x86_64/test-pr-check-2/Dockerfile index 0a9b6f0fda91c..ac690d0bbdc08 100644 --- a/src/ci/docker/host-x86_64/test-pr-check-2/Dockerfile +++ b/src/ci/docker/host-x86_64/test-pr-check-2/Dockerfile @@ -21,7 +21,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ mingw-w64 \ && rm -rf /var/lib/apt/lists/* -RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-34/wasi-sdk-34.0-x86_64-linux.tar.gz | \ +RUN curl -L https://ci-mirrors.rust-lang.org/rustc/wasi-sdk-34.0-x86_64-linux.tar.gz | \ tar -xz ENV WASI_SDK_PATH=/wasi-sdk-34.0-x86_64-linux @@ -34,9 +34,9 @@ ENV SCRIPT="python3 ../x.py check && \ python3 ../x.py clippy ci --stage 2 && \ python3 ../x.py test --stage 1 core alloc std test proc_macro && \ # Elsewhere, we run all tests for the host. A number of codegen tests are sensitive to the target pointer - # width, for example because they mention a usize. wasm32-wasip1 in test-various, so using it here can't make + # width, for example because they mention a usize. wasm32-wasip3 in test-various, so using it here can't make # PR CI more strict than full CI. - python3 ../x.py test --stage 1 tests/codegen-llvm --target wasm32-wasip1 && \ + python3 ../x.py test --stage 1 tests/codegen-llvm --target wasm32-wasip3 && \ python3 ../x.py test --stage 1 src/tools/compiletest && \ python3 ../x.py doc bootstrap --stage 1 && \ # Build both public and internal documentation. diff --git a/src/ci/docker/host-x86_64/test-various/Dockerfile b/src/ci/docker/host-x86_64/test-various/Dockerfile index 6034f3d1d883c..3136e38da5a43 100644 --- a/src/ci/docker/host-x86_64/test-various/Dockerfile +++ b/src/ci/docker/host-x86_64/test-various/Dockerfile @@ -43,12 +43,13 @@ WORKDIR / COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh -RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-34/wasi-sdk-34.0-x86_64-linux.tar.gz | \ +RUN curl -L https://ci-mirrors.rust-lang.org/rustc/wasi-sdk-34.0-x86_64-linux.tar.gz | \ tar -xz ENV WASI_SDK_PATH=/wasi-sdk-34.0-x86_64-linux ENV RUST_CONFIGURE_ARGS="--musl-root-x86_64=/usr/local/x86_64-linux-musl \ - --set rust.lld" + --set rust.lld \ + --set rust.wasm-panic-unwind-default" # Some run-make tests have assertions about code size, and enabling debug # assertions in libstd causes the binary to be much bigger than it would @@ -57,11 +58,11 @@ ENV RUST_CONFIGURE_ARGS="--musl-root-x86_64=/usr/local/x86_64-linux-musl \ ENV NO_DEBUG_ASSERTIONS=1 ENV NO_OVERFLOW_CHECKS=1 -RUN curl -L https://github.com/bytecodealliance/wasmtime/releases/download/v44.0.1/wasmtime-v44.0.1-x86_64-linux.tar.xz | \ +RUN curl -L https://ci-mirrors.rust-lang.org/rustc/wasmtime-v48.0.2-x86_64-linux.tar.xz | \ tar -xJ -ENV PATH="$PATH:/wasmtime-v44.0.1-x86_64-linux" +ENV PATH="$PATH:/wasmtime-v48.0.2-x86_64-linux" -ENV WASM_WASIP_TARGET=wasm32-wasip1 +ENV WASM_WASIP_TARGET=wasm32-wasip3 ENV WASM_WASIP_SCRIPT="python3 /checkout/x.py --stage 2 test --host= --target $WASM_WASIP_TARGET \ tests/run-make \ tests/run-make-cargo \ @@ -70,7 +71,8 @@ ENV WASM_WASIP_SCRIPT="python3 /checkout/x.py --stage 2 test --host= --target $W tests/codegen-units \ tests/codegen-llvm \ tests/assembly-llvm \ - library/core" + library/core \ + library/alloc" ENV NVPTX_TARGETS=nvptx64-nvidia-cuda ENV NVPTX_SCRIPT="python3 /checkout/x.py --stage 2 test --host= --target $NVPTX_TARGETS \ diff --git a/src/tools/compiletest/src/directives/directive_names.rs b/src/tools/compiletest/src/directives/directive_names.rs index 732f4ab275dd7..520a5468e45f0 100644 --- a/src/tools/compiletest/src/directives/directive_names.rs +++ b/src/tools/compiletest/src/directives/directive_names.rs @@ -273,7 +273,6 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "only-wasm32", "only-wasm32-unknown-emscripten", "only-wasm32-unknown-unknown", - "only-wasm32-wasip1", "only-watchos", "only-windows", "only-windows-gnu", diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml index 0e1310862589d..2f503aaec5f92 100644 --- a/src/tools/run-make-support/Cargo.toml +++ b/src/tools/run-make-support/Cargo.toml @@ -18,7 +18,7 @@ regex = "1.11" serde_json = "1.0" similar = "2.7" tempfile = "3" -wasmparser = { version = "0.247", default-features = false, features = ["std", "features", "validate"] } +wasmparser = { version = "0.247", default-features = false, features = ["std", "features", "validate", "component-model"] } # tidy-alphabetical-end # Shared with bootstrap and compiletest diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index b9717bda1e2c7..f800dd61e47d6 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -22,6 +22,7 @@ pub mod scoped_run; pub mod string; pub mod symbols; pub mod targets; +pub mod wasm; // Internally we call our fs-related support module as `fs`, but re-export its content as `rfs` // to tests to avoid colliding with commonly used `use std::fs;`. diff --git a/src/tools/run-make-support/src/wasm.rs b/src/tools/run-make-support/src/wasm.rs new file mode 100644 index 0000000000000..16395ab6c90b1 --- /dev/null +++ b/src/tools/run-make-support/src/wasm.rs @@ -0,0 +1,52 @@ +//! Wasm-specific helpers for various tests + +use wasmparser::{Parser, Payload}; + +use crate::{Rustc, target}; + +/// Returns whether the current compilation target will emit a WebAssembly +/// component. +pub fn target_emits_components() -> bool { + let target = target(); + if target.contains("wasi") { + return !target.contains("wasip1"); + } + if target.contains("component") { + return true; + } + return false; +} + +/// Helper methods for the `Rustc` type. +impl Rustc { + /// Helper to emit a core module for this wasm target, skipping the + /// component output part of compilation. + pub fn emit_wasm_core_module(&mut self) -> &mut Self { + if target_emits_components() { + self.arg("-Clink-arg=--skip-wit-component"); + } + self + } +} + +/// Returns the size, in bytes, of the wasm module in `bytes`. +/// +/// This excludes custom sections to avoid counting easily strippable sections +/// such as debuginfo. +pub fn wasm_binary_size(bytes: &[u8]) -> usize { + let mut size = 0; + for payload in Parser::new(0).parse_all(bytes) { + let payload = payload.unwrap(); + match payload { + // Don't count the size of custom sections as they're easily + // stripped, and don't double-count modules since this'll recurse + // into module. + Payload::CustomSection { .. } | Payload::ModuleSection { .. } => continue, + _ => {} + } + if let Some((_, range)) = payload.as_section() { + size += range.len(); + } + } + size +} diff --git a/tests/assembly-llvm/wasm_exceptions.rs b/tests/assembly-llvm/wasm_exceptions.rs index 910e508b86f65..89ff390f3c082 100644 --- a/tests/assembly-llvm/wasm_exceptions.rs +++ b/tests/assembly-llvm/wasm_exceptions.rs @@ -32,7 +32,6 @@ pub fn test_cleanup() { may_panic(); } - // CHECK-NOT: call // CHECK: try_table (catch_all_ref 0) // CHECK: call may_panic // CHECK: end_try_table @@ -56,7 +55,6 @@ pub fn test_rtry() { ); } - // CHECK-NOT: call // CHECK: try_table (catch __cpp_exception 0) // CHECK: call may_panic // CHECK: end_try_table diff --git a/tests/assembly-llvm/wasm_legacy_eh.rs b/tests/assembly-llvm/wasm_legacy_eh.rs index 89388bfec1d5f..fd5b84eee7c9b 100644 --- a/tests/assembly-llvm/wasm_legacy_eh.rs +++ b/tests/assembly-llvm/wasm_legacy_eh.rs @@ -42,7 +42,6 @@ pub fn test_cleanup() { // CHECK-NOT: try_table // CHECK-NOT: catch_all_ref // CHECK-NOT: throw_ref - // CHECK-NOT: call // CHECK: try // CHECK: call may_panic // CHECK: catch_all @@ -69,7 +68,6 @@ pub fn test_rtry() { // CHECK-NOT: try_table // CHECK-NOT: catch_all_ref // CHECK-NOT: throw_ref - // CHECK-NOT: call // CHECK: try // CHECK: call may_panic // CHECK: catch diff --git a/tests/codegen-llvm/double_panic_wasm.rs b/tests/codegen-llvm/double_panic_wasm.rs index 1eafe60503809..e69066b8ddece 100644 --- a/tests/codegen-llvm/double_panic_wasm.rs +++ b/tests/codegen-llvm/double_panic_wasm.rs @@ -21,7 +21,7 @@ impl Drop for PanicOnDrop { // CHECK-LABEL: @double_panic // CHECK: invoke void @may_panic() -// CHECK: invoke void @{{.+}}drop_in_place{{.+}} +// CHECK: invoke void @{{.+}}drop_glue{{.+}} // CHECK: unwind label %[[TERMINATE:.*]] // // CHECK: [[TERMINATE]]: diff --git a/tests/codegen-llvm/personality_lifetimes.rs b/tests/codegen-llvm/personality_lifetimes.rs index cd81db6395349..9d587961cbeef 100644 --- a/tests/codegen-llvm/personality_lifetimes.rs +++ b/tests/codegen-llvm/personality_lifetimes.rs @@ -1,4 +1,5 @@ //@ ignore-msvc +//@ ignore-wasm //@ needs-unwind //@ compile-flags: -Copt-level=3 -C no-prepopulate-passes diff --git a/tests/codegen-llvm/wasm_custom_section.rs b/tests/codegen-llvm/wasm_custom_section.rs index 7e111c6acc352..4e9bed0ec66d7 100644 --- a/tests/codegen-llvm/wasm_custom_section.rs +++ b/tests/codegen-llvm/wasm_custom_section.rs @@ -11,9 +11,9 @@ static FOO: [u8; 22] = *b"custom section content"; // Data segment in foo section -// CHECK: @FOO = dso_local constant [22 x i8] c"custom section content", section "foo" +// CHECK: @FOO = {{.*}}constant [22 x i8] c"custom section content", section "foo" // CHECK: @llvm.used = appending global [1 x ptr] [ptr @FOO], section "llvm.metadata" // Custom section -// CHECK: !wasm.custom_sections = !{!3} -// CHECK: !3 = !{!"foo", !"custom section content"} +// CHECK: !wasm.custom_sections = !{![[ID:[0-9]+]]} +// CHECK: ![[ID]] = !{!"foo", !"custom section content"} diff --git a/tests/codegen-llvm/wasm_exceptions.rs b/tests/codegen-llvm/wasm_exceptions.rs index b4c024e4ed0e6..e586f5b111253 100644 --- a/tests/codegen-llvm/wasm_exceptions.rs +++ b/tests/codegen-llvm/wasm_exceptions.rs @@ -1,5 +1,6 @@ //@ only-wasm32 //@ revisions: WASM WASMEXN +//@ [WASM] compile-flags: -C panic=abort //@ [WASMEXN] compile-flags: -C panic=unwind #![crate_type = "lib"] diff --git a/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs b/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs index 420cf0e48e402..308733f0b4333 100644 --- a/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs +++ b/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs @@ -11,14 +11,21 @@ // FIXME: Once GCC backend is fixed, remove this `ignore-backends`. //@ ignore-backends: gcc -use run_make_support::{cc, extra_c_flags, run, rustc, static_lib_name}; +use run_make_support::{cc, extra_c_flags, run, rustc, static_lib_name, target}; fn main() { rustc().edition("2021").input("checkrust.rs").run(); - cc().input("test.c") - .input(static_lib_name("checkrust")) - .out_exe("test") - .args(extra_c_flags()) - .run(); + let mut cmd = cc(); + cmd.input("test.c").input(static_lib_name("checkrust")).out_exe("test").args(extra_c_flags()); + + // Wasm targets with `-Cpanic=unwind` are a bit nonstandard right now and + // require some extra flags. + if target().contains("wasm") + && !rustc().arg("--print=cfg").run().stdout_utf8().contains("abort") + { + cmd.arg("-lunwind").arg("-fwasm-exceptions").arg("-Wl,--export-if-defined=cabi_realloc"); + } + cmd.run(); + run("test"); } diff --git a/tests/run-make/complex-abi/rmake.rs b/tests/run-make/complex-abi/rmake.rs index 4836e72717513..175cd47e1300b 100644 --- a/tests/run-make/complex-abi/rmake.rs +++ b/tests/run-make/complex-abi/rmake.rs @@ -5,10 +5,14 @@ //@ ignore-pauthtest: (it requires non-trivial compilation of c sources, and only supports dynamic // linking, ignore the test). -use run_make_support::{bin_name, build_native_static_lib, env_var, run, rustc}; +use run_make_support::{bin_name, build_native_static_lib, env_var, run, rustc, target}; fn main() { build_native_static_lib("test"); - rustc().linker(&env_var("CC")).input("main.rs").run(); + let mut cmd = rustc(); + if !target().contains("wasm") { + cmd.linker(&env_var("CC")); + } + cmd.input("main.rs").run(); run(&bin_name("main")); } diff --git a/tests/run-make/wasm-custom-section/rmake.rs b/tests/run-make/wasm-custom-section/rmake.rs index ff4c29a9d3090..429b85e51e243 100644 --- a/tests/run-make/wasm-custom-section/rmake.rs +++ b/tests/run-make/wasm-custom-section/rmake.rs @@ -1,12 +1,12 @@ -//@ only-wasm32-wasip1 +//@ only-wasm32 use std::collections::HashMap; use run_make_support::{rfs, rustc, wasmparser}; fn main() { - rustc().input("foo.rs").target("wasm32-wasip1").run(); - rustc().input("bar.rs").target("wasm32-wasip1").arg("-Clto").opt().run(); + rustc().input("foo.rs").run(); + rustc().input("bar.rs").arg("-Clto").opt().run(); let file = rfs::read("bar.wasm"); @@ -14,8 +14,7 @@ fn main() { for payload in wasmparser::Parser::new(0).parse_all(&file) { let payload = payload.unwrap(); if let wasmparser::Payload::CustomSection(s) = payload { - let prev = custom.insert(s.name(), s.data()); - assert!(prev.is_none()); + custom.insert(s.name(), s.data()); } } diff --git a/tests/run-make/wasm-custom-sections-opt/rmake.rs b/tests/run-make/wasm-custom-sections-opt/rmake.rs index 063a4f2257c80..c718ba0b7adbc 100644 --- a/tests/run-make/wasm-custom-sections-opt/rmake.rs +++ b/tests/run-make/wasm-custom-sections-opt/rmake.rs @@ -1,4 +1,4 @@ -//@ only-wasm32-wasip1 +//@ only-wasm32 use std::collections::HashMap; use std::path::Path; @@ -6,7 +6,7 @@ use std::path::Path; use run_make_support::{rfs, rustc, wasmparser}; fn main() { - rustc().input("foo.rs").target("wasm32-wasip1").opt().run(); + rustc().input("foo.rs").opt().run(); verify(Path::new("foo.wasm")); } @@ -19,8 +19,7 @@ fn verify(path: &Path) { for payload in wasmparser::Parser::new(0).parse_all(&file) { let payload = payload.unwrap(); if let wasmparser::Payload::CustomSection(s) = payload { - let prev = custom.insert(s.name(), s.data()); - assert!(prev.is_none()); + custom.insert(s.name(), s.data()); } } diff --git a/tests/run-make/wasm-export-all-symbols/rmake.rs b/tests/run-make/wasm-export-all-symbols/rmake.rs index 58abfb996461e..3916c81363f9d 100644 --- a/tests/run-make/wasm-export-all-symbols/rmake.rs +++ b/tests/run-make/wasm-export-all-symbols/rmake.rs @@ -1,4 +1,4 @@ -//@ only-wasm32-wasip1 +//@ only-wasm32 use std::collections::HashMap; use std::path::Path; @@ -15,9 +15,9 @@ fn main() { fn test(args: &[&str]) { eprintln!("running with {args:?}"); - rustc().input("bar.rs").target("wasm32-wasip1").args(args).run(); - rustc().input("foo.rs").target("wasm32-wasip1").args(args).run(); - rustc().input("main.rs").target("wasm32-wasip1").args(args).run(); + rustc().input("bar.rs").args(args).run(); + rustc().input("foo.rs").args(args).emit_wasm_core_module().run(); + rustc().input("main.rs").args(args).emit_wasm_core_module().run(); verify_exports( Path::new("foo.wasm"), @@ -25,13 +25,7 @@ fn test(args: &[&str]) { ); verify_exports( Path::new("main.wasm"), - &[ - ("foo", Func), - ("FOO", Global), - ("_start", Func), - ("__main_void", Func), - ("memory", Memory), - ], + &[("foo", Func), ("FOO", Global), ("__main_void", Func), ("memory", Memory)], ); } @@ -44,7 +38,16 @@ fn verify_exports(path: &Path, exports: &[(&str, wasmparser::ExternalKind)]) { if let wasmparser::Payload::ExportSection(s) = payload { for export in s { let export = export.unwrap(); - wasm_exports.insert(export.name, export.kind); + match export.name { + "__wasm_task_hook" + | "cabi_realloc" + | "__indirect_function_table" + | "_start" => {} + name if name.starts_with("wasi:") => {} + other => { + wasm_exports.insert(other, export.kind); + } + } } } } diff --git a/tests/run-make/wasm-import-module/rmake.rs b/tests/run-make/wasm-import-module/rmake.rs index 77614ce1ceb86..8cafd0ad20d25 100644 --- a/tests/run-make/wasm-import-module/rmake.rs +++ b/tests/run-make/wasm-import-module/rmake.rs @@ -1,4 +1,4 @@ -//@ only-wasm32-wasip1 +//@ only-wasm32 use std::collections::HashMap; @@ -6,8 +6,8 @@ use run_make_support::{rfs, rustc, wasmparser}; use wasmparser::TypeRef::Func; fn main() { - rustc().input("foo.rs").target("wasm32-wasip1").run(); - rustc().input("bar.rs").target("wasm32-wasip1").arg("-Clto").opt().run(); + rustc().input("foo.rs").run(); + rustc().input("bar.rs").emit_wasm_core_module().arg("-Clto").opt().run(); let file = rfs::read("bar.wasm"); diff --git a/tests/run-make/wasm-panic-small/rmake.rs b/tests/run-make/wasm-panic-small/rmake.rs index 64c17571437c5..946d6ca625b72 100644 --- a/tests/run-make/wasm-panic-small/rmake.rs +++ b/tests/run-make/wasm-panic-small/rmake.rs @@ -1,7 +1,7 @@ -//@ only-wasm32-wasip1 +//@ only-wasm32 #![deny(warnings)] -use run_make_support::{rfs, rustc}; +use run_make_support::{rfs, rustc, wasm}; fn main() { test("a"); @@ -15,18 +15,15 @@ fn test(cfg: &str) { rustc() .input("foo.rs") - .target("wasm32-wasip1") .arg("-Clto") + .arg("-Cpanic=abort") .arg("-Cstrip=debuginfo") .opt() .cfg(cfg) .run(); let bytes = rfs::read("foo.wasm"); - println!("{}", bytes.len()); - assert!( - bytes.len() < 50_000, - "wasm binary was larger than expected. size in bytes: {}", - bytes.len() - ); + let size = wasm::wasm_binary_size(&bytes); + println!("{size}"); + assert!(size < 60_000, "bytes len was: {size}"); } diff --git a/tests/run-make/wasm-spurious-import/main.rs b/tests/run-make/wasm-spurious-import/main.rs index fcbead5e28bd2..a83f3e2326e94 100644 --- a/tests/run-make/wasm-spurious-import/main.rs +++ b/tests/run-make/wasm-spurious-import/main.rs @@ -12,3 +12,12 @@ pub fn multer(a: i128, b: i128) -> i128 { // panic function in case of a bug. We verify that no imports exist in our verifier. a * b } + +#[cfg(target_env = "p3")] +#[no_mangle] +pub extern "C" fn __wasm_task_hook(_: u32) {} +#[cfg(target_env = "p3")] +#[no_mangle] +pub extern "C" fn cabi_realloc(_: *mut u8, _: usize, _: usize, _: usize) -> *mut u8 { + loop {} +} diff --git a/tests/run-make/wasm-spurious-import/rmake.rs b/tests/run-make/wasm-spurious-import/rmake.rs index 435c6219aa502..e6e8a4dfc4104 100644 --- a/tests/run-make/wasm-spurious-import/rmake.rs +++ b/tests/run-make/wasm-spurious-import/rmake.rs @@ -1,4 +1,4 @@ -//@ only-wasm32-wasip1 +//@ only-wasm32 use std::collections::HashMap; @@ -7,11 +7,11 @@ use run_make_support::{rfs, rustc, wasmparser}; fn main() { rustc() .input("main.rs") - .target("wasm32-wasip1") .arg("-Coverflow-checks") .arg("-Cpanic=abort") .arg("-Clto") .arg("-Copt-level=z") + .emit_wasm_core_module() .run(); let file = rfs::read("main.wasm"); @@ -22,6 +22,10 @@ fn main() { if let wasmparser::Payload::ImportSection(s) = payload { for i in s.into_imports() { let i = i.unwrap(); + // ignore intrinsics like `__wasm_{get,set}_stack_pointer` + if i.name.starts_with("__wasm_") { + continue; + } imports.entry(i.module).or_insert(Vec::new()).push((i.name, i.ty)); } } diff --git a/tests/run-make/wasm-stringify-ints-small/rmake.rs b/tests/run-make/wasm-stringify-ints-small/rmake.rs index c0448c59c037b..060fe578d5c2d 100644 --- a/tests/run-make/wasm-stringify-ints-small/rmake.rs +++ b/tests/run-make/wasm-stringify-ints-small/rmake.rs @@ -1,18 +1,13 @@ -//@ only-wasm32-wasip1 +//@ only-wasm32 #![deny(warnings)] -use run_make_support::{rfs, rustc}; +use run_make_support::{rfs, rustc, wasm}; fn main() { - rustc() - .input("foo.rs") - .target("wasm32-wasip1") - .arg("-Clto") - .arg("-Cstrip=debuginfo") - .opt() - .run(); + rustc().input("foo.rs").arg("-Clto").emit_wasm_core_module().opt().run(); let bytes = rfs::read("foo.wasm"); - println!("{}", bytes.len()); - assert!(bytes.len() < 50_000); + let size = wasm::wasm_binary_size(&bytes); + println!("{size}"); + assert!(size < 75_000); } diff --git a/tests/run-make/wasm-symbols-different-module/rmake.rs b/tests/run-make/wasm-symbols-different-module/rmake.rs index 0e7113c3df8f9..8c8b6f21b2014 100644 --- a/tests/run-make/wasm-symbols-different-module/rmake.rs +++ b/tests/run-make/wasm-symbols-different-module/rmake.rs @@ -1,4 +1,4 @@ -//@ only-wasm32-wasip1 +//@ only-wasm32 use std::collections::{HashMap, HashSet}; use std::path::Path; @@ -22,7 +22,7 @@ fn test_file(file: &str, expected_imports: &[(&str, &[&str])]) { fn test(file: &str, args: &[&str], expected_imports: &[(&str, &[&str])]) { println!("test {file:?} {args:?} for {expected_imports:?}"); - rustc().input(file).target("wasm32-wasip1").args(args).run(); + rustc().input(file).emit_wasm_core_module().args(args).run(); let file = rfs::read(Path::new(file).with_extension("wasm")); @@ -32,6 +32,11 @@ fn test(file: &str, args: &[&str], expected_imports: &[(&str, &[&str])]) { if let wasmparser::Payload::ImportSection(s) = payload { for i in s.into_imports() { let i = i.unwrap(); + // Ignore intrinsics like `__wasm_{get,set}_stack_pointer` as + // well as standard library dependencies + if i.name.starts_with("__wasm_") || i.module.starts_with("wasi:") { + continue; + } imports.entry(i.module).or_insert(HashSet::new()).insert(i.name); } } diff --git a/tests/run-make/wasm-symbols-not-exported/bar.rs b/tests/run-make/wasm-symbols-not-exported/bar.rs index dd4d14416e70e..79d0fb4bf0def 100644 --- a/tests/run-make/wasm-symbols-not-exported/bar.rs +++ b/tests/run-make/wasm-symbols-not-exported/bar.rs @@ -1,4 +1,4 @@ -#![feature(panic_handler, alloc_error_handler)] +#![feature(alloc_error_handler)] #![crate_type = "cdylib"] #![no_std] @@ -32,3 +32,12 @@ fn a(_: core::alloc::Layout) -> ! { fn b(_: &core::panic::PanicInfo) -> ! { loop {} } + +#[cfg(target_env = "p3")] +#[no_mangle] +pub extern "C" fn __wasm_task_hook(_: u32) {} +#[cfg(target_env = "p3")] +#[no_mangle] +pub extern "C" fn cabi_realloc(_: *mut u8, _: usize, _: usize, _: usize) -> *mut u8 { + loop {} +} diff --git a/tests/run-make/wasm-symbols-not-exported/rmake.rs b/tests/run-make/wasm-symbols-not-exported/rmake.rs index 62da63db26dbf..de8852439a00f 100644 --- a/tests/run-make/wasm-symbols-not-exported/rmake.rs +++ b/tests/run-make/wasm-symbols-not-exported/rmake.rs @@ -1,18 +1,18 @@ -//@ only-wasm32-wasip1 +//@ only-wasm32 use std::path::Path; use run_make_support::{rfs, rustc, wasmparser}; fn main() { - rustc().input("foo.rs").target("wasm32-wasip1").run(); + rustc().input("foo.rs").emit_wasm_core_module().run(); verify_symbols(Path::new("foo.wasm")); - rustc().input("foo.rs").target("wasm32-wasip1").opt().run(); + rustc().input("foo.rs").emit_wasm_core_module().opt().run(); verify_symbols(Path::new("foo.wasm")); - rustc().input("bar.rs").target("wasm32-wasip1").run(); + rustc().input("bar.rs").emit_wasm_core_module().arg("-Cpanic=abort").run(); verify_symbols(Path::new("bar.wasm")); - rustc().input("bar.rs").target("wasm32-wasip1").opt().run(); + rustc().input("bar.rs").emit_wasm_core_module().arg("-Cpanic=abort").opt().run(); verify_symbols(Path::new("bar.wasm")); } @@ -28,7 +28,11 @@ fn verify_symbols(path: &Path) { if e.kind != wasmparser::ExternalKind::Func { continue; } - if e.name == "foo" || e.name == "_initialize" { + if e.name == "foo" + || e.name == "_initialize" + || e.name == "__wasm_task_hook" + || e.name == "cabi_realloc" + { continue; } panic!("unexpected export {e:?}"); diff --git a/tests/run-make/wasm-symbols-not-imported/foo.rs b/tests/run-make/wasm-symbols-not-imported/foo.rs index 6af8d50a2ace2..187d8434b8572 100644 --- a/tests/run-make/wasm-symbols-not-imported/foo.rs +++ b/tests/run-make/wasm-symbols-not-imported/foo.rs @@ -12,3 +12,12 @@ pub extern "C" fn foo() { fn panic(_info: &PanicInfo) -> ! { loop {} } + +#[cfg(target_env = "p3")] +#[no_mangle] +pub extern "C" fn __wasm_task_hook(_: u32) {} +#[cfg(target_env = "p3")] +#[no_mangle] +pub extern "C" fn cabi_realloc(_: *mut u8, _: usize, _: usize, _: usize) -> *mut u8 { + loop {} +} diff --git a/tests/run-make/wasm-symbols-not-imported/rmake.rs b/tests/run-make/wasm-symbols-not-imported/rmake.rs index fc22e31356aaf..e2e06ed31abf4 100644 --- a/tests/run-make/wasm-symbols-not-imported/rmake.rs +++ b/tests/run-make/wasm-symbols-not-imported/rmake.rs @@ -1,17 +1,17 @@ -//@ only-wasm32-wasip1 +//@ only-wasm32 use std::path::Path; use run_make_support::{rfs, rustc, wasmparser}; fn main() { - rustc().input("foo.rs").target("wasm32-wasip1").run(); + rustc().input("foo.rs").emit_wasm_core_module().arg("-Cpanic=abort").run(); verify_symbols(Path::new("foo.wasm")); - rustc().input("foo.rs").target("wasm32-wasip1").arg("-Clto").run(); + rustc().input("foo.rs").emit_wasm_core_module().arg("-Cpanic=abort").arg("-Clto").run(); verify_symbols(Path::new("foo.wasm")); - rustc().input("foo.rs").target("wasm32-wasip1").opt().run(); + rustc().input("foo.rs").emit_wasm_core_module().arg("-Cpanic=abort").opt().run(); verify_symbols(Path::new("foo.wasm")); - rustc().input("foo.rs").target("wasm32-wasip1").arg("-Clto").opt().run(); + rustc().input("foo.rs").emit_wasm_core_module().arg("-Cpanic=abort").arg("-Clto").opt().run(); verify_symbols(Path::new("foo.wasm")); } @@ -21,8 +21,13 @@ fn verify_symbols(path: &Path) { for payload in wasmparser::Parser::new(0).parse_all(&file) { let payload = payload.unwrap(); - if let wasmparser::Payload::ImportSection(_) = payload { - panic!("import section found"); + if let wasmparser::Payload::ImportSection(i) = payload { + for import in i.into_imports() { + let import = import.unwrap(); + if !import.name.starts_with("__wasm_") { + panic!("unexpected import: {import:?}"); + } + } } } } diff --git a/tests/ui/backtrace/dylib-dep.rs b/tests/ui/backtrace/dylib-dep.rs index f3d0d2e6a76db..a51ce3d8d04cc 100644 --- a/tests/ui/backtrace/dylib-dep.rs +++ b/tests/ui/backtrace/dylib-dep.rs @@ -15,7 +15,7 @@ //@ needs-unwind //@ ignore-backends: gcc //@ compile-flags: -g -Copt-level=0 -Cstrip=none -Cforce-frame-pointers=yes -//@ ignore-emscripten Requires custom symbolization code +//@ ignore-wasm Requires custom symbolization code //@ aux-crate: dylib_dep_helper=dylib-dep-helper.rs //@ aux-crate: auxiliary=dylib-dep-helper-aux.rs //@ run-pass diff --git a/tests/ui/backtrace/line-tables-only.rs b/tests/ui/backtrace/line-tables-only.rs index 33d5774079d94..39577385e123e 100644 --- a/tests/ui/backtrace/line-tables-only.rs +++ b/tests/ui/backtrace/line-tables-only.rs @@ -11,7 +11,7 @@ //@ compile-flags: -Cstrip=none -Cdebuginfo=line-tables-only //@ ignore-android FIXME #17520 //@ ignore-fuchsia Backtraces not symbolized -//@ ignore-emscripten Requires custom symbolization code +//@ ignore-wasm Requires custom symbolization code //@ ignore-ios needs the `.dSYM` files to be moved to the device //@ ignore-tvos needs the `.dSYM` files to be moved to the device //@ ignore-watchos needs the `.dSYM` files to be moved to the device diff --git a/tests/ui/internal/internal-unstable-asm-experimental-arch.rs b/tests/ui/internal/internal-unstable-asm-experimental-arch.rs index 6d97779ff911d..a073b397d9bd6 100644 --- a/tests/ui/internal/internal-unstable-asm-experimental-arch.rs +++ b/tests/ui/internal/internal-unstable-asm-experimental-arch.rs @@ -1,4 +1,4 @@ -//@ only-wasm32-wasip1 +//@ only-wasm32 //@ compile-flags: --crate-type=lib //@ build-pass //@ aux-build:internal_unstable.rs diff --git a/tests/ui/panics/short-ice-remove-middle-frames-2.rs b/tests/ui/panics/short-ice-remove-middle-frames-2.rs index 28556304be622..9761c9e7a514c 100644 --- a/tests/ui/panics/short-ice-remove-middle-frames-2.rs +++ b/tests/ui/panics/short-ice-remove-middle-frames-2.rs @@ -9,6 +9,7 @@ //@ ignore-sgx Backtraces not symbolized //@ ignore-fuchsia Backtraces not symbolized //@ ignore-msvc the `__rust_{begin,end}_short_backtrace` symbols aren't reliable. +//@ ignore-wasm backtraces not symbolized // This is needed to avoid test output differences across std being built with v0 symbols vs legacy // symbols. //@ normalize-stderr: "begin_panic::<&str>" -> "begin_panic" diff --git a/tests/ui/panics/short-ice-remove-middle-frames-2.run.stderr b/tests/ui/panics/short-ice-remove-middle-frames-2.run.stderr index 1efd7dc59b591..31bd4662af041 100644 --- a/tests/ui/panics/short-ice-remove-middle-frames-2.run.stderr +++ b/tests/ui/panics/short-ice-remove-middle-frames-2.run.stderr @@ -1,5 +1,5 @@ -thread 'main' ($TID) panicked at $DIR/short-ice-remove-middle-frames-2.rs:63:5: +thread 'main' ($TID) panicked at $DIR/short-ice-remove-middle-frames-2.rs:64:5: debug!!! stack backtrace: 0: std::panicking::begin_panic diff --git a/tests/ui/panics/short-ice-remove-middle-frames.rs b/tests/ui/panics/short-ice-remove-middle-frames.rs index 0468bffbe4446..5754414bdf48e 100644 --- a/tests/ui/panics/short-ice-remove-middle-frames.rs +++ b/tests/ui/panics/short-ice-remove-middle-frames.rs @@ -9,6 +9,7 @@ //@ ignore-sgx Backtraces not symbolized //@ ignore-fuchsia Backtraces not symbolized //@ ignore-msvc the `__rust_{begin,end}_short_backtrace` symbols aren't reliable. +//@ ignore-wasm backtraces not symbolized // This is needed to avoid test output differences across std being built with v0 symbols vs legacy // symbols. diff --git a/tests/ui/panics/short-ice-remove-middle-frames.run.stderr b/tests/ui/panics/short-ice-remove-middle-frames.run.stderr index 6ad72bbb43ca2..5796347fea6fe 100644 --- a/tests/ui/panics/short-ice-remove-middle-frames.run.stderr +++ b/tests/ui/panics/short-ice-remove-middle-frames.run.stderr @@ -1,5 +1,5 @@ -thread 'main' ($TID) panicked at $DIR/short-ice-remove-middle-frames.rs:59:5: +thread 'main' ($TID) panicked at $DIR/short-ice-remove-middle-frames.rs:60:5: debug!!! stack backtrace: 0: std::panicking::begin_panic diff --git a/tests/ui/range/issue-54505-no-std.rs b/tests/ui/range/issue-54505-no-std.rs index 6ddfc7e231464..bf5d9e64d0d60 100644 --- a/tests/ui/range/issue-54505-no-std.rs +++ b/tests/ui/range/issue-54505-no-std.rs @@ -9,7 +9,7 @@ use core::ops::RangeBounds; -#[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))] +#[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten", target_os = "wasi"))] #[lang = "eh_personality"] extern "C" fn eh_personality() {} diff --git a/tests/ui/target-feature/implicit-features-cli.rs b/tests/ui/target-feature/implicit-features-cli.rs index 34e7c3d506659..488929b662ccb 100644 --- a/tests/ui/target-feature/implicit-features-cli.rs +++ b/tests/ui/target-feature/implicit-features-cli.rs @@ -1,4 +1,4 @@ -//@ only-wasm32-wasip1 +//@ only-wasm32 //@ compile-flags: -Ctarget-feature=+relaxed-simd --crate-type=lib //@ build-pass diff --git a/tests/ui/target-feature/implicit-features.rs b/tests/ui/target-feature/implicit-features.rs index b9c48b0822d53..c5d8a4907a8db 100644 --- a/tests/ui/target-feature/implicit-features.rs +++ b/tests/ui/target-feature/implicit-features.rs @@ -1,4 +1,4 @@ -//@ only-wasm32-wasip1 +//@ only-wasm32 //@ compile-flags: --crate-type=lib //@ build-pass diff --git a/tests/ui/target-feature/wasm-disable-simd.rs b/tests/ui/target-feature/wasm-disable-simd.rs index 998c867482372..6115a6acb30aa 100644 --- a/tests/ui/target-feature/wasm-disable-simd.rs +++ b/tests/ui/target-feature/wasm-disable-simd.rs @@ -1,4 +1,4 @@ -//@ only-wasm32-wasip1 +//@ only-wasm32 //@ compile-flags: -Ctarget-feature=-simd128 --crate-type=lib //@ build-pass diff --git a/tests/ui/target-feature/wasm-relaxed-simd.rs b/tests/ui/target-feature/wasm-relaxed-simd.rs index 34e7c3d506659..488929b662ccb 100644 --- a/tests/ui/target-feature/wasm-relaxed-simd.rs +++ b/tests/ui/target-feature/wasm-relaxed-simd.rs @@ -1,4 +1,4 @@ -//@ only-wasm32-wasip1 +//@ only-wasm32 //@ compile-flags: -Ctarget-feature=+relaxed-simd --crate-type=lib //@ build-pass diff --git a/tests/ui/wasm/wasm-link-name-in-foreign-crate-respected.rs b/tests/ui/wasm/wasm-link-name-in-foreign-crate-respected.rs index e2ceeb8ae13e2..a39a9c6ada399 100644 --- a/tests/ui/wasm/wasm-link-name-in-foreign-crate-respected.rs +++ b/tests/ui/wasm/wasm-link-name-in-foreign-crate-respected.rs @@ -1,6 +1,6 @@ //@ only-wasm32 //@ aux-build:link-name-in-foreign-crate.rs -//@ compile-flags: --crate-type cdylib +//@ compile-flags: --crate-type cdylib -C link-arg=--skip-wit-component //@ build-pass //@ no-prefer-dynamic From 533c8d7e12da549b84752648a869a4c7cde763c2 Mon Sep 17 00:00:00 2001 From: amartora Date: Fri, 2 Oct 2026 20:33:38 -0700 Subject: [PATCH 17/23] Add aarch64-unknown-qtee as a tier 3 target --- compiler/rustc_target/src/spec/base/mod.rs | 1 + compiler/rustc_target/src/spec/base/qtee.rs | 25 ++++++ compiler/rustc_target/src/spec/mod.rs | 3 + .../src/spec/targets/aarch64_unknown_qtee.rs | 20 +++++ .../aarch64_unknown_qtee_linker_script.ld | 85 ++++++++++++++++++ src/bootstrap/src/core/sanity.rs | 1 + src/doc/rustc/src/SUMMARY.md | 1 + src/doc/rustc/src/platform-support.md | 1 + .../platform-support/aarch64-unknown-qtee.md | 86 +++++++++++++++++++ src/librustdoc/clean/cfg.rs | 1 + tests/assembly-llvm/targets/targets-elf.rs | 3 + tests/ui/check-cfg/cfg-crate-features.stderr | 2 +- tests/ui/check-cfg/well-known-values.stderr | 4 +- 13 files changed, 230 insertions(+), 3 deletions(-) create mode 100644 compiler/rustc_target/src/spec/base/qtee.rs create mode 100644 compiler/rustc_target/src/spec/targets/aarch64_unknown_qtee.rs create mode 100644 compiler/rustc_target/src/spec/targets/aarch64_unknown_qtee_linker_script.ld create mode 100644 src/doc/rustc/src/platform-support/aarch64-unknown-qtee.md diff --git a/compiler/rustc_target/src/spec/base/mod.rs b/compiler/rustc_target/src/spec/base/mod.rs index 1fc2deed765f6..726ec2c228596 100644 --- a/compiler/rustc_target/src/spec/base/mod.rs +++ b/compiler/rustc_target/src/spec/base/mod.rs @@ -29,6 +29,7 @@ pub(crate) mod msvc; pub(crate) mod netbsd; pub(crate) mod openbsd; pub(crate) mod qnx_sdp; +pub(crate) mod qtee; pub(crate) mod redox; pub(crate) mod solaris; pub(crate) mod solid; diff --git a/compiler/rustc_target/src/spec/base/qtee.rs b/compiler/rustc_target/src/spec/base/qtee.rs new file mode 100644 index 0000000000000..4a8a0c5827ba7 --- /dev/null +++ b/compiler/rustc_target/src/spec/base/qtee.rs @@ -0,0 +1,25 @@ +use crate::spec::targets::aarch64_unknown_none; +use crate::spec::{Os, PanicStrategy, RelocModel, RelroLevel, TargetOptions}; + +const ENTRY_POINT: &str = "tz_app_init"; + +pub(crate) fn opts() -> TargetOptions { + let base = aarch64_unknown_none::target(); + + TargetOptions { + os: Os::Qtee, + singlethread: true, + relocation_model: RelocModel::Pic, + dynamic_linking: true, + only_cdylib: true, + relro_level: RelroLevel::Full, + has_thread_local: false, + panic_strategy: PanicStrategy::Unwind, + eh_frame_header: false, + position_independent_executables: true, + emit_debug_gdb_scripts: false, + exe_suffix: ".rta".into(), + entry_name: ENTRY_POINT.into(), + ..base.options + } +} diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index b5196599eb120..a16eed94b718e 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1657,6 +1657,8 @@ supported_targets! { ("aarch64-unknown-teeos", aarch64_unknown_teeos), + ("aarch64-unknown-qtee", aarch64_unknown_qtee), + ("mips64-openwrt-linux-musl", mips64_openwrt_linux_musl), ("aarch64-unknown-nto-qnx700", aarch64_unknown_nto_qnx700), @@ -1878,6 +1880,7 @@ crate::target_spec_enum! { Psp = "psp", Psx = "psx", Qnx = "qnx", + Qtee = "qtee", Qurt = "qurt", Redox = "redox", Rtems = "rtems", diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_qtee.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_qtee.rs new file mode 100644 index 0000000000000..8b268122d9f0a --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_qtee.rs @@ -0,0 +1,20 @@ +//QTEE target for aarch64 + +use crate::spec::{StaticCow, Target, TargetOptions, base, targets}; + +const LINKER_SCRIPT: &str = include_str!("./aarch64_unknown_qtee_linker_script.ld"); + +pub(crate) fn target() -> Target { + Target { + metadata: crate::spec::TargetMetadata { + description: Some(StaticCow::Borrowed( + "Trusted Applications running on QTEE for ARM64", + )), + tier: Some(3), + host_tools: Some(false), + std: None, + }, + options: TargetOptions { link_script: Some(LINKER_SCRIPT.into()), ..base::qtee::opts() }, + ..targets::aarch64_unknown_none::target() + } +} diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_qtee_linker_script.ld b/compiler/rustc_target/src/spec/targets/aarch64_unknown_qtee_linker_script.ld new file mode 100644 index 0000000000000..c763cbb8c3610 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_qtee_linker_script.ld @@ -0,0 +1,85 @@ +ENTRY(tz_app_init); + +PHDRS { + CODE_RO PT_LOAD FILEHDR PHDRS; + MTDATA PT_LOAD FLAGS(0x04000004); + GOT PT_LOAD; + DATA PT_LOAD; + DYN PT_DYNAMIC; + REL PT_LOAD; + RELRO PT_GNU_RELRO; +} + +SECTIONS { + . = SIZEOF_HEADERS; + + .plt : { + PROVIDE (text_section_base = .); + * (.plt*) + } : CODE_RO + + .text : { + * (.text* kthread_shared) + KEEP (*(tzapp_syscall_asm)) + * (.rodata*) + } : CODE_RO + + .eh_frame ALIGN(8) : { + PROVIDE (__eh_frame = .); + KEEP (*(.eh_frame)) *(.eh_frame.*) + } + + PROVIDE (text_section_limit = .); + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + + .metadata ALIGN(4096) : { + KEEP (*(.metadata)) + KEEP (*(.metadata.*)) + } : MTDATA + + .got ALIGN(4096) : { *(.got.plt) *(.igot.plt) *(.got) *(.igot) } : GOT + : RELRO + + .data.rel.ro.local : { + *(.data.rel.ro.local*) + } : GOT + : RELRO + + .data.rel.ro : { + *(.data.rel.ro*) + } : GOT + : RELRO + + INIT_ARRAY : { + FILL(0x0) + __init_array_start = .; + *(.init_array) + __init_array_end = .; + } : GOT : RELRO + + FINI_ARRAY : { + FILL(0x0) + __fini_array_start = .; + *(.fini_array) + __fini_array_end = .; + } : GOT : RELRO + + .data ALIGN(4096) : { + data_section_base = .; + * (.data*) + * (.bss* COMMON) + data_section_limit = .; + } : DATA + + .dynamic ALIGN(4096) : { *(.dynamic) } : DYN : REL + .rela.dyn : { *(.rela.dyn) } : REL + .rela.plt : { *(.rela.plt) } : REL + .hash : { *(.hash) } : REL + .dynsym : { *(.dynsym) } : REL + .dynstr : { *(.dynstr) } : REL + .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) } + .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) } + /DISCARD/ : { *(.ARM.exidx*) *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) *(.init) *(.fini) } +} diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index 6983030fedd0a..ac44a4dfca9b1 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -36,6 +36,7 @@ pub struct Finder { /// when the newly-bumped stage 0 compiler now knows about the formerly-missing targets. const STAGE0_MISSING_TARGETS: &[&str] = &[ // just a dummy comment so the list doesn't get onelined + "aarch64-unknown-qtee", ]; /// Minimum version threshold for libstdc++ required when using prebuilt LLVM diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 1c2935ff264e0..953789648485b 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -81,6 +81,7 @@ - [\*-linux-ohos](platform-support/openharmony.md) - [\*-hurd-gnu](platform-support/hurd.md) - [aarch64-unknown-teeos](platform-support/aarch64-unknown-teeos.md) + - [aarch64-unknown-qtee](platform-support/aarch64-unknown-qtee.md) - [avr-none](platform-support/avr-none.md) - [\*-espidf](platform-support/esp-idf.md) - [\*-unknown-fuchsia](platform-support/fuchsia.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 11a6ada2c4f89..2321dec16b8eb 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -288,6 +288,7 @@ target | std | host | notes [`aarch64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | ARM64 OpenBSD [`aarch64-unknown-redox`](platform-support/redox.md) | ✓ | | ARM64 Redox OS [`aarch64-unknown-teeos`](platform-support/aarch64-unknown-teeos.md) | ? | | ARM64 TEEOS | +[`aarch64-unknown-qtee`](platform-support/aarch64-unknown-qtee.md) | ? | | ARM64 QTEE | [`aarch64-unknown-trusty`](platform-support/trusty.md) | ✓ | | [`aarch64-uwp-windows-msvc`](platform-support/uwp-windows-msvc.md) | ✓ | | [`aarch64-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | ARM64 VxWorks OS diff --git a/src/doc/rustc/src/platform-support/aarch64-unknown-qtee.md b/src/doc/rustc/src/platform-support/aarch64-unknown-qtee.md new file mode 100644 index 0000000000000..7422eebcd3121 --- /dev/null +++ b/src/doc/rustc/src/platform-support/aarch64-unknown-qtee.md @@ -0,0 +1,86 @@ +# aarch64-unknown-qtee + +**Tier: 3** + +Target support for the Qualcomm Trusted Execution Environment [QTEE](https://docs.qualcomm.com/doc/80-88500-4/topic/77_TrustZone_and_secure_application.html), which runs +Trusted Applications (TAs) in ARM's TrustZone. + +## Target maintainers + +- [@atmartorana](mailto:amartora@qti.qualcomm.com) +- [Gaurav Kashyap](mailto:gaurkash@qti.qualcomm.com) +- [Neal Haas](mailto:nhaas@qti.qualcomm.com) +- As a fallback rustzone@qti.qualcomm.com + +## Requirements + +This target is cross-compiled, and there is no host-tools support. + +QTEE support for the standard library is a work in progress: `alloc` will be fully supported and `std` will have support that excludes +things like network I/O, spawning processes / threads (as QTEE is single threaded). +Filesystem access is limited to QTEE's Secure File System (SFS) rather than a general-purpose fileystem. +File descriptors are supported for the purpose of IPC. I/O is limited to standard output and standard error logging. + +QTEE uses the ELF format. Each TA is linked as a position-dependent executable and must export `TA_APP_NAME` symbol which +identifies it to the QTEE loader. + +## Setup + +A copy of the QTEE SDK and built artifacts will be needed for runtime support. + +## Building the target + +Rust does not yet ship pre-compiled artifacts for this target. To compile you will either need to +build Rust with the target enabled (see example below), or use `build-std` to compile `std` +from source yourself. + +To build a Rust toolchain, create a `bootstrap.toml` with following contents as an example: + + ```toml + [build] + target = ["x86_64-unknown-linux-gnu", "aarch64-unknown-qtee"] + + [target.aarch64-unknown-qtee] + profiler = false + sanitizers = false + cc = "/path/to/llvm/bin/clang" + cxx = "/path/to/llvm/bin/clang++" + linker = "/path/to/llvm/bin/clang" + ar = "/path/to/llvm/bin/llvm-ar" + ranlib = "/path/to/llvm/bin/llvm-ranlib" + +The upstream LLVM build packaged with Rust is sufficient to compile for the target. + +## Building Rust programs + +Compiling binaries for this target requires some additional linker inputs supplied from the QTEE SDK, +primarily: + +- `libcmnlib.so` +- `applib.lib` +- `common_applib.o` + +The directory containing these objects can optionally be set when building the Rust toolchain +for the target itself. For example setting the directory containing these objects from the prebuilts +directory of the QTEE SDK, via the `qtee-dist-path` target option in `bootstrap.toml`: + +```toml +[target.aarch64-unknown-qtee] +qtee-dist-path = "/path/to/qteesdk/prebuilts/x86-64/" + +Building the `aarch64-unknown-qtee` compiler and standard library does not require these objects +at all - they are only consumed when self-contained linking copies them into the target's lib +directory for an actual TA binary. If `qtee-dist-path` is unset this copy step is skipped +and the toolchain will still build. However, compiling a TA will then fail at the linker stage. + +To build executables and on-target tests: + +```shell +$ rustc --target aarch64-unknown-qtee your-code.rs +``` + +## Testing + +Currently there is no support to run the rustc test suite for this target. This +is majorly due to the fact we enforce symbols like `TA_APP_NAME` to be exported +that just cannot happen when we link test files. diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index b0fb3543160e0..de240d8f87092 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -688,6 +688,7 @@ fn human_readable_target_os(os: Symbol) -> Option<&'static str> { Psp => "Play Station Portable", Psx => "Play Station 1", Qnx => "QNX SDP 8.0+", + Qtee => "QTEE", Qurt => "QuRT", Redox => "Redox OS", Rtems => "RTEMS OS", diff --git a/tests/assembly-llvm/targets/targets-elf.rs b/tests/assembly-llvm/targets/targets-elf.rs index 971e9700035e5..260820237b118 100644 --- a/tests/assembly-llvm/targets/targets-elf.rs +++ b/tests/assembly-llvm/targets/targets-elf.rs @@ -106,6 +106,9 @@ //@ revisions: aarch64_unknown_teeos //@ [aarch64_unknown_teeos] compile-flags: --target aarch64-unknown-teeos //@ [aarch64_unknown_teeos] needs-llvm-components: aarch64 +//@ revisions: aarch64_unknown_qtee +//@ [aarch64_unknown_qtee] compile-flags: --target aarch64-unknown-qtee +//@ [aarch64_unknown_qtee] needs-llvm-components: aarch64 //@ revisions: aarch64_unknown_nuttx //@ [aarch64_unknown_nuttx] compile-flags: --target aarch64-unknown-nuttx //@ [aarch64_unknown_nuttx] needs-llvm-components: aarch64 diff --git a/tests/ui/check-cfg/cfg-crate-features.stderr b/tests/ui/check-cfg/cfg-crate-features.stderr index 6bc375e0383aa..f2f7a235c2a0a 100644 --- a/tests/ui/check-cfg/cfg-crate-features.stderr +++ b/tests/ui/check-cfg/cfg-crate-features.stderr @@ -24,7 +24,7 @@ warning: unexpected `cfg` condition value: `does_not_exist` LL | #![cfg(not(target(os = "does_not_exist")))] | ^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_os` are: `aix`, `amdhsa`, `android`, `cuda`, `cygwin`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `helenos`, `hermit`, `horizon`, `hurd`, `hyperlight`, `illumos`, `ios`, `l4re`, `linux`, `lynxos178`, `macos`, `managarm`, `motor`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `ps3`, `psp`, `psx`, `qnx`, `qurt`, and `redox` and 17 more + = note: expected values for `target_os` are: `aix`, `amdhsa`, `android`, `cuda`, `cygwin`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `helenos`, `hermit`, `horizon`, `hurd`, `hyperlight`, `illumos`, `ios`, `l4re`, `linux`, `lynxos178`, `macos`, `managarm`, `motor`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `ps3`, `psp`, `psx`, `qnx`, `qtee`, and `qurt` and 18 more = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr index a99f230c6f911..e2f8f8cfb571f 100644 --- a/tests/ui/check-cfg/well-known-values.stderr +++ b/tests/ui/check-cfg/well-known-values.stderr @@ -232,7 +232,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_os = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_os` are: `aix`, `amdhsa`, `android`, `cuda`, `cygwin`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `helenos`, `hermit`, `horizon`, `hurd`, `hyperlight`, `illumos`, `ios`, `l4re`, `linux`, `lynxos178`, `macos`, `managarm`, `motor`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `ps3`, `psp`, `psx`, `qnx`, `qurt`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, `trusty`, `tvos`, `uefi`, `unknown`, `vexos`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm` + = note: expected values for `target_os` are: `aix`, `amdhsa`, `android`, `cuda`, `cygwin`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `helenos`, `hermit`, `horizon`, `hurd`, `hyperlight`, `illumos`, `ios`, `l4re`, `linux`, `lynxos178`, `macos`, `managarm`, `motor`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `ps3`, `psp`, `psx`, `qnx`, `qtee`, `qurt`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, `trusty`, `tvos`, `uefi`, `unknown`, `vexos`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -305,7 +305,7 @@ LL | #[cfg(target_os = "linuz")] // testing that we suggest `linux` | | | help: there is a expected value with a similar name: `"linux"` | - = note: expected values for `target_os` are: `aix`, `amdhsa`, `android`, `cuda`, `cygwin`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `helenos`, `hermit`, `horizon`, `hurd`, `hyperlight`, `illumos`, `ios`, `l4re`, `linux`, `lynxos178`, `macos`, `managarm`, `motor`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `ps3`, `psp`, `psx`, `qnx`, `qurt`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, `trusty`, `tvos`, `uefi`, `unknown`, `vexos`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm` + = note: expected values for `target_os` are: `aix`, `amdhsa`, `android`, `cuda`, `cygwin`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `helenos`, `hermit`, `horizon`, `hurd`, `hyperlight`, `illumos`, `ios`, `l4re`, `linux`, `lynxos178`, `macos`, `managarm`, `motor`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `ps3`, `psp`, `psx`, `qnx`, `qtee`, `qurt`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, `trusty`, `tvos`, `uefi`, `unknown`, `vexos`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm` = note: see for more information about checking conditional configuration warning: 31 warnings emitted From 7fec32e649049621df4fd758c218e5800f021180 Mon Sep 17 00:00:00 2001 From: Urgau Date: Wed, 14 Oct 2026 23:17:01 +0200 Subject: [PATCH 18/23] Ignore the remap rustdoc-json tests on windows --- tests/rustdoc-json/remap-path-prefix/doc.rs | 1 + tests/rustdoc-json/remap-path-prefix/remap-path-prefix.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/rustdoc-json/remap-path-prefix/doc.rs b/tests/rustdoc-json/remap-path-prefix/doc.rs index 11fa449983754..aa3d13255753e 100644 --- a/tests/rustdoc-json/remap-path-prefix/doc.rs +++ b/tests/rustdoc-json/remap-path-prefix/doc.rs @@ -1,3 +1,4 @@ +//@ ignore-windows //@ compile-flags: --remap-path-prefix={{src-base}}=/remapped //@ compile-flags: --remap-path-scope=documentation -Zunstable-options diff --git a/tests/rustdoc-json/remap-path-prefix/remap-path-prefix.rs b/tests/rustdoc-json/remap-path-prefix/remap-path-prefix.rs index 1d0a1fa65fad0..b3c2001ac5fb2 100644 --- a/tests/rustdoc-json/remap-path-prefix/remap-path-prefix.rs +++ b/tests/rustdoc-json/remap-path-prefix/remap-path-prefix.rs @@ -1,3 +1,4 @@ +//@ ignore-windows //@ compile-flags: --remap-path-prefix={{src-base}}=/remapped //@ is "$.index[?(@.name=='StrLike')].span.filename" \"/remapped/remap-path-prefix/remap-path-prefix.rs\" From e60c62fdc0586b4c345a64c7e655f950394a7e9d Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Thu, 15 Oct 2026 05:54:00 +0000 Subject: [PATCH 19/23] disallow invalid const parameter types in function signatures --- compiler/rustc_parse/src/parser/function.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_parse/src/parser/function.rs b/compiler/rustc_parse/src/parser/function.rs index a3ad62a9d817f..443c7fae43a0a 100644 --- a/compiler/rustc_parse/src/parser/function.rs +++ b/compiler/rustc_parse/src/parser/function.rs @@ -803,8 +803,9 @@ impl<'a> Parser<'a> { Err(err) }; } + let ty = if is_const { this.parse_ty() } else { this.parse_ty_for_param() }; - (pat, this.parse_ty_for_param()?) + (pat, ty?) } else { debug!("parse_param_general ident_to_pat"); let parser_snapshot_before_ty = this.create_snapshot_for_diagnostic(); From a85352de2bffe81c9fd7b61cb232e9c159ea1e3b Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Thu, 15 Oct 2026 05:54:25 +0000 Subject: [PATCH 20/23] add test with invalid param argument --- .../const-param-variadic.rs | 5 +++++ .../const-param-variadic.stderr | 10 ++++++++++ 2 files changed, 15 insertions(+) create mode 100644 tests/ui/const-generics/function_arg_const_generics/const-param-variadic.rs create mode 100644 tests/ui/const-generics/function_arg_const_generics/const-param-variadic.stderr diff --git a/tests/ui/const-generics/function_arg_const_generics/const-param-variadic.rs b/tests/ui/const-generics/function_arg_const_generics/const-param-variadic.rs new file mode 100644 index 0000000000000..8bd2c72838e93 --- /dev/null +++ b/tests/ui/const-generics/function_arg_const_generics/const-param-variadic.rs @@ -0,0 +1,5 @@ +#![feature(function_arg_const_generics, gca_min_const_items)] + +fn foo(const X: ...) {} //~ ERROR + +fn main() {} diff --git a/tests/ui/const-generics/function_arg_const_generics/const-param-variadic.stderr b/tests/ui/const-generics/function_arg_const_generics/const-param-variadic.stderr new file mode 100644 index 0000000000000..e199717cf708b --- /dev/null +++ b/tests/ui/const-generics/function_arg_const_generics/const-param-variadic.stderr @@ -0,0 +1,10 @@ +error: unexpected `...` + --> $DIR/const-param-variadic.rs:3:17 + | +LL | fn foo(const X: ...) {} + | ^^^ + | + = note: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: aborting due to 1 previous error + From 3f1d1cbf69a8600067d627a2004fa29a66d22d04 Mon Sep 17 00:00:00 2001 From: khyperia <953151+khyperia@users.noreply.github.com> Date: Thu, 15 Oct 2026 08:44:32 +0200 Subject: [PATCH 21/23] format abby dsl errors better --- .../rustc_hir_analysis/src/check/wfcheck.rs | 96 ++++++++----------- .../rustc_type_ir/src/region_constraint.rs | 69 +++++++++++-- .../test-infra-fails-properly.stderr | 36 +++---- 3 files changed, 114 insertions(+), 87 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 2074213c167b2..bf226a93f5d7e 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -2539,22 +2539,6 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { expected: &SolverRegionConstraint<'tcx>, actual: &SolverRegionConstraint<'tcx>, ) { - fn err<'tcx>( - tcx: TyCtxt<'tcx>, - expected_span: Span, - expected: impl std::fmt::Debug, - actual_span: Option, - actual: impl std::fmt::Debug, - ) { - let mut err = tcx.dcx().struct_span_err(expected_span, "forall expect clause failed"); - if let Some(actual_span) = actual_span { - err.span_note(actual_span, "constraint from here"); - } - err.note(format!("expected: {expected:#?}")); - err.note(format!("actual: {actual:#?}")); - err.emit(); - } - let span_of_and = |c: &And<_, _>| { c.0.iter().map(|leaf| leaf.span()).reduce(|span: Span, acc| acc.to(span)) }; @@ -2564,64 +2548,68 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { }; let check_leaf_constraint = - |expected: LeafRegionConstraint<_, _>, actual: LeafRegionConstraint<_, _>| { + |expected: &LeafRegionConstraint<_, _>, actual: &LeafRegionConstraint<_, _>| { if let LeafRegionConstraint::AliasTyOutlivesViaEnv(expected, expected_span) = expected && let LeafRegionConstraint::AliasTyOutlivesViaEnv(actual, actual_span) = actual { - let expected_anon = self.tcx().anonymize_bound_vars(expected); - let actual_anon = self.tcx().anonymize_bound_vars(actual); - if expected_anon != actual_anon { - let mut err = self - .tcx() - .dcx() - .struct_span_err(expected_span, "forall expect clause failed"); - err.span_note(actual_span, "constraint from here"); - err.note(format!("expected: {expected:#?}")); - err.note(format!("actual: {actual:#?}")); - err.note(format!("expected_anon: {expected_anon:#?}")); - err.note(format!("actual_anon: {actual_anon:#?}")); - err.emit(); + let expected_anon = self.tcx().anonymize_bound_vars(expected.clone()); + let actual_anon = self.tcx().anonymize_bound_vars(actual.clone()); + if expected_anon == actual_anon { + Ok(()) + } else { + Err((Some(*expected_span), Some(*actual_span))) + } + } else { + if expected.clone().without_span() == actual.clone().without_span() { + Ok(()) + } else { + Err((Some(expected.span()), Some(actual.span()))) } - } else if expected.clone().without_span() != actual.clone().without_span() { - err(self.tcx(), expected.span(), expected, Some(actual.span()), actual); } }; - let check_and_constraint = |expected: And<_, _>, actual: And<_, _>| { + let check_and_constraint = |expected: &And<_, _>, actual: &And<_, _>| { if expected.0.len() != actual.0.len() { - err( - self.tcx(), - span_of_and(&expected).unwrap_or(fallback_span), - expected, - span_of_and(&actual), - actual, - ) + Err((span_of_and(expected), span_of_and(actual))) } else { - for (expected, actual) in expected.0.into_iter().zip(actual.0.into_iter()) { - check_leaf_constraint(expected, actual); + for (expected, actual) in expected.0.iter().zip(&actual.0) { + check_leaf_constraint(expected, actual)?; } + Ok(()) } }; - let check_or_constraint = |expected: Or<_, _>, actual: Or<_, _>| { + let check_or_constraint = |expected: &Or<_, _>, actual: &Or<_, _>| { if expected.0.len() != actual.0.len() { - err( - self.tcx(), - span_of_or(&expected).unwrap_or(fallback_span), - expected, - span_of_or(&actual), - actual, - ) + Err((span_of_or(expected), span_of_or(actual))) } else { - for (expected, actual) in expected.0.into_iter().zip(actual.0.into_iter()) { - check_and_constraint(expected, actual); + for (expected, actual) in expected.0.iter().zip(&actual.0) { + check_and_constraint(expected, actual)?; } + Ok(()) } }; - check_or_constraint(expected.or_constraint.clone(), actual.or_constraint.clone()); - check_and_constraint(expected.and_constraint.clone(), actual.and_constraint.clone()); + let check = |expected: &SolverRegionConstraint<'tcx>, + actual: &SolverRegionConstraint<'tcx>| { + check_or_constraint(&expected.or_constraint, &actual.or_constraint)?; + check_and_constraint(&expected.and_constraint, &actual.and_constraint)?; + Ok(()) + }; + + if let Err((expected_span, actual_span)) = check(expected, actual) { + let mut err = self.tcx().dcx().struct_span_err( + expected_span.unwrap_or(fallback_span), + "forall expect clause failed", + ); + if let Some(actual_span) = actual_span { + err.span_note(actual_span, "constraint from here"); + } + err.note(format!("expected: {expected:#?}")); + err.note(format!("actual: {actual:#?}")); + err.emit(); + } } #[instrument(level = "debug", skip(self))] diff --git a/compiler/rustc_type_ir/src/region_constraint.rs b/compiler/rustc_type_ir/src/region_constraint.rs index 6f7a1ede0315d..c65103841e51c 100644 --- a/compiler/rustc_type_ir/src/region_constraint.rs +++ b/compiler/rustc_type_ir/src/region_constraint.rs @@ -197,15 +197,25 @@ pub enum LeafRegionConstraint { impl std::fmt::Debug for LeafRegionConstraint { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { match self { - Self::Ambiguity(_span) => write!(f, "Ambiguity"), + Self::Ambiguity(_span) => f.write_str("ambiguity"), Self::RegionOutlives(r1, r2, _span) => { - f.debug_tuple("RegionOutlives").field(r1).field(r2).finish() + r1.fmt(f)?; + f.write_str(": ")?; + r2.fmt(f) } Self::AliasTyOutlivesViaEnv(bound_outlives, _span) => { - f.debug_tuple("AliasTyOutlivesViaEnv").field(bound_outlives).finish() + f.write_str("for<")?; + bound_outlives.bound_vars().fmt(f)?; + f.write_str(">")?; + let (ty, r) = bound_outlives.skip_binder(); + ty.fmt(f)?; + f.write_str(": ")?; + r.fmt(f) } Self::PlaceholderTyOutlives(ty, r, _span) => { - f.debug_tuple("PlaceholderTyOutlives").field(ty).field(r).finish() + ty.fmt(f)?; + f.write_str(": ")?; + r.fmt(f) } } } @@ -252,7 +262,7 @@ impl LeafRegionC } } -#[derive_where(Clone, Hash, PartialEq, Eq, Debug; I: Interner, S)] +#[derive_where(Clone, Hash, PartialEq, Eq; I: Interner, S)] #[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] #[cfg_attr(feature = "nightly", derive(StableHash_NoContext))] /// An OR of AND of LEAF constraints. Always in "canonical form" meaning: @@ -282,7 +292,7 @@ impl Or { pub fn is_true(&self) -> bool { // OR([AND([])]) if let [and] = &*self.0 - && and.0.len() == 0 + && and.0.is_empty() { true } else { @@ -296,7 +306,7 @@ impl Or { pub fn is_false(&self) -> bool { // OR([]) - self.0.len() == 0 + self.0.is_empty() } pub fn new(i: impl IntoIterator>) -> Self { @@ -355,7 +365,20 @@ impl Or { } } -#[derive_where(Clone, Hash, PartialEq, Eq, Debug; I: Interner, S)] +impl std::fmt::Debug for Or { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.0.is_empty() { + f.write_str("false") + } else if let [single] = &*self.0 { + single.fmt(f) + } else { + f.write_str("or ")?; + f.debug_set().entries(&self.0).finish() + } + } +} + +#[derive_where(Clone, Hash, PartialEq, Eq; I: Interner, S)] #[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] #[cfg_attr(feature = "nightly", derive(StableHash_NoContext))] /// An AND of leaf constraints. Always in "canonical form", meaning: @@ -415,7 +438,20 @@ impl And { } } -#[derive_where(Clone, Hash, PartialEq, Debug; I: Interner, S)] +impl std::fmt::Debug for And { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.0.is_empty() { + f.write_str("true") + } else if let [single] = &*self.0 { + single.fmt(f) + } else { + f.write_str("and ")?; + f.debug_set().entries(&self.0).finish() + } + } +} + +#[derive_where(Clone, Hash, PartialEq; I: Interner, S)] #[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] #[cfg_attr(feature = "nightly", derive(StableHash_NoContext))] /// An `And` and an `Or` constraint both in canonical forms, with two additional constraints: @@ -538,6 +574,21 @@ impl RegionConst } } +impl std::fmt::Debug for RegionConstraint { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let [single_or] = &*self.or_constraint.0 + && single_or.0.is_empty() + { + self.and_constraint.fmt(f) + } else if self.and_constraint.0.is_empty() { + self.or_constraint.fmt(f) + } else { + f.write_str("and ")?; + f.debug_set().entries(&self.and_constraint.0).entry(&self.or_constraint).finish() + } + } +} + impl LeafRegionConstraint { pub fn is_ambig(&self) -> bool { matches!(self, Self::Ambiguity(_)) diff --git a/tests/ui/assumptions_on_binders/test-infra-fails-properly.stderr b/tests/ui/assumptions_on_binders/test-infra-fails-properly.stderr index 5736d8ef5dbaa..f5d1238241022 100644 --- a/tests/ui/assumptions_on_binders/test-infra-fails-properly.stderr +++ b/tests/ui/assumptions_on_binders/test-infra-fails-properly.stderr @@ -69,14 +69,14 @@ note: constraint from here | LL | forall<'a> where 'b: 'a { | ^^^^^^ - = note: expected: RegionOutlives( - 'b/#0, - 'c/#1, - ) - = note: actual: RegionOutlives( - 'c/#1, - 'static, - ) + = note: expected: or { + 'c/#1: 'b/#0, + 'b/#0: 'c/#1, + } + = note: actual: or { + 'c/#1: 'b/#0, + 'c/#1: 'static, + } error: bound type test binder constraint must be alias (it's a AliasTyOutlivesViaEnv) --> $DIR/test-infra-fails-properly.rs:73:15 @@ -106,14 +106,8 @@ error: forall expect clause failed LL | ambiguity | ^^^^^^^^^ | - = note: expected: And( - [ - Ambiguity, - ], - ) - = note: actual: And( - [], - ) + = note: expected: ambiguity + = note: actual: true error: forall expect clause failed --> $DIR/test-infra-fails-properly.rs:99:9 @@ -126,14 +120,8 @@ note: constraint from here | LL | forall<'a> where 'a: 'static { | ^^^^^^ - = note: expected: And( - [], - ) - = note: actual: And( - [ - Ambiguity, - ], - ) + = note: expected: true + = note: actual: ambiguity error: unable to satisfy constraints involving placeholders due to unknown implied bounds --> $DIR/test-infra-fails-properly.rs:99:9 From 8c2fbbceb76ff7209ddddd54d4dd0ad7743e5c64 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Thu, 15 Oct 2026 09:58:15 +0200 Subject: [PATCH 22/23] expand and document tool attribute shadowing test --- .../tool-attributes-shadowing.rs | 28 +++++++++++++++++-- .../tool-attributes-shadowing.stderr | 10 +++++-- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/tests/ui/tool-attributes/tool-attributes-shadowing.rs b/tests/ui/tool-attributes/tool-attributes-shadowing.rs index 582c99b089f5a..bcb4d9e1e490d 100644 --- a/tests/ui/tool-attributes/tool-attributes-shadowing.rs +++ b/tests/ui/tool-attributes/tool-attributes-shadowing.rs @@ -1,4 +1,28 @@ +//! In this case, `#[rustfmt::skip]` could be interpreted as either: +//! - a module or crate named `rustfmt` that contains an attribute macro +//! named `skip` +//! - the `skip` attribute of the `rustfmt` tool. +//! +//! In this case there's a module named `rustfmt`, but it doesn't have +//! `skip` in it. Rather than continue iterating through scopes, which +//! could resolve it as the tool attribute, we emit an error. +//! +//! The same applies for the tool explicitly registered with the `register_tool` +//! attribute. +//! +//! See https://github.com/rust-lang/rust/pull/162597#issuecomment-6172442348 +//! for the lang decision on this matter. + +#![feature(register_tool)] +#![register_tool(custom_tool)] +#![crate_type = "lib"] + mod rustfmt {} -#[rustfmt::skip] //~ ERROR: cannot find `skip` in `rustfmt` -fn main() {} +#[rustfmt::skip] //~ ERROR cannot find `skip` in `rustfmt` +struct X; + +mod custom_tool {} + +#[custom_tool::custom_attr] //~ ERROR cannot find `custom_attr` in `custom_tool` +struct Y; diff --git a/tests/ui/tool-attributes/tool-attributes-shadowing.stderr b/tests/ui/tool-attributes/tool-attributes-shadowing.stderr index 5ca1fdf586d42..1b0f11b7600c2 100644 --- a/tests/ui/tool-attributes/tool-attributes-shadowing.stderr +++ b/tests/ui/tool-attributes/tool-attributes-shadowing.stderr @@ -1,9 +1,15 @@ error[E0433]: cannot find `skip` in `rustfmt` - --> $DIR/tool-attributes-shadowing.rs:3:12 + --> $DIR/tool-attributes-shadowing.rs:22:12 | LL | #[rustfmt::skip] | ^^^^ could not find `skip` in `rustfmt` -error: aborting due to 1 previous error +error[E0433]: cannot find `custom_attr` in `custom_tool` + --> $DIR/tool-attributes-shadowing.rs:27:16 + | +LL | #[custom_tool::custom_attr] + | ^^^^^^^^^^^ could not find `custom_attr` in `custom_tool` + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0433`. From 8c2dd05b924a94f2a7cab4e4fe4847071e190392 Mon Sep 17 00:00:00 2001 From: Boxy Uwu Date: Tue, 13 Oct 2026 20:15:54 +0100 Subject: [PATCH 23/23] no weird Option --- compiler/rustc_infer/src/infer/mod.rs | 3 ++- .../src/infer/region_constraints/mod.rs | 21 ++++++++++++------- compiler/rustc_type_ir/src/fold.rs | 11 ++++++++++ .../rustc_type_ir/src/region_constraint.rs | 21 +++++++++++-------- 4 files changed, 39 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index b261c21ea532a..76234c7459f84 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1034,6 +1034,7 @@ impl<'tcx> InferCtxt<'tcx> { self.next_region_var_in_universe(RegionVariableOrigin::Nll(origin), universe) } + #[inline(always)] pub fn var_for_def(&self, span: Span, param: &ty::GenericParamDef) -> GenericArg<'tcx> { match param.kind { GenericParamDefKind::Lifetime => { @@ -1108,7 +1109,7 @@ impl<'tcx> InferCtxt<'tcx> { let inner = self.inner.borrow(); assert!(!UndoLogs::>::in_snapshot(&inner.undo_log)); let storage = inner.region_constraint_storage.as_ref().expect("regions already resolved"); - // FIXME(-Zassumptions-on-binders): Handle abby_data + // FIXME(-Zassumptions-on-binders): Handle solver_data assert!(storage.legacy_data.is_empty(), "{:#?}", storage.legacy_data); // We clone instead of taking because borrowck still wants to use the // inference context after calling this for diagnostics and the new diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index 936c78ab166d3..cde1fc8491488 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -86,20 +86,27 @@ pub struct RegionConstraintData<'tcx> { pub type SolverRegionConstraint<'tcx> = rustc_type_ir::region_constraint::RegionConstraint, Span>; -#[derive(Clone, Debug, Default)] -pub struct SolverRegionConstraintData<'tcx>(Option>); +#[derive(Clone, Debug)] +pub struct SolverRegionConstraintData<'tcx>(SolverRegionConstraint<'tcx>); + +impl<'tcx> Default for SolverRegionConstraintData<'tcx> { + fn default() -> Self { + Self::new() + } +} impl<'tcx> SolverRegionConstraintData<'tcx> { + pub(crate) fn new() -> Self { + Self(SolverRegionConstraint::new_true()) + } + pub(crate) fn get_constraint(&self) -> SolverRegionConstraint<'tcx> { - match &self.0 { - Some(v) => v.clone(), - None => SolverRegionConstraint::new_true(), - } + self.0.clone() } #[instrument(level = "debug", skip(self))] pub(crate) fn overwrite(&mut self, constraint: SolverRegionConstraint<'tcx>) { - self.0 = Some(constraint); + self.0 = constraint; } } diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs index 37dfd9b921285..a1b749b62a5f0 100644 --- a/compiler/rustc_type_ir/src/fold.rs +++ b/compiler/rustc_type_ir/src/fold.rs @@ -50,6 +50,7 @@ use std::mem; use std::sync::Arc; use rustc_index::{Idx, IndexVec}; +use smallvec::SmallVec; use thin_vec::ThinVec; use tracing::{debug, instrument}; @@ -370,6 +371,16 @@ impl> TypeFoldable for ThinVec { } } +impl, const N: usize> TypeFoldable for SmallVec<[T; N]> { + fn try_fold_with>(self, folder: &mut F) -> Result { + self.into_iter().map(|t| t.try_fold_with(folder)).collect() + } + + fn fold_with>(self, folder: &mut F) -> Self { + self.into_iter().map(|t| t.fold_with(folder)).collect() + } +} + impl> TypeFoldable for Box<[T]> { fn try_fold_with>(self, folder: &mut F) -> Result { Vec::from(self).try_fold_with(folder).map(Vec::into_boxed_slice) diff --git a/compiler/rustc_type_ir/src/region_constraint.rs b/compiler/rustc_type_ir/src/region_constraint.rs index 6f7a1ede0315d..0bec4da4d34a7 100644 --- a/compiler/rustc_type_ir/src/region_constraint.rs +++ b/compiler/rustc_type_ir/src/region_constraint.rs @@ -48,6 +48,8 @@ impl Default for TransitiveRelationBuilder { } } +use smallvec::SmallVec; + use crate::data_structures::IndexMap; use crate::fold::TypeSuperFoldable; use crate::inherent::*; @@ -265,7 +267,7 @@ impl LeafRegionC /// than proving the subset, so it can never be the candidate which makes the OR hold. E.g. /// `OR(AND('a: 'b), AND('a: 'b, 'b: 'c))` really ought to just be `OR(AND('a: 'b))`. Only keeping /// an empty AND is the degenerate case of that rule. -pub struct Or(pub Box<[And]>); +pub struct Or(pub SmallVec<[And; 1]>); impl Or { pub fn with_spans( self, @@ -276,7 +278,7 @@ impl Or { } impl Or { pub fn new_true() -> Self { - Self(Box::new([And::new_true()])) + Self(SmallVec::from_buf([And::new_true()])) } pub fn is_true(&self) -> bool { @@ -291,7 +293,7 @@ impl Or { } pub fn new_false() -> Self { - Self(Box::new([])) + Self(SmallVec::new()) } pub fn is_false(&self) -> bool { @@ -300,10 +302,9 @@ impl Or { } pub fn new(i: impl IntoIterator>) -> Self { - let ands = i.into_iter().collect::>().into_boxed_slice(); - let mut new_ands: Vec> = Vec::new(); + let mut new_ands: SmallVec<[And; _]> = SmallVec::new(); - for and in ands { + for and in i { // An empty AND is trivially true, which makes the whole OR true no matter what the // other candidates are. `And::new` discards leaf constraints which are trivially // true, so this is how e.g. a reflexive `'a: 'a` candidate discharges an OR. @@ -320,7 +321,7 @@ impl Or { } } - Self(new_ands.into_boxed_slice()) + Self(new_ands) } pub fn new_ambig(s: S) -> Self { @@ -361,7 +362,9 @@ impl Or { /// An AND of leaf constraints. Always in "canonical form", meaning: /// - No leaf constraints are present twice in this AND /// - No leaf constraint is trivially true, i.e. a reflexive `'a: 'a` -pub struct And(pub Box<[LeafRegionConstraint]>); +pub struct And( + pub SmallVec<[LeafRegionConstraint; 1]>, +); impl And { pub fn with_spans( self, @@ -372,7 +375,7 @@ impl And { } impl And { pub fn new_true() -> Self { - Self(Box::new([])) + Self(SmallVec::new()) } pub fn new(i: impl IntoIterator>) -> Self {