From a56b1d2a1378d0559a6174b4392fe49b245268df Mon Sep 17 00:00:00 2001 From: Giang Dao Date: Mon, 7 Oct 2024 19:07:57 +0800 Subject: [PATCH 01/19] fix missing rustfmt and clippy for msi --- src/bootstrap/src/core/build_steps/dist.rs | 31 +++++++++++++++- src/etc/installer/msi/rust.wxs | 41 +++++++++++++++++++++- 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 2354fe1ebafbe..177696b922e05 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -1627,6 +1627,8 @@ impl Step for Extended { "rust-analyzer-preview".to_string() } else if name == "clippy" { "clippy-preview".to_string() + } else if name == "rustfmt" { + "rustfmt-preview".to_string() } else if name == "miri" { "miri-preview".to_string() } else if name == "rustc-codegen-cranelift" { @@ -1646,7 +1648,7 @@ impl Step for Extended { prepare("cargo"); prepare("rust-analysis"); prepare("rust-std"); - for tool in &["clippy", "rust-analyzer", "rust-docs", "miri"] { + for tool in &["clippy", "rustfmt", "rust-analyzer", "rust-docs", "miri"] { if built_tools.contains(tool) { prepare(tool); } @@ -1764,6 +1766,24 @@ impl Step for Extended { .arg(etc.join("msi/remove-duplicates.xsl")) .run(builder); } + if built_tools.contains("rustfmt") { + command(&heat) + .current_dir(&exe) + .arg("dir") + .arg("rustfmt") + .args(heat_flags) + .arg("-cg") + .arg("RustFmtGroup") + .arg("-dr") + .arg("RustFmt") + .arg("-var") + .arg("var.RustFmtDir") + .arg("-out") + .arg(exe.join("RustFmtGroup.wxs")) + .arg("-t") + .arg(etc.join("msi/remove-duplicates.xsl")) + .run(builder); + } if built_tools.contains("miri") { command(&heat) .current_dir(&exe) @@ -1835,6 +1855,9 @@ impl Step for Extended { if built_tools.contains("clippy") { cmd.arg("-dClippyDir=clippy"); } + if built_tools.contains("rustfmt") { + cmd.arg("-dRustFmtDir=rustfmt"); + } if built_tools.contains("rust-docs") { cmd.arg("-dDocsDir=rust-docs"); } @@ -1861,6 +1884,9 @@ impl Step for Extended { if built_tools.contains("clippy") { candle("ClippyGroup.wxs".as_ref()); } + if built_tools.contains("rustfmt") { + candle("RustFmtGroup.wxs".as_ref()); + } if built_tools.contains("miri") { candle("MiriGroup.wxs".as_ref()); } @@ -1899,6 +1925,9 @@ impl Step for Extended { if built_tools.contains("clippy") { cmd.arg("ClippyGroup.wixobj"); } + if built_tools.contains("rustfmt") { + cmd.arg("RustFmtGroup.wixobj"); + } if built_tools.contains("miri") { cmd.arg("MiriGroup.wixobj"); } diff --git a/src/etc/installer/msi/rust.wxs b/src/etc/installer/msi/rust.wxs index f29e1e4d27a27..2d155bf0b1019 100644 --- a/src/etc/installer/msi/rust.wxs +++ b/src/etc/installer/msi/rust.wxs @@ -172,6 +172,11 @@ + + + + + @@ -279,7 +284,41 @@ - + + + + + + + + + + + + + + + From a0abd613d031407c5d17bb1e7debebddf748d468 Mon Sep 17 00:00:00 2001 From: Giang Dao Date: Mon, 7 Oct 2024 19:10:18 +0800 Subject: [PATCH 02/19] fix missing rustfmt for apple darwin --- src/bootstrap/src/core/build_steps/dist.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 177696b922e05..b6c5a5add01c3 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -1585,9 +1585,15 @@ impl Step for Extended { prepare("cargo"); prepare("rust-std"); prepare("rust-analysis"); - prepare("clippy"); - prepare("rust-analyzer"); - for tool in &["rust-docs", "miri", "rustc-codegen-cranelift"] { + + for tool in &[ + "clippy", + "rustfmt", + "rust-analyzer", + "rust-docs", + "miri", + "rustc-codegen-cranelift", + ] { if built_tools.contains(tool) { prepare(tool); } From 43152ad47bb15e46c001820df854d4f65964e74a Mon Sep 17 00:00:00 2001 From: klensy Date: Sat, 16 Nov 2024 11:31:34 +0300 Subject: [PATCH 03/19] wix: allow to skip more components --- src/bootstrap/src/core/build_steps/dist.rs | 32 ++++++++- src/etc/installer/msi/rust.wxs | 80 +++++++++++++--------- 2 files changed, 78 insertions(+), 34 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index b6c5a5add01c3..33155ab379595 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -1856,7 +1856,7 @@ impl Step for Extended { .arg("-out") .arg(&output) .arg(input); - add_env(builder, &mut cmd, target); + add_env(builder, &mut cmd, target, &built_tools); if built_tools.contains("clippy") { cmd.arg("-dClippyDir=clippy"); @@ -1960,7 +1960,14 @@ impl Step for Extended { } } -fn add_env(builder: &Builder<'_>, cmd: &mut BootstrapCommand, target: TargetSelection) { +fn add_env( + builder: &Builder<'_>, + cmd: &mut BootstrapCommand, + target: TargetSelection, + built_tools: &HashSet<&'static str>, +) { + // envs for wix should be always defined, even if not used + // FIXME: is they affect ccache? let mut parts = builder.version.split('.'); cmd.env("CFG_RELEASE_INFO", builder.rust_version()) .env("CFG_RELEASE_NUM", &builder.version) @@ -1981,6 +1988,27 @@ fn add_env(builder: &Builder<'_>, cmd: &mut BootstrapCommand, target: TargetSele } else { cmd.env("CFG_MINGW", "0").env("CFG_ABI", "MSVC"); } + + if built_tools.contains("rustfmt") { + cmd.env("CFG_RUSTFMT", "1"); + } else { + cmd.env("CFG_RUSTFMT", "0"); + } + if built_tools.contains("clippy") { + cmd.env("CFG_CLIPPY", "1"); + } else { + cmd.env("CFG_CLIPPY", "0"); + } + if built_tools.contains("miri") { + cmd.env("CFG_MIRI", "1"); + } else { + cmd.env("CFG_MIRI", "0"); + } + if built_tools.contains("rust-analyzer") { + cmd.env("CFG_RA", "1"); + } else { + cmd.env("CFG_RA", "0"); + } } fn install_llvm_file( diff --git a/src/etc/installer/msi/rust.wxs b/src/etc/installer/msi/rust.wxs index 2d155bf0b1019..64cceccc97582 100644 --- a/src/etc/installer/msi/rust.wxs +++ b/src/etc/installer/msi/rust.wxs @@ -172,11 +172,19 @@ - - - + + + + + + + + + - + + + @@ -284,34 +292,42 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + Date: Mon, 17 Mar 2025 18:34:28 +0800 Subject: [PATCH 04/19] adjust comment --- src/bootstrap/src/core/build_steps/dist.rs | 30 ++++++---------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 33155ab379595..a4a9cb7ef3155 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -1966,8 +1966,6 @@ fn add_env( target: TargetSelection, built_tools: &HashSet<&'static str>, ) { - // envs for wix should be always defined, even if not used - // FIXME: is they affect ccache? let mut parts = builder.version.split('.'); cmd.env("CFG_RELEASE_INFO", builder.rust_version()) .env("CFG_RELEASE_NUM", &builder.version) @@ -1989,26 +1987,14 @@ fn add_env( cmd.env("CFG_MINGW", "0").env("CFG_ABI", "MSVC"); } - if built_tools.contains("rustfmt") { - cmd.env("CFG_RUSTFMT", "1"); - } else { - cmd.env("CFG_RUSTFMT", "0"); - } - if built_tools.contains("clippy") { - cmd.env("CFG_CLIPPY", "1"); - } else { - cmd.env("CFG_CLIPPY", "0"); - } - if built_tools.contains("miri") { - cmd.env("CFG_MIRI", "1"); - } else { - cmd.env("CFG_MIRI", "0"); - } - if built_tools.contains("rust-analyzer") { - cmd.env("CFG_RA", "1"); - } else { - cmd.env("CFG_RA", "0"); - } + // ensure these variables are defined + let mut define_optional_tool = |tool_name: &str, env_name: &str| { + cmd.env(env_name, if built_tools.contains(tool_name) { "1" } else { "0" }); + }; + define_optional_tool("rustfmt", "CFG_RUSTFMT"); + define_optional_tool("clippy", "CFG_CLIPPY"); + define_optional_tool("miri", "CFG_MIRI"); + define_optional_tool("rust-analyzer", "CFG_RA"); } fn install_llvm_file( From 0ee94562c9e7e2e75a13f06d9820cd191e21a06a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 17 Mar 2025 15:49:28 +0100 Subject: [PATCH 05/19] fix download-llvm logic for subtree sync branches --- src/build_helper/src/git.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/build_helper/src/git.rs b/src/build_helper/src/git.rs index 9f778a2fd7742..3837a4e41ff14 100644 --- a/src/build_helper/src/git.rs +++ b/src/build_helper/src/git.rs @@ -140,6 +140,7 @@ pub fn get_closest_merge_commit( // cd \"/checkout\" && \"git\" \"merge-base\" \"origin/master\" \"HEAD\"\nexpected success, got: exit status: 1\n" // ``` // Investigate and resolve this issue instead of skipping it like this. + // NOTE (2025-03): this is probably caused by CI using a sparse checkout. (channel == "nightly" || !CiEnv::is_rust_lang_managed_ci_job()) { git_upstream_merge_base(config, git_dir).unwrap() @@ -150,11 +151,18 @@ pub fn get_closest_merge_commit( } }; + // Now that rust-lang/rust is the only repo using bors, we can search the entire + // history for a bors commit, not just "first parents". This is crucial to make + // this logic work when the user has currently checked out a subtree sync branch. + // At the same time, we use this logic in CI where only a tiny part of the history + // is even checked out, making this kind of history search very fragile. It turns + // out that by adding `--diff-merges=first-parent`, we get a usable reply + // even for sparse checkouts: it will just return the most recent bors commit. git.args([ "rev-list", &format!("--author={}", config.git_merge_commit_email), "-n1", - "--first-parent", + "--diff-merges=first-parent", &merge_base, ]); From 636285180daa00b62a604acee9c843128d70262f Mon Sep 17 00:00:00 2001 From: Wesley Wiser Date: Wed, 1 May 2024 18:32:45 -0500 Subject: [PATCH 06/19] [bootstrap] Distribute split debuginfo if present If debuginfo has been requested in `config.toml`, it should be packaged alongside the appropriate binary when running `x.py dist`. Currently, this is only implemented for msvc environments where split debuginfo is (basically) the only option. I've tested that this correctly packages the `.pdb` for each binary in the various dist packages. --- src/bootstrap/src/core/build_steps/compile.rs | 56 ++++--- src/bootstrap/src/core/build_steps/dist.rs | 155 ++++++++++++------ src/bootstrap/src/core/build_steps/doc.rs | 3 +- src/bootstrap/src/core/build_steps/tool.rs | 32 ++-- src/bootstrap/src/lib.rs | 63 ++++++- src/bootstrap/src/utils/helpers.rs | 17 ++ src/bootstrap/src/utils/tarball.rs | 28 +++- 7 files changed, 255 insertions(+), 99 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 846b4de81426e..18b5d4426b1ee 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -33,7 +33,7 @@ use crate::utils::exec::command; use crate::utils::helpers::{ exe, get_clang_cl_resource_dir, is_debug_info, is_dylib, symlink_dir, t, up_to_date, }; -use crate::{CLang, Compiler, DependencyType, GitRepo, LLVM_TOOLS, Mode, debug, trace}; +use crate::{CLang, Compiler, DependencyType, FileType, GitRepo, LLVM_TOOLS, Mode, debug, trace}; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Std { @@ -321,7 +321,7 @@ fn copy_and_stamp( dependency_type: DependencyType, ) { let target = libdir.join(name); - builder.copy_link(&sourcedir.join(name), &target); + builder.copy_link(&sourcedir.join(name), &target, FileType::Regular); target_deps.push((target, dependency_type)); } @@ -330,7 +330,7 @@ fn copy_llvm_libunwind(builder: &Builder<'_>, target: TargetSelection, libdir: & let libunwind_path = builder.ensure(llvm::Libunwind { target }); let libunwind_source = libunwind_path.join("libunwind.a"); let libunwind_target = libdir.join("libunwind.a"); - builder.copy_link(&libunwind_source, &libunwind_target); + builder.copy_link(&libunwind_source, &libunwind_target, FileType::NativeLibrary); libunwind_target } @@ -401,7 +401,7 @@ fn copy_self_contained_objects( for &obj in &["crtbegin.o", "crtbeginS.o", "crtend.o", "crtendS.o"] { let src = crt_path.join(obj); let target = libdir_self_contained.join(obj); - builder.copy_link(&src, &target); + builder.copy_link(&src, &target, FileType::NativeLibrary); target_deps.push((target, DependencyType::TargetSelfContained)); } } else { @@ -443,9 +443,9 @@ fn copy_self_contained_objects( } else if target.is_windows_gnu() { for obj in ["crt2.o", "dllcrt2.o"].iter() { let src = compiler_file(builder, &builder.cc(target), target, CLang::C, obj); - let target = libdir_self_contained.join(obj); - builder.copy_link(&src, &target); - target_deps.push((target, DependencyType::TargetSelfContained)); + let dst = libdir_self_contained.join(obj); + builder.copy_link(&src, &dst, FileType::NativeLibrary); + target_deps.push((dst, DependencyType::TargetSelfContained)); } } @@ -790,8 +790,11 @@ impl Step for StdLink { let file = t!(file); let path = file.path(); if path.is_file() { - builder - .copy_link(&path, &sysroot.join("lib").join(path.file_name().unwrap())); + builder.copy_link( + &path, + &sysroot.join("lib").join(path.file_name().unwrap()), + FileType::Regular, + ); } } } @@ -829,7 +832,7 @@ fn copy_sanitizers( for runtime in &runtimes { let dst = libdir.join(&runtime.name); - builder.copy_link(&runtime.path, &dst); + builder.copy_link(&runtime.path, &dst, FileType::NativeLibrary); // The `aarch64-apple-ios-macabi` and `x86_64-apple-ios-macabi` are also supported for // sanitizers, but they share a sanitizer runtime with `${arch}-apple-darwin`, so we do @@ -934,9 +937,9 @@ impl Step for StartupObjects { .run(builder); } - let target = sysroot_dir.join((*file).to_string() + ".o"); - builder.copy_link(dst_file, &target); - target_deps.push((target, DependencyType::Target)); + let obj = sysroot_dir.join((*file).to_string() + ".o"); + builder.copy_link(dst_file, &obj, FileType::NativeLibrary); + target_deps.push((obj, DependencyType::Target)); } target_deps @@ -952,7 +955,7 @@ fn cp_rustc_component_to_ci_sysroot(builder: &Builder<'_>, sysroot: &Path, conte if src.is_dir() { t!(fs::create_dir_all(dst)); } else { - builder.copy_link(&src, &dst); + builder.copy_link(&src, &dst, FileType::Regular); } } } @@ -1707,7 +1710,7 @@ fn copy_codegen_backends_to_sysroot( let dot = filename.find('.').unwrap(); format!("{}-{}{}", &filename[..dash], builder.rust_release(), &filename[dot..]) }; - builder.copy_link(file, &dst.join(target_filename)); + builder.copy_link(file, &dst.join(target_filename), FileType::NativeLibrary); } } @@ -2011,7 +2014,11 @@ impl Step for Assemble { extra_features: vec![], }); let tool_exe = exe("llvm-bitcode-linker", target_compiler.host); - builder.copy_link(&llvm_bitcode_linker.tool_path, &libdir_bin.join(tool_exe)); + builder.copy_link( + &llvm_bitcode_linker.tool_path, + &libdir_bin.join(tool_exe), + FileType::Executable, + ); } }; @@ -2072,8 +2079,8 @@ impl Step for Assemble { builder.sysroot_target_libdir(target_compiler, target_compiler.host); let dst_lib = libdir.join(&libenzyme).with_extension(lib_ext); let target_dst_lib = target_libdir.join(&libenzyme).with_extension(lib_ext); - builder.copy_link(&src_lib, &dst_lib); - builder.copy_link(&src_lib, &target_dst_lib); + builder.copy_link(&src_lib, &dst_lib, FileType::NativeLibrary); + builder.copy_link(&src_lib, &target_dst_lib, FileType::NativeLibrary); } // Build the libraries for this compiler to link to (i.e., the libraries @@ -2168,7 +2175,7 @@ impl Step for Assemble { }; if is_dylib_or_debug && can_be_rustc_dynamic_dep && !is_proc_macro { - builder.copy_link(&f.path(), &rustc_libdir.join(&filename)); + builder.copy_link(&f.path(), &rustc_libdir.join(&filename), FileType::Regular); } } @@ -2196,7 +2203,11 @@ impl Step for Assemble { // See . let src_exe = exe("llvm-objcopy", target_compiler.host); let dst_exe = exe("rust-objcopy", target_compiler.host); - builder.copy_link(&libdir_bin.join(src_exe), &libdir_bin.join(dst_exe)); + builder.copy_link( + &libdir_bin.join(src_exe), + &libdir_bin.join(dst_exe), + FileType::Executable, + ); } // In addition to `rust-lld` also install `wasm-component-ld` when @@ -2212,6 +2223,7 @@ impl Step for Assemble { builder.copy_link( &wasm_component.tool_path, &libdir_bin.join(wasm_component.tool_path.file_name().unwrap()), + FileType::Executable, ); } @@ -2234,7 +2246,7 @@ impl Step for Assemble { t!(fs::create_dir_all(bindir)); let compiler = builder.rustc(target_compiler); debug!(src = ?rustc, dst = ?compiler, "linking compiler binary itself"); - builder.copy_link(&rustc, &compiler); + builder.copy_link(&rustc, &compiler, FileType::Executable); target_compiler } @@ -2260,7 +2272,7 @@ pub fn add_to_sysroot( DependencyType::Target => sysroot_dst, DependencyType::TargetSelfContained => self_contained_dst, }; - builder.copy_link(&path, &dst.join(path.file_name().unwrap())); + builder.copy_link(&path, &dst.join(path.file_name().unwrap()), FileType::Regular); } } diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 39f9680cb2f6f..3305b7c2d3627 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -32,7 +32,7 @@ use crate::utils::helpers::{ exe, is_dylib, move_file, t, target_supports_cranelift_backend, timeit, }; use crate::utils::tarball::{GeneratedTarball, OverlayKind, Tarball}; -use crate::{Compiler, DependencyType, LLVM_TOOLS, Mode, trace}; +use crate::{Compiler, DependencyType, FileType, LLVM_TOOLS, Mode, trace}; pub fn pkgname(builder: &Builder<'_>, component: &str) -> String { format!("{}-{}", component, builder.rust_package_vers()) @@ -81,7 +81,7 @@ impl Step for Docs { let mut tarball = Tarball::new(builder, "rust-docs", &host.triple); tarball.set_product_name("Rust Documentation"); tarball.add_bulk_dir(builder.doc_out(host), dest); - tarball.add_file(builder.src.join("src/doc/robots.txt"), dest, 0o644); + tarball.add_file(builder.src.join("src/doc/robots.txt"), dest, FileType::Regular); Some(tarball.generate()) } } @@ -418,7 +418,7 @@ impl Step for Rustc { .is_none_or(|tools| tools.iter().any(|tool| tool == "rustdoc")) { let rustdoc = builder.rustdoc(compiler); - builder.install(&rustdoc, &image.join("bin"), 0o755); + builder.install(&rustdoc, &image.join("bin"), FileType::Executable); } if let Some(ra_proc_macro_srv) = builder.ensure_if_default( @@ -432,7 +432,8 @@ impl Step for Rustc { }, builder.kind, ) { - builder.install(&ra_proc_macro_srv.tool_path, &image.join("libexec"), 0o755); + let dst = image.join("libexec"); + builder.install(&ra_proc_macro_srv.tool_path, &dst, FileType::Executable); } let libdir_relative = builder.libdir_relative(compiler); @@ -444,7 +445,7 @@ impl Step for Rustc { if is_dylib(&entry.path()) { // Don't use custom libdir here because ^lib/ will be resolved again // with installer - builder.install(&entry.path(), &image.join("lib"), 0o644); + builder.install(&entry.path(), &image.join("lib"), FileType::NativeLibrary); } } } @@ -463,7 +464,11 @@ impl Step for Rustc { if builder.config.lld_enabled { let src_dir = builder.sysroot_target_bindir(compiler, host); let rust_lld = exe("rust-lld", compiler.host); - builder.copy_link(&src_dir.join(&rust_lld), &dst_dir.join(&rust_lld)); + builder.copy_link( + &src_dir.join(&rust_lld), + &dst_dir.join(&rust_lld), + FileType::Executable, + ); let self_contained_lld_src_dir = src_dir.join("gcc-ld"); let self_contained_lld_dst_dir = dst_dir.join("gcc-ld"); t!(fs::create_dir(&self_contained_lld_dst_dir)); @@ -472,6 +477,7 @@ impl Step for Rustc { builder.copy_link( &self_contained_lld_src_dir.join(&exe_name), &self_contained_lld_dst_dir.join(&exe_name), + FileType::Executable, ); } } @@ -480,13 +486,17 @@ impl Step for Rustc { let src_dir = builder.sysroot_target_bindir(compiler, host); let llvm_objcopy = exe("llvm-objcopy", compiler.host); let rust_objcopy = exe("rust-objcopy", compiler.host); - builder.copy_link(&src_dir.join(&llvm_objcopy), &dst_dir.join(&rust_objcopy)); + builder.copy_link( + &src_dir.join(&llvm_objcopy), + &dst_dir.join(&rust_objcopy), + FileType::Executable, + ); } if builder.tool_enabled("wasm-component-ld") { let src_dir = builder.sysroot_target_bindir(compiler, host); let ld = exe("wasm-component-ld", compiler.host); - builder.copy_link(&src_dir.join(&ld), &dst_dir.join(&ld)); + builder.copy_link(&src_dir.join(&ld), &dst_dir.join(&ld), FileType::Executable); } // Man pages @@ -511,15 +521,19 @@ impl Step for Rustc { // HTML copyright files let file_list = builder.ensure(super::run::GenerateCopyright); for file in file_list { - builder.install(&file, &image.join("share/doc/rust"), 0o644); + builder.install(&file, &image.join("share/doc/rust"), FileType::Regular); } // README - builder.install(&builder.src.join("README.md"), &image.join("share/doc/rust"), 0o644); + builder.install( + &builder.src.join("README.md"), + &image.join("share/doc/rust"), + FileType::Regular, + ); // The REUSE-managed license files let license = |path: &Path| { - builder.install(path, &image.join("share/doc/rust/licenses"), 0o644); + builder.install(path, &image.join("share/doc/rust/licenses"), FileType::Regular); }; for entry in t!(std::fs::read_dir(builder.src.join("LICENSES"))).flatten() { license(&entry.path()); @@ -548,14 +562,14 @@ impl Step for DebuggerScripts { let dst = sysroot.join("lib/rustlib/etc"); t!(fs::create_dir_all(&dst)); let cp_debugger_script = |file: &str| { - builder.install(&builder.src.join("src/etc/").join(file), &dst, 0o644); + builder.install(&builder.src.join("src/etc/").join(file), &dst, FileType::Regular); }; if host.contains("windows-msvc") { // windbg debugger scripts builder.install( &builder.src.join("src/etc/rust-windbg.cmd"), &sysroot.join("bin"), - 0o755, + FileType::Script, ); cp_debugger_script("natvis/intrinsic.natvis"); @@ -567,15 +581,27 @@ impl Step for DebuggerScripts { cp_debugger_script("rust_types.py"); // gdb debugger scripts - builder.install(&builder.src.join("src/etc/rust-gdb"), &sysroot.join("bin"), 0o755); - builder.install(&builder.src.join("src/etc/rust-gdbgui"), &sysroot.join("bin"), 0o755); + builder.install( + &builder.src.join("src/etc/rust-gdb"), + &sysroot.join("bin"), + FileType::Script, + ); + builder.install( + &builder.src.join("src/etc/rust-gdbgui"), + &sysroot.join("bin"), + FileType::Script, + ); cp_debugger_script("gdb_load_rust_pretty_printers.py"); cp_debugger_script("gdb_lookup.py"); cp_debugger_script("gdb_providers.py"); // lldb debugger scripts - builder.install(&builder.src.join("src/etc/rust-lldb"), &sysroot.join("bin"), 0o755); + builder.install( + &builder.src.join("src/etc/rust-lldb"), + &sysroot.join("bin"), + FileType::Script, + ); cp_debugger_script("lldb_lookup.py"); cp_debugger_script("lldb_providers.py"); @@ -640,9 +666,13 @@ fn copy_target_libs( t!(fs::create_dir_all(&self_contained_dst)); for (path, dependency_type) in builder.read_stamp_file(stamp) { if dependency_type == DependencyType::TargetSelfContained { - builder.copy_link(&path, &self_contained_dst.join(path.file_name().unwrap())); + builder.copy_link( + &path, + &self_contained_dst.join(path.file_name().unwrap()), + FileType::NativeLibrary, + ); } else if dependency_type == DependencyType::Target || builder.is_builder_target(target) { - builder.copy_link(&path, &dst.join(path.file_name().unwrap())); + builder.copy_link(&path, &dst.join(path.file_name().unwrap()), FileType::NativeLibrary); } } } @@ -750,7 +780,11 @@ impl Step for RustcDev { &tarball.image_dir().join("lib/rustlib/rustc-src/rust"), ); for file in src_files { - tarball.add_file(builder.src.join(file), "lib/rustlib/rustc-src/rust", 0o644); + tarball.add_file( + builder.src.join(file), + "lib/rustlib/rustc-src/rust", + FileType::Regular, + ); } Some(tarball.generate()) @@ -1045,7 +1079,11 @@ impl Step for PlainSourceTarball { // Copy the files normally for item in &src_files { - builder.copy_link(&builder.src.join(item), &plain_dst_src.join(item)); + builder.copy_link( + &builder.src.join(item), + &plain_dst_src.join(item), + FileType::Regular, + ); } // Create the version file @@ -1147,9 +1185,14 @@ impl Step for Cargo { let mut tarball = Tarball::new(builder, "cargo", &target.triple); tarball.set_overlay(OverlayKind::Cargo); - tarball.add_file(cargo.tool_path, "bin", 0o755); - tarball.add_file(etc.join("_cargo"), "share/zsh/site-functions", 0o644); - tarball.add_renamed_file(etc.join("cargo.bashcomp.sh"), "etc/bash_completion.d", "cargo"); + tarball.add_file(&cargo.tool_path, "bin", FileType::Executable); + tarball.add_file(etc.join("_cargo"), "share/zsh/site-functions", FileType::Regular); + tarball.add_renamed_file( + etc.join("cargo.bashcomp.sh"), + "etc/bash_completion.d", + "cargo", + FileType::Regular, + ); tarball.add_dir(etc.join("man"), "share/man/man1"); tarball.add_legal_and_readme_to("share/doc/cargo"); @@ -1193,7 +1236,7 @@ impl Step for RustAnalyzer { let mut tarball = Tarball::new(builder, "rust-analyzer", &target.triple); tarball.set_overlay(OverlayKind::RustAnalyzer); tarball.is_preview(true); - tarball.add_file(rust_analyzer.tool_path, "bin", 0o755); + tarball.add_file(&rust_analyzer.tool_path, "bin", FileType::Executable); tarball.add_legal_and_readme_to("share/doc/rust-analyzer"); Some(tarball.generate()) } @@ -1239,8 +1282,8 @@ impl Step for Clippy { let mut tarball = Tarball::new(builder, "clippy", &target.triple); tarball.set_overlay(OverlayKind::Clippy); tarball.is_preview(true); - tarball.add_file(clippy.tool_path, "bin", 0o755); - tarball.add_file(cargoclippy.tool_path, "bin", 0o755); + tarball.add_file(&clippy.tool_path, "bin", FileType::Executable); + tarball.add_file(&cargoclippy.tool_path, "bin", FileType::Executable); tarball.add_legal_and_readme_to("share/doc/clippy"); Some(tarball.generate()) } @@ -1289,8 +1332,8 @@ impl Step for Miri { let mut tarball = Tarball::new(builder, "miri", &target.triple); tarball.set_overlay(OverlayKind::Miri); tarball.is_preview(true); - tarball.add_file(miri.tool_path, "bin", 0o755); - tarball.add_file(cargomiri.tool_path, "bin", 0o755); + tarball.add_file(&miri.tool_path, "bin", FileType::Executable); + tarball.add_file(&cargomiri.tool_path, "bin", FileType::Executable); tarball.add_legal_and_readme_to("share/doc/miri"); Some(tarball.generate()) } @@ -1374,7 +1417,11 @@ impl Step for CodegenBackend { for backend in fs::read_dir(&backends_src).unwrap() { let file_name = backend.unwrap().file_name(); if file_name.to_str().unwrap().contains(&backend_name) { - tarball.add_file(backends_src.join(file_name), &backends_dst, 0o644); + tarball.add_file( + backends_src.join(file_name), + &backends_dst, + FileType::NativeLibrary, + ); found_backend = true; } } @@ -1420,8 +1467,8 @@ impl Step for Rustfmt { let mut tarball = Tarball::new(builder, "rustfmt", &target.triple); tarball.set_overlay(OverlayKind::Rustfmt); tarball.is_preview(true); - tarball.add_file(rustfmt.tool_path, "bin", 0o755); - tarball.add_file(cargofmt.tool_path, "bin", 0o755); + tarball.add_file(&rustfmt.tool_path, "bin", FileType::Executable); + tarball.add_file(&cargofmt.tool_path, "bin", FileType::Executable); tarball.add_legal_and_readme_to("share/doc/rustfmt"); Some(tarball.generate()) } @@ -1578,7 +1625,7 @@ impl Step for Extended { &work.join(format!("{}-{}", pkgname(builder, name), target.triple)), &pkg.join(name), ); - builder.install(&etc.join("pkg/postinstall"), &pkg.join(name), 0o755); + builder.install(&etc.join("pkg/postinstall"), &pkg.join(name), FileType::Script); pkgbuild(name); }; prepare("rustc"); @@ -1593,12 +1640,12 @@ impl Step for Extended { } } // create an 'uninstall' package - builder.install(&etc.join("pkg/postinstall"), &pkg.join("uninstall"), 0o755); + builder.install(&etc.join("pkg/postinstall"), &pkg.join("uninstall"), FileType::Script); pkgbuild("uninstall"); builder.create_dir(&pkg.join("res")); builder.create(&pkg.join("res/LICENSE.txt"), &license); - builder.install(&etc.join("gfx/rust-logo.png"), &pkg.join("res"), 0o644); + builder.install(&etc.join("gfx/rust-logo.png"), &pkg.join("res"), FileType::Regular); let mut cmd = command("productbuild"); cmd.arg("--distribution") .arg(xform(&etc.join("pkg/Distribution.xml"))) @@ -1655,7 +1702,7 @@ impl Step for Extended { prepare("rust-mingw"); } - builder.install(&etc.join("gfx/rust-logo.ico"), &exe, 0o644); + builder.install(&etc.join("gfx/rust-logo.ico"), &exe, FileType::Regular); // Generate msi installer let wix_path = env::var_os("WIX") @@ -1874,8 +1921,8 @@ impl Step for Extended { } builder.create(&exe.join("LICENSE.rtf"), &rtf); - builder.install(&etc.join("gfx/banner.bmp"), &exe, 0o644); - builder.install(&etc.join("gfx/dialogbg.bmp"), &exe, 0o644); + builder.install(&etc.join("gfx/banner.bmp"), &exe, FileType::Regular); + builder.install(&etc.join("gfx/dialogbg.bmp"), &exe, FileType::Regular); builder.info(&format!("building `msi` installer with {light:?}")); let filename = format!("{}-{}.msi", pkgname(builder, "rust"), target.triple); @@ -1961,13 +2008,13 @@ fn install_llvm_file( if source.is_symlink() { // If we have a symlink like libLLVM-18.so -> libLLVM.so.18.1, install the target of the // symlink, which is what will actually get loaded at runtime. - builder.install(&t!(fs::canonicalize(source)), destination, 0o644); + builder.install(&t!(fs::canonicalize(source)), destination, FileType::NativeLibrary); let full_dest = destination.join(source.file_name().unwrap()); if install_symlink { // For download-ci-llvm, also install the symlink, to match what LLVM does. Using a // symlink is fine here, as this is not a rustup component. - builder.copy_link(source, &full_dest); + builder.copy_link(source, &full_dest, FileType::NativeLibrary); } else { // Otherwise, replace the symlink with an equivalent linker script. This is used when // projects like miri link against librustc_driver.so. We don't use a symlink, as @@ -1984,7 +2031,7 @@ fn install_llvm_file( } } } else { - builder.install(source, destination, 0o644); + builder.install(source, destination, FileType::NativeLibrary); } } @@ -2036,7 +2083,7 @@ fn maybe_install_llvm( let src_libdir = builder.llvm_out(target).join("lib"); let llvm_dylib_path = src_libdir.join("libLLVM.dylib"); if llvm_dylib_path.exists() { - builder.install(&llvm_dylib_path, dst_libdir, 0o644); + builder.install(&llvm_dylib_path, dst_libdir, FileType::NativeLibrary); } !builder.config.dry_run() } else if let llvm::LlvmBuildStatus::AlreadyBuilt(llvm::LlvmResult { llvm_config, .. }) = @@ -2186,7 +2233,7 @@ impl Step for LlvmTools { let dst_bindir = format!("lib/rustlib/{}/bin", target.triple); for tool in tools_to_install(&builder.paths) { let exe = src_bindir.join(exe(tool, target)); - tarball.add_file(&exe, &dst_bindir, 0o755); + tarball.add_file(&exe, &dst_bindir, FileType::Executable); } } @@ -2241,7 +2288,7 @@ impl Step for LlvmBitcodeLinker { tarball.set_overlay(OverlayKind::LlvmBitcodeLinker); tarball.is_preview(true); - tarball.add_file(llbc_linker.tool_path, self_contained_bin_dir, 0o755); + tarball.add_file(&llbc_linker.tool_path, self_contained_bin_dir, FileType::Executable); Some(tarball.generate()) } @@ -2302,7 +2349,7 @@ impl Step for RustDev { let entry = t!(entry); if entry.file_type().is_file() && !entry.path_is_symlink() { let name = entry.file_name().to_str().unwrap(); - tarball.add_file(src_bindir.join(name), "bin", 0o755); + tarball.add_file(src_bindir.join(name), "bin", FileType::Executable); } } } @@ -2314,11 +2361,11 @@ impl Step for RustDev { // We don't build LLD on some platforms, so only add it if it exists let lld_path = lld_out.join("bin").join(exe("lld", target)); if lld_path.exists() { - tarball.add_file(lld_path, "bin", 0o755); + tarball.add_file(&lld_path, "bin", FileType::Executable); } } - tarball.add_file(builder.llvm_filecheck(target), "bin", 0o755); + tarball.add_file(builder.llvm_filecheck(target), "bin", FileType::Executable); // Copy the include directory as well; needed mostly to build // librustc_llvm properly (e.g., llvm-config.h is in here). But also @@ -2379,7 +2426,11 @@ impl Step for Bootstrap { let bootstrap_outdir = &builder.bootstrap_out; for file in &["bootstrap", "rustc", "rustdoc"] { - tarball.add_file(bootstrap_outdir.join(exe(file, target)), "bootstrap/bin", 0o755); + tarball.add_file( + bootstrap_outdir.join(exe(file, target)), + "bootstrap/bin", + FileType::Executable, + ); } Some(tarball.generate()) @@ -2412,7 +2463,7 @@ impl Step for BuildManifest { let build_manifest = builder.tool_exe(Tool::BuildManifest); let tarball = Tarball::new(builder, "build-manifest", &self.target.triple); - tarball.add_file(build_manifest, "bin", 0o755); + tarball.add_file(&build_manifest, "bin", FileType::Executable); tarball.generate() } } @@ -2444,15 +2495,15 @@ impl Step for ReproducibleArtifacts { let mut added_anything = false; let tarball = Tarball::new(builder, "reproducible-artifacts", &self.target.triple); if let Some(path) = builder.config.rust_profile_use.as_ref() { - tarball.add_file(path, ".", 0o644); + tarball.add_file(path, ".", FileType::Regular); added_anything = true; } if let Some(path) = builder.config.llvm_profile_use.as_ref() { - tarball.add_file(path, ".", 0o644); + tarball.add_file(path, ".", FileType::Regular); added_anything = true; } for profile in &builder.config.reproducible_artifacts { - tarball.add_file(profile, ".", 0o644); + tarball.add_file(profile, ".", FileType::Regular); added_anything = true; } if added_anything { Some(tarball.generate()) } else { None } @@ -2481,7 +2532,7 @@ impl Step for Gcc { fn run(self, builder: &Builder<'_>) -> Self::Output { let tarball = Tarball::new(builder, "gcc", &self.target.triple); let output = builder.ensure(super::gcc::Gcc { target: self.target }); - tarball.add_file(output.libgccjit, "lib", 0o644); + tarball.add_file(&output.libgccjit, "lib", FileType::NativeLibrary); tarball.generate() } } diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index a8da414610064..7fccf85a0ea9f 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -11,7 +11,6 @@ use std::io::{self, Write}; use std::path::{Path, PathBuf}; use std::{env, fs, mem}; -use crate::Mode; use crate::core::build_steps::compile; use crate::core::build_steps::tool::{self, SourceType, Tool, prepare_tool_cargo}; use crate::core::builder::{ @@ -19,6 +18,7 @@ use crate::core::builder::{ }; use crate::core::config::{Config, TargetSelection}; use crate::helpers::{submodule_path_of, symlink_dir, t, up_to_date}; +use crate::{FileType, Mode}; macro_rules! book { ($($name:ident, $path:expr, $book_name:expr, $lang:expr ;)+) => { @@ -546,6 +546,7 @@ impl Step for SharedAssets { builder.copy_link( &builder.src.join("src").join("doc").join("rust.css"), &out.join("rust.css"), + FileType::Regular, ); SharedAssetsPaths { version_info } diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index aaf6712102cfc..cd57e06ae04a3 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -26,7 +26,7 @@ use crate::core::config::{DebuginfoLevel, RustcLto, TargetSelection}; use crate::utils::channel::GitInfo; use crate::utils::exec::{BootstrapCommand, command}; use crate::utils::helpers::{add_dylib_path, exe, t}; -use crate::{Compiler, Kind, Mode, gha}; +use crate::{Compiler, FileType, Kind, Mode, gha}; #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub enum SourceType { @@ -353,7 +353,7 @@ fn copy_link_tool_bin( ) -> PathBuf { let cargo_out = builder.cargo_out(compiler, mode, target).join(exe(name, target)); let bin = builder.tools_dir(compiler).join(exe(name, target)); - builder.copy_link(&cargo_out, &bin); + builder.copy_link(&cargo_out, &bin, FileType::Executable); bin } @@ -696,7 +696,7 @@ impl Step for Rustdoc { .join(exe("rustdoc", target_compiler.host)); let bin_rustdoc = bin_rustdoc(); - builder.copy_link(&precompiled_rustdoc, &bin_rustdoc); + builder.copy_link(&precompiled_rustdoc, &bin_rustdoc, FileType::Executable); return ToolBuildResult { tool_path: bin_rustdoc, @@ -743,7 +743,7 @@ impl Step for Rustdoc { compile::strip_debug(builder, target, &tool_path); } let bin_rustdoc = bin_rustdoc(); - builder.copy_link(&tool_path, &bin_rustdoc); + builder.copy_link(&tool_path, &bin_rustdoc, FileType::Executable); ToolBuildResult { tool_path: bin_rustdoc, build_compiler, target_compiler } } else { ToolBuildResult { tool_path, build_compiler, target_compiler } @@ -846,13 +846,20 @@ impl Step for LldWrapper { let src_exe = exe("lld", target); let dst_exe = exe("rust-lld", target); - builder.copy_link(&lld_install.join("bin").join(src_exe), &libdir_bin.join(dst_exe)); + builder.copy_link( + &lld_install.join("bin").join(src_exe), + &libdir_bin.join(dst_exe), + FileType::Executable, + ); let self_contained_lld_dir = libdir_bin.join("gcc-ld"); t!(fs::create_dir_all(&self_contained_lld_dir)); for name in crate::LLD_FILE_NAMES { - builder - .copy_link(&tool_result.tool_path, &self_contained_lld_dir.join(exe(name, target))); + builder.copy_link( + &tool_result.tool_path, + &self_contained_lld_dir.join(exe(name, target)), + FileType::Executable, + ); } tool_result @@ -949,8 +956,11 @@ impl Step for RustAnalyzerProcMacroSrv { // so that r-a can use it. let libexec_path = builder.sysroot(self.compiler).join("libexec"); t!(fs::create_dir_all(&libexec_path)); - builder - .copy_link(&tool_result.tool_path, &libexec_path.join("rust-analyzer-proc-macro-srv")); + builder.copy_link( + &tool_result.tool_path, + &libexec_path.join("rust-analyzer-proc-macro-srv"), + FileType::Executable, + ); Some(tool_result) } @@ -1007,7 +1017,7 @@ impl Step for LlvmBitcodeLinker { t!(fs::create_dir_all(&bindir_self_contained)); let bin_destination = bindir_self_contained .join(exe("llvm-bitcode-linker", tool_result.target_compiler.host)); - builder.copy_link(&tool_result.tool_path, &bin_destination); + builder.copy_link(&tool_result.tool_path, &bin_destination, FileType::Executable); ToolBuildResult { tool_path: bin_destination, build_compiler: tool_result.build_compiler, @@ -1189,7 +1199,7 @@ fn run_tool_build_step( for add_bin in add_bins_to_sysroot { let bin_destination = bindir.join(exe(add_bin, target_compiler.host)); - builder.copy_link(&tool_path, &bin_destination); + builder.copy_link(&tool_path, &bin_destination, FileType::Executable); } // Return a path into the bin dir. diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 1943d0299b9e7..a47b7a27790bb 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -36,7 +36,9 @@ use crate::core::builder; use crate::core::builder::Kind; use crate::core::config::{DryRun, LldMode, LlvmLibunwind, Target, TargetSelection, flags}; use crate::utils::exec::{BehaviorOnFailure, BootstrapCommand, CommandOutput, OutputMode, command}; -use crate::utils::helpers::{self, dir_is_empty, exe, libdir, output, set_file_times, symlink_dir}; +use crate::utils::helpers::{ + self, dir_is_empty, exe, libdir, output, set_file_times, split_debuginfo, symlink_dir, +}; mod core; mod utils; @@ -274,6 +276,35 @@ pub enum CLang { Cxx, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum FileType { + /// An executable binary file (like a `.exe`). + Executable, + /// A native, binary library file (like a `.so`, `.dll`, `.a`, `.lib` or `.o`). + NativeLibrary, + /// An executable (non-binary) script file (like a `.py` or `.sh`). + Script, + /// Any other regular file that is non-executable. + Regular, +} + +impl FileType { + /// Get Unix permissions appropriate for this file type. + pub fn perms(self) -> u32 { + match self { + FileType::Executable | FileType::Script => 0o755, + FileType::Regular | FileType::NativeLibrary => 0o644, + } + } + + pub fn could_have_split_debuginfo(self) -> bool { + match self { + FileType::Executable | FileType::NativeLibrary => true, + FileType::Script | FileType::Regular => false, + } + } +} + macro_rules! forward { ( $( $fn:ident( $($param:ident: $ty:ty),* ) $( -> $ret:ty)? ),+ $(,)? ) => { impl Build { @@ -1744,8 +1775,18 @@ Executed at: {executed_at}"#, /// Attempts to use hard links if possible, falling back to copying. /// You can neither rely on this being a copy nor it being a link, /// so do not write to dst. - pub fn copy_link(&self, src: &Path, dst: &Path) { + pub fn copy_link(&self, src: &Path, dst: &Path, file_type: FileType) { self.copy_link_internal(src, dst, false); + + if file_type.could_have_split_debuginfo() { + if let Some(dbg_file) = split_debuginfo(src) { + self.copy_link_internal( + &dbg_file, + &dst.with_extension(dbg_file.extension().unwrap()), + false, + ); + } + } } fn copy_link_internal(&self, src: &Path, dst: &Path, dereference_symlinks: bool) { @@ -1808,7 +1849,7 @@ Executed at: {executed_at}"#, t!(fs::create_dir_all(&dst)); self.cp_link_r(&path, &dst); } else { - self.copy_link(&path, &dst); + self.copy_link(&path, &dst, FileType::Regular); } } } @@ -1844,7 +1885,7 @@ Executed at: {executed_at}"#, self.cp_link_filtered_recurse(&path, &dst, &relative, filter); } else { let _ = fs::remove_file(&dst); - self.copy_link(&path, &dst); + self.copy_link(&path, &dst, FileType::Regular); } } } @@ -1853,10 +1894,10 @@ Executed at: {executed_at}"#, fn copy_link_to_folder(&self, src: &Path, dest_folder: &Path) { let file_name = src.file_name().unwrap(); let dest = dest_folder.join(file_name); - self.copy_link(src, &dest); + self.copy_link(src, &dest, FileType::Regular); } - fn install(&self, src: &Path, dstdir: &Path, perms: u32) { + fn install(&self, src: &Path, dstdir: &Path, file_type: FileType) { if self.config.dry_run() { return; } @@ -1866,8 +1907,16 @@ Executed at: {executed_at}"#, if !src.exists() { panic!("ERROR: File \"{}\" not found!", src.display()); } + self.copy_link_internal(src, &dst, true); - chmod(&dst, perms); + chmod(&dst, file_type.perms()); + + // If this file can have debuginfo, look for split debuginfo and install it too. + if file_type.could_have_split_debuginfo() { + if let Some(dbg_file) = split_debuginfo(src) { + self.install(&dbg_file, dstdir, FileType::Regular); + } + } } fn read(&self, path: &Path) -> String { diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index 89d93a29acbca..f8e4d4e04717d 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -52,6 +52,23 @@ pub fn exe(name: &str, target: TargetSelection) -> String { crate::utils::shared_helpers::exe(name, &target.triple) } +/// Returns the path to the split debug info for the specified file if it exists. +pub fn split_debuginfo(name: impl Into) -> Option { + // FIXME: only msvc is currently supported + + let path = name.into(); + let pdb = path.with_extension("pdb"); + if pdb.exists() { + return Some(pdb); + } + + // pdbs get named with '-' replaced by '_' + let file_name = pdb.file_name()?.to_str()?.replace("-", "_"); + + let pdb: PathBuf = [path.parent()?, Path::new(&file_name)].into_iter().collect(); + pdb.exists().then_some(pdb) +} + /// Returns `true` if the file name given looks like a dynamic library. pub fn is_dylib(path: &Path) -> bool { path.extension().and_then(|ext| ext.to_str()).is_some_and(|ext| { diff --git a/src/bootstrap/src/utils/tarball.rs b/src/bootstrap/src/utils/tarball.rs index f1678bacc9769..7b77b21293413 100644 --- a/src/bootstrap/src/utils/tarball.rs +++ b/src/bootstrap/src/utils/tarball.rs @@ -7,6 +7,7 @@ use std::path::{Path, PathBuf}; +use crate::FileType; use crate::core::build_steps::dist::distdir; use crate::core::builder::{Builder, Kind}; use crate::core::config::BUILDER_CONFIG_FILENAME; @@ -182,7 +183,12 @@ impl<'a> Tarball<'a> { &self.image_dir } - pub(crate) fn add_file(&self, src: impl AsRef, destdir: impl AsRef, perms: u32) { + pub(crate) fn add_file( + &self, + src: impl AsRef, + destdir: impl AsRef, + file_type: FileType, + ) { // create_dir_all fails to create `foo/bar/.`, so when the destination is "." this simply // uses the base directory as the destination directory. let destdir = if destdir.as_ref() == Path::new(".") { @@ -192,7 +198,7 @@ impl<'a> Tarball<'a> { }; t!(std::fs::create_dir_all(&destdir)); - self.builder.install(src.as_ref(), &destdir, perms); + self.builder.install(src.as_ref(), &destdir, file_type); } pub(crate) fn add_renamed_file( @@ -200,15 +206,16 @@ impl<'a> Tarball<'a> { src: impl AsRef, destdir: impl AsRef, new_name: &str, + file_type: FileType, ) { let destdir = self.image_dir.join(destdir.as_ref()); t!(std::fs::create_dir_all(&destdir)); - self.builder.copy_link(src.as_ref(), &destdir.join(new_name)); + self.builder.copy_link(src.as_ref(), &destdir.join(new_name), file_type); } pub(crate) fn add_legal_and_readme_to(&self, destdir: impl AsRef) { for file in self.overlay.legal_and_readme() { - self.add_file(self.builder.src.join(file), destdir.as_ref(), 0o644); + self.add_file(self.builder.src.join(file), destdir.as_ref(), FileType::Regular); } } @@ -318,11 +325,20 @@ impl<'a> Tarball<'a> { // Add config file if present. if let Some(config) = &self.builder.config.config { - self.add_renamed_file(config, &self.overlay_dir, BUILDER_CONFIG_FILENAME); + self.add_renamed_file( + config, + &self.overlay_dir, + BUILDER_CONFIG_FILENAME, + FileType::Regular, + ); } for file in self.overlay.legal_and_readme() { - self.builder.install(&self.builder.src.join(file), &self.overlay_dir, 0o644); + self.builder.install( + &self.builder.src.join(file), + &self.overlay_dir, + FileType::Regular, + ); } let mut cmd = self.builder.tool_cmd(crate::core::build_steps::tool::Tool::RustInstaller); From 3bfb6af978868f89785126e37e149136bf581655 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Tue, 18 Mar 2025 16:43:39 +0000 Subject: [PATCH 07/19] Test windows file type equality --- library/std/src/fs/tests.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 6dd18e4f4c837..4712e58980cc6 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -1719,6 +1719,23 @@ fn test_eq_direntry_metadata() { } } +/// Test that windows file type equality is not affected by attributes unrelated +/// to the file type. +#[test] +#[cfg(target_os = "windows")] +fn test_eq_windows_file_type() { + let tmpdir = tmpdir(); + let file1 = File::create(tmpdir.join("file1")).unwrap(); + let file2 = File::create(tmpdir.join("file2")).unwrap(); + assert_eq!(file1.metadata().unwrap().file_type(), file2.metadata().unwrap().file_type()); + + // Change the readonly attribute of one file. + let mut perms = file1.metadata().unwrap().permissions(); + perms.set_readonly(true); + file1.set_permissions(perms).unwrap(); + assert_eq!(file1.metadata().unwrap().file_type(), file2.metadata().unwrap().file_type()); +} + /// Regression test for https://github.com/rust-lang/rust/issues/50619. #[test] #[cfg(target_os = "linux")] From 6b2fa32f142f341ba227b5ea784ea2a1a5e79fe8 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Tue, 18 Mar 2025 16:27:24 +0000 Subject: [PATCH 08/19] Windows: fix FileType PartialEq implementation --- library/std/src/sys/fs/windows.rs | 33 ++++++++++++++----------------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs index 362e64abf1ac3..06bba019393a5 100644 --- a/library/std/src/sys/fs/windows.rs +++ b/library/std/src/sys/fs/windows.rs @@ -41,8 +41,8 @@ pub struct FileAttr { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct FileType { - attributes: u32, - reparse_tag: u32, + is_directory: bool, + is_symlink: bool, } pub struct ReadDir { @@ -1111,32 +1111,29 @@ impl FileTimes { } impl FileType { - fn new(attrs: u32, reparse_tag: u32) -> FileType { - FileType { attributes: attrs, reparse_tag } + fn new(attributes: u32, reparse_tag: u32) -> FileType { + let is_directory = attributes & c::FILE_ATTRIBUTE_DIRECTORY != 0; + let is_symlink = { + let is_reparse_point = attributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0; + let is_reparse_tag_name_surrogate = reparse_tag & 0x20000000 != 0; + is_reparse_point && is_reparse_tag_name_surrogate + }; + FileType { is_directory, is_symlink } } pub fn is_dir(&self) -> bool { - !self.is_symlink() && self.is_directory() + !self.is_symlink && self.is_directory } pub fn is_file(&self) -> bool { - !self.is_symlink() && !self.is_directory() + !self.is_symlink && !self.is_directory } pub fn is_symlink(&self) -> bool { - self.is_reparse_point() && self.is_reparse_tag_name_surrogate() + self.is_symlink } pub fn is_symlink_dir(&self) -> bool { - self.is_symlink() && self.is_directory() + self.is_symlink && self.is_directory } pub fn is_symlink_file(&self) -> bool { - self.is_symlink() && !self.is_directory() - } - fn is_directory(&self) -> bool { - self.attributes & c::FILE_ATTRIBUTE_DIRECTORY != 0 - } - fn is_reparse_point(&self) -> bool { - self.attributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 - } - fn is_reparse_tag_name_surrogate(&self) -> bool { - self.reparse_tag & 0x20000000 != 0 + self.is_symlink && !self.is_directory } } From b54ca0e433e9cf5ef139aeebb36133be8ddee111 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Thu, 6 Mar 2025 15:09:14 -0800 Subject: [PATCH 09/19] Add a MIR pre-codegen test for tuple comparisons We have codegen ones, but it looks like we could make those less flakey by just doing something better in the first place... --- ...e_ord.demo_ge_partial.PreCodegen.after.mir | 237 ++++++++++++++++++ ...ple_ord.demo_le_total.PreCodegen.after.mir | 145 +++++++++++ tests/mir-opt/pre-codegen/tuple_ord.rs | 16 ++ 3 files changed, 398 insertions(+) create mode 100644 tests/mir-opt/pre-codegen/tuple_ord.demo_ge_partial.PreCodegen.after.mir create mode 100644 tests/mir-opt/pre-codegen/tuple_ord.demo_le_total.PreCodegen.after.mir create mode 100644 tests/mir-opt/pre-codegen/tuple_ord.rs diff --git a/tests/mir-opt/pre-codegen/tuple_ord.demo_ge_partial.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/tuple_ord.demo_ge_partial.PreCodegen.after.mir new file mode 100644 index 0000000000000..696a451177791 --- /dev/null +++ b/tests/mir-opt/pre-codegen/tuple_ord.demo_ge_partial.PreCodegen.after.mir @@ -0,0 +1,237 @@ +// MIR for `demo_ge_partial` after PreCodegen + +fn demo_ge_partial(_1: &(f32, f32), _2: &(f32, f32)) -> bool { + debug a => _1; + debug b => _2; + let mut _0: bool; + scope 1 (inlined std::cmp::impls::::le) { + scope 2 (inlined core::tuple::::le) { + let mut _12: bool; + let _15: std::option::Option; + let _19: &f32; + let _20: &f32; + scope 3 { + let mut _9: &std::option::Option; + let mut _13: &std::option::Option; + scope 4 (inlined as PartialEq>::ne) { + let mut _11: bool; + scope 5 (inlined as PartialEq>::eq) { + let mut _10: isize; + let mut _16: isize; + scope 6 { + scope 7 (inlined ::eq) { + let _17: i8; + scope 8 { + let _18: i8; + scope 9 { + } + } + } + } + } + } + scope 10 (inlined as PartialEq>::eq) { + let mut _14: isize; + let mut _21: isize; + scope 11 { + scope 12 (inlined ::eq) { + let _22: i8; + scope 13 { + let _23: i8; + scope 14 { + } + } + } + } + } + } + scope 15 (inlined std::cmp::impls::::partial_cmp) { + let mut _3: f32; + let mut _4: f32; + let mut _5: bool; + let mut _6: f32; + let mut _7: f32; + let mut _8: bool; + } + } + } + + bb0: { + StorageLive(_19); + StorageLive(_20); + StorageLive(_13); + StorageLive(_9); + StorageLive(_15); + StorageLive(_5); + StorageLive(_8); + StorageLive(_3); + _3 = copy ((*_1).0: f32); + StorageLive(_4); + _4 = copy ((*_2).0: f32); + _5 = Le(move _3, move _4); + StorageDead(_4); + StorageDead(_3); + StorageLive(_6); + _6 = copy ((*_1).0: f32); + StorageLive(_7); + _7 = copy ((*_2).0: f32); + _8 = Ge(move _6, move _7); + StorageDead(_7); + StorageDead(_6); + switchInt(copy _5) -> [0: bb1, otherwise: bb5]; + } + + bb1: { + switchInt(copy _8) -> [0: bb2, otherwise: bb4]; + } + + bb2: { + StorageDead(_8); + StorageDead(_5); + StorageLive(_12); + _9 = const core::tuple::::le::promoted[1]; + StorageLive(_11); + StorageLive(_16); + StorageLive(_10); + _10 = discriminant((*_9)); + _11 = Eq(copy _10, const 0_isize); + StorageDead(_10); + StorageDead(_16); + _12 = Not(move _11); + StorageDead(_11); + switchInt(move _12) -> [0: bb11, otherwise: bb3]; + } + + bb3: { + _13 = const core::tuple::::le::promoted[0]; + StorageLive(_21); + StorageLive(_14); + _14 = discriminant((*_13)); + _0 = Eq(copy _14, const 0_isize); + goto -> bb16; + } + + bb4: { + _15 = const Option::::Some(Greater); + StorageDead(_8); + StorageDead(_5); + StorageLive(_12); + _9 = const core::tuple::::le::promoted[1]; + StorageLive(_11); + StorageLive(_16); + StorageLive(_10); + goto -> bb8; + } + + bb5: { + switchInt(copy _8) -> [0: bb6, otherwise: bb7]; + } + + bb6: { + _15 = const Option::::Some(Less); + StorageDead(_8); + StorageDead(_5); + StorageLive(_12); + _9 = const core::tuple::::le::promoted[1]; + StorageLive(_11); + StorageLive(_16); + StorageLive(_10); + goto -> bb8; + } + + bb7: { + _15 = const Option::::Some(Equal); + StorageDead(_8); + StorageDead(_5); + StorageLive(_12); + _9 = const core::tuple::::le::promoted[1]; + StorageLive(_11); + StorageLive(_16); + StorageLive(_10); + goto -> bb8; + } + + bb8: { + _16 = discriminant((*_9)); + switchInt(move _16) -> [0: bb9, 1: bb10, otherwise: bb18]; + } + + bb9: { + StorageDead(_10); + StorageDead(_16); + StorageDead(_11); + _13 = const core::tuple::::le::promoted[0]; + StorageLive(_21); + StorageLive(_14); + goto -> bb13; + } + + bb10: { + StorageLive(_17); + StorageLive(_18); + _17 = discriminant(((_15 as Some).0: std::cmp::Ordering)); + _18 = discriminant((((*_9) as Some).0: std::cmp::Ordering)); + _11 = Eq(copy _17, copy _18); + StorageDead(_18); + StorageDead(_17); + StorageDead(_10); + StorageDead(_16); + _12 = Not(move _11); + StorageDead(_11); + switchInt(move _12) -> [0: bb11, otherwise: bb12]; + } + + bb11: { + _19 = &((*_1).1: f32); + _20 = &((*_2).1: f32); + _0 = ::le(move _19, move _20) -> [return: bb17, unwind continue]; + } + + bb12: { + _13 = const core::tuple::::le::promoted[0]; + StorageLive(_21); + StorageLive(_14); + goto -> bb13; + } + + bb13: { + _21 = discriminant((*_13)); + switchInt(move _21) -> [0: bb14, 1: bb15, otherwise: bb18]; + } + + bb14: { + _0 = const false; + goto -> bb16; + } + + bb15: { + StorageLive(_22); + StorageLive(_23); + _22 = discriminant(((_15 as Some).0: std::cmp::Ordering)); + _23 = discriminant((((*_13) as Some).0: std::cmp::Ordering)); + _0 = Eq(copy _22, copy _23); + StorageDead(_23); + StorageDead(_22); + goto -> bb16; + } + + bb16: { + StorageDead(_14); + StorageDead(_21); + goto -> bb17; + } + + bb17: { + StorageDead(_12); + StorageDead(_15); + StorageDead(_9); + StorageDead(_13); + StorageDead(_20); + StorageDead(_19); + return; + } + + bb18: { + unreachable; + } +} diff --git a/tests/mir-opt/pre-codegen/tuple_ord.demo_le_total.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/tuple_ord.demo_le_total.PreCodegen.after.mir new file mode 100644 index 0000000000000..15c3ae76ae936 --- /dev/null +++ b/tests/mir-opt/pre-codegen/tuple_ord.demo_le_total.PreCodegen.after.mir @@ -0,0 +1,145 @@ +// MIR for `demo_le_total` after PreCodegen + +fn demo_le_total(_1: &(u16, i16), _2: &(u16, i16)) -> bool { + debug a => _1; + debug b => _2; + let mut _0: bool; + scope 1 (inlined std::cmp::impls::::le) { + scope 2 (inlined core::tuple::::le) { + let mut _12: bool; + let _13: &i16; + let _14: &i16; + scope 3 { + let mut _6: &std::option::Option; + let mut _8: &std::option::Option; + scope 4 (inlined as PartialEq>::ne) { + let mut _11: bool; + scope 5 (inlined as PartialEq>::eq) { + let mut _7: isize; + scope 6 { + scope 7 (inlined ::eq) { + let _9: i8; + scope 8 { + let _10: i8; + scope 9 { + } + } + } + } + } + } + scope 10 (inlined as PartialEq>::eq) { + let mut _15: isize; + scope 11 { + scope 12 (inlined ::eq) { + let _16: i8; + scope 13 { + let _17: i8; + scope 14 { + } + } + } + } + } + } + scope 15 (inlined std::cmp::impls::::partial_cmp) { + let mut _3: u16; + let mut _4: u16; + let mut _5: std::cmp::Ordering; + } + } + } + + bb0: { + StorageLive(_13); + StorageLive(_14); + StorageLive(_8); + StorageLive(_6); + StorageLive(_3); + _3 = copy ((*_1).0: u16); + StorageLive(_4); + _4 = copy ((*_2).0: u16); + _5 = Cmp(move _3, move _4); + StorageDead(_4); + StorageDead(_3); + StorageLive(_12); + _6 = const core::tuple::::le::promoted[1]; + StorageLive(_11); + StorageLive(_7); + _7 = discriminant((*_6)); + switchInt(move _7) -> [0: bb1, 1: bb2, otherwise: bb10]; + } + + bb1: { + StorageDead(_7); + StorageDead(_11); + _8 = const core::tuple::::le::promoted[0]; + StorageLive(_15); + goto -> bb5; + } + + bb2: { + StorageLive(_9); + StorageLive(_10); + _9 = discriminant(_5); + _10 = discriminant((((*_6) as Some).0: std::cmp::Ordering)); + _11 = Eq(copy _9, copy _10); + StorageDead(_10); + StorageDead(_9); + StorageDead(_7); + _12 = Not(move _11); + StorageDead(_11); + switchInt(move _12) -> [0: bb3, otherwise: bb4]; + } + + bb3: { + _13 = &((*_1).1: i16); + _14 = &((*_2).1: i16); + _0 = ::le(move _13, move _14) -> [return: bb9, unwind continue]; + } + + bb4: { + _8 = const core::tuple::::le::promoted[0]; + StorageLive(_15); + goto -> bb5; + } + + bb5: { + _15 = discriminant((*_8)); + switchInt(move _15) -> [0: bb6, 1: bb7, otherwise: bb10]; + } + + bb6: { + _0 = const false; + goto -> bb8; + } + + bb7: { + StorageLive(_16); + StorageLive(_17); + _16 = discriminant(_5); + _17 = discriminant((((*_8) as Some).0: std::cmp::Ordering)); + _0 = Eq(copy _16, copy _17); + StorageDead(_17); + StorageDead(_16); + goto -> bb8; + } + + bb8: { + StorageDead(_15); + goto -> bb9; + } + + bb9: { + StorageDead(_12); + StorageDead(_6); + StorageDead(_8); + StorageDead(_14); + StorageDead(_13); + return; + } + + bb10: { + unreachable; + } +} diff --git a/tests/mir-opt/pre-codegen/tuple_ord.rs b/tests/mir-opt/pre-codegen/tuple_ord.rs new file mode 100644 index 0000000000000..435ac7a5be938 --- /dev/null +++ b/tests/mir-opt/pre-codegen/tuple_ord.rs @@ -0,0 +1,16 @@ +//@ compile-flags: -O -Zmir-opt-level=2 -Cdebuginfo=0 -Z inline-mir-hint-threshold=9999 +//@ needs-unwind + +#![crate_type = "lib"] + +// EMIT_MIR tuple_ord.demo_le_total.PreCodegen.after.mir +pub fn demo_le_total(a: &(u16, i16), b: &(u16, i16)) -> bool { + // CHECK-LABEL: demo_le_total + a <= b +} + +// EMIT_MIR tuple_ord.demo_ge_partial.PreCodegen.after.mir +pub fn demo_ge_partial(a: &(f32, f32), b: &(f32, f32)) -> bool { + // CHECK-LABEL: demo_ge_partial + a <= b +} From 35248c6830383e67b8e2890ddd5ee48d44ad6b87 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Thu, 6 Mar 2025 15:55:40 -0800 Subject: [PATCH 10/19] Add chaining versions of lt/le/gt/ge and use them in tuple PartialOrd --- library/core/src/cmp.rs | 84 ++++++ library/core/src/tuple.rs | 25 +- ...e_ord.demo_ge_partial.PreCodegen.after.mir | 239 +++--------------- ...ple_ord.demo_le_total.PreCodegen.after.mir | 143 +++-------- tests/mir-opt/pre-codegen/tuple_ord.rs | 4 +- 5 files changed, 170 insertions(+), 325 deletions(-) diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 25bd17d5802fd..af14c4b50725f 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -29,6 +29,7 @@ mod bytewise; pub(crate) use bytewise::BytewiseEq; use self::Ordering::*; +use crate::ops::ControlFlow::{self, Break, Continue}; /// Trait for comparisons using the equality operator. /// @@ -1446,6 +1447,54 @@ pub macro PartialOrd($item:item) { /* compiler built-in */ } +/// Helpers for chaining together field PartialOrds into the full type's ordering. +/// +/// If the two values are equal, returns `ControlFlow::Continue`. +/// If the two values are not equal, returns `ControlFlow::Break(self OP other)`. +/// +/// This allows simple types like `i32` and `f64` to just emit two comparisons +/// directly, instead of needing to optimize the 3-way comparison. +/// +/// Currently this is done using specialization, but it doesn't need that: +/// it could be provided methods on `PartialOrd` instead and work fine. +pub(crate) trait SpecChainingPartialOrd: PartialOrd { + fn spec_chain_lt(&self, other: &Rhs) -> ControlFlow; + fn spec_chain_le(&self, other: &Rhs) -> ControlFlow; + fn spec_chain_gt(&self, other: &Rhs) -> ControlFlow; + fn spec_chain_ge(&self, other: &Rhs) -> ControlFlow; +} + +impl, U> SpecChainingPartialOrd for T { + #[inline] + default fn spec_chain_lt(&self, other: &U) -> ControlFlow { + match PartialOrd::partial_cmp(self, other) { + Some(Equal) => Continue(()), + c => Break(c.is_some_and(Ordering::is_lt)), + } + } + #[inline] + default fn spec_chain_le(&self, other: &U) -> ControlFlow { + match PartialOrd::partial_cmp(self, other) { + Some(Equal) => Continue(()), + c => Break(c.is_some_and(Ordering::is_le)), + } + } + #[inline] + default fn spec_chain_gt(&self, other: &U) -> ControlFlow { + match PartialOrd::partial_cmp(self, other) { + Some(Equal) => Continue(()), + c => Break(c.is_some_and(Ordering::is_gt)), + } + } + #[inline] + default fn spec_chain_ge(&self, other: &U) -> ControlFlow { + match PartialOrd::partial_cmp(self, other) { + Some(Equal) => Continue(()), + c => Break(c.is_some_and(Ordering::is_ge)), + } + } +} + /// Compares and returns the minimum of two values. /// /// Returns the first argument if the comparison determines them to be equal. @@ -1741,6 +1790,7 @@ where mod impls { use crate::cmp::Ordering::{self, Equal, Greater, Less}; use crate::hint::unreachable_unchecked; + use crate::ops::ControlFlow::{self, Break, Continue}; macro_rules! partial_eq_impl { ($($t:ty)*) => ($( @@ -1779,6 +1829,36 @@ mod impls { eq_impl! { () bool char usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } + macro_rules! chaining_impl { + ($t:ty) => { + // These implementations are the same for `Ord` or `PartialOrd` types + // because if either is NAN the `==` test will fail so we end up in + // the `Break` case and the comparison will correctly return `false`. + impl super::SpecChainingPartialOrd<$t> for $t { + #[inline] + fn spec_chain_lt(&self, other: &Self) -> ControlFlow { + let (lhs, rhs) = (*self, *other); + if lhs == rhs { Continue(()) } else { Break(lhs < rhs) } + } + #[inline] + fn spec_chain_le(&self, other: &Self) -> ControlFlow { + let (lhs, rhs) = (*self, *other); + if lhs == rhs { Continue(()) } else { Break(lhs <= rhs) } + } + #[inline] + fn spec_chain_gt(&self, other: &Self) -> ControlFlow { + let (lhs, rhs) = (*self, *other); + if lhs == rhs { Continue(()) } else { Break(lhs > rhs) } + } + #[inline] + fn spec_chain_ge(&self, other: &Self) -> ControlFlow { + let (lhs, rhs) = (*self, *other); + if lhs == rhs { Continue(()) } else { Break(lhs >= rhs) } + } + } + }; + } + macro_rules! partial_ord_impl { ($($t:ty)*) => ($( #[stable(feature = "rust1", since = "1.0.0")] @@ -1801,6 +1881,8 @@ mod impls { #[inline(always)] fn gt(&self, other: &$t) -> bool { (*self) > (*other) } } + + chaining_impl!($t); )*) } @@ -1840,6 +1922,8 @@ mod impls { fn gt(&self, other: &$t) -> bool { (*self) > (*other) } } + chaining_impl!($t); + #[stable(feature = "rust1", since = "1.0.0")] impl Ord for $t { #[inline] diff --git a/library/core/src/tuple.rs b/library/core/src/tuple.rs index 206b5b9e2c24f..75faaa06ee7fe 100644 --- a/library/core/src/tuple.rs +++ b/library/core/src/tuple.rs @@ -1,7 +1,9 @@ // See core/src/primitive_docs.rs for documentation. use crate::cmp::Ordering::{self, *}; +use crate::cmp::SpecChainingPartialOrd; use crate::marker::{ConstParamTy_, StructuralPartialEq, UnsizedConstParamTy}; +use crate::ops::ControlFlow::{Break, Continue}; // Recursive macro for implementing n-ary tuple functions and operations // @@ -80,19 +82,19 @@ macro_rules! tuple_impls { } #[inline] fn lt(&self, other: &($($T,)+)) -> bool { - lexical_ord!(lt, Less, $( ${ignore($T)} self.${index()}, other.${index()} ),+) + lexical_ord!(lt, spec_chain_lt, $( ${ignore($T)} self.${index()}, other.${index()} ),+) } #[inline] fn le(&self, other: &($($T,)+)) -> bool { - lexical_ord!(le, Less, $( ${ignore($T)} self.${index()}, other.${index()} ),+) + lexical_ord!(le, spec_chain_le, $( ${ignore($T)} self.${index()}, other.${index()} ),+) } #[inline] fn ge(&self, other: &($($T,)+)) -> bool { - lexical_ord!(ge, Greater, $( ${ignore($T)} self.${index()}, other.${index()} ),+) + lexical_ord!(ge, spec_chain_ge, $( ${ignore($T)} self.${index()}, other.${index()} ),+) } #[inline] fn gt(&self, other: &($($T,)+)) -> bool { - lexical_ord!(gt, Greater, $( ${ignore($T)} self.${index()}, other.${index()} ),+) + lexical_ord!(gt, spec_chain_gt, $( ${ignore($T)} self.${index()}, other.${index()} ),+) } } } @@ -171,15 +173,16 @@ macro_rules! maybe_tuple_doc { // `(a1, a2, a3) < (b1, b2, b3)` would be `lexical_ord!(lt, opt_is_lt, a1, b1, // a2, b2, a3, b3)` (and similarly for `lexical_cmp`) // -// `$ne_rel` is only used to determine the result after checking that they're -// not equal, so `lt` and `le` can both just use `Less`. +// `$chain_rel` is the method from `SpecChainingPartialOrd` to use for all but the +// final value, to produce better results for simple primitives. macro_rules! lexical_ord { - ($rel: ident, $ne_rel: ident, $a:expr, $b:expr, $($rest_a:expr, $rest_b:expr),+) => {{ - let c = PartialOrd::partial_cmp(&$a, &$b); - if c != Some(Equal) { c == Some($ne_rel) } - else { lexical_ord!($rel, $ne_rel, $($rest_a, $rest_b),+) } + ($rel: ident, $chain_rel: ident, $a:expr, $b:expr, $($rest_a:expr, $rest_b:expr),+) => {{ + match SpecChainingPartialOrd::$chain_rel(&$a, &$b) { + Break(val) => val, + Continue(()) => lexical_ord!($rel, $chain_rel, $($rest_a, $rest_b),+), + } }}; - ($rel: ident, $ne_rel: ident, $a:expr, $b:expr) => { + ($rel: ident, $chain_rel: ident, $a:expr, $b:expr) => { // Use the specific method for the last element PartialOrd::$rel(&$a, &$b) }; diff --git a/tests/mir-opt/pre-codegen/tuple_ord.demo_ge_partial.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/tuple_ord.demo_ge_partial.PreCodegen.after.mir index 696a451177791..6531683b64450 100644 --- a/tests/mir-opt/pre-codegen/tuple_ord.demo_ge_partial.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/tuple_ord.demo_ge_partial.PreCodegen.after.mir @@ -4,234 +4,67 @@ fn demo_ge_partial(_1: &(f32, f32), _2: &(f32, f32)) -> bool { debug a => _1; debug b => _2; let mut _0: bool; - scope 1 (inlined std::cmp::impls::::le) { - scope 2 (inlined core::tuple::::le) { - let mut _12: bool; - let _15: std::option::Option; - let _19: &f32; - let _20: &f32; + scope 1 (inlined std::cmp::impls::::ge) { + scope 2 (inlined core::tuple::::ge) { + let mut _7: std::ops::ControlFlow; + let _8: bool; scope 3 { - let mut _9: &std::option::Option; - let mut _13: &std::option::Option; - scope 4 (inlined as PartialEq>::ne) { - let mut _11: bool; - scope 5 (inlined as PartialEq>::eq) { - let mut _10: isize; - let mut _16: isize; - scope 6 { - scope 7 (inlined ::eq) { - let _17: i8; - scope 8 { - let _18: i8; - scope 9 { - } - } - } - } - } - } - scope 10 (inlined as PartialEq>::eq) { - let mut _14: isize; - let mut _21: isize; - scope 11 { - scope 12 (inlined ::eq) { - let _22: i8; - scope 13 { - let _23: i8; - scope 14 { - } - } - } - } - } } - scope 15 (inlined std::cmp::impls::::partial_cmp) { + scope 4 (inlined std::cmp::impls:: for f32>::spec_chain_ge) { let mut _3: f32; let mut _4: f32; let mut _5: bool; - let mut _6: f32; - let mut _7: f32; - let mut _8: bool; + let mut _6: bool; + scope 5 { + } + } + scope 6 (inlined std::cmp::impls::::ge) { + let mut _9: f32; + let mut _10: f32; } } } bb0: { - StorageLive(_19); - StorageLive(_20); - StorageLive(_13); - StorageLive(_9); - StorageLive(_15); - StorageLive(_5); - StorageLive(_8); + StorageLive(_7); StorageLive(_3); - _3 = copy ((*_1).0: f32); StorageLive(_4); + _3 = copy ((*_1).0: f32); _4 = copy ((*_2).0: f32); - _5 = Le(move _3, move _4); - StorageDead(_4); - StorageDead(_3); - StorageLive(_6); - _6 = copy ((*_1).0: f32); - StorageLive(_7); - _7 = copy ((*_2).0: f32); - _8 = Ge(move _6, move _7); - StorageDead(_7); - StorageDead(_6); - switchInt(copy _5) -> [0: bb1, otherwise: bb5]; + StorageLive(_5); + _5 = Eq(copy _3, copy _4); + switchInt(move _5) -> [0: bb1, otherwise: bb2]; } bb1: { - switchInt(copy _8) -> [0: bb2, otherwise: bb4]; + StorageLive(_6); + _6 = Ge(copy _3, copy _4); + _7 = ControlFlow::::Break(move _6); + StorageDead(_6); + StorageDead(_5); + StorageDead(_4); + StorageDead(_3); + _8 = copy ((_7 as Break).0: bool); + _0 = copy _8; + goto -> bb3; } bb2: { - StorageDead(_8); StorageDead(_5); - StorageLive(_12); - _9 = const core::tuple::::le::promoted[1]; - StorageLive(_11); - StorageLive(_16); + StorageDead(_4); + StorageDead(_3); + StorageLive(_9); + _9 = copy ((*_1).1: f32); StorageLive(_10); - _10 = discriminant((*_9)); - _11 = Eq(copy _10, const 0_isize); + _10 = copy ((*_2).1: f32); + _0 = Ge(move _9, move _10); StorageDead(_10); - StorageDead(_16); - _12 = Not(move _11); - StorageDead(_11); - switchInt(move _12) -> [0: bb11, otherwise: bb3]; + StorageDead(_9); + goto -> bb3; } bb3: { - _13 = const core::tuple::::le::promoted[0]; - StorageLive(_21); - StorageLive(_14); - _14 = discriminant((*_13)); - _0 = Eq(copy _14, const 0_isize); - goto -> bb16; - } - - bb4: { - _15 = const Option::::Some(Greater); - StorageDead(_8); - StorageDead(_5); - StorageLive(_12); - _9 = const core::tuple::::le::promoted[1]; - StorageLive(_11); - StorageLive(_16); - StorageLive(_10); - goto -> bb8; - } - - bb5: { - switchInt(copy _8) -> [0: bb6, otherwise: bb7]; - } - - bb6: { - _15 = const Option::::Some(Less); - StorageDead(_8); - StorageDead(_5); - StorageLive(_12); - _9 = const core::tuple::::le::promoted[1]; - StorageLive(_11); - StorageLive(_16); - StorageLive(_10); - goto -> bb8; - } - - bb7: { - _15 = const Option::::Some(Equal); - StorageDead(_8); - StorageDead(_5); - StorageLive(_12); - _9 = const core::tuple::::le::promoted[1]; - StorageLive(_11); - StorageLive(_16); - StorageLive(_10); - goto -> bb8; - } - - bb8: { - _16 = discriminant((*_9)); - switchInt(move _16) -> [0: bb9, 1: bb10, otherwise: bb18]; - } - - bb9: { - StorageDead(_10); - StorageDead(_16); - StorageDead(_11); - _13 = const core::tuple::::le::promoted[0]; - StorageLive(_21); - StorageLive(_14); - goto -> bb13; - } - - bb10: { - StorageLive(_17); - StorageLive(_18); - _17 = discriminant(((_15 as Some).0: std::cmp::Ordering)); - _18 = discriminant((((*_9) as Some).0: std::cmp::Ordering)); - _11 = Eq(copy _17, copy _18); - StorageDead(_18); - StorageDead(_17); - StorageDead(_10); - StorageDead(_16); - _12 = Not(move _11); - StorageDead(_11); - switchInt(move _12) -> [0: bb11, otherwise: bb12]; - } - - bb11: { - _19 = &((*_1).1: f32); - _20 = &((*_2).1: f32); - _0 = ::le(move _19, move _20) -> [return: bb17, unwind continue]; - } - - bb12: { - _13 = const core::tuple::::le::promoted[0]; - StorageLive(_21); - StorageLive(_14); - goto -> bb13; - } - - bb13: { - _21 = discriminant((*_13)); - switchInt(move _21) -> [0: bb14, 1: bb15, otherwise: bb18]; - } - - bb14: { - _0 = const false; - goto -> bb16; - } - - bb15: { - StorageLive(_22); - StorageLive(_23); - _22 = discriminant(((_15 as Some).0: std::cmp::Ordering)); - _23 = discriminant((((*_13) as Some).0: std::cmp::Ordering)); - _0 = Eq(copy _22, copy _23); - StorageDead(_23); - StorageDead(_22); - goto -> bb16; - } - - bb16: { - StorageDead(_14); - StorageDead(_21); - goto -> bb17; - } - - bb17: { - StorageDead(_12); - StorageDead(_15); - StorageDead(_9); - StorageDead(_13); - StorageDead(_20); - StorageDead(_19); + StorageDead(_7); return; } - - bb18: { - unreachable; - } } diff --git a/tests/mir-opt/pre-codegen/tuple_ord.demo_le_total.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/tuple_ord.demo_le_total.PreCodegen.after.mir index 15c3ae76ae936..d252052f0aef0 100644 --- a/tests/mir-opt/pre-codegen/tuple_ord.demo_le_total.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/tuple_ord.demo_le_total.PreCodegen.after.mir @@ -6,140 +6,65 @@ fn demo_le_total(_1: &(u16, i16), _2: &(u16, i16)) -> bool { let mut _0: bool; scope 1 (inlined std::cmp::impls::::le) { scope 2 (inlined core::tuple::::le) { - let mut _12: bool; - let _13: &i16; - let _14: &i16; + let mut _7: std::ops::ControlFlow; + let _8: bool; scope 3 { - let mut _6: &std::option::Option; - let mut _8: &std::option::Option; - scope 4 (inlined as PartialEq>::ne) { - let mut _11: bool; - scope 5 (inlined as PartialEq>::eq) { - let mut _7: isize; - scope 6 { - scope 7 (inlined ::eq) { - let _9: i8; - scope 8 { - let _10: i8; - scope 9 { - } - } - } - } - } - } - scope 10 (inlined as PartialEq>::eq) { - let mut _15: isize; - scope 11 { - scope 12 (inlined ::eq) { - let _16: i8; - scope 13 { - let _17: i8; - scope 14 { - } - } - } - } - } } - scope 15 (inlined std::cmp::impls::::partial_cmp) { + scope 4 (inlined std::cmp::impls:: for u16>::spec_chain_le) { let mut _3: u16; let mut _4: u16; - let mut _5: std::cmp::Ordering; + let mut _5: bool; + let mut _6: bool; + scope 5 { + } + } + scope 6 (inlined std::cmp::impls::::le) { + let mut _9: i16; + let mut _10: i16; } } } bb0: { - StorageLive(_13); - StorageLive(_14); - StorageLive(_8); - StorageLive(_6); + StorageLive(_7); StorageLive(_3); - _3 = copy ((*_1).0: u16); StorageLive(_4); + _3 = copy ((*_1).0: u16); _4 = copy ((*_2).0: u16); - _5 = Cmp(move _3, move _4); - StorageDead(_4); - StorageDead(_3); - StorageLive(_12); - _6 = const core::tuple::::le::promoted[1]; - StorageLive(_11); - StorageLive(_7); - _7 = discriminant((*_6)); - switchInt(move _7) -> [0: bb1, 1: bb2, otherwise: bb10]; + StorageLive(_5); + _5 = Eq(copy _3, copy _4); + switchInt(move _5) -> [0: bb1, otherwise: bb2]; } bb1: { - StorageDead(_7); - StorageDead(_11); - _8 = const core::tuple::::le::promoted[0]; - StorageLive(_15); - goto -> bb5; + StorageLive(_6); + _6 = Le(copy _3, copy _4); + _7 = ControlFlow::::Break(move _6); + StorageDead(_6); + StorageDead(_5); + StorageDead(_4); + StorageDead(_3); + _8 = copy ((_7 as Break).0: bool); + _0 = copy _8; + goto -> bb3; } bb2: { + StorageDead(_5); + StorageDead(_4); + StorageDead(_3); StorageLive(_9); + _9 = copy ((*_1).1: i16); StorageLive(_10); - _9 = discriminant(_5); - _10 = discriminant((((*_6) as Some).0: std::cmp::Ordering)); - _11 = Eq(copy _9, copy _10); + _10 = copy ((*_2).1: i16); + _0 = Le(move _9, move _10); StorageDead(_10); StorageDead(_9); - StorageDead(_7); - _12 = Not(move _11); - StorageDead(_11); - switchInt(move _12) -> [0: bb3, otherwise: bb4]; + goto -> bb3; } bb3: { - _13 = &((*_1).1: i16); - _14 = &((*_2).1: i16); - _0 = ::le(move _13, move _14) -> [return: bb9, unwind continue]; - } - - bb4: { - _8 = const core::tuple::::le::promoted[0]; - StorageLive(_15); - goto -> bb5; - } - - bb5: { - _15 = discriminant((*_8)); - switchInt(move _15) -> [0: bb6, 1: bb7, otherwise: bb10]; - } - - bb6: { - _0 = const false; - goto -> bb8; - } - - bb7: { - StorageLive(_16); - StorageLive(_17); - _16 = discriminant(_5); - _17 = discriminant((((*_8) as Some).0: std::cmp::Ordering)); - _0 = Eq(copy _16, copy _17); - StorageDead(_17); - StorageDead(_16); - goto -> bb8; - } - - bb8: { - StorageDead(_15); - goto -> bb9; - } - - bb9: { - StorageDead(_12); - StorageDead(_6); - StorageDead(_8); - StorageDead(_14); - StorageDead(_13); + StorageDead(_7); return; } - - bb10: { - unreachable; - } } diff --git a/tests/mir-opt/pre-codegen/tuple_ord.rs b/tests/mir-opt/pre-codegen/tuple_ord.rs index 435ac7a5be938..74a919e54246c 100644 --- a/tests/mir-opt/pre-codegen/tuple_ord.rs +++ b/tests/mir-opt/pre-codegen/tuple_ord.rs @@ -1,4 +1,4 @@ -//@ compile-flags: -O -Zmir-opt-level=2 -Cdebuginfo=0 -Z inline-mir-hint-threshold=9999 +//@ compile-flags: -O -Zmir-opt-level=2 -Cdebuginfo=0 //@ needs-unwind #![crate_type = "lib"] @@ -12,5 +12,5 @@ pub fn demo_le_total(a: &(u16, i16), b: &(u16, i16)) -> bool { // EMIT_MIR tuple_ord.demo_ge_partial.PreCodegen.after.mir pub fn demo_ge_partial(a: &(f32, f32), b: &(f32, f32)) -> bool { // CHECK-LABEL: demo_ge_partial - a <= b + a >= b } From 521d0c4a30083dd447fcf397e8b1de278acb81af Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 21 Mar 2025 12:30:25 +0000 Subject: [PATCH 11/19] Cache current_dll_path output Computing the current dll path is somewhat expensive relative to other work when compiling `fn main() {}` as `dladdr` needs to iterate over the symbol table of librustc_driver.so until it finds a match. --- compiler/rustc_session/src/filesearch.rs | 122 ++++++++++++----------- 1 file changed, 66 insertions(+), 56 deletions(-) diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs index 50f09c57107e6..bdeca91eb64a4 100644 --- a/compiler/rustc_session/src/filesearch.rs +++ b/compiler/rustc_session/src/filesearch.rs @@ -60,66 +60,76 @@ pub fn make_target_bin_path(sysroot: &Path, target_triple: &str) -> PathBuf { #[cfg(unix)] fn current_dll_path() -> Result { - use std::ffi::{CStr, OsStr}; - use std::os::unix::prelude::*; - - #[cfg(not(target_os = "aix"))] - unsafe { - let addr = current_dll_path as usize as *mut _; - let mut info = std::mem::zeroed(); - if libc::dladdr(addr, &mut info) == 0 { - return Err("dladdr failed".into()); - } - if info.dli_fname.is_null() { - return Err("dladdr returned null pointer".into()); - } - let bytes = CStr::from_ptr(info.dli_fname).to_bytes(); - let os = OsStr::from_bytes(bytes); - Ok(PathBuf::from(os)) - } - - #[cfg(target_os = "aix")] - unsafe { - // On AIX, the symbol `current_dll_path` references a function descriptor. - // A function descriptor is consisted of (See https://reviews.llvm.org/D62532) - // * The address of the entry point of the function. - // * The TOC base address for the function. - // * The environment pointer. - // The function descriptor is in the data section. - let addr = current_dll_path as u64; - let mut buffer = vec![std::mem::zeroed::(); 64]; - loop { - if libc::loadquery( - libc::L_GETINFO, - buffer.as_mut_ptr() as *mut u8, - (size_of::() * buffer.len()) as u32, - ) >= 0 - { - break; - } else { - if std::io::Error::last_os_error().raw_os_error().unwrap() != libc::ENOMEM { - return Err("loadquery failed".into()); + use std::sync::OnceLock; + + // This is somewhat expensive relative to other work when compiling `fn main() {}` as `dladdr` + // needs to iterate over the symbol table of librustc_driver.so until it finds a match. + // As such cache this to avoid recomputing if we try to get the sysroot in multiple places. + static CURRENT_DLL_PATH: OnceLock> = OnceLock::new(); + CURRENT_DLL_PATH + .get_or_init(|| { + use std::ffi::{CStr, OsStr}; + use std::os::unix::prelude::*; + + #[cfg(not(target_os = "aix"))] + unsafe { + let addr = current_dll_path as usize as *mut _; + let mut info = std::mem::zeroed(); + if libc::dladdr(addr, &mut info) == 0 { + return Err("dladdr failed".into()); } - buffer.resize(buffer.len() * 2, std::mem::zeroed::()); - } - } - let mut current = buffer.as_mut_ptr() as *mut libc::ld_info; - loop { - let data_base = (*current).ldinfo_dataorg as u64; - let data_end = data_base + (*current).ldinfo_datasize; - if (data_base..data_end).contains(&addr) { - let bytes = CStr::from_ptr(&(*current).ldinfo_filename[0]).to_bytes(); + if info.dli_fname.is_null() { + return Err("dladdr returned null pointer".into()); + } + let bytes = CStr::from_ptr(info.dli_fname).to_bytes(); let os = OsStr::from_bytes(bytes); - return Ok(PathBuf::from(os)); + Ok(PathBuf::from(os)) } - if (*current).ldinfo_next == 0 { - break; + + #[cfg(target_os = "aix")] + unsafe { + // On AIX, the symbol `current_dll_path` references a function descriptor. + // A function descriptor is consisted of (See https://reviews.llvm.org/D62532) + // * The address of the entry point of the function. + // * The TOC base address for the function. + // * The environment pointer. + // The function descriptor is in the data section. + let addr = current_dll_path as u64; + let mut buffer = vec![std::mem::zeroed::(); 64]; + loop { + if libc::loadquery( + libc::L_GETINFO, + buffer.as_mut_ptr() as *mut u8, + (size_of::() * buffer.len()) as u32, + ) >= 0 + { + break; + } else { + if std::io::Error::last_os_error().raw_os_error().unwrap() != libc::ENOMEM { + return Err("loadquery failed".into()); + } + buffer.resize(buffer.len() * 2, std::mem::zeroed::()); + } + } + let mut current = buffer.as_mut_ptr() as *mut libc::ld_info; + loop { + let data_base = (*current).ldinfo_dataorg as u64; + let data_end = data_base + (*current).ldinfo_datasize; + if (data_base..data_end).contains(&addr) { + let bytes = CStr::from_ptr(&(*current).ldinfo_filename[0]).to_bytes(); + let os = OsStr::from_bytes(bytes); + return Ok(PathBuf::from(os)); + } + if (*current).ldinfo_next == 0 { + break; + } + current = (current as *mut i8).offset((*current).ldinfo_next as isize) + as *mut libc::ld_info; + } + return Err(format!("current dll's address {} is not in the load map", addr)); } - current = - (current as *mut i8).offset((*current).ldinfo_next as isize) as *mut libc::ld_info; - } - return Err(format!("current dll's address {} is not in the load map", addr)); - } + }) + .clone() } #[cfg(windows)] From 63cfd47cb10940e593f6755a54c12438f7fced8f Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 27 Feb 2025 16:35:57 +0000 Subject: [PATCH 12/19] Don't attempt to export compiler-builtins symbols from rust dylibs They are marked with hidden visibility to prevent them from getting exported, so we shouldn't ask the linker to export them anyway. The only thing that does it cause a warning on macOS. --- compiler/rustc_codegen_ssa/src/back/linker.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index e2a59c6efb883..7df2d128d2fba 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -1782,7 +1782,10 @@ fn exported_symbols_for_non_proc_macro(tcx: TyCtxt<'_>, crate_type: CrateType) - let mut symbols = Vec::new(); let export_threshold = symbol_export::crates_export_threshold(&[crate_type]); for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| { - if info.level.is_below_threshold(export_threshold) { + // Do not export mangled symbols from cdylibs and don't attempt to export compiler-builtins + // from any cdylib. The latter doesn't work anyway as we use hidden visibility for + // compiler-builtins. Most linkers silently ignore it, but ld64 gives a warning. + if info.level.is_below_threshold(export_threshold) && !tcx.is_compiler_builtins(cnum) { symbols.push(symbol_export::exporting_symbol_name_for_instance_in_crate( tcx, symbol, cnum, )); From 530ab61c0eca891773bb71716849e3e934e84e9f Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 21 Mar 2025 14:06:34 +0000 Subject: [PATCH 13/19] Also check for compiler-builtins in linked_symbols Otherwise the linker complains about EC symbols missing when compiling for arm64ec. --- compiler/rustc_codegen_ssa/src/back/linker.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 7df2d128d2fba..3f5e0c1bce9c1 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -1824,7 +1824,9 @@ pub(crate) fn linked_symbols( let export_threshold = symbol_export::crates_export_threshold(&[crate_type]); for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| { - if info.level.is_below_threshold(export_threshold) || info.used { + if info.level.is_below_threshold(export_threshold) && !tcx.is_compiler_builtins(cnum) + || info.used + { symbols.push(( symbol_export::linking_symbol_name_for_instance_in_crate(tcx, symbol, cnum), info.kind, From b46412f6d7fde0c24780b0808aa7aefbc18d9e1e Mon Sep 17 00:00:00 2001 From: binarycat Date: Sun, 16 Mar 2025 15:42:04 -0500 Subject: [PATCH 14/19] rustdoc: be more strict about "Methods from Deref" hack: is_doc_subtype_of always returns true for TyAlias it's worth noting that this function is only used in the handling of "Methods from Deref", and we were previously assuming all generic parameters were meaningless, so this is still an improvment from the status quo. this change means that we will have strictly less false positives without adding any new false negitives. Co-authored-by: Guillaume Gomez --- src/librustdoc/clean/types.rs | 14 ++++++++++ src/librustdoc/html/render/mod.rs | 16 +++++++++-- src/librustdoc/html/render/sidebar.rs | 5 +++- .../deref/deref-methods-24686-target.rs | 27 +++++++++++++++++++ 4 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 tests/rustdoc/deref/deref-methods-24686-target.rs diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 3f9023659dbac..257687ae0a38d 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1533,6 +1533,10 @@ impl Type { matches!(self, Type::BorrowedRef { .. }) } + fn is_type_alias(&self) -> bool { + matches!(self, Type::Path { path: Path { res: Res::Def(DefKind::TyAlias, _), .. } }) + } + /// Check if two types are "the same" for documentation purposes. /// /// This is different from `Eq`, because it knows that things like @@ -1561,6 +1565,16 @@ impl Type { } else { (self, other) }; + + // FIXME: `Cache` does not have the data required to unwrap type aliases, + // so we just assume they are equal. + // This is only remotely acceptable because we were previously + // assuming all types were equal when used + // as a generic parameter of a type in `Deref::Target`. + if self_cleared.is_type_alias() || other_cleared.is_type_alias() { + return true; + } + match (self_cleared, other_cleared) { // Recursive cases. (Type::Tuple(a), Type::Tuple(b)) => { diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index b2ad2fa773ae5..900de90c73662 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -89,7 +89,7 @@ pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display { /// Specifies whether rendering directly implemented trait items or ones from a certain Deref /// impl. -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub(crate) enum AssocItemRender<'a> { All, DerefFor { trait_: &'a clean::Path, type_: &'a clean::Type, deref_mut_: bool }, @@ -1296,7 +1296,8 @@ fn render_assoc_items_inner( info!("Documenting associated items of {:?}", containing_item.name); let cache = &cx.shared.cache; let Some(v) = cache.impls.get(&it) else { return }; - let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none()); + let (mut non_trait, traits): (Vec<_>, _) = + v.iter().partition(|i| i.inner_impl().trait_.is_none()); if !non_trait.is_empty() { let mut close_tags = >::with_capacity(1); let mut tmp_buf = String::new(); @@ -1314,6 +1315,16 @@ fn render_assoc_items_inner( AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => { let id = cx.derive_id(small_url_encode(format!("deref-methods-{:#}", type_.print(cx)))); + // the `impls.get` above only looks at the outermost type, + // and the Deref impl may only be implemented for certain + // values of generic parameters. + // for example, if an item impls `Deref<[u8]>`, + // we should not show methods from `[MaybeUninit]`. + // this `retain` filters out any instances where + // the types do not line up perfectly. + non_trait.retain(|impl_| { + type_.is_doc_subtype_of(&impl_.inner_impl().for_, &cx.shared.cache) + }); let derived_id = cx.derive_id(&id); close_tags.push(""); write_str( @@ -1392,6 +1403,7 @@ fn render_assoc_items_inner( } } +/// `derefs` is the set of all deref targets that have already been handled. fn render_deref_methods( mut w: impl Write, cx: &Context<'_>, diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index 3130815af0bd0..9c78dcdc571d5 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -533,7 +533,10 @@ fn sidebar_deref_methods<'a>( debug!("found inner_impl: {impls:?}"); let mut ret = impls .iter() - .filter(|i| i.inner_impl().trait_.is_none()) + .filter(|i| { + i.inner_impl().trait_.is_none() + && real_target.is_doc_subtype_of(&i.inner_impl().for_, &c) + }) .flat_map(|i| get_methods(i.inner_impl(), true, used_links, deref_mut, cx.tcx())) .collect::>(); if !ret.is_empty() { diff --git a/tests/rustdoc/deref/deref-methods-24686-target.rs b/tests/rustdoc/deref/deref-methods-24686-target.rs new file mode 100644 index 0000000000000..e019488ca8023 --- /dev/null +++ b/tests/rustdoc/deref/deref-methods-24686-target.rs @@ -0,0 +1,27 @@ +#![crate_name = "foo"] + +// test for https://github.com/rust-lang/rust/issues/24686 +use std::ops::Deref; + +pub struct Foo(T); +impl Foo { + pub fn get_i32(&self) -> i32 { self.0 } +} +impl Foo { + pub fn get_u32(&self) -> u32 { self.0 } +} + +// Note that the same href is used both on the method itself, +// and on the sidebar items. +//@ has foo/struct.Bar.html +//@ has - '//a[@href="#method.get_i32"]' 'get_i32' +//@ !has - '//a[@href="#method.get_u32"]' 'get_u32' +//@ count - '//ul[@class="block deref-methods"]//a' 1 +//@ count - '//a[@href="#method.get_i32"]' 2 +pub struct Bar(Foo); +impl Deref for Bar { + type Target = Foo; + fn deref(&self) -> &Foo { + &self.0 + } +} From 575f129faa5126869f11ef945276072b097a2b2a Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 22 Mar 2025 22:09:16 +0000 Subject: [PATCH 15/19] Obligation::as_goal --- .../src/fn_ctxt/inspect_obligations.rs | 4 ++-- compiler/rustc_infer/src/infer/opaque_types/mod.rs | 3 +-- compiler/rustc_infer/src/traits/mod.rs | 12 ++++++------ compiler/rustc_trait_selection/src/solve/delegate.rs | 2 +- compiler/rustc_trait_selection/src/solve/fulfill.rs | 4 ++-- .../src/solve/fulfill/derive_errors.rs | 8 ++++---- .../rustc_trait_selection/src/traits/coherence.rs | 2 +- 7 files changed, 17 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs index 95b9cb3be627c..e068e60790277 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs @@ -1,7 +1,7 @@ //! A utility module to inspect currently ambiguous obligations in the current context. use rustc_infer::traits::{self, ObligationCause, PredicateObligations}; -use rustc_middle::traits::solve::{Goal, GoalSource}; +use rustc_middle::traits::solve::GoalSource; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; use rustc_span::Span; use rustc_trait_selection::solve::inspect::{ @@ -85,7 +85,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { root_cause: &obligation.cause, }; - let goal = Goal::new(self.tcx, obligation.param_env, obligation.predicate); + let goal = obligation.as_goal(); self.visit_proof_tree(goal, &mut visitor); } diff --git a/compiler/rustc_infer/src/infer/opaque_types/mod.rs b/compiler/rustc_infer/src/infer/opaque_types/mod.rs index 3fa1923121a23..215b133372664 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/mod.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/mod.rs @@ -246,8 +246,7 @@ impl<'tcx> InferCtxt<'tcx> { .eq(DefineOpaqueTypes::Yes, prev, hidden_ty)? .obligations .into_iter() - // FIXME: Shuttling between obligations and goals is awkward. - .map(Goal::from), + .map(|obligation| obligation.as_goal()), ); } } diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs index ac641ef565228..b537750f1b51b 100644 --- a/compiler/rustc_infer/src/traits/mod.rs +++ b/compiler/rustc_infer/src/traits/mod.rs @@ -54,6 +54,12 @@ pub struct Obligation<'tcx, T> { pub recursion_depth: usize, } +impl<'tcx, T: Copy> Obligation<'tcx, T> { + pub fn as_goal(&self) -> solve::Goal<'tcx, T> { + solve::Goal { param_env: self.param_env, predicate: self.predicate } + } +} + impl<'tcx, T: PartialEq> PartialEq> for Obligation<'tcx, T> { #[inline] fn eq(&self, other: &Obligation<'tcx, T>) -> bool { @@ -75,12 +81,6 @@ impl Hash for Obligation<'_, T> { } } -impl<'tcx, P> From> for solve::Goal<'tcx, P> { - fn from(value: Obligation<'tcx, P>) -> Self { - solve::Goal { param_env: value.param_env, predicate: value.predicate } - } -} - pub type PredicateObligation<'tcx> = Obligation<'tcx, ty::Predicate<'tcx>>; pub type TraitObligation<'tcx> = Obligation<'tcx, ty::TraitPredicate<'tcx>>; pub type PolyTraitObligation<'tcx> = Obligation<'tcx, ty::PolyTraitPredicate<'tcx>>; diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index af5a60027ba4a..3d9a90eb74e7a 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -96,7 +96,7 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< ) -> Option>>> { crate::traits::wf::unnormalized_obligations(&self.0, param_env, arg, DUMMY_SP, CRATE_DEF_ID) .map(|obligations| { - obligations.into_iter().map(|obligation| obligation.into()).collect() + obligations.into_iter().map(|obligation| obligation.as_goal()).collect() }) } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 704ba6e501d8c..192e632a2d5b9 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -80,7 +80,7 @@ impl<'tcx> ObligationStorage<'tcx> { // change. // FIXME: is merged, this can be removed. self.overflowed.extend(ExtractIf::new(&mut self.pending, |o| { - let goal = o.clone().into(); + let goal = o.as_goal(); let result = <&SolverDelegate<'tcx>>::from(infcx) .evaluate_root_goal(goal, GenerateProofTree::No, o.cause.span) .0; @@ -161,7 +161,7 @@ where let mut has_changed = false; for obligation in self.obligations.unstalled_for_select() { - let goal = obligation.clone().into(); + let goal = obligation.as_goal(); let result = <&SolverDelegate<'tcx>>::from(infcx) .evaluate_root_goal(goal, GenerateProofTree::No, obligation.cause.span) .0; 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 352ac7c1a4e6f..3a939df25e07b 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -10,7 +10,7 @@ use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_next_trait_solver::solve::{GenerateProofTree, SolverDelegateEvalExt as _}; -use rustc_type_ir::solve::{Goal, NoSolution}; +use rustc_type_ir::solve::NoSolution; use tracing::{instrument, trace}; use crate::solve::Certainty; @@ -89,7 +89,7 @@ pub(super) fn fulfillment_error_for_stalled<'tcx>( let (code, refine_obligation) = infcx.probe(|_| { match <&SolverDelegate<'tcx>>::from(infcx) .evaluate_root_goal( - root_obligation.clone().into(), + root_obligation.as_goal(), GenerateProofTree::No, root_obligation.cause.span, ) @@ -155,7 +155,7 @@ fn find_best_leaf_obligation<'tcx>( .fudge_inference_if_ok(|| { infcx .visit_proof_tree( - obligation.clone().into(), + obligation.as_goal(), &mut BestObligation { obligation: obligation.clone(), consider_ambiguities }, ) .break_value() @@ -245,7 +245,7 @@ impl<'tcx> BestObligation<'tcx> { { let nested_goal = candidate.instantiate_proof_tree_for_nested_goal( GoalSource::Misc, - Goal::new(infcx.tcx, obligation.param_env, obligation.predicate), + obligation.as_goal(), self.span(), ); // Skip nested goals that aren't the *reason* for our goal's failure. diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 4c7172c32781a..bcc247ba53c2b 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -625,7 +625,7 @@ fn compute_intercrate_ambiguity_causes<'tcx>( let mut causes: FxIndexSet> = Default::default(); for obligation in obligations { - search_ambiguity_causes(infcx, obligation.clone().into(), &mut causes); + search_ambiguity_causes(infcx, obligation.as_goal(), &mut causes); } causes From d588bc2a2b49cb9f18fe051774a3f4a41ab5068e Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 22 Mar 2025 21:01:11 +0000 Subject: [PATCH 16/19] Don't super fold const in Resolver --- compiler/rustc_hir_typeck/src/writeback.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 6a3417ae5d6fb..748cb11290d93 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -864,10 +864,7 @@ impl<'cx, 'tcx> TypeFolder> for Resolver<'cx, 'tcx> { } fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - self.handle_term(ct, ty::Const::outer_exclusive_binder, |tcx, guar| { - ty::Const::new_error(tcx, guar) - }) - .super_fold_with(self) + self.handle_term(ct, ty::Const::outer_exclusive_binder, ty::Const::new_error) } fn fold_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { From fad34c603cd6c7e432f297e682bb68a2cf55df0b Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 23 Mar 2025 18:34:33 +0000 Subject: [PATCH 17/19] Explicitly don't fold coroutine obligations in writeback --- compiler/rustc_hir_typeck/src/writeback.rs | 45 +++++++++++++++------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 748cb11290d93..b63c0b6ab7e09 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -548,7 +548,8 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { let fcx_typeck_results = self.fcx.typeck_results.borrow(); assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner); for (predicate, cause) in &fcx_typeck_results.coroutine_stalled_predicates { - let (predicate, cause) = self.resolve((*predicate, cause.clone()), &cause.span); + let (predicate, cause) = + self.resolve_coroutine_predicate((*predicate, cause.clone()), &cause.span); self.typeck_results.coroutine_stalled_predicates.insert((predicate, cause)); } } @@ -730,7 +731,25 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { T: TypeFoldable>, { let value = self.fcx.resolve_vars_if_possible(value); - let value = value.fold_with(&mut Resolver::new(self.fcx, span, self.body)); + let value = value.fold_with(&mut Resolver::new(self.fcx, span, self.body, true)); + assert!(!value.has_infer()); + + // We may have introduced e.g. `ty::Error`, if inference failed, make sure + // to mark the `TypeckResults` as tainted in that case, so that downstream + // users of the typeck results don't produce extra errors, or worse, ICEs. + if let Err(guar) = value.error_reported() { + self.typeck_results.tainted_by_errors = Some(guar); + } + + value + } + + fn resolve_coroutine_predicate(&mut self, value: T, span: &dyn Locatable) -> T + where + T: TypeFoldable>, + { + let value = self.fcx.resolve_vars_if_possible(value); + let value = value.fold_with(&mut Resolver::new(self.fcx, span, self.body, false)); assert!(!value.has_infer()); // We may have introduced e.g. `ty::Error`, if inference failed, make sure @@ -774,8 +793,9 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { fcx: &'cx FnCtxt<'cx, 'tcx>, span: &'cx dyn Locatable, body: &'tcx hir::Body<'tcx>, + should_normalize: bool, ) -> Resolver<'cx, 'tcx> { - Resolver { fcx, span, body, should_normalize: fcx.next_trait_solver() } + Resolver { fcx, span, body, should_normalize } } fn report_error(&self, p: impl Into>) -> ErrorGuaranteed { @@ -805,10 +825,9 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { T: Into> + TypeSuperFoldable> + Copy, { let tcx = self.fcx.tcx; - // We must deeply normalize in the new solver, since later lints - // expect that types that show up in the typeck are fully - // normalized. - let mut value = if self.should_normalize { + // We must deeply normalize in the new solver, since later lints expect + // that types that show up in the typeck are fully normalized. + let mut value = if self.should_normalize && self.fcx.next_trait_solver() { let body_id = tcx.hir_body_owner_def_id(self.body.id()); let cause = ObligationCause::misc(self.span.to_span(tcx), body_id); let at = self.fcx.at(&cause, self.fcx.param_env); @@ -868,13 +887,11 @@ impl<'cx, 'tcx> TypeFolder> for Resolver<'cx, 'tcx> { } fn fold_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { - // Do not normalize predicates in the new solver. The new solver is - // supposed to handle unnormalized predicates and incorrectly normalizing - // them can be unsound, e.g. for `WellFormed` predicates. - let prev = mem::replace(&mut self.should_normalize, false); - let predicate = predicate.super_fold_with(self); - self.should_normalize = prev; - predicate + assert!( + !self.should_normalize, + "normalizing predicates in writeback is not generally sound" + ); + predicate.super_fold_with(self) } } From 7781346243c1e1a038e0bc6fa11e5e1aefea7d4a Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 23 Mar 2025 15:27:31 -0700 Subject: [PATCH 18/19] Stop using specialization for this Uses `__`-named `doc(hidden)` methods instead. --- library/core/src/cmp.rs | 162 ++++++++++-------- library/core/src/tuple.rs | 13 +- ...e_ord.demo_ge_partial.PreCodegen.after.mir | 2 +- ...ple_ord.demo_le_total.PreCodegen.after.mir | 2 +- 4 files changed, 95 insertions(+), 84 deletions(-) diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index af14c4b50725f..0b0dbf723b658 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -29,7 +29,7 @@ mod bytewise; pub(crate) use bytewise::BytewiseEq; use self::Ordering::*; -use crate::ops::ControlFlow::{self, Break, Continue}; +use crate::ops::ControlFlow; /// Trait for comparisons using the equality operator. /// @@ -1436,65 +1436,78 @@ pub trait PartialOrd: PartialEq { fn ge(&self, other: &Rhs) -> bool { self.partial_cmp(other).is_some_and(Ordering::is_ge) } -} - -/// Derive macro generating an impl of the trait [`PartialOrd`]. -/// The behavior of this macro is described in detail [here](PartialOrd#derivable). -#[rustc_builtin_macro] -#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[allow_internal_unstable(core_intrinsics)] -pub macro PartialOrd($item:item) { - /* compiler built-in */ -} - -/// Helpers for chaining together field PartialOrds into the full type's ordering. -/// -/// If the two values are equal, returns `ControlFlow::Continue`. -/// If the two values are not equal, returns `ControlFlow::Break(self OP other)`. -/// -/// This allows simple types like `i32` and `f64` to just emit two comparisons -/// directly, instead of needing to optimize the 3-way comparison. -/// -/// Currently this is done using specialization, but it doesn't need that: -/// it could be provided methods on `PartialOrd` instead and work fine. -pub(crate) trait SpecChainingPartialOrd: PartialOrd { - fn spec_chain_lt(&self, other: &Rhs) -> ControlFlow; - fn spec_chain_le(&self, other: &Rhs) -> ControlFlow; - fn spec_chain_gt(&self, other: &Rhs) -> ControlFlow; - fn spec_chain_ge(&self, other: &Rhs) -> ControlFlow; -} -impl, U> SpecChainingPartialOrd for T { + /// If `self == other`, returns `ControlFlow::Continue(())`. + /// Otherwise, returns `ControlFlow::Break(self < other)`. + /// + /// This is useful for chaining together calls when implementing a lexical + /// `PartialOrd::lt`, as it allows types (like primitives) which can cheaply + /// check `==` and `<` separately to do rather than needing to calculate + /// (then optimize out) the three-way `Ordering` result. #[inline] - default fn spec_chain_lt(&self, other: &U) -> ControlFlow { - match PartialOrd::partial_cmp(self, other) { - Some(Equal) => Continue(()), - c => Break(c.is_some_and(Ordering::is_lt)), - } + #[must_use] + // Added to improve the behaviour of tuples; not necessarily stabilization-track. + #[unstable(feature = "partial_ord_chaining_methods", issue = "none")] + #[doc(hidden)] + fn __chaining_lt(&self, other: &Rhs) -> ControlFlow { + default_chaining_impl(self, other, Ordering::is_lt) } + + /// Same as `__chaining_lt`, but for `<=` instead of `<`. #[inline] - default fn spec_chain_le(&self, other: &U) -> ControlFlow { - match PartialOrd::partial_cmp(self, other) { - Some(Equal) => Continue(()), - c => Break(c.is_some_and(Ordering::is_le)), - } + #[must_use] + #[unstable(feature = "partial_ord_chaining_methods", issue = "none")] + #[doc(hidden)] + fn __chaining_le(&self, other: &Rhs) -> ControlFlow { + default_chaining_impl(self, other, Ordering::is_le) } + + /// Same as `__chaining_lt`, but for `>` instead of `<`. #[inline] - default fn spec_chain_gt(&self, other: &U) -> ControlFlow { - match PartialOrd::partial_cmp(self, other) { - Some(Equal) => Continue(()), - c => Break(c.is_some_and(Ordering::is_gt)), - } + #[must_use] + #[unstable(feature = "partial_ord_chaining_methods", issue = "none")] + #[doc(hidden)] + fn __chaining_gt(&self, other: &Rhs) -> ControlFlow { + default_chaining_impl(self, other, Ordering::is_gt) } + + /// Same as `__chaining_lt`, but for `>=` instead of `<`. #[inline] - default fn spec_chain_ge(&self, other: &U) -> ControlFlow { - match PartialOrd::partial_cmp(self, other) { - Some(Equal) => Continue(()), - c => Break(c.is_some_and(Ordering::is_ge)), - } + #[must_use] + #[unstable(feature = "partial_ord_chaining_methods", issue = "none")] + #[doc(hidden)] + fn __chaining_ge(&self, other: &Rhs) -> ControlFlow { + default_chaining_impl(self, other, Ordering::is_ge) } } +fn default_chaining_impl( + lhs: &T, + rhs: &U, + p: impl FnOnce(Ordering) -> bool, +) -> ControlFlow +where + T: PartialOrd, +{ + // It's important that this only call `partial_cmp` once, not call `eq` then + // one of the relational operators. We don't want to `bcmp`-then-`memcp` a + // `String`, for example, or similarly for other data structures (#108157). + match >::partial_cmp(lhs, rhs) { + Some(Equal) => ControlFlow::Continue(()), + Some(c) => ControlFlow::Break(p(c)), + None => ControlFlow::Break(false), + } +} + +/// Derive macro generating an impl of the trait [`PartialOrd`]. +/// The behavior of this macro is described in detail [here](PartialOrd#derivable). +#[rustc_builtin_macro] +#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] +#[allow_internal_unstable(core_intrinsics)] +pub macro PartialOrd($item:item) { + /* compiler built-in */ +} + /// Compares and returns the minimum of two values. /// /// Returns the first argument if the comparison determines them to be equal. @@ -1829,32 +1842,31 @@ mod impls { eq_impl! { () bool char usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } - macro_rules! chaining_impl { + macro_rules! chaining_methods_impl { ($t:ty) => { // These implementations are the same for `Ord` or `PartialOrd` types // because if either is NAN the `==` test will fail so we end up in // the `Break` case and the comparison will correctly return `false`. - impl super::SpecChainingPartialOrd<$t> for $t { - #[inline] - fn spec_chain_lt(&self, other: &Self) -> ControlFlow { - let (lhs, rhs) = (*self, *other); - if lhs == rhs { Continue(()) } else { Break(lhs < rhs) } - } - #[inline] - fn spec_chain_le(&self, other: &Self) -> ControlFlow { - let (lhs, rhs) = (*self, *other); - if lhs == rhs { Continue(()) } else { Break(lhs <= rhs) } - } - #[inline] - fn spec_chain_gt(&self, other: &Self) -> ControlFlow { - let (lhs, rhs) = (*self, *other); - if lhs == rhs { Continue(()) } else { Break(lhs > rhs) } - } - #[inline] - fn spec_chain_ge(&self, other: &Self) -> ControlFlow { - let (lhs, rhs) = (*self, *other); - if lhs == rhs { Continue(()) } else { Break(lhs >= rhs) } - } + + #[inline] + fn __chaining_lt(&self, other: &Self) -> ControlFlow { + let (lhs, rhs) = (*self, *other); + if lhs == rhs { Continue(()) } else { Break(lhs < rhs) } + } + #[inline] + fn __chaining_le(&self, other: &Self) -> ControlFlow { + let (lhs, rhs) = (*self, *other); + if lhs == rhs { Continue(()) } else { Break(lhs <= rhs) } + } + #[inline] + fn __chaining_gt(&self, other: &Self) -> ControlFlow { + let (lhs, rhs) = (*self, *other); + if lhs == rhs { Continue(()) } else { Break(lhs > rhs) } + } + #[inline] + fn __chaining_ge(&self, other: &Self) -> ControlFlow { + let (lhs, rhs) = (*self, *other); + if lhs == rhs { Continue(()) } else { Break(lhs >= rhs) } } }; } @@ -1880,9 +1892,9 @@ mod impls { fn ge(&self, other: &$t) -> bool { (*self) >= (*other) } #[inline(always)] fn gt(&self, other: &$t) -> bool { (*self) > (*other) } - } - chaining_impl!($t); + chaining_methods_impl!($t); + } )*) } @@ -1920,9 +1932,9 @@ mod impls { fn ge(&self, other: &$t) -> bool { (*self) >= (*other) } #[inline(always)] fn gt(&self, other: &$t) -> bool { (*self) > (*other) } - } - chaining_impl!($t); + chaining_methods_impl!($t); + } #[stable(feature = "rust1", since = "1.0.0")] impl Ord for $t { diff --git a/library/core/src/tuple.rs b/library/core/src/tuple.rs index 75faaa06ee7fe..d754bb9034300 100644 --- a/library/core/src/tuple.rs +++ b/library/core/src/tuple.rs @@ -1,7 +1,6 @@ // See core/src/primitive_docs.rs for documentation. use crate::cmp::Ordering::{self, *}; -use crate::cmp::SpecChainingPartialOrd; use crate::marker::{ConstParamTy_, StructuralPartialEq, UnsizedConstParamTy}; use crate::ops::ControlFlow::{Break, Continue}; @@ -82,19 +81,19 @@ macro_rules! tuple_impls { } #[inline] fn lt(&self, other: &($($T,)+)) -> bool { - lexical_ord!(lt, spec_chain_lt, $( ${ignore($T)} self.${index()}, other.${index()} ),+) + lexical_ord!(lt, __chaining_lt, $( ${ignore($T)} self.${index()}, other.${index()} ),+) } #[inline] fn le(&self, other: &($($T,)+)) -> bool { - lexical_ord!(le, spec_chain_le, $( ${ignore($T)} self.${index()}, other.${index()} ),+) + lexical_ord!(le, __chaining_le, $( ${ignore($T)} self.${index()}, other.${index()} ),+) } #[inline] fn ge(&self, other: &($($T,)+)) -> bool { - lexical_ord!(ge, spec_chain_ge, $( ${ignore($T)} self.${index()}, other.${index()} ),+) + lexical_ord!(ge, __chaining_ge, $( ${ignore($T)} self.${index()}, other.${index()} ),+) } #[inline] fn gt(&self, other: &($($T,)+)) -> bool { - lexical_ord!(gt, spec_chain_gt, $( ${ignore($T)} self.${index()}, other.${index()} ),+) + lexical_ord!(gt, __chaining_gt, $( ${ignore($T)} self.${index()}, other.${index()} ),+) } } } @@ -173,11 +172,11 @@ macro_rules! maybe_tuple_doc { // `(a1, a2, a3) < (b1, b2, b3)` would be `lexical_ord!(lt, opt_is_lt, a1, b1, // a2, b2, a3, b3)` (and similarly for `lexical_cmp`) // -// `$chain_rel` is the method from `SpecChainingPartialOrd` to use for all but the +// `$chain_rel` is the chaining method from `PartialOrd` to use for all but the // final value, to produce better results for simple primitives. macro_rules! lexical_ord { ($rel: ident, $chain_rel: ident, $a:expr, $b:expr, $($rest_a:expr, $rest_b:expr),+) => {{ - match SpecChainingPartialOrd::$chain_rel(&$a, &$b) { + match PartialOrd::$chain_rel(&$a, &$b) { Break(val) => val, Continue(()) => lexical_ord!($rel, $chain_rel, $($rest_a, $rest_b),+), } diff --git a/tests/mir-opt/pre-codegen/tuple_ord.demo_ge_partial.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/tuple_ord.demo_ge_partial.PreCodegen.after.mir index 6531683b64450..dd2eebc8f4a14 100644 --- a/tests/mir-opt/pre-codegen/tuple_ord.demo_ge_partial.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/tuple_ord.demo_ge_partial.PreCodegen.after.mir @@ -10,7 +10,7 @@ fn demo_ge_partial(_1: &(f32, f32), _2: &(f32, f32)) -> bool { let _8: bool; scope 3 { } - scope 4 (inlined std::cmp::impls:: for f32>::spec_chain_ge) { + scope 4 (inlined std::cmp::impls::::__chaining_ge) { let mut _3: f32; let mut _4: f32; let mut _5: bool; diff --git a/tests/mir-opt/pre-codegen/tuple_ord.demo_le_total.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/tuple_ord.demo_le_total.PreCodegen.after.mir index d252052f0aef0..ea1d164cefafb 100644 --- a/tests/mir-opt/pre-codegen/tuple_ord.demo_le_total.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/tuple_ord.demo_le_total.PreCodegen.after.mir @@ -10,7 +10,7 @@ fn demo_le_total(_1: &(u16, i16), _2: &(u16, i16)) -> bool { let _8: bool; scope 3 { } - scope 4 (inlined std::cmp::impls:: for u16>::spec_chain_le) { + scope 4 (inlined std::cmp::impls::::__chaining_le) { let mut _3: u16; let mut _4: u16; let mut _5: bool; From 95181ae170e6961bf541c01dee8320f026e85d0a Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Thu, 20 Mar 2025 02:12:40 +0000 Subject: [PATCH 19/19] Update `compiler-builtins` to 0.1.152 Includes the following changes related to unordered atomics: * Remove element_unordered_atomic intrinsics [1] * Remove use of `atomic_load_unordered` and undefined behaviour [2] There are a handful of other small changes, but nothing else user-visible. [1]: https://github.com/rust-lang/compiler-builtins/pull/789 [2]: https://github.com/rust-lang/compiler-builtins/pull/790 --- ...029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch | 4 ++-- library/Cargo.lock | 4 ++-- library/alloc/Cargo.toml | 2 +- library/std/Cargo.toml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch b/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch index 754025ff49db3..34249ea483451 100644 --- a/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch +++ b/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch @@ -16,8 +16,8 @@ index 7165c3e48af..968552ad435 100644 [dependencies] core = { path = "../core", public = true } --compiler_builtins = { version = "=0.1.151", features = ['rustc-dep-of-std'] } -+compiler_builtins = { version = "=0.1.151", features = ['rustc-dep-of-std', 'no-f16-f128'] } +-compiler_builtins = { version = "=0.1.152", features = ['rustc-dep-of-std'] } ++compiler_builtins = { version = "=0.1.152", features = ['rustc-dep-of-std', 'no-f16-f128'] } [features] compiler-builtins-mem = ['compiler_builtins/mem'] diff --git a/library/Cargo.lock b/library/Cargo.lock index 104e0a3d01079..6b1a0a080551f 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -67,9 +67,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.151" +version = "0.1.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abc30f1766d387c35f2405e586d3e7a88230dc728ff78cd1d0bc59ae0b63154b" +checksum = "2153cf213eb259361567720ce55f6446f17acd0ccca87fb6dc05360578228a58" dependencies = [ "cc", "rustc-std-workspace-core", diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index 8d0253bd29aa2..b729d5e116d2c 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -16,7 +16,7 @@ bench = false [dependencies] core = { path = "../core", public = true } -compiler_builtins = { version = "=0.1.151", features = ['rustc-dep-of-std'] } +compiler_builtins = { version = "=0.1.152", features = ['rustc-dep-of-std'] } [features] compiler-builtins-mem = ['compiler_builtins/mem'] diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 9d9601b79a7ed..176da603d58d7 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -18,7 +18,7 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] } panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core", public = true } -compiler_builtins = { version = "=0.1.151" } +compiler_builtins = { version = "=0.1.152" } unwind = { path = "../unwind" } hashbrown = { version = "0.15", default-features = false, features = [ 'rustc-dep-of-std',