From 1e10dfc7c4cf130e2f2f42013a0667eaedc28813 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 8 May 2025 14:34:40 +0000 Subject: [PATCH 01/15] Add all rustc_std_internal_symbol to symbols.o rustc_std_internal_symbol is meant to call functions from crates where there is no direct dependency on said crate. As they either have to be added to symbols.o or rustc has to introduce an implicit dependency on them to avoid linker errors. The latter is done for some things like the panic runtime, but adding these symbols to symbols.o allows removing those implicit dependencies. --- compiler/rustc_codegen_ssa/src/back/linker.rs | 1 + .../src/back/symbol_export.rs | 17 ++++++++++++++ compiler/rustc_codegen_ssa/src/base.rs | 22 +------------------ .../src/middle/exported_symbols.rs | 3 +++ src/tools/miri/src/bin/miri.rs | 1 + 5 files changed, 23 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 8fc83908efbcc..438313b6d1021 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -1829,6 +1829,7 @@ pub(crate) fn linked_symbols( for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| { if info.level.is_below_threshold(export_threshold) && !tcx.is_compiler_builtins(cnum) || info.used + || info.rustc_std_internal_symbol { symbols.push(( symbol_export::linking_symbol_name_for_instance_in_crate(tcx, symbol, cnum), diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index e26f999773dc7..0e1a93b6bd3e7 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -131,6 +131,9 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap, _: LocalCrate) -> DefIdMap( level: info.level, kind: SymbolExportKind::Text, used: info.used, + rustc_std_internal_symbol: info.rustc_std_internal_symbol, }, ) }) @@ -207,6 +212,7 @@ fn exported_symbols_provider_local<'tcx>( level: SymbolExportLevel::C, kind: SymbolExportKind::Text, used: false, + rustc_std_internal_symbol: false, }, )); } @@ -229,6 +235,7 @@ fn exported_symbols_provider_local<'tcx>( level: SymbolExportLevel::Rust, kind: SymbolExportKind::Text, used: false, + rustc_std_internal_symbol: true, }, )); } @@ -243,6 +250,7 @@ fn exported_symbols_provider_local<'tcx>( level: SymbolExportLevel::Rust, kind: SymbolExportKind::Data, used: false, + rustc_std_internal_symbol: true, }, )) } @@ -262,6 +270,7 @@ fn exported_symbols_provider_local<'tcx>( level: SymbolExportLevel::C, kind: SymbolExportKind::Data, used: false, + rustc_std_internal_symbol: false, }, ) })); @@ -287,6 +296,7 @@ fn exported_symbols_provider_local<'tcx>( level: SymbolExportLevel::C, kind: SymbolExportKind::Data, used: false, + rustc_std_internal_symbol: false, }, ) })); @@ -304,6 +314,7 @@ fn exported_symbols_provider_local<'tcx>( level: SymbolExportLevel::C, kind: SymbolExportKind::Data, used: true, + rustc_std_internal_symbol: false, }, )); } @@ -379,6 +390,8 @@ fn exported_symbols_provider_local<'tcx>( } } + // Note: These all set rustc_std_internal_symbol to false as generic functions must not + // be marked with this attribute and we are only handling generic functions here. match *mono_item { MonoItem::Fn(Instance { def: InstanceKind::Item(def), args }) => { let has_generics = args.non_erasable_generics().next().is_some(); @@ -394,6 +407,7 @@ fn exported_symbols_provider_local<'tcx>( level: SymbolExportLevel::Rust, kind: SymbolExportKind::Text, used: false, + rustc_std_internal_symbol: false, }, )); } @@ -416,6 +430,7 @@ fn exported_symbols_provider_local<'tcx>( level: SymbolExportLevel::Rust, kind: SymbolExportKind::Text, used: false, + rustc_std_internal_symbol: false, }, )); } @@ -432,6 +447,7 @@ fn exported_symbols_provider_local<'tcx>( level: SymbolExportLevel::Rust, kind: SymbolExportKind::Text, used: false, + rustc_std_internal_symbol: false, }, )); } @@ -442,6 +458,7 @@ fn exported_symbols_provider_local<'tcx>( level: SymbolExportLevel::Rust, kind: SymbolExportKind::Text, used: false, + rustc_std_internal_symbol: false, }, )); } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index f7863fe4ae264..028f7742b333f 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -6,7 +6,7 @@ use std::time::{Duration, Instant}; use itertools::Itertools; use rustc_abi::FIRST_VARIANT; use rustc_ast as ast; -use rustc_ast::expand::allocator::{ALLOCATOR_METHODS, AllocatorKind, global_fn_name}; +use rustc_ast::expand::allocator::AllocatorKind; use rustc_attr_data_structures::OptimizeAttr; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry}; @@ -1075,26 +1075,6 @@ impl CrateInfo { .collect::>(); symbols.sort_unstable_by(|a, b| a.0.cmp(&b.0)); linked_symbols.extend(symbols); - if tcx.allocator_kind(()).is_some() { - // At least one crate needs a global allocator. This crate may be placed - // after the crate that defines it in the linker order, in which case some - // linkers return an error. By adding the global allocator shim methods to - // the linked_symbols list, linking the generated symbols.o will ensure that - // circular dependencies involving the global allocator don't lead to linker - // errors. - linked_symbols.extend(ALLOCATOR_METHODS.iter().map(|method| { - ( - format!( - "{prefix}{}", - mangle_internal_symbol( - tcx, - global_fn_name(method.name).as_str() - ) - ), - SymbolExportKind::Text, - ) - })); - } }); } diff --git a/compiler/rustc_middle/src/middle/exported_symbols.rs b/compiler/rustc_middle/src/middle/exported_symbols.rs index 1d67d0fe3bbf4..4469a5532f95f 100644 --- a/compiler/rustc_middle/src/middle/exported_symbols.rs +++ b/compiler/rustc_middle/src/middle/exported_symbols.rs @@ -35,7 +35,10 @@ pub enum SymbolExportKind { pub struct SymbolExportInfo { pub level: SymbolExportLevel, pub kind: SymbolExportKind, + /// Was the symbol marked as `#[used(compiler)]` or `#[used(linker)]`? pub used: bool, + /// Was the symbol marked as `#[rustc_std_internal_symbol]`? + pub rustc_std_internal_symbol: bool, } #[derive(Eq, PartialEq, Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)] diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 0121472d330f5..c6baf8ee1bf9d 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -295,6 +295,7 @@ impl rustc_driver::Callbacks for MiriBeRustCompilerCalls { level: SymbolExportLevel::C, kind: SymbolExportKind::Text, used: false, + rustc_std_internal_symbol: false, }, )) } else { From 2d2d70f7325da1588d5c5358556388107ad4e13d Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 8 May 2025 11:32:22 +0000 Subject: [PATCH 02/15] Remove dependency injection for the panic runtime This used to be necessary for a correct linker order, but ever since the introduction of symbols.o adding the symbols in question to symbols.o would work just as well. We do still add dependencies on the panic runtime to the local crate, but not for #![needs_panic_runtime] crates. This also removes the runtime-depends-on-needs-runtime test. inject_dependency_if used to emit this error, but with symbols.o it is no longer important that there is no dependency and in fact it may be nice to have panic_abort and panic_unwind directly depend on libstd in the future for calling std::process::abort(). --- compiler/rustc_metadata/src/creader.rs | 73 +------------------ compiler/rustc_metadata/src/rmeta/decoder.rs | 4 - tests/ui/panic-runtime/auxiliary/depends.rs | 8 -- .../auxiliary/needs-panic-runtime.rs | 6 -- .../runtime-depend-on-needs-runtime.rs | 9 --- 5 files changed, 3 insertions(+), 97 deletions(-) delete mode 100644 tests/ui/panic-runtime/auxiliary/depends.rs delete mode 100644 tests/ui/panic-runtime/auxiliary/needs-panic-runtime.rs delete mode 100644 tests/ui/panic-runtime/runtime-depend-on-needs-runtime.rs diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index de19c10bc57c8..60788a43bd75b 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -1,7 +1,6 @@ //! Validates all used crates and extern libraries and loads their metadata use std::error::Error; -use std::ops::Fn; use std::path::Path; use std::str::FromStr; use std::time::Duration; @@ -276,12 +275,6 @@ impl CStore { .filter_map(|(cnum, data)| data.as_deref().map(|data| (cnum, data))) } - fn iter_crate_data_mut(&mut self) -> impl Iterator { - self.metas - .iter_enumerated_mut() - .filter_map(|(cnum, data)| data.as_deref_mut().map(|data| (cnum, data))) - } - fn push_dependencies_in_postorder(&self, deps: &mut IndexSet, cnum: CrateNum) { if !deps.contains(&cnum) { let data = self.get_crate_data(cnum); @@ -307,13 +300,6 @@ impl CStore { deps } - fn crate_dependencies_in_reverse_postorder( - &self, - cnum: CrateNum, - ) -> impl Iterator { - self.crate_dependencies_in_postorder(cnum).into_iter().rev() - } - pub(crate) fn injected_panic_runtime(&self) -> Option { self.injected_panic_runtime } @@ -966,27 +952,16 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { // If we need a panic runtime, we try to find an existing one here. At // the same time we perform some general validation of the DAG we've got // going such as ensuring everything has a compatible panic strategy. - // - // The logic for finding the panic runtime here is pretty much the same - // as the allocator case with the only addition that the panic strategy - // compilation mode also comes into play. let desired_strategy = self.sess.panic_strategy(); let mut runtime_found = false; let mut needs_panic_runtime = attr::contains_name(&krate.attrs, sym::needs_panic_runtime); - let mut panic_runtimes = Vec::new(); - for (cnum, data) in self.cstore.iter_crate_data() { + for (_cnum, data) in self.cstore.iter_crate_data() { needs_panic_runtime = needs_panic_runtime || data.needs_panic_runtime(); if data.is_panic_runtime() { - // Inject a dependency from all #![needs_panic_runtime] to this - // #![panic_runtime] crate. - panic_runtimes.push(cnum); runtime_found = runtime_found || data.dep_kind() == CrateDepKind::Explicit; } } - for cnum in panic_runtimes { - self.inject_dependency_if(cnum, "a panic runtime", &|data| data.needs_panic_runtime()); - } // If an explicitly linked and matching panic runtime was found, or if // we just don't need one at all, then we're done here and there's @@ -997,12 +972,10 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { // By this point we know that we (a) need a panic runtime and (b) no // panic runtime was explicitly linked. Here we just load an appropriate - // default runtime for our panic strategy and then inject the - // dependencies. + // default runtime for our panic strategy. // // We may resolve to an already loaded crate (as the crate may not have - // been explicitly linked prior to this) and we may re-inject - // dependencies again, but both of those situations are fine. + // been explicitly linked prior to this), but this is fine. // // Also note that we have yet to perform validation of the crate graph // in terms of everyone has a compatible panic runtime format, that's @@ -1031,7 +1004,6 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { } self.cstore.injected_panic_runtime = Some(cnum); - self.inject_dependency_if(cnum, "a panic runtime", &|data| data.needs_panic_runtime()); } fn inject_profiler_runtime(&mut self) { @@ -1212,45 +1184,6 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { } } - fn inject_dependency_if( - &mut self, - krate: CrateNum, - what: &str, - needs_dep: &dyn Fn(&CrateMetadata) -> bool, - ) { - // Don't perform this validation if the session has errors, as one of - // those errors may indicate a circular dependency which could cause - // this to stack overflow. - if self.dcx().has_errors().is_some() { - return; - } - - // Before we inject any dependencies, make sure we don't inject a - // circular dependency by validating that this crate doesn't - // transitively depend on any crates satisfying `needs_dep`. - for dep in self.cstore.crate_dependencies_in_reverse_postorder(krate) { - let data = self.cstore.get_crate_data(dep); - if needs_dep(&data) { - self.dcx().emit_err(errors::NoTransitiveNeedsDep { - crate_name: self.cstore.get_crate_data(krate).name(), - needs_crate_name: what, - deps_crate_name: data.name(), - }); - } - } - - // All crates satisfying `needs_dep` do not explicitly depend on the - // crate provided for this compile, but in order for this compilation to - // be successfully linked we need to inject a dependency (to order the - // crates on the command line correctly). - for (cnum, data) in self.cstore.iter_crate_data_mut() { - if needs_dep(data) { - info!("injecting a dep from {} to {}", cnum, krate); - data.add_dependency(krate); - } - } - } - fn report_unused_deps(&mut self, krate: &ast::Crate) { // Make a point span rather than covering the whole file let span = krate.spans.inner_span.shrink_to_lo(); diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 1dae858b7ef1a..c35eee8e8162e 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1895,10 +1895,6 @@ impl CrateMetadata { self.dependencies.iter().copied() } - pub(crate) fn add_dependency(&mut self, cnum: CrateNum) { - self.dependencies.push(cnum); - } - pub(crate) fn target_modifiers(&self) -> TargetModifiers { self.root.decode_target_modifiers(&self.blob).collect() } diff --git a/tests/ui/panic-runtime/auxiliary/depends.rs b/tests/ui/panic-runtime/auxiliary/depends.rs deleted file mode 100644 index 7a35619b68133..0000000000000 --- a/tests/ui/panic-runtime/auxiliary/depends.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@ no-prefer-dynamic - -#![feature(panic_runtime)] -#![crate_type = "rlib"] -#![panic_runtime] -#![no_std] - -extern crate needs_panic_runtime; diff --git a/tests/ui/panic-runtime/auxiliary/needs-panic-runtime.rs b/tests/ui/panic-runtime/auxiliary/needs-panic-runtime.rs deleted file mode 100644 index fbafee0c24177..0000000000000 --- a/tests/ui/panic-runtime/auxiliary/needs-panic-runtime.rs +++ /dev/null @@ -1,6 +0,0 @@ -//@ no-prefer-dynamic - -#![feature(needs_panic_runtime)] -#![crate_type = "rlib"] -#![needs_panic_runtime] -#![no_std] diff --git a/tests/ui/panic-runtime/runtime-depend-on-needs-runtime.rs b/tests/ui/panic-runtime/runtime-depend-on-needs-runtime.rs deleted file mode 100644 index eb00c071702c3..0000000000000 --- a/tests/ui/panic-runtime/runtime-depend-on-needs-runtime.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@ dont-check-compiler-stderr -//@ aux-build:needs-panic-runtime.rs -//@ aux-build:depends.rs - -extern crate depends; - -fn main() {} - -//~? ERROR the crate `depends` cannot depend on a crate that needs a panic runtime, but it depends on `needs_panic_runtime` From 3a7ae677b86b7a765143da8cfac5e3f712a663df Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 8 May 2025 14:58:26 +0000 Subject: [PATCH 03/15] Stop handling explicit dependencies on the panic runtime You shouldn't ever need to explicitly depend on it. And we weren't checking that the panic runtime used the correct panic strategy either. --- compiler/rustc_metadata/src/creader.rs | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 60788a43bd75b..2c882b84c588d 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -952,27 +952,19 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { // If we need a panic runtime, we try to find an existing one here. At // the same time we perform some general validation of the DAG we've got // going such as ensuring everything has a compatible panic strategy. - let desired_strategy = self.sess.panic_strategy(); - let mut runtime_found = false; let mut needs_panic_runtime = attr::contains_name(&krate.attrs, sym::needs_panic_runtime); - for (_cnum, data) in self.cstore.iter_crate_data() { - needs_panic_runtime = needs_panic_runtime || data.needs_panic_runtime(); - if data.is_panic_runtime() { - runtime_found = runtime_found || data.dep_kind() == CrateDepKind::Explicit; - } + needs_panic_runtime |= data.needs_panic_runtime(); } - // If an explicitly linked and matching panic runtime was found, or if - // we just don't need one at all, then we're done here and there's - // nothing else to do. - if !needs_panic_runtime || runtime_found { + // If we just don't need a panic runtime at all, then we're done here + // and there's nothing else to do. + if !needs_panic_runtime { return; } - // By this point we know that we (a) need a panic runtime and (b) no - // panic runtime was explicitly linked. Here we just load an appropriate - // default runtime for our panic strategy. + // By this point we know that we need a panic runtime. Here we just load + // an appropriate default runtime for our panic strategy. // // We may resolve to an already loaded crate (as the crate may not have // been explicitly linked prior to this), but this is fine. @@ -980,6 +972,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { // Also note that we have yet to perform validation of the crate graph // in terms of everyone has a compatible panic runtime format, that's // performed later as part of the `dependency_format` module. + let desired_strategy = self.sess.panic_strategy(); let name = match desired_strategy { PanicStrategy::Unwind => sym::panic_unwind, PanicStrategy::Abort => sym::panic_abort, From f8d1bfa0a680ffc848e512ef799f687aae723cbe Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 8 May 2025 15:03:04 +0000 Subject: [PATCH 04/15] Avoid exporting panic_unwind as stdlib cargo feature There is already panic-unwind to enable it. --- library/std/Cargo.toml | 2 +- library/sysroot/Cargo.toml | 2 +- src/tools/miri/cargo-miri/src/setup.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 31371f06b3865..844e2af209118 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -93,7 +93,7 @@ backtrace = [ 'miniz_oxide/rustc-dep-of-std', ] -panic-unwind = ["panic_unwind"] +panic-unwind = ["dep:panic_unwind"] compiler-builtins-c = ["alloc/compiler-builtins-c"] compiler-builtins-mem = ["alloc/compiler-builtins-mem"] compiler-builtins-no-asm = ["alloc/compiler-builtins-no-asm"] diff --git a/library/sysroot/Cargo.toml b/library/sysroot/Cargo.toml index c149d513c32b4..3adc022497167 100644 --- a/library/sysroot/Cargo.toml +++ b/library/sysroot/Cargo.toml @@ -26,7 +26,7 @@ debug_typeid = ["std/debug_typeid"] llvm-libunwind = ["std/llvm-libunwind"] system-llvm-libunwind = ["std/system-llvm-libunwind"] optimize_for_size = ["std/optimize_for_size"] -panic-unwind = ["std/panic_unwind"] +panic-unwind = ["std/panic-unwind"] panic_immediate_abort = ["std/panic_immediate_abort"] profiler = ["dep:profiler_builtins"] std_detect_file_io = ["std/std_detect_file_io"] diff --git a/src/tools/miri/cargo-miri/src/setup.rs b/src/tools/miri/cargo-miri/src/setup.rs index b9b58c04f9e4a..e399f66fbc9cd 100644 --- a/src/tools/miri/cargo-miri/src/setup.rs +++ b/src/tools/miri/cargo-miri/src/setup.rs @@ -83,7 +83,7 @@ pub fn setup( SysrootConfig::NoStd } else { SysrootConfig::WithStd { - std_features: ["panic_unwind", "backtrace"].into_iter().map(Into::into).collect(), + std_features: ["panic-unwind", "backtrace"].into_iter().map(Into::into).collect(), } }; let cargo_cmd = { From 6df545614789f2b2e8db6bb2b34e97105c8619be Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 9 May 2025 09:56:27 +0000 Subject: [PATCH 05/15] Make comment on activate_injected_dep a doc comment --- .../rustc_metadata/src/dependency_format.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_metadata/src/dependency_format.rs b/compiler/rustc_metadata/src/dependency_format.rs index fcae33c73c9c0..f2f5afd84a09a 100644 --- a/compiler/rustc_metadata/src/dependency_format.rs +++ b/compiler/rustc_metadata/src/dependency_format.rs @@ -370,15 +370,15 @@ fn attempt_static(tcx: TyCtxt<'_>, unavailable: &mut Vec) -> Option, list: &mut DependencyList, From 3cb0cba054d9d1871f3a10345d5c30cfc7ac214c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=AE=87=E9=80=B8?= Date: Sun, 1 Jun 2025 23:12:35 +0800 Subject: [PATCH 06/15] Handle win32 separator & prefixes for cygwin paths --- library/std/src/path.rs | 17 +- library/std/src/sys/path/cygwin.rs | 92 ++++ library/std/src/sys/path/mod.rs | 5 + library/std/src/sys/path/windows.rs | 175 +------- library/std/src/sys/path/windows/tests.rs | 2 + library/std/src/sys/path/windows_prefix.rs | 182 ++++++++ library/std/tests/path.rs | 472 +++++++++++++++++++++ 7 files changed, 772 insertions(+), 173 deletions(-) create mode 100644 library/std/src/sys/path/cygwin.rs create mode 100644 library/std/src/sys/path/windows_prefix.rs diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 826d9f0f39dc6..be8d9f1b05416 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -1316,8 +1316,17 @@ impl PathBuf { need_sep = false } + let need_clear = if cfg!(target_os = "cygwin") { + // If path is absolute and its prefix is none, it is like `/foo`, + // and will be handled below. + path.prefix().is_some() + } else { + // On Unix: prefix is always None. + path.is_absolute() || path.prefix().is_some() + }; + // absolute `path` replaces `self` - if path.is_absolute() || path.prefix().is_some() { + if need_clear { self.inner.truncate(0); // verbatim paths need . and .. removed @@ -3616,6 +3625,11 @@ impl Error for NormalizeError {} /// paths, this is currently equivalent to calling /// [`GetFullPathNameW`][windows-path]. /// +/// On Cygwin, this is currently equivalent to calling [`cygwin_conv_path`][cygwin-path] +/// with mode `CCP_WIN_A_TO_POSIX`, and then being processed like other POSIX platforms. +/// If a Windows path is given, it will be converted to an absolute POSIX path without +/// keeping `..`. +/// /// Note that these [may change in the future][changes]. /// /// # Errors @@ -3673,6 +3687,7 @@ impl Error for NormalizeError {} /// [changes]: io#platform-specific-behavior /// [posix-semantics]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13 /// [windows-path]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfullpathnamew +/// [cygwin-path]: https://cygwin.com/cygwin-api/func-cygwin-conv-path.html #[stable(feature = "absolute_path", since = "1.79.0")] pub fn absolute>(path: P) -> io::Result { let path = path.as_ref(); diff --git a/library/std/src/sys/path/cygwin.rs b/library/std/src/sys/path/cygwin.rs new file mode 100644 index 0000000000000..e90372805bbf9 --- /dev/null +++ b/library/std/src/sys/path/cygwin.rs @@ -0,0 +1,92 @@ +use crate::ffi::OsString; +use crate::os::unix::ffi::OsStringExt; +use crate::path::{Path, PathBuf}; +use crate::sys::common::small_c_string::run_path_with_cstr; +use crate::sys::cvt; +use crate::{io, ptr}; + +#[inline] +pub fn is_sep_byte(b: u8) -> bool { + b == b'/' || b == b'\\' +} + +/// Cygwin allways prefers `/` over `\`, and it always converts all `/` to `\` +/// internally when calling Win32 APIs. Therefore, the server component of path +/// `\\?\UNC\localhost/share` is `localhost/share` on Win32, but `localhost` +/// on Cygwin. +#[inline] +pub fn is_verbatim_sep(b: u8) -> bool { + b == b'/' || b == b'\\' +} + +pub use super::windows_prefix::parse_prefix; + +pub const MAIN_SEP_STR: &str = "/"; +pub const MAIN_SEP: char = '/'; + +unsafe extern "C" { + // Doc: https://cygwin.com/cygwin-api/func-cygwin-conv-path.html + // Src: https://github.com/cygwin/cygwin/blob/718a15ba50e0d01c79800bd658c2477f9a603540/winsup/cygwin/path.cc#L3902 + // Safety: + // * `what` should be `CCP_WIN_A_TO_POSIX` here + // * `from` is null-terminated UTF-8 path + // * `to` is buffer, the buffer size is `size`. + // + // Converts a path to an absolute POSIX path, no matter the input is Win32 path or POSIX path. + fn cygwin_conv_path( + what: libc::c_uint, + from: *const libc::c_char, + to: *mut u8, + size: libc::size_t, + ) -> libc::ssize_t; +} + +const CCP_WIN_A_TO_POSIX: libc::c_uint = 2; + +/// Make a POSIX path absolute. +pub(crate) fn absolute(path: &Path) -> io::Result { + run_path_with_cstr(path, &|path| { + let conv = CCP_WIN_A_TO_POSIX; + let size = cvt(unsafe { cygwin_conv_path(conv, path.as_ptr(), ptr::null_mut(), 0) })?; + // If success, size should not be 0. + debug_assert!(size >= 1); + let size = size as usize; + let mut buffer = Vec::with_capacity(size); + cvt(unsafe { cygwin_conv_path(conv, path.as_ptr(), buffer.as_mut_ptr(), size) })?; + unsafe { + buffer.set_len(size - 1); + } + Ok(PathBuf::from(OsString::from_vec(buffer))) + }) + .map(|path| { + if path.prefix().is_some() { + return path; + } + + // From unix.rs + let mut components = path.components(); + let path_os = path.as_os_str().as_encoded_bytes(); + + let mut normalized = if path_os.starts_with(b"//") && !path_os.starts_with(b"///") { + components.next(); + PathBuf::from("//") + } else { + PathBuf::new() + }; + normalized.extend(components); + + if path_os.ends_with(b"/") { + normalized.push(""); + } + + normalized + }) +} + +pub(crate) fn is_absolute(path: &Path) -> bool { + if path.as_os_str().as_encoded_bytes().starts_with(b"\\") { + path.has_root() && path.prefix().is_some() + } else { + path.has_root() + } +} diff --git a/library/std/src/sys/path/mod.rs b/library/std/src/sys/path/mod.rs index 1fa4e80d6780c..a4ff4338cf5f4 100644 --- a/library/std/src/sys/path/mod.rs +++ b/library/std/src/sys/path/mod.rs @@ -1,6 +1,7 @@ cfg_if::cfg_if! { if #[cfg(target_os = "windows")] { mod windows; + mod windows_prefix; pub use windows::*; } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { mod sgx; @@ -11,6 +12,10 @@ cfg_if::cfg_if! { } else if #[cfg(target_os = "uefi")] { mod uefi; pub use uefi::*; + } else if #[cfg(target_os = "cygwin")] { + mod cygwin; + mod windows_prefix; + pub use cygwin::*; } else { mod unix; pub use unix::*; diff --git a/library/std/src/sys/path/windows.rs b/library/std/src/sys/path/windows.rs index e0e003f6a8192..f124e1e5a71c7 100644 --- a/library/std/src/sys/path/windows.rs +++ b/library/std/src/sys/path/windows.rs @@ -1,5 +1,5 @@ use crate::ffi::{OsStr, OsString}; -use crate::path::{Path, PathBuf, Prefix}; +use crate::path::{Path, PathBuf}; use crate::sys::api::utf16; use crate::sys::pal::{c, fill_utf16_buf, os2path, to_u16s}; use crate::{io, ptr}; @@ -7,6 +7,8 @@ use crate::{io, ptr}; #[cfg(test)] mod tests; +pub use super::windows_prefix::parse_prefix; + pub const MAIN_SEP_STR: &str = "\\"; pub const MAIN_SEP: char = '\\'; @@ -77,177 +79,6 @@ pub(crate) fn append_suffix(path: PathBuf, suffix: &OsStr) -> PathBuf { path.into() } -struct PrefixParser<'a, const LEN: usize> { - path: &'a OsStr, - prefix: [u8; LEN], -} - -impl<'a, const LEN: usize> PrefixParser<'a, LEN> { - #[inline] - fn get_prefix(path: &OsStr) -> [u8; LEN] { - let mut prefix = [0; LEN]; - // SAFETY: Only ASCII characters are modified. - for (i, &ch) in path.as_encoded_bytes().iter().take(LEN).enumerate() { - prefix[i] = if ch == b'/' { b'\\' } else { ch }; - } - prefix - } - - fn new(path: &'a OsStr) -> Self { - Self { path, prefix: Self::get_prefix(path) } - } - - fn as_slice(&self) -> PrefixParserSlice<'a, '_> { - PrefixParserSlice { - path: self.path, - prefix: &self.prefix[..LEN.min(self.path.len())], - index: 0, - } - } -} - -struct PrefixParserSlice<'a, 'b> { - path: &'a OsStr, - prefix: &'b [u8], - index: usize, -} - -impl<'a> PrefixParserSlice<'a, '_> { - fn strip_prefix(&self, prefix: &str) -> Option { - self.prefix[self.index..] - .starts_with(prefix.as_bytes()) - .then_some(Self { index: self.index + prefix.len(), ..*self }) - } - - fn prefix_bytes(&self) -> &'a [u8] { - &self.path.as_encoded_bytes()[..self.index] - } - - fn finish(self) -> &'a OsStr { - // SAFETY: The unsafety here stems from converting between &OsStr and - // &[u8] and back. This is safe to do because (1) we only look at ASCII - // contents of the encoding and (2) new &OsStr values are produced only - // from ASCII-bounded slices of existing &OsStr values. - unsafe { OsStr::from_encoded_bytes_unchecked(&self.path.as_encoded_bytes()[self.index..]) } - } -} - -pub fn parse_prefix(path: &OsStr) -> Option> { - use Prefix::{DeviceNS, Disk, UNC, Verbatim, VerbatimDisk, VerbatimUNC}; - - let parser = PrefixParser::<8>::new(path); - let parser = parser.as_slice(); - if let Some(parser) = parser.strip_prefix(r"\\") { - // \\ - - // The meaning of verbatim paths can change when they use a different - // separator. - if let Some(parser) = parser.strip_prefix(r"?\") - && !parser.prefix_bytes().iter().any(|&x| x == b'/') - { - // \\?\ - if let Some(parser) = parser.strip_prefix(r"UNC\") { - // \\?\UNC\server\share - - let path = parser.finish(); - let (server, path) = parse_next_component(path, true); - let (share, _) = parse_next_component(path, true); - - Some(VerbatimUNC(server, share)) - } else { - let path = parser.finish(); - - // in verbatim paths only recognize an exact drive prefix - if let Some(drive) = parse_drive_exact(path) { - // \\?\C: - Some(VerbatimDisk(drive)) - } else { - // \\?\prefix - let (prefix, _) = parse_next_component(path, true); - Some(Verbatim(prefix)) - } - } - } else if let Some(parser) = parser.strip_prefix(r".\") { - // \\.\COM42 - let path = parser.finish(); - let (prefix, _) = parse_next_component(path, false); - Some(DeviceNS(prefix)) - } else { - let path = parser.finish(); - let (server, path) = parse_next_component(path, false); - let (share, _) = parse_next_component(path, false); - - if !server.is_empty() && !share.is_empty() { - // \\server\share - Some(UNC(server, share)) - } else { - // no valid prefix beginning with "\\" recognized - None - } - } - } else { - // If it has a drive like `C:` then it's a disk. - // Otherwise there is no prefix. - parse_drive(path).map(Disk) - } -} - -// Parses a drive prefix, e.g. "C:" and "C:\whatever" -fn parse_drive(path: &OsStr) -> Option { - // In most DOS systems, it is not possible to have more than 26 drive letters. - // See . - fn is_valid_drive_letter(drive: &u8) -> bool { - drive.is_ascii_alphabetic() - } - - match path.as_encoded_bytes() { - [drive, b':', ..] if is_valid_drive_letter(drive) => Some(drive.to_ascii_uppercase()), - _ => None, - } -} - -// Parses a drive prefix exactly, e.g. "C:" -fn parse_drive_exact(path: &OsStr) -> Option { - // only parse two bytes: the drive letter and the drive separator - if path.as_encoded_bytes().get(2).map(|&x| is_sep_byte(x)).unwrap_or(true) { - parse_drive(path) - } else { - None - } -} - -// Parse the next path component. -// -// Returns the next component and the rest of the path excluding the component and separator. -// Does not recognize `/` as a separator character if `verbatim` is true. -fn parse_next_component(path: &OsStr, verbatim: bool) -> (&OsStr, &OsStr) { - let separator = if verbatim { is_verbatim_sep } else { is_sep_byte }; - - match path.as_encoded_bytes().iter().position(|&x| separator(x)) { - Some(separator_start) => { - let separator_end = separator_start + 1; - - let component = &path.as_encoded_bytes()[..separator_start]; - - // Panic safe - // The max `separator_end` is `bytes.len()` and `bytes[bytes.len()..]` is a valid index. - let path = &path.as_encoded_bytes()[separator_end..]; - - // SAFETY: `path` is a valid wtf8 encoded slice and each of the separators ('/', '\') - // is encoded in a single byte, therefore `bytes[separator_start]` and - // `bytes[separator_end]` must be code point boundaries and thus - // `bytes[..separator_start]` and `bytes[separator_end..]` are valid wtf8 slices. - unsafe { - ( - OsStr::from_encoded_bytes_unchecked(component), - OsStr::from_encoded_bytes_unchecked(path), - ) - } - } - None => (path, OsStr::new("")), - } -} - /// Returns a UTF-16 encoded path capable of bypassing the legacy `MAX_PATH` limits. /// /// This path may or may not have a verbatim prefix. diff --git a/library/std/src/sys/path/windows/tests.rs b/library/std/src/sys/path/windows/tests.rs index 9eb79203dcac7..830f48d7bfc94 100644 --- a/library/std/src/sys/path/windows/tests.rs +++ b/library/std/src/sys/path/windows/tests.rs @@ -1,4 +1,6 @@ +use super::super::windows_prefix::*; use super::*; +use crate::path::Prefix; #[test] fn test_parse_next_component() { diff --git a/library/std/src/sys/path/windows_prefix.rs b/library/std/src/sys/path/windows_prefix.rs new file mode 100644 index 0000000000000..b9dfe754485ab --- /dev/null +++ b/library/std/src/sys/path/windows_prefix.rs @@ -0,0 +1,182 @@ +//! Parse Windows prefixes, for both Windows and Cygwin. + +use super::{is_sep_byte, is_verbatim_sep}; +use crate::ffi::OsStr; +use crate::path::Prefix; + +struct PrefixParser<'a, const LEN: usize> { + path: &'a OsStr, + prefix: [u8; LEN], +} + +impl<'a, const LEN: usize> PrefixParser<'a, LEN> { + #[inline] + fn get_prefix(path: &OsStr) -> [u8; LEN] { + let mut prefix = [0; LEN]; + // SAFETY: Only ASCII characters are modified. + for (i, &ch) in path.as_encoded_bytes().iter().take(LEN).enumerate() { + prefix[i] = if ch == b'/' { b'\\' } else { ch }; + } + prefix + } + + fn new(path: &'a OsStr) -> Self { + Self { path, prefix: Self::get_prefix(path) } + } + + fn as_slice(&self) -> PrefixParserSlice<'a, '_> { + PrefixParserSlice { + path: self.path, + prefix: &self.prefix[..LEN.min(self.path.len())], + index: 0, + } + } +} + +struct PrefixParserSlice<'a, 'b> { + path: &'a OsStr, + prefix: &'b [u8], + index: usize, +} + +impl<'a> PrefixParserSlice<'a, '_> { + fn strip_prefix(&self, prefix: &str) -> Option { + self.prefix[self.index..] + .starts_with(prefix.as_bytes()) + .then_some(Self { index: self.index + prefix.len(), ..*self }) + } + + fn prefix_bytes(&self) -> &'a [u8] { + &self.path.as_encoded_bytes()[..self.index] + } + + fn finish(self) -> &'a OsStr { + // SAFETY: The unsafety here stems from converting between &OsStr and + // &[u8] and back. This is safe to do because (1) we only look at ASCII + // contents of the encoding and (2) new &OsStr values are produced only + // from ASCII-bounded slices of existing &OsStr values. + unsafe { OsStr::from_encoded_bytes_unchecked(&self.path.as_encoded_bytes()[self.index..]) } + } +} + +pub fn parse_prefix(path: &OsStr) -> Option> { + use Prefix::{DeviceNS, Disk, UNC, Verbatim, VerbatimDisk, VerbatimUNC}; + + let parser = PrefixParser::<8>::new(path); + let parser = parser.as_slice(); + if let Some(parser) = parser.strip_prefix(r"\\") { + // \\ + + // It's a POSIX path. + if cfg!(target_os = "cygwin") && !path.as_encoded_bytes().iter().any(|&x| x == b'\\') { + return None; + } + + // The meaning of verbatim paths can change when they use a different + // separator. + if let Some(parser) = parser.strip_prefix(r"?\") + // Cygwin allows `/` in verbatim paths. + && (cfg!(target_os = "cygwin") || !parser.prefix_bytes().iter().any(|&x| x == b'/')) + { + // \\?\ + if let Some(parser) = parser.strip_prefix(r"UNC\") { + // \\?\UNC\server\share + + let path = parser.finish(); + let (server, path) = parse_next_component(path, true); + let (share, _) = parse_next_component(path, true); + + Some(VerbatimUNC(server, share)) + } else { + let path = parser.finish(); + + // in verbatim paths only recognize an exact drive prefix + if let Some(drive) = parse_drive_exact(path) { + // \\?\C: + Some(VerbatimDisk(drive)) + } else { + // \\?\prefix + let (prefix, _) = parse_next_component(path, true); + Some(Verbatim(prefix)) + } + } + } else if let Some(parser) = parser.strip_prefix(r".\") { + // \\.\COM42 + let path = parser.finish(); + let (prefix, _) = parse_next_component(path, false); + Some(DeviceNS(prefix)) + } else { + let path = parser.finish(); + let (server, path) = parse_next_component(path, false); + let (share, _) = parse_next_component(path, false); + + if !server.is_empty() && !share.is_empty() { + // \\server\share + Some(UNC(server, share)) + } else { + // no valid prefix beginning with "\\" recognized + None + } + } + } else { + // If it has a drive like `C:` then it's a disk. + // Otherwise there is no prefix. + Some(Disk(parse_drive(path)?)) + } +} + +// Parses a drive prefix, e.g. "C:" and "C:\whatever" +fn parse_drive(path: &OsStr) -> Option { + // In most DOS systems, it is not possible to have more than 26 drive letters. + // See . + fn is_valid_drive_letter(drive: &u8) -> bool { + drive.is_ascii_alphabetic() + } + + match path.as_encoded_bytes() { + [drive, b':', ..] if is_valid_drive_letter(drive) => Some(drive.to_ascii_uppercase()), + _ => None, + } +} + +// Parses a drive prefix exactly, e.g. "C:" +fn parse_drive_exact(path: &OsStr) -> Option { + // only parse two bytes: the drive letter and the drive separator + if path.as_encoded_bytes().get(2).map(|&x| is_sep_byte(x)).unwrap_or(true) { + parse_drive(path) + } else { + None + } +} + +// Parse the next path component. +// +// Returns the next component and the rest of the path excluding the component and separator. +// Does not recognize `/` as a separator character on Windows if `verbatim` is true. +pub(crate) fn parse_next_component(path: &OsStr, verbatim: bool) -> (&OsStr, &OsStr) { + let separator = if verbatim { is_verbatim_sep } else { is_sep_byte }; + + match path.as_encoded_bytes().iter().position(|&x| separator(x)) { + Some(separator_start) => { + let separator_end = separator_start + 1; + + let component = &path.as_encoded_bytes()[..separator_start]; + + // Panic safe + // The max `separator_end` is `bytes.len()` and `bytes[bytes.len()..]` is a valid index. + let path = &path.as_encoded_bytes()[separator_end..]; + + // SAFETY: `path` is a valid wtf8 encoded slice and each of the separators ('/', '\') + // is encoded in a single byte, therefore `bytes[separator_start]` and + // `bytes[separator_end]` must be code point boundaries and thus + // `bytes[..separator_start]` and `bytes[separator_end..]` are valid wtf8 slices. + unsafe { + ( + OsStr::from_encoded_bytes_unchecked(component), + OsStr::from_encoded_bytes_unchecked(path), + ) + } + } + None => (path, OsStr::new("")), + } +} diff --git a/library/std/tests/path.rs b/library/std/tests/path.rs index be0dda1d426f3..901d2770f203e 100644 --- a/library/std/tests/path.rs +++ b/library/std/tests/path.rs @@ -1112,6 +1112,473 @@ pub fn test_decompositions_windows() { ); } +// Unix paths are tested in `test_decompositions_unix` above. +#[test] +#[cfg(target_os = "cygwin")] +pub fn test_decompositions_cygwin() { + t!("\\", + iter: ["/"], + has_root: true, + is_absolute: false, + parent: None, + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("c:", + iter: ["c:"], + has_root: false, + is_absolute: false, + parent: None, + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("c:\\", + iter: ["c:", "/"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("c:/", + iter: ["c:", "/"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("a\\b\\c", + iter: ["a", "b", "c"], + has_root: false, + is_absolute: false, + parent: Some("a\\b"), + file_name: Some("c"), + file_stem: Some("c"), + extension: None, + file_prefix: Some("c") + ); + + t!("\\a", + iter: ["/", "a"], + has_root: true, + is_absolute: false, + parent: Some("\\"), + file_name: Some("a"), + file_stem: Some("a"), + extension: None, + file_prefix: Some("a") + ); + + t!("c:\\foo.txt", + iter: ["c:", "/", "foo.txt"], + has_root: true, + is_absolute: true, + parent: Some("c:\\"), + file_name: Some("foo.txt"), + file_stem: Some("foo"), + extension: Some("txt"), + file_prefix: Some("foo") + ); + + t!("\\\\server\\share\\foo.txt", + iter: ["\\\\server\\share", "/", "foo.txt"], + has_root: true, + is_absolute: true, + parent: Some("\\\\server\\share\\"), + file_name: Some("foo.txt"), + file_stem: Some("foo"), + extension: Some("txt"), + file_prefix: Some("foo") + ); + + t!("//server/share\\foo.txt", + iter: ["//server/share", "/", "foo.txt"], + has_root: true, + is_absolute: true, + parent: Some("//server/share\\"), + file_name: Some("foo.txt"), + file_stem: Some("foo"), + extension: Some("txt"), + file_prefix: Some("foo") + ); + + t!("//server/share/foo.txt", + iter: ["/", "server", "share", "foo.txt"], + has_root: true, + is_absolute: true, + parent: Some("//server/share"), + file_name: Some("foo.txt"), + file_stem: Some("foo"), + extension: Some("txt"), + file_prefix: Some("foo") + ); + + t!("\\\\server\\share", + iter: ["\\\\server\\share", "/"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("\\\\server", + iter: ["/", "server"], + has_root: true, + is_absolute: false, + parent: Some("\\"), + file_name: Some("server"), + file_stem: Some("server"), + extension: None, + file_prefix: Some("server") + ); + + t!("\\\\?\\bar\\foo.txt", + iter: ["\\\\?\\bar", "/", "foo.txt"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\bar\\"), + file_name: Some("foo.txt"), + file_stem: Some("foo"), + extension: Some("txt"), + file_prefix: Some("foo") + ); + + t!("\\\\?\\bar", + iter: ["\\\\?\\bar"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("\\\\?\\", + iter: ["\\\\?\\"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("\\\\?\\UNC\\server\\share\\foo.txt", + iter: ["\\\\?\\UNC\\server\\share", "/", "foo.txt"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\UNC\\server\\share\\"), + file_name: Some("foo.txt"), + file_stem: Some("foo"), + extension: Some("txt"), + file_prefix: Some("foo") + ); + + t!("\\\\?\\UNC\\server/share\\foo.txt", + iter: ["\\\\?\\UNC\\server/share", "/", "foo.txt"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\UNC\\server/share\\"), + file_name: Some("foo.txt"), + file_stem: Some("foo"), + extension: Some("txt"), + file_prefix: Some("foo") + ); + + t!("//?/UNC/server\\share/foo.txt", + iter: ["//?/UNC/server\\share", "/", "foo.txt"], + has_root: true, + is_absolute: true, + parent: Some("//?/UNC/server\\share/"), + file_name: Some("foo.txt"), + file_stem: Some("foo"), + extension: Some("txt"), + file_prefix: Some("foo") + ); + + t!("//?/UNC/server/share/foo.txt", + iter: ["/", "?", "UNC", "server", "share", "foo.txt"], + has_root: true, + is_absolute: true, + parent: Some("//?/UNC/server/share"), + file_name: Some("foo.txt"), + file_stem: Some("foo"), + extension: Some("txt"), + file_prefix: Some("foo") + ); + + t!("\\\\?\\UNC\\server", + iter: ["\\\\?\\UNC\\server"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("\\\\?\\UNC\\", + iter: ["\\\\?\\UNC\\"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("\\\\?\\C:\\foo.txt", + iter: ["\\\\?\\C:", "/", "foo.txt"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\C:\\"), + file_name: Some("foo.txt"), + file_stem: Some("foo"), + extension: Some("txt"), + file_prefix: Some("foo") + ); + + t!("//?/C:\\foo.txt", + iter: ["//?/C:", "/", "foo.txt"], + has_root: true, + is_absolute: true, + parent: Some("//?/C:\\"), + file_name: Some("foo.txt"), + file_stem: Some("foo"), + extension: Some("txt"), + file_prefix: Some("foo") + ); + + t!("//?/C:/foo.txt", + iter: ["/", "?", "C:", "foo.txt"], + has_root: true, + is_absolute: true, + parent: Some("//?/C:"), + file_name: Some("foo.txt"), + file_stem: Some("foo"), + extension: Some("txt"), + file_prefix: Some("foo") + ); + + t!("\\\\?\\C:\\", + iter: ["\\\\?\\C:", "/"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("\\\\?\\C:", + iter: ["\\\\?\\C:"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("\\\\?\\foo/bar", + iter: ["\\\\?\\foo", "/", "bar"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\foo/"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None, + file_prefix: Some("bar") + ); + + t!("\\\\?\\C:/foo/bar", + iter: ["\\\\?\\C:", "/", "foo", "bar"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\C:/foo"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None, + file_prefix: Some("bar") + ); + + t!("\\\\.\\foo\\bar", + iter: ["\\\\.\\foo", "/", "bar"], + has_root: true, + is_absolute: true, + parent: Some("\\\\.\\foo\\"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None, + file_prefix: Some("bar") + ); + + t!("\\\\.\\foo", + iter: ["\\\\.\\foo", "/"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("\\\\.\\foo/bar", + iter: ["\\\\.\\foo", "/", "bar"], + has_root: true, + is_absolute: true, + parent: Some("\\\\.\\foo/"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None, + file_prefix: Some("bar") + ); + + t!("\\\\.\\foo\\bar/baz", + iter: ["\\\\.\\foo", "/", "bar", "baz"], + has_root: true, + is_absolute: true, + parent: Some("\\\\.\\foo\\bar"), + file_name: Some("baz"), + file_stem: Some("baz"), + extension: None, + file_prefix: Some("baz") + ); + + t!("\\\\.\\", + iter: ["\\\\.\\", "/"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("//.\\foo/bar", + iter: ["//.\\foo", "/", "bar"], + has_root: true, + is_absolute: true, + parent: Some("//.\\foo/"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None, + file_prefix: Some("bar") + ); + + t!("\\\\./foo/bar", + iter: ["\\\\./foo", "/", "bar"], + has_root: true, + is_absolute: true, + parent: Some("\\\\./foo/"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None, + file_prefix: Some("bar") + ); + + t!("//./foo\\bar", + iter: ["//./foo", "/", "bar"], + has_root: true, + is_absolute: true, + parent: Some("//./foo\\"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None, + file_prefix: Some("bar") + ); + + t!("//./?/C:/foo/bar", + iter: ["/", "?", "C:", "foo", "bar"], + has_root: true, + is_absolute: true, + parent: Some("//./?/C:/foo"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None, + file_prefix: Some("bar") + ); + + t!("//././../././../?/C:/foo/bar", + iter: ["/", "..", "..", "?", "C:", "foo", "bar"], + has_root: true, + is_absolute: true, + parent: Some("//././../././../?/C:/foo"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None, + file_prefix: Some("bar") + ); + + t!("\\\\?\\a\\b\\", + iter: ["\\\\?\\a", "/", "b"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\a\\"), + file_name: Some("b"), + file_stem: Some("b"), + extension: None, + file_prefix: Some("b") + ); + + t!("\\\\?\\C:\\foo.txt.zip", + iter: ["\\\\?\\C:", "/", "foo.txt.zip"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\C:\\"), + file_name: Some("foo.txt.zip"), + file_stem: Some("foo.txt"), + extension: Some("zip"), + file_prefix: Some("foo") + ); + + t!("\\\\?\\C:\\.foo.txt.zip", + iter: ["\\\\?\\C:", "/", ".foo.txt.zip"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\C:\\"), + file_name: Some(".foo.txt.zip"), + file_stem: Some(".foo.txt"), + extension: Some("zip"), + file_prefix: Some(".foo") + ); + + t!("\\\\?\\C:\\.foo", + iter: ["\\\\?\\C:", "/", ".foo"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\C:\\"), + file_name: Some(".foo"), + file_stem: Some(".foo"), + extension: None, + file_prefix: Some(".foo") + ); +} + #[test] pub fn test_stem_ext() { t!("foo", @@ -1227,6 +1694,11 @@ pub fn test_push() { tp!("/foo/bar", "/", "/"); tp!("/foo/bar", "/baz", "/baz"); tp!("/foo/bar", "./baz", "/foo/bar/./baz"); + + if cfg!(target_os = "cygwin") { + tp!("c:\\", "windows", "c:\\windows"); + tp!("c:", "windows", "c:windows"); + } } else { tp!("", "foo", "foo"); tp!("foo", "bar", r"foo\bar"); From 7cfc51bd509f91ca7418f5dc01780003f48fa2f1 Mon Sep 17 00:00:00 2001 From: rustbot <47979223+rustbot@users.noreply.github.com> Date: Mon, 16 Jun 2025 19:00:49 +0200 Subject: [PATCH 07/15] Update books --- src/doc/book | 2 +- src/doc/reference | 2 +- src/doc/rust-by-example | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/book b/src/doc/book index 634724ea85ebb..4433c9f0cad84 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit 634724ea85ebb08a542970bf8871ac8b0f77fd15 +Subproject commit 4433c9f0cad8460bee05ede040587f8a1fa3f1de diff --git a/src/doc/reference b/src/doc/reference index 8e0f593a30f3b..d4c66b346f4b7 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 8e0f593a30f3b56ddb0908fb7ab9249974e08738 +Subproject commit d4c66b346f4b72d29e70390a3fa3ea7d4e064db1 diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example index 21f4e32b8b40d..9baa9e863116c 160000 --- a/src/doc/rust-by-example +++ b/src/doc/rust-by-example @@ -1 +1 @@ -Subproject commit 21f4e32b8b40d36453fae16ec07ad4b857c445b6 +Subproject commit 9baa9e863116cb9524a177d5a5c475baac18928a From 8a7b50a5da5315031883bee788b0da92690a4063 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 16 Jun 2025 17:32:09 +0000 Subject: [PATCH 08/15] Fold unnecessary visit_struct_field_def in AstValidator --- .../rustc_ast_passes/src/ast_validation.rs | 20 ++----------------- 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 018887d0e8eaa..b69a91e2f5f86 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -224,20 +224,6 @@ impl<'a> AstValidator<'a> { } } - fn visit_struct_field_def(&mut self, field: &'a FieldDef) { - if let Some(ref ident) = field.ident - && ident.name == kw::Underscore - { - self.visit_vis(&field.vis); - self.visit_ident(ident); - self.visit_ty_common(&field.ty); - self.walk_ty(&field.ty); - walk_list!(self, visit_attribute, &field.attrs); - } else { - self.visit_field_def(field); - } - } - fn dcx(&self) -> DiagCtxtHandle<'a> { self.sess.dcx() } @@ -1135,8 +1121,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { VariantData::Struct { fields, .. } => { self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident); self.visit_generics(generics); - // Permit `Anon{Struct,Union}` as field type. - walk_list!(self, visit_struct_field_def, fields); + walk_list!(self, visit_field_def, fields); } _ => visit::walk_item(self, item), }, @@ -1148,8 +1133,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { VariantData::Struct { fields, .. } => { self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident); self.visit_generics(generics); - // Permit `Anon{Struct,Union}` as field type. - walk_list!(self, visit_struct_field_def, fields); + walk_list!(self, visit_field_def, fields); } _ => visit::walk_item(self, item), } From ad60d8eace04b8d9c498e102b845ff477fcf47a8 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 16 Jun 2025 17:48:03 +0000 Subject: [PATCH 09/15] Make sure to propagate result from visit_expr_fields --- compiler/rustc_ast/src/visit.rs | 2 +- ...lid-rustc_legacy_const_generics-issue-123077.rs | 7 +++++++ ...rustc_legacy_const_generics-issue-123077.stderr | 14 +++++++++++++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 1449a4a5fb30a..44ab0c2f258b3 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -1420,7 +1420,7 @@ macro_rules! common_visitor_and_walkers { let StructExpr { qself, path, fields, rest } = &$($mut)?**se; try_visit!(vis.visit_qself(qself)); try_visit!(vis.visit_path(path)); - visit_expr_fields(vis, fields); + try_visit!(visit_expr_fields(vis, fields)); match rest { StructRest::Base(expr) => try_visit!(vis.visit_expr(expr)), StructRest::Rest(_span) => {} diff --git a/tests/ui/invalid/invalid-rustc_legacy_const_generics-issue-123077.rs b/tests/ui/invalid/invalid-rustc_legacy_const_generics-issue-123077.rs index f3a15a58f26a3..70e3ef9814d55 100644 --- a/tests/ui/invalid/invalid-rustc_legacy_const_generics-issue-123077.rs +++ b/tests/ui/invalid/invalid-rustc_legacy_const_generics-issue-123077.rs @@ -28,4 +28,11 @@ fn main() { std::arch::x86_64::_mm_inserti_si64(loop {}, loop {}, || (), 1 + || ()); //~^ ERROR invalid argument to a legacy const generic + + // A regression test for . + struct Struct { + field: T, + } + std::arch::x86_64::_mm_blend_ps(loop {}, loop {}, Struct { field: || () }); + //~^ ERROR invalid argument to a legacy const generic } diff --git a/tests/ui/invalid/invalid-rustc_legacy_const_generics-issue-123077.stderr b/tests/ui/invalid/invalid-rustc_legacy_const_generics-issue-123077.stderr index 8d120ae98d90d..6f5d719006d8f 100644 --- a/tests/ui/invalid/invalid-rustc_legacy_const_generics-issue-123077.stderr +++ b/tests/ui/invalid/invalid-rustc_legacy_const_generics-issue-123077.stderr @@ -84,5 +84,17 @@ LL - std::arch::x86_64::_mm_inserti_si64(loop {}, loop {}, || (), 1 + || ()) LL + std::arch::x86_64::_mm_inserti_si64::<{ || () }, { 1 + || () }>(loop {}, loop {}); | -error: aborting due to 7 previous errors +error: invalid argument to a legacy const generic: cannot have const blocks, closures, async blocks or items + --> $DIR/invalid-rustc_legacy_const_generics-issue-123077.rs:36:71 + | +LL | std::arch::x86_64::_mm_blend_ps(loop {}, loop {}, Struct { field: || () }); + | ^^^^^ + | +help: try using a const generic argument instead + | +LL - std::arch::x86_64::_mm_blend_ps(loop {}, loop {}, Struct { field: || () }); +LL + std::arch::x86_64::_mm_blend_ps::<{ Struct { field: || () } }>(loop {}, loop {}); + | + +error: aborting due to 8 previous errors From ac8b506ca7a803939e20065fdd1a0949efebd6e9 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 16 Jun 2025 18:14:14 +0000 Subject: [PATCH 10/15] Fix some unused fields in AST visitor --- compiler/rustc_ast/src/visit.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 44ab0c2f258b3..bd2ab34bfc19b 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -884,7 +884,7 @@ macro_rules! common_visitor_and_walkers { TyKind::BareFn(function_declaration) => { let BareFnTy { safety, ext: _, generic_params, decl, decl_span } = &$($mut)? **function_declaration; - visit_safety(vis, safety); + try_visit!(visit_safety(vis, safety)); try_visit!(visit_generic_params(vis, generic_params)); try_visit!(vis.visit_fn_decl(decl)); try_visit!(visit_span(vis, decl_span)); @@ -1235,7 +1235,7 @@ macro_rules! common_visitor_and_walkers { bounds, bound_generic_params, }) => { - visit_generic_params(vis, bound_generic_params); + try_visit!(visit_generic_params(vis, bound_generic_params)); try_visit!(vis.visit_ty(bounded_ty)); walk_list!(vis, visit_param_bound, bounds, BoundKind::Bound); } From 7b29a5d2827f4336ee2c3ca163378c9dc5a82238 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Mon, 16 Jun 2025 12:07:24 -0700 Subject: [PATCH 11/15] Revert overeager warning for misuse of `--print native-static-libs` In a PR to emit warnings on misuse of `--print native-static-libs`, we did not consider the matter of composing parts of build systems. If you are not directly invoking rustc, it can be difficult to know when you will in fact compile a staticlib, so making sure everyone uses `--print native-static-lib` correctly can be just a nuisance. This reverts the following commits: - f66787a08d57dc1296619b314d2be596085bfeef - 72a9219e82c157041bfc8dfd378c9cb2b09c0650 - 98bb597c05c32365abbd6898f278b097352774ed - c59b70841c36277464b51161e3fcf12dfcb667e0 Next cycle we can reland a slightly more narrowly focused variant or one that focuses on `--emit` instead of `--print native-static-libs`. But in its current state, I am not sure the warning is very useful. --- compiler/rustc_codegen_ssa/src/back/link.rs | 19 ------------------- ...rning-print-link-info-without-staticlib.rs | 5 ----- ...g-print-link-info-without-staticlib.stderr | 6 ------ ...t-warning-while-exe-and-print-link-info.rs | 3 --- ...rning-while-exe-and-print-link-info.stderr | 4 ---- tests/ui/print-request/stability.rs | 1 - 6 files changed, 38 deletions(-) delete mode 100644 tests/ui/print-request/emit-warning-print-link-info-without-staticlib.rs delete mode 100644 tests/ui/print-request/emit-warning-print-link-info-without-staticlib.stderr delete mode 100644 tests/ui/print-request/emit-warning-while-exe-and-print-link-info.rs delete mode 100644 tests/ui/print-request/emit-warning-while-exe-and-print-link-info.stderr diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 8c52ed6ed1234..8882ba359b774 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -69,23 +69,6 @@ pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) { } } -fn check_link_info_print_request(sess: &Session, crate_types: &[CrateType]) { - let print_native_static_libs = - sess.opts.prints.iter().any(|p| p.kind == PrintKind::NativeStaticLibs); - let has_staticlib = crate_types.iter().any(|ct| *ct == CrateType::Staticlib); - if print_native_static_libs { - if !has_staticlib { - sess.dcx() - .warn(format!("cannot output linkage information without staticlib crate-type")); - sess.dcx() - .note(format!("consider `--crate-type staticlib` to print linkage information")); - } else if !sess.opts.output_types.should_link() { - sess.dcx() - .warn(format!("cannot output linkage information when --emit link is not passed")); - } - } -} - /// Performs the linkage portion of the compilation phase. This will generate all /// of the requested outputs for this compilation session. pub fn link_binary( @@ -208,8 +191,6 @@ pub fn link_binary( } } - check_link_info_print_request(sess, &codegen_results.crate_info.crate_types); - // Remove the temporary object file and metadata if we aren't saving temps. sess.time("link_binary_remove_temps", || { // If the user requests that temporaries are saved, don't delete any. diff --git a/tests/ui/print-request/emit-warning-print-link-info-without-staticlib.rs b/tests/ui/print-request/emit-warning-print-link-info-without-staticlib.rs deleted file mode 100644 index b100c062bba4c..0000000000000 --- a/tests/ui/print-request/emit-warning-print-link-info-without-staticlib.rs +++ /dev/null @@ -1,5 +0,0 @@ -//@ compile-flags: --print native-static-libs -//@ check-pass -//~? WARN cannot output linkage information without staticlib crate-type - -fn main() {} diff --git a/tests/ui/print-request/emit-warning-print-link-info-without-staticlib.stderr b/tests/ui/print-request/emit-warning-print-link-info-without-staticlib.stderr deleted file mode 100644 index ceff08baa13a4..0000000000000 --- a/tests/ui/print-request/emit-warning-print-link-info-without-staticlib.stderr +++ /dev/null @@ -1,6 +0,0 @@ -warning: cannot output linkage information without staticlib crate-type - -note: consider `--crate-type staticlib` to print linkage information - -warning: 1 warning emitted - diff --git a/tests/ui/print-request/emit-warning-while-exe-and-print-link-info.rs b/tests/ui/print-request/emit-warning-while-exe-and-print-link-info.rs deleted file mode 100644 index 3e9ca457a9c9b..0000000000000 --- a/tests/ui/print-request/emit-warning-while-exe-and-print-link-info.rs +++ /dev/null @@ -1,3 +0,0 @@ -//@ compile-flags: --print native-static-libs --crate-type staticlib --emit metadata -//@ check-pass -//~? WARN cannot output linkage information when --emit link is not passed diff --git a/tests/ui/print-request/emit-warning-while-exe-and-print-link-info.stderr b/tests/ui/print-request/emit-warning-while-exe-and-print-link-info.stderr deleted file mode 100644 index b32e1437d6b50..0000000000000 --- a/tests/ui/print-request/emit-warning-while-exe-and-print-link-info.stderr +++ /dev/null @@ -1,4 +0,0 @@ -warning: cannot output linkage information when --emit link is not passed - -warning: 1 warning emitted - diff --git a/tests/ui/print-request/stability.rs b/tests/ui/print-request/stability.rs index fbcdf916cc7cd..54142ce78cefb 100644 --- a/tests/ui/print-request/stability.rs +++ b/tests/ui/print-request/stability.rs @@ -110,4 +110,3 @@ fn main() {} //[check_cfg]~? ERROR the `-Z unstable-options` flag must also be passed to enable the `check-cfg` print option //[supported_crate_types]~? ERROR the `-Z unstable-options` flag must also be passed to enable the `supported-crate-types` print option //[target_spec_json]~? ERROR the `-Z unstable-options` flag must also be passed to enable the `target-spec-json` print option -//[native_static_libs]~? WARNING cannot output linkage information without staticlib crate-type From 7859a37ffe0484389d17645f04b79326379525f0 Mon Sep 17 00:00:00 2001 From: ostylk Date: Wed, 11 Jun 2025 18:58:01 +0200 Subject: [PATCH 12/15] explicitly set llvm_abiname option on existing ppc64 targets --- .../rustc_target/src/spec/targets/powerpc64_unknown_freebsd.rs | 1 + .../src/spec/targets/powerpc64_unknown_linux_gnu.rs | 1 + .../src/spec/targets/powerpc64_unknown_linux_musl.rs | 1 + .../rustc_target/src/spec/targets/powerpc64_unknown_openbsd.rs | 1 + compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs | 1 + .../src/spec/targets/powerpc64le_unknown_freebsd.rs | 1 + .../src/spec/targets/powerpc64le_unknown_linux_gnu.rs | 1 + .../src/spec/targets/powerpc64le_unknown_linux_musl.rs | 1 + tests/ui/check-cfg/well-known-values.stderr | 2 +- 9 files changed, 9 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_freebsd.rs index dba45776c949a..66733d5d4b89c 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_freebsd.rs @@ -10,6 +10,7 @@ pub(crate) fn target() -> Target { base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.max_atomic_width = Some(64); base.stack_probes = StackProbeType::Inline; + base.llvm_abiname = "elfv2".into(); Target { llvm_target: "powerpc64-unknown-freebsd".into(), diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_gnu.rs index 1f67bc7f3c28a..ecf68ddff8c2f 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_gnu.rs @@ -10,6 +10,7 @@ pub(crate) fn target() -> Target { base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.max_atomic_width = Some(64); base.stack_probes = StackProbeType::Inline; + base.llvm_abiname = "elfv1".into(); Target { llvm_target: "powerpc64-unknown-linux-gnu".into(), diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs index 49413d27a4555..e205aef82856e 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs @@ -12,6 +12,7 @@ pub(crate) fn target() -> Target { base.stack_probes = StackProbeType::Inline; // FIXME(compiler-team#422): musl targets should be dynamically linked by default. base.crt_static_default = true; + base.llvm_abiname = "elfv2".into(); Target { llvm_target: "powerpc64-unknown-linux-musl".into(), diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_openbsd.rs index f5ca54291c697..bcb328020eea8 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_openbsd.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_openbsd.rs @@ -10,6 +10,7 @@ pub(crate) fn target() -> Target { base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.max_atomic_width = Some(64); base.stack_probes = StackProbeType::Inline; + base.llvm_abiname = "elfv2".into(); Target { llvm_target: "powerpc64-unknown-openbsd".into(), diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs index 3e4a58f568af9..37c888ba51443 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs @@ -10,6 +10,7 @@ pub(crate) fn target() -> Target { base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.max_atomic_width = Some(64); base.stack_probes = StackProbeType::Inline; + base.llvm_abiname = "elfv1".into(); Target { llvm_target: "powerpc64-unknown-linux-gnu".into(), diff --git a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_freebsd.rs b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_freebsd.rs index 4640d537e8ea8..3096c4d14ad87 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_freebsd.rs @@ -8,6 +8,7 @@ pub(crate) fn target() -> Target { base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.max_atomic_width = Some(64); base.stack_probes = StackProbeType::Inline; + base.llvm_abiname = "elfv2".into(); Target { llvm_target: "powerpc64le-unknown-freebsd".into(), diff --git a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_gnu.rs index dd3f660d81e4e..9e406af53b5b5 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_gnu.rs @@ -8,6 +8,7 @@ pub(crate) fn target() -> Target { base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.max_atomic_width = Some(64); base.stack_probes = StackProbeType::Inline; + base.llvm_abiname = "elfv2".into(); Target { llvm_target: "powerpc64le-unknown-linux-gnu".into(), diff --git a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs index 9e2bfe2c56fe4..f145c5b8c1451 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs @@ -10,6 +10,7 @@ pub(crate) fn target() -> Target { base.stack_probes = StackProbeType::Inline; // FIXME(compiler-team#422): musl targets should be dynamically linked by default. base.crt_static_default = true; + base.llvm_abiname = "elfv2".into(); Target { llvm_target: "powerpc64le-unknown-linux-musl".into(), diff --git a/tests/ui/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr index 532c1ab13d118..2484974cdc27c 100644 --- a/tests/ui/check-cfg/well-known-values.stderr +++ b/tests/ui/check-cfg/well-known-values.stderr @@ -129,7 +129,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_abi = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_abi` are: ``, `abi64`, `abiv2`, `abiv2hf`, `eabi`, `eabihf`, `fortanix`, `ilp32`, `ilp32e`, `llvm`, `macabi`, `sim`, `softfloat`, `spe`, `uwp`, `vec-extabi`, and `x32` + = note: expected values for `target_abi` are: ``, `abi64`, `abiv2`, `abiv2hf`, `eabi`, `eabihf`, `elfv1`, `elfv2`, `fortanix`, `ilp32`, `ilp32e`, `llvm`, `macabi`, `sim`, `softfloat`, `spe`, `uwp`, `vec-extabi`, and `x32` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` From 9c1180b6238d163fc384d60d85647385d9210343 Mon Sep 17 00:00:00 2001 From: ostylk Date: Wed, 11 Jun 2025 16:28:23 +0200 Subject: [PATCH 13/15] indicate ppc64 elf abi in e_flags --- .../rustc_codegen_ssa/src/back/metadata.rs | 18 ++++++++++++++++++ tests/ui/check-cfg/well-known-values.stderr | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index a16862c41ee53..d091c46d9c181 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -379,6 +379,24 @@ pub(super) fn elf_e_flags(architecture: Architecture, sess: &Session) -> u32 { }; e_flags } + Architecture::PowerPc64 => { + const EF_PPC64_ABI_UNKNOWN: u32 = 0; + const EF_PPC64_ABI_ELF_V1: u32 = 1; + const EF_PPC64_ABI_ELF_V2: u32 = 2; + + match sess.target.options.llvm_abiname.as_ref() { + // If the flags do not correctly indicate the ABI, + // linkers such as ld.lld assume that the ppc64 object files are always ELFv2 + // which leads to broken binaries if ELFv1 is used for the object files. + "elfv1" => EF_PPC64_ABI_ELF_V1, + "elfv2" => EF_PPC64_ABI_ELF_V2, + "" if sess.target.options.binary_format.to_object() == BinaryFormat::Elf => { + bug!("No ABI specified for this PPC64 ELF target"); + } + // Fall back + _ => EF_PPC64_ABI_UNKNOWN, + } + } _ => 0, } } diff --git a/tests/ui/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr index 2484974cdc27c..532c1ab13d118 100644 --- a/tests/ui/check-cfg/well-known-values.stderr +++ b/tests/ui/check-cfg/well-known-values.stderr @@ -129,7 +129,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_abi = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_abi` are: ``, `abi64`, `abiv2`, `abiv2hf`, `eabi`, `eabihf`, `elfv1`, `elfv2`, `fortanix`, `ilp32`, `ilp32e`, `llvm`, `macabi`, `sim`, `softfloat`, `spe`, `uwp`, `vec-extabi`, and `x32` + = note: expected values for `target_abi` are: ``, `abi64`, `abiv2`, `abiv2hf`, `eabi`, `eabihf`, `fortanix`, `ilp32`, `ilp32e`, `llvm`, `macabi`, `sim`, `softfloat`, `spe`, `uwp`, `vec-extabi`, and `x32` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` From 00c104225fad5d3eb27dc37719d9b6c26844c389 Mon Sep 17 00:00:00 2001 From: binarycat Date: Thu, 5 Jun 2025 18:42:54 -0500 Subject: [PATCH 14/15] rustdoc: make srcIndex no longer a global variable this is one-time initialization data, it can just be a function parameter. we also move the json parsing into createSrcSidebar to save a few bytes. --- src/librustdoc/html/render/write_shared.rs | 6 +----- .../html/render/write_shared/tests.rs | 18 +++--------------- src/librustdoc/html/static/js/rustdoc.d.ts | 6 ++---- src/librustdoc/html/static/js/src-script.js | 9 +++++---- tests/rustdoc-gui/globals.goml | 3 --- 5 files changed, 11 insertions(+), 31 deletions(-) diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index 33738f7a242f6..fb2b45802a671 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -477,11 +477,7 @@ impl SourcesPart { // This needs to be `var`, not `const`. // This variable needs declared in the current global scope so that if // src-script.js loads first, it can pick it up. - SortedTemplate::from_before_after( - r"var srcIndex = new Map(JSON.parse('[", - r"]')); -createSrcSidebar();", - ) + SortedTemplate::from_before_after(r"createSrcSidebar('[", r"]');") } fn get(cx: &Context<'_>, crate_name: &OrderedJson) -> Result, Error> { diff --git a/src/librustdoc/html/render/write_shared/tests.rs b/src/librustdoc/html/render/write_shared/tests.rs index a235f1d37243a..6f185e85345bc 100644 --- a/src/librustdoc/html/render/write_shared/tests.rs +++ b/src/librustdoc/html/render/write_shared/tests.rs @@ -22,23 +22,11 @@ fn but_last_line(s: &str) -> &str { #[test] fn sources_template() { let mut template = SourcesPart::blank(); - assert_eq!( - but_last_line(&template.to_string()), - r"var srcIndex = new Map(JSON.parse('[]')); -createSrcSidebar();" - ); + assert_eq!(but_last_line(&template.to_string()), r"createSrcSidebar('[]');"); template.append(EscapedJson::from(OrderedJson::serialize("u").unwrap()).to_string()); - assert_eq!( - but_last_line(&template.to_string()), - r#"var srcIndex = new Map(JSON.parse('["u"]')); -createSrcSidebar();"# - ); + assert_eq!(but_last_line(&template.to_string()), r#"createSrcSidebar('["u"]');"#); template.append(EscapedJson::from(OrderedJson::serialize("v").unwrap()).to_string()); - assert_eq!( - but_last_line(&template.to_string()), - r#"var srcIndex = new Map(JSON.parse('["u","v"]')); -createSrcSidebar();"# - ); + assert_eq!(but_last_line(&template.to_string()), r#"createSrcSidebar('["u","v"]');"#); } #[test] diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts index 0d2e19e019f34..6af16441de88b 100644 --- a/src/librustdoc/html/static/js/rustdoc.d.ts +++ b/src/librustdoc/html/static/js/rustdoc.d.ts @@ -4,8 +4,6 @@ /* eslint-disable */ declare global { - /** Map from crate name to directory structure, for source view */ - declare var srcIndex: Map; /** Defined and documented in `storage.js` */ declare function nonnull(x: T|null, msg: string|undefined); /** Defined and documented in `storage.js` */ @@ -64,7 +62,7 @@ declare global { * create's the sidebar in source code view. * called in generated `src-files.js`. */ - createSrcSidebar?: function(), + createSrcSidebar?: function(string), /** * Set up event listeners for a scraped source example. */ @@ -129,7 +127,7 @@ declare namespace rustdoc { /** * A single parsed "atom" in a search query. For example, - * + * * std::fmt::Formatter, Write -> Result<()> * ┏━━━━━━━━━━━━━━━━━━ ┌──── ┏━━━━━┅┅┅┅┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐ * ┃ │ ┗ QueryElement { ┊ diff --git a/src/librustdoc/html/static/js/src-script.js b/src/librustdoc/html/static/js/src-script.js index b9ab6e85603bc..0c6afbeed22e2 100644 --- a/src/librustdoc/html/static/js/src-script.js +++ b/src/librustdoc/html/static/js/src-script.js @@ -1,6 +1,3 @@ -// From rust: -/* global srcIndex */ - // Local js definitions: /* global addClass, onEachLazy, removeClass, browserSupportsHistoryApi */ /* global updateLocalStorage, getVar, nonnull */ @@ -100,11 +97,15 @@ window.rustdocToggleSrcSidebar = () => { // This function is called from "src-files.js", generated in `html/render/write_shared.rs`. // eslint-disable-next-line no-unused-vars -function createSrcSidebar() { +/** + * @param {string} srcIndexStr - strinified json map from crate name to dir structure + */ +function createSrcSidebar(srcIndexStr) { const container = nonnull(document.querySelector("nav.sidebar")); const sidebar = document.createElement("div"); sidebar.id = "src-sidebar"; + const srcIndex = new Map(JSON.parse(srcIndexStr)); let hasFoundFile = false; diff --git a/tests/rustdoc-gui/globals.goml b/tests/rustdoc-gui/globals.goml index f8c495ec18a69..7a0e2b9eb7462 100644 --- a/tests/rustdoc-gui/globals.goml +++ b/tests/rustdoc-gui/globals.goml @@ -6,7 +6,6 @@ go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=sa'%3Bda'%3Bds" wait-for: "#search-tabs" assert-window-property-false: {"searchIndex": null} -assert-window-property: {"srcIndex": null} // Form input go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" @@ -14,11 +13,9 @@ write-into: (".search-input", "Foo") press-key: 'Enter' wait-for: "#search-tabs" assert-window-property-false: {"searchIndex": null} -assert-window-property: {"srcIndex": null} // source sidebar go-to: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html" click: "#sidebar-button" wait-for: "#src-sidebar details" -assert-window-property-false: {"srcIndex": null} assert-window-property: {"searchIndex": null} From fb75e09914bdba9280a0dd3e992ad066e0a5c5a3 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 17 Jun 2025 08:13:25 +1000 Subject: [PATCH 15/15] Add a comment to `FORMAT_VERSION`. This minimizes the chance of two PRs changing it from N to N+1. --- src/rustdoc-json-types/lib.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index 8a3ab6f864072..2c94b8f914f7d 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -30,6 +30,14 @@ pub type FxHashMap = HashMap; // re-export for use in src/librustdoc /// This integer is incremented with every breaking change to the API, /// and is returned along with the JSON blob as [`Crate::format_version`]. /// Consuming code should assert that this value matches the format version(s) that it supports. +// +// WARNING: When you update `FORMAT_VERSION`, please also update the "Latest feature" line with a +// description of the change. This minimizes the risk of two concurrent PRs changing +// `FORMAT_VERSION` from N to N+1 and git merging them without conflicts; the "Latest feature" line +// will instead cause conflicts. See #94591 for more. (This paragraph and the "Latest feature" line +// are deliberately not in a doc comment, because they need not be in public docs.) +// +// Latest feature: rustdoc JSON: Don't apply #[repr] privacy heuristics pub const FORMAT_VERSION: u32 = 46; /// The root of the emitted JSON blob.