diff options
| author | The Miri Cronjob Bot <miri@cron.bot> | 2025-06-10 05:02:33 +0000 |
|---|---|---|
| committer | The Miri Cronjob Bot <miri@cron.bot> | 2025-06-10 05:02:33 +0000 |
| commit | c601681d95b0f984850d84ab60a47de7e1d673a8 (patch) | |
| tree | 83928fd3e3f9c2f023d470342462daf721cf3aab /src | |
| parent | 5eb5f2a2269c1f83a65cde57fb0ce0562d931be3 (diff) | |
| parent | c6768de2d63de7a41124a0fb8fc78f9e26111c01 (diff) | |
Merge from rustc
Diffstat (limited to 'src')
39 files changed, 772 insertions, 384 deletions
diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index c60c6b8db64..d8c6be78247 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -394,6 +394,7 @@ def default_build_triple(verbose): "i686": "i686", "i686-AT386": "i686", "i786": "i686", + "loongarch32": "loongarch32", "loongarch64": "loongarch64", "m68k": "m68k", "csky": "csky", diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 1e9af68a92d..cf7f962d026 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -11,7 +11,7 @@ use crate::utils::build_stamp; use crate::utils::helpers::{self, LldThreads, check_cfg_arg, linker_args, linker_flags}; use crate::{ BootstrapCommand, CLang, Compiler, Config, DocTests, DryRun, EXTRA_CHECK_CFGS, GitRepo, Mode, - TargetSelection, command, prepare_behaviour_dump_dir, t, + RemapScheme, TargetSelection, command, prepare_behaviour_dump_dir, t, }; /// Represents flag values in `String` form with whitespace delimiter to pass it to the compiler @@ -636,6 +636,15 @@ impl Builder<'_> { for (restricted_mode, name, values) in EXTRA_CHECK_CFGS { if restricted_mode.is_none() || *restricted_mode == Some(mode) { rustflags.arg(&check_cfg_arg(name, *values)); + + if *name == "bootstrap" { + // Cargo doesn't pass RUSTFLAGS to proc_macros: + // https://github.com/rust-lang/cargo/issues/4423 + // Thus, if we are on stage 0, we explicitly set `--cfg=bootstrap`. + // We also declare that the flag is expected, which we need to do to not + // get warnings about it being unexpected. + hostflags.arg(check_cfg_arg(name, *values)); + } } } @@ -645,13 +654,6 @@ impl Builder<'_> { if stage == 0 { hostflags.arg("--cfg=bootstrap"); } - // Cargo doesn't pass RUSTFLAGS to proc_macros: - // https://github.com/rust-lang/cargo/issues/4423 - // Thus, if we are on stage 0, we explicitly set `--cfg=bootstrap`. - // We also declare that the flag is expected, which we need to do to not - // get warnings about it being unexpected. - hostflags.arg("-Zunstable-options"); - hostflags.arg("--check-cfg=cfg(bootstrap)"); // FIXME: It might be better to use the same value for both `RUSTFLAGS` and `RUSTDOCFLAGS`, // but this breaks CI. At the very least, stage0 `rustdoc` needs `--cfg bootstrap`. See @@ -920,13 +922,46 @@ impl Builder<'_> { hostflags.arg(format!("-Ctarget-feature={sign}crt-static")); } - if let Some(map_to) = self.build.debuginfo_map_to(GitRepo::Rustc) { - let map = format!("{}={}", self.build.src.display(), map_to); - cargo.env("RUSTC_DEBUGINFO_MAP", map); + // `rustc` needs to know the remapping scheme, in order to know how to reverse it (unremap) + // later. Two env vars are set and made available to the compiler + // + // - `CFG_VIRTUAL_RUST_SOURCE_BASE_DIR`: `rust-src` remap scheme (`NonCompiler`) + // - `CFG_VIRTUAL_RUSTC_DEV_SOURCE_BASE_DIR`: `rustc-dev` remap scheme (`Compiler`) + // + // Keep this scheme in sync with `rustc_metadata::rmeta::decoder`'s + // `try_to_translate_virtual_to_real`. + // + // `RUSTC_DEBUGINFO_MAP` is used to pass through to the underlying rustc + // `--remap-path-prefix`. + match mode { + Mode::Rustc | Mode::Codegen => { + if let Some(ref map_to) = + self.build.debuginfo_map_to(GitRepo::Rustc, RemapScheme::NonCompiler) + { + cargo.env("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR", map_to); + } - // `rustc` needs to know the virtual `/rustc/$hash` we're mapping to, - // in order to opportunistically reverse it later. - cargo.env("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR", map_to); + if let Some(ref map_to) = + self.build.debuginfo_map_to(GitRepo::Rustc, RemapScheme::Compiler) + { + // When building compiler sources, we want to apply the compiler remap scheme. + cargo.env( + "RUSTC_DEBUGINFO_MAP", + format!("{}={}", self.build.src.display(), map_to), + ); + cargo.env("CFG_VIRTUAL_RUSTC_DEV_SOURCE_BASE_DIR", map_to); + } + } + Mode::Std | Mode::ToolBootstrap | Mode::ToolRustc | Mode::ToolStd => { + if let Some(ref map_to) = + self.build.debuginfo_map_to(GitRepo::Rustc, RemapScheme::NonCompiler) + { + cargo.env( + "RUSTC_DEBUGINFO_MAP", + format!("{}={}", self.build.src.display(), map_to), + ); + } + } } if self.config.rust_remap_debuginfo { diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index af4ec679d08..59ae303e21e 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -34,6 +34,8 @@ pub struct Finder { // Targets can be removed from this list once they are present in the stage0 compiler (usually by updating the beta compiler of the bootstrap). const STAGE0_MISSING_TARGETS: &[&str] = &[ // just a dummy comment so the list doesn't get onelined + "loongarch32-unknown-none", + "loongarch32-unknown-none-softfloat", ]; /// Minimum version threshold for libstdc++ required when using prebuilt LLVM diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 07772b8932d..7db57889009 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -81,7 +81,10 @@ const LLD_FILE_NAMES: &[&str] = &["ld.lld", "ld64.lld", "lld-link", "wasm-ld"]; /// (Mode restriction, config name, config values (if any)) #[expect(clippy::type_complexity)] // It's fine for hard-coded list and type is explained above. const EXTRA_CHECK_CFGS: &[(Option<Mode>, &str, Option<&[&'static str]>)] = &[ - (None, "bootstrap", None), + (Some(Mode::Rustc), "bootstrap", None), + (Some(Mode::Codegen), "bootstrap", None), + (Some(Mode::ToolRustc), "bootstrap", None), + (Some(Mode::ToolStd), "bootstrap", None), (Some(Mode::Rustc), "llvm_enzyme", None), (Some(Mode::Codegen), "llvm_enzyme", None), (Some(Mode::ToolRustc), "llvm_enzyme", None), @@ -272,6 +275,16 @@ impl Mode { } } +/// When `rust.rust_remap_debuginfo` is requested, the compiler needs to know how to +/// opportunistically unremap compiler vs non-compiler sources. We use two schemes, +/// [`RemapScheme::Compiler`] and [`RemapScheme::NonCompiler`]. +pub enum RemapScheme { + /// The [`RemapScheme::Compiler`] scheme will remap to `/rustc-dev/{hash}`. + Compiler, + /// The [`RemapScheme::NonCompiler`] scheme will remap to `/rustc/{hash}`. + NonCompiler, +} + #[derive(Debug, Hash, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum CLang { C, @@ -1217,7 +1230,7 @@ Executed at: {executed_at}"#, }) } - fn debuginfo_map_to(&self, which: GitRepo) -> Option<String> { + fn debuginfo_map_to(&self, which: GitRepo, remap_scheme: RemapScheme) -> Option<String> { if !self.config.rust_remap_debuginfo { return None; } @@ -1225,7 +1238,24 @@ Executed at: {executed_at}"#, match which { GitRepo::Rustc => { let sha = self.rust_sha().unwrap_or(&self.version); - Some(format!("/rustc/{sha}")) + + match remap_scheme { + RemapScheme::Compiler => { + // For compiler sources, remap via `/rustc-dev/{sha}` to allow + // distinguishing between compiler sources vs library sources, since + // `rustc-dev` dist component places them under + // `$sysroot/lib/rustlib/rustc-src/rust` as opposed to `rust-src`'s + // `$sysroot/lib/rustlib/src/rust`. + // + // Keep this scheme in sync with `rustc_metadata::rmeta::decoder`'s + // `try_to_translate_virtual_to_real`. + Some(format!("/rustc-dev/{sha}")) + } + RemapScheme::NonCompiler => { + // For non-compiler sources, use `/rustc/{sha}` remapping scheme. + Some(format!("/rustc/{sha}")) + } + } } GitRepo::Llvm => Some(String::from("/rustc/llvm")), } @@ -1292,7 +1322,7 @@ Executed at: {executed_at}"#, base.push("-fno-omit-frame-pointer".into()); } - if let Some(map_to) = self.debuginfo_map_to(which) { + if let Some(map_to) = self.debuginfo_map_to(which, RemapScheme::NonCompiler) { let map = format!("{}={}", self.src.display(), map_to); let cc = self.cc(target); if cc.ends_with("clang") || cc.ends_with("gcc") { diff --git a/src/ci/citool/src/jobs.rs b/src/ci/citool/src/jobs.rs index 2884ae08ea8..81e002edb15 100644 --- a/src/ci/citool/src/jobs.rs +++ b/src/ci/citool/src/jobs.rs @@ -161,6 +161,8 @@ pub enum RunType { TryJob { job_patterns: Option<Vec<String>> }, /// Merge attempt workflow AutoJob, + /// Fake job only used for sharing Github Actions cache. + MasterJob, } /// Maximum number of custom try jobs that can be requested in a single @@ -210,6 +212,7 @@ fn calculate_jobs( (jobs, "try", &db.envs.try_env) } RunType::AutoJob => (db.auto_jobs.clone(), "auto", &db.envs.auto_env), + RunType::MasterJob => return Ok(vec![]), }; let jobs = substitute_github_vars(jobs.clone()) .context("Failed to substitute GitHub context variables in jobs")?; @@ -262,7 +265,7 @@ pub fn calculate_job_matrix( eprintln!("Run type: {run_type:?}"); let jobs = calculate_jobs(&run_type, &db, channel)?; - if jobs.is_empty() { + if jobs.is_empty() && !matches!(run_type, RunType::MasterJob) { return Err(anyhow::anyhow!("Computed job list is empty")); } @@ -270,6 +273,7 @@ pub fn calculate_job_matrix( RunType::PullRequest => "pr", RunType::TryJob { .. } => "try", RunType::AutoJob => "auto", + RunType::MasterJob => "master", }; eprintln!("Output"); diff --git a/src/ci/citool/src/main.rs b/src/ci/citool/src/main.rs index bb73a5ef909..fe1b36673a1 100644 --- a/src/ci/citool/src/main.rs +++ b/src/ci/citool/src/main.rs @@ -47,6 +47,7 @@ impl GitHubContext { Some(RunType::TryJob { job_patterns: patterns }) } ("push", "refs/heads/auto") => Some(RunType::AutoJob), + ("push", "refs/heads/master") => Some(RunType::MasterJob), _ => None, } } diff --git a/src/ci/citool/tests/jobs.rs b/src/ci/citool/tests/jobs.rs index fcdca899e06..83f2fc0ed1f 100644 --- a/src/ci/citool/tests/jobs.rs +++ b/src/ci/citool/tests/jobs.rs @@ -45,17 +45,31 @@ fn pr_jobs() { "#); } +#[test] +fn master_jobs() { + let stdout = get_matrix("push", "commit", "refs/heads/master"); + insta::assert_snapshot!(stdout, @r#" + jobs=[] + run_type=master + "#); +} + fn get_matrix(event_name: &str, commit_msg: &str, branch_ref: &str) -> String { - let output = Command::new("cargo") - .args(["run", "-q", "calculate-job-matrix", "--jobs-file", TEST_JOBS_YML_PATH]) + let path = std::env::var("PATH"); + let mut cmd = Command::new("cargo"); + cmd.args(["run", "-q", "calculate-job-matrix", "--jobs-file", TEST_JOBS_YML_PATH]) + .env_clear() .env("GITHUB_EVENT_NAME", event_name) .env("COMMIT_MESSAGE", commit_msg) .env("GITHUB_REF", branch_ref) .env("GITHUB_RUN_ID", "123") .env("GITHUB_RUN_ATTEMPT", "1") - .stdout(Stdio::piped()) - .output() - .expect("Failed to execute command"); + .stdout(Stdio::piped()); + if let Ok(path) = path { + cmd.env("PATH", path); + } + + let output = cmd.output().expect("Failed to execute command"); let stdout = String::from_utf8(output.stdout).unwrap(); let stderr = String::from_utf8(output.stderr).unwrap(); diff --git a/src/ci/docker/scripts/rfl-build.sh b/src/ci/docker/scripts/rfl-build.sh index fa18f67583f..c5992891398 100755 --- a/src/ci/docker/scripts/rfl-build.sh +++ b/src/ci/docker/scripts/rfl-build.sh @@ -2,8 +2,7 @@ set -euo pipefail -# https://github.com/Rust-for-Linux/linux/issues/1163 -LINUX_VERSION=3ca02fc80cc4fdac63aaa6796642f1e07be591d6 +LINUX_VERSION=v6.16-rc1 # Build rustc, rustdoc, cargo, clippy-driver and rustfmt ../x.py build --stage 2 library rustdoc clippy rustfmt diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index b6b2792d0ec..43c77d1ddf7 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -128,6 +128,11 @@ pr: <<: *job-linux-4c - name: mingw-check-tidy continue_on_error: true + free_disk: false + env: + # This submodule is expensive to checkout, and it should not be needed for + # tidy. This speeds up the PR CI job by ~1 minute. + SKIP_SUBMODULES: src/gcc <<: *job-linux-4c - name: x86_64-gnu-llvm-19 env: @@ -305,6 +310,10 @@ auto: - name: mingw-check-2 <<: *job-linux-4c + - name: mingw-check-tidy + free_disk: false + <<: *job-linux-4c + - name: test-various <<: *job-linux-4c diff --git a/src/ci/run.sh b/src/ci/run.sh index b6143af632d..e0c00dc1925 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -186,9 +186,11 @@ else # Download GCC from CI on test builders RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set gcc.download-ci-gcc=true" - if [ "$NO_DOWNLOAD_CI_RUSTC" = "" ]; then - RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.download-rustc=if-unchanged" - fi + # download-rustc seems to be broken on CI after the stage0 redesign + # Disable it until these issues are debugged and resolved +# if [ "$NO_DOWNLOAD_CI_RUSTC" = "" ]; then +# RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.download-rustc=if-unchanged" +# fi fi if [ "$ENABLE_GCC_CODEGEN" = "1" ]; then diff --git a/src/ci/scripts/checkout-submodules.sh b/src/ci/scripts/checkout-submodules.sh index 5bb343241ae..3b646587dc2 100755 --- a/src/ci/scripts/checkout-submodules.sh +++ b/src/ci/scripts/checkout-submodules.sh @@ -55,7 +55,11 @@ for i in ${!modules[@]}; do bg_pids[${i}]=$! continue else + # Submodule paths contained in SKIP_SUBMODULES (comma-separated list) will not be + # checked out. + if [ -z "${SKIP_SUBMODULES:-}" ] || [[ ! ",$SKIP_SUBMODULES," = *",$module,"* ]]; then use_git="$use_git $module" + fi fi done retry sh -c "git submodule deinit -f $use_git && \ diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index 8b48bd518bd..c8721bb3600 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -c68032fd4c442d275f4daa571ba19c076106b490 +c31cccb7b5cc098b1a8c1794ed38d7fdbec0ccb0 diff --git a/src/doc/rustc-dev-guide/src/SUMMARY.md b/src/doc/rustc-dev-guide/src/SUMMARY.md index a7b76233d19..cba8eac617d 100644 --- a/src/doc/rustc-dev-guide/src/SUMMARY.md +++ b/src/doc/rustc-dev-guide/src/SUMMARY.md @@ -63,10 +63,8 @@ - [Notification groups](notification-groups/about.md) - [Apple](notification-groups/apple.md) - [ARM](notification-groups/arm.md) - - [Cleanup Crew](notification-groups/cleanup-crew.md) - [Emscripten](notification-groups/emscripten.md) - [Fuchsia](notification-groups/fuchsia.md) - - [LLVM](notification-groups/llvm.md) - [RISC-V](notification-groups/risc-v.md) - [Rust for Linux](notification-groups/rust-for-linux.md) - [WASI](notification-groups/wasi.md) @@ -101,6 +99,8 @@ - [Rustdoc internals](./rustdoc-internals.md) - [Search](./rustdoc-internals/search.md) - [The `rustdoc` test suite](./rustdoc-internals/rustdoc-test-suite.md) + - [The `rustdoc-gui` test suite](./rustdoc-internals/rustdoc-gui-test-suite.md) + - [The `rustdoc-json` test suite](./rustdoc-internals/rustdoc-json-test-suite.md) - [Autodiff internals](./autodiff/internals.md) - [Installation](./autodiff/installation.md) - [How to debug](./autodiff/debugging.md) diff --git a/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md b/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md index 35d33ebdb0e..ed267850401 100644 --- a/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md +++ b/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md @@ -55,7 +55,7 @@ Bootstrap will conditionally build `tracing` support and enable `tracing` output Example basic usage[^just-trace]: -[^just-trace]: It is not recommend to use *just* `BOOTSTRAP_TRACING=TRACE` because that will dump *everything* at `TRACE` level, including logs intentionally gated behind custom targets as they are too verbose even for `TRACE` level by default. +[^just-trace]: It is not recommended to use *just* `BOOTSTRAP_TRACING=TRACE` because that will dump *everything* at `TRACE` level, including logs intentionally gated behind custom targets as they are too verbose even for `TRACE` level by default. ```bash $ BOOTSTRAP_TRACING=bootstrap=TRACE ./x build library --stage 1 diff --git a/src/doc/rustc-dev-guide/src/getting-started.md b/src/doc/rustc-dev-guide/src/getting-started.md index 435202ca6c8..d6c5c3ac852 100644 --- a/src/doc/rustc-dev-guide/src/getting-started.md +++ b/src/doc/rustc-dev-guide/src/getting-started.md @@ -158,9 +158,6 @@ feel comfortable jumping straight into the large `rust-lang/rust` codebase. The following tasks are doable without much background knowledge but are incredibly helpful: -- [Cleanup crew][iceb]: find minimal reproductions of ICEs, bisect - regressions, etc. This is a way of helping that saves a ton of time for - others to fix an error later. - [Writing documentation][wd]: if you are feeling a bit more intrepid, you could try to read a part of the code and write doc comments for it. This will help you to learn some part of the compiler while also producing a useful artifact! @@ -179,7 +176,6 @@ incredibly helpful: [users]: https://users.rust-lang.org/ [so]: http://stackoverflow.com/questions/tagged/rust [community-library]: https://github.com/rust-lang/rfcs/labels/A-community-library -[iceb]: ./notification-groups/cleanup-crew.md [wd]: ./contributing.md#writing-documentation [wg]: https://rust-lang.github.io/compiler-team/working-groups/ [triage]: ./contributing.md#issue-triage diff --git a/src/doc/rustc-dev-guide/src/notification-groups/about.md b/src/doc/rustc-dev-guide/src/notification-groups/about.md index af305f0103a..d75891ecf7b 100644 --- a/src/doc/rustc-dev-guide/src/notification-groups/about.md +++ b/src/doc/rustc-dev-guide/src/notification-groups/about.md @@ -21,9 +21,7 @@ search for existing issues that haven't been claimed yet. Here's the list of the notification groups: - [Apple](./apple.md) - [ARM](./arm.md) -- [Cleanup Crew](./cleanup-crew.md) - [Emscripten](./emscripten.md) -- [LLVM Icebreakers](./llvm.md) - [RISC-V](./risc-v.md) - [WASI](./wasi.md) - [WebAssembly](./wasm.md) @@ -64,9 +62,7 @@ Example PRs: * [Example of adding yourself to the Apple group.](https://github.com/rust-lang/team/pull/1434) * [Example of adding yourself to the ARM group.](https://github.com/rust-lang/team/pull/358) -* [Example of adding yourself to the Cleanup Crew.](https://github.com/rust-lang/team/pull/221) * [Example of adding yourself to the Emscripten group.](https://github.com/rust-lang/team/pull/1579) -* [Example of adding yourself to the LLVM group.](https://github.com/rust-lang/team/pull/140) * [Example of adding yourself to the RISC-V group.](https://github.com/rust-lang/team/pull/394) * [Example of adding yourself to the WASI group.](https://github.com/rust-lang/team/pull/1580) * [Example of adding yourself to the WebAssembly group.](https://github.com/rust-lang/team/pull/1581) @@ -81,9 +77,7 @@ group. For example: ```text @rustbot ping apple @rustbot ping arm -@rustbot ping cleanup-crew @rustbot ping emscripten -@rustbot ping icebreakers-llvm @rustbot ping risc-v @rustbot ping wasi @rustbot ping wasm @@ -92,12 +86,12 @@ group. For example: To make some commands shorter and easier to remember, there are aliases, defined in the [`triagebot.toml`] file. For example, all of these commands -are equivalent and will ping the Cleanup Crew: +are equivalent and will ping the Apple group: ```text -@rustbot ping cleanup -@rustbot ping bisect -@rustbot ping reduce +@rustbot ping apple +@rustbot ping macos +@rustbot ping ios ``` Keep in mind that these aliases are meant to make humans' life easier. diff --git a/src/doc/rustc-dev-guide/src/notification-groups/cleanup-crew.md b/src/doc/rustc-dev-guide/src/notification-groups/cleanup-crew.md deleted file mode 100644 index 9cf4e512cbd..00000000000 --- a/src/doc/rustc-dev-guide/src/notification-groups/cleanup-crew.md +++ /dev/null @@ -1,90 +0,0 @@ -# Cleanup Crew - -**Github Label:** [ICEBreaker-Cleanup-Crew] <br> -**Ping command:** `@rustbot ping cleanup-crew` - -[ICEBreaker-Cleanup-Crew]: https://github.com/rust-lang/rust/labels/ICEBreaker-Cleanup-Crew - -The "Cleanup Crew" are focused on improving bug reports. Specifically, -the goal is to try to ensure that every bug report has all the -information that will be needed for someone to fix it: - -* a minimal, standalone example that shows the problem -* links to duplicates or related bugs -* if the bug is a regression (something that used to work, but no longer does), - then a bisection to the PR or nightly that caused the regression - -This kind of cleanup is invaluable in getting bugs fixed. Better -still, it can be done by anybody who knows Rust, without any -particularly deep knowledge of the compiler. - -Let's look a bit at the workflow for doing "cleanup crew" actions. - -## Finding a minimal, standalone example - -Here the ultimate goal is to produce an example that reproduces the same -problem but without relying on any external crates. Such a test ought to contain -as little code as possible, as well. This will make it much easier to isolate the problem. - -However, even if the "ultimate minimal test" cannot be achieved, it's -still useful to post incremental minimizations. For example, if you -can eliminate some of the external dependencies, that is helpful, and -so forth. - -It's particularly useful to reduce to an example that works -in the [Rust playground](https://play.rust-lang.org/), rather than -requiring people to checkout a cargo build. - -There are many resources for how to produce minimized test cases. Here -are a few: - -* The [rust-reduce](https://github.com/jethrogb/rust-reduce) tool can try to reduce - code automatically. - * The [C-reduce](https://github.com/csmith-project/creduce) tool also works - on Rust code, though it requires that you start from a single - file. (A post explaining how to do it can be found [here](https://insaneinside.net/2017/09/12/whole-crate-bug-reduction-with-creduce.html).) -* pnkfelix's [Rust Bug Minimization Patterns] blog post - * This post focuses on "heavy bore" techniques, where you are - starting with a large, complex cargo project that you wish to - narrow down to something standalone. - -[Rust Bug Minimization Patterns]: http://blog.pnkfx.org/blog/2019/11/18/rust-bug-minimization-patterns/ - -## Links to duplicate or related bugs - -If you are on the "Cleanup Crew", you will sometimes see multiple bug -reports that seem very similar. You can link one to the other just by -mentioning the other bug number in a Github comment. Sometimes it is -useful to close duplicate bugs. But if you do so, you should always -copy any test case from the bug you are closing to the other bug that -remains open, as sometimes duplicate-looking bugs will expose -different facets of the same problem. - -## Bisecting regressions - -For regressions (something that used to work, but no longer does), it -is super useful if we can figure out precisely when the code stopped -working. The gold standard is to be able to identify the precise -**PR** that broke the code, so we can ping the author, but even -narrowing it down to a nightly build is helpful, especially as that -then gives us a range of PRs. (One other challenge is that we -sometimes land "rollup" PRs, which combine multiple PRs into one.) - -### cargo-bisect-rustc - -To help in figuring out the cause of a regression we have a tool -called [cargo-bisect-rustc]. It will automatically download and test -various builds of rustc. For recent regressions, it is even able to -use the builds from our CI to track down the regression to a specific -PR; for older regressions, it will simply identify a nightly. - -To learn to use [cargo-bisect-rustc], check out [this blog post][learn], which -gives a quick introduction to how it works. Additionally, there is a [Guide] -which goes into more detail on how to use it. You can also ask questions at -the Zulip stream [`#t-compiler/cargo-bisect-rustc`][zcbr], or help in -improving the tool. - -[cargo-bisect-rustc]: https://github.com/rust-lang/cargo-bisect-rustc/ -[learn]: https://blog.rust-lang.org/inside-rust/2019/12/18/bisecting-rust-compiler.html -[zcbr]: https://rust-lang.zulipchat.com/#narrow/stream/217417-t-compiler.2Fcargo-bisect-rustc -[Guide]: https://rust-lang.github.io/cargo-bisect-rustc/ diff --git a/src/doc/rustc-dev-guide/src/notification-groups/llvm.md b/src/doc/rustc-dev-guide/src/notification-groups/llvm.md deleted file mode 100644 index 9d008728543..00000000000 --- a/src/doc/rustc-dev-guide/src/notification-groups/llvm.md +++ /dev/null @@ -1,38 +0,0 @@ -# LLVM Icebreakers Notification group - -**Github Label:** [A-LLVM] <br> -**Ping command:** `@rustbot ping icebreakers-llvm` - -[A-LLVM]: https://github.com/rust-lang/rust/labels/A-LLVM - -*Note*: this notification group is *not* the same as the LLVM working group -(WG-llvm). - -The "LLVM Icebreakers Notification Group" are focused on bugs that center around -LLVM. These bugs often arise because of LLVM optimizations gone awry, or as the -result of an LLVM upgrade. The goal here is: - -- to determine whether the bug is a result of us generating invalid LLVM IR, - or LLVM misoptimizing; -- if the former, to fix our IR; -- if the latter, to try and file a bug on LLVM (or identify an existing bug). - -The group may also be asked to weigh in on other sorts of LLVM-focused -questions. - -## Helpful tips and options - -The ["Debugging LLVM"][d] section of the -rustc-dev-guide gives a step-by-step process for how to help debug bugs -caused by LLVM. In particular, it discusses how to emit LLVM IR, run -the LLVM IR optimization pipelines, and so forth. You may also find -it useful to look at the various codegen options listed under `-C help` -and the internal options under `-Z help` -- there are a number that -pertain to LLVM (just search for LLVM). - -[d]: ../backend/debugging.md - -## If you do narrow to an LLVM bug - -The ["Debugging LLVM"][d] section also describes what to do once -you've identified the bug. diff --git a/src/doc/rustc-dev-guide/src/rustdoc-internals.md b/src/doc/rustc-dev-guide/src/rustdoc-internals.md index bc91c62d873..0234d4a920e 100644 --- a/src/doc/rustc-dev-guide/src/rustdoc-internals.md +++ b/src/doc/rustc-dev-guide/src/rustdoc-internals.md @@ -270,35 +270,6 @@ in `test.rs` is the function `make_test`, which is where hand-written Some extra reading about `make_test` can be found [here](https://quietmisdreavus.net/code/2018/02/23/how-the-doctests-get-made/). -## Dotting i's And Crossing t's - -So that's `rustdoc`'s code in a nutshell, but there's more things in the -compiler that deal with it. Since we have the full `compiletest` suite at hand, -there's a set of tests in `tests/rustdoc` that make sure the final `HTML` is -what we expect in various situations. These tests also use a supplementary -script, `src/etc/htmldocck.py`, that allows it to look through the final `HTML` -using `XPath` notation to get a precise look at the output. The full -description of all the commands available to `rustdoc` tests (e.g. [`@has`] and -[`@matches`]) is in [`htmldocck.py`]. - -To use multiple crates in a `rustdoc` test, add `//@ aux-build:filename.rs` -to the top of the test file. `filename.rs` should be placed in an `auxiliary` -directory relative to the test file with the comment. If you need to build -docs for the auxiliary file, use `//@ build-aux-docs`. - -In addition, there are separate tests for the search index and `rustdoc`'s -ability to query it. The files in `tests/rustdoc-js` each contain a -different search query and the expected results, broken out by search tab. -These files are processed by a script in `src/tools/rustdoc-js` and the `Node.js` -runtime. These tests don't have as thorough of a writeup, but a broad example -that features results in all tabs can be found in `basic.js`. The basic idea is -that you match a given `QUERY` with a set of `EXPECTED` results, complete with -the full item path of each item. - -[`@has`]: https://github.com/rust-lang/rust/blob/master/src/etc/htmldocck.py#L39 -[`@matches`]: https://github.com/rust-lang/rust/blob/master/src/etc/htmldocck.py#L44 -[`htmldocck.py`]: https://github.com/rust-lang/rust/blob/master/src/etc/htmldocck.py - ## Testing Locally Some features of the generated `HTML` documentation might require local diff --git a/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-gui-test-suite.md b/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-gui-test-suite.md new file mode 100644 index 00000000000..e155f960e3d --- /dev/null +++ b/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-gui-test-suite.md @@ -0,0 +1,14 @@ +# The `rustdoc-gui` test suite + +> **FIXME**: This section is a stub. Please help us flesh it out! + +This page is about the test suite named `rustdoc-gui` used to test the "GUI" of `rustdoc` (i.e., the HTML/JS/CSS as rendered in a browser). +For other rustdoc-specific test suites, see [Rustdoc test suites]. + +These use a NodeJS-based tool called [`browser-UI-test`] that uses [puppeteer] to run tests in a headless browser and check rendering and interactivity. For information on how to write this form of test, see [`tests/rustdoc-gui/README.md`][rustdoc-gui-readme] as well as [the description of the `.goml` format][goml-script] + +[Rustdoc test suites]: ../tests/compiletest.md#rustdoc-test-suites +[`browser-UI-test`]: https://github.com/GuillaumeGomez/browser-UI-test/ +[puppeteer]: https://pptr.dev/ +[rustdoc-gui-readme]: https://github.com/rust-lang/rust/blob/master/tests/rustdoc-gui/README.md +[goml-script]: https://github.com/GuillaumeGomez/browser-UI-test/blob/master/goml-script.md diff --git a/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-json-test-suite.md b/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-json-test-suite.md new file mode 100644 index 00000000000..e08f7709506 --- /dev/null +++ b/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-json-test-suite.md @@ -0,0 +1,3 @@ +# The `rustdoc-json` test suite + +> **FIXME**: This section is a stub. It will be populated by [PR #2422](https://github.com/rust-lang/rustc-dev-guide/pull/2422/). diff --git a/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-test-suite.md b/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-test-suite.md index bad7ac19da2..b05318ce9e6 100644 --- a/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-test-suite.md +++ b/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-test-suite.md @@ -1,112 +1,191 @@ # The `rustdoc` test suite -This page is specifically about the test suite named `rustdoc`. -For other test suites used for testing rustdoc, see [Rustdoc tests](../rustdoc.md#tests). +This page is about the test suite named `rustdoc` used to test the HTML output of `rustdoc`. +For other rustdoc-specific test suites, see [Rustdoc test suites]. -The `rustdoc` test suite is specifically used to test the HTML output of rustdoc. +Each test file in this test suite is simply a Rust source file `file.rs` sprinkled with +so-called *directives* located inside normal Rust code comments. +These come in two flavors: *Compiletest* and *HtmlDocCk*. -This is achieved by means of `htmldocck.py`, a custom checker script that leverages [XPath]. +To learn more about the former, read [Compiletest directives]. +For the latter, continue reading. -[XPath]: https://en.wikipedia.org/wiki/XPath +Internally, [`compiletest`] invokes the supplementary checker script [`htmldocck.py`]. -## Directives -Directives to htmldocck are similar to those given to `compiletest` in that they take the form of `//@` comments. +[Rustdoc test suites]: ../tests/compiletest.md#rustdoc-test-suites +[`compiletest`]: ../tests/compiletest.md +[`htmldocck.py`]: https://github.com/rust-lang/rust/blob/master/src/etc/htmldocck.py -In addition to the directives listed here, -`rustdoc` tests also support most -[compiletest directives](../tests/directives.html). +## HtmlDocCk Directives -All `PATH`s in directives are relative to the rustdoc output directory (`build/TARGET/test/rustdoc/TESTNAME`), -so it is conventional to use a `#![crate_name = "foo"]` attribute to avoid -having to write a long crate name multiple times. -To avoid repetition, `-` can be used in any `PATH` argument to re-use the previous `PATH` argument. +Directives to HtmlDocCk are assertions that place constraints on the generated HTML. +They look similar to those given to `compiletest` in that they take the form of `//@` comments +but ultimately, they are completey distinct and processed by different programs. -All arguments take the form of quoted strings -(both single and double quotes are supported), -with the exception of `COUNT` and the special `-` form of `PATH`. +[XPath] is used to query parts of the HTML document tree. + +**Introductory example**: + +```rust,ignore (illustrative) +//@ has file/type.Alias.html +//@ has - '//*[@class="rust item-decl"]//code' 'type Alias = Option<i32>;' +pub type Alias = Option<i32>; +``` + +Here, we check that documentation generated for crate `file` contains a page for the +public type alias `Alias` where the code block that is found at the top contains the +expected rendering of the item. The `//*[@class="rust item-decl"]//code` is an XPath +expression. -Directives are assertions that place constraints on the generated HTML. +Conventionally, you place these directives directly above the thing they are meant to test. +Technically speaking however, they don't need to be as HtmlDocCk only looks for the directives. -All directives (except `files`) can be negated by putting a `!` in front of their name. +All directives take a `PATH` argument. +To avoid repetition, `-` can be passed to it to re-use the previous `PATH` argument. +Since the path contains the name of the crate, it is conventional to add a +`#![crate_name = "foo"]` attribute to the crate root to shorten the resulting path. + +All arguments take the form of shell-style (single or double) quoted strings, +with the exception of `COUNT` and the special `-` form of `PATH`. + +All directives (except `files`) can be *negated* by putting a `!` in front of their name. +Before you add negated directives, please read about [their caveats](#caveats). Similar to shell commands, directives can extend across multiple lines if their last char is `\`. In this case, the start of the next line should be `//`, with no `@`. -For example, `//@ !has 'foo/struct.Bar.html'` checks that crate `foo` does not have a page for a struct named `Bar` in the crate root. +Use the special string `{{channel}}` in XPaths, `PATTERN` arguments and [snapshot files](#snapshot) +if you'd like to refer to the URL `https://doc.rust-lang.org/CHANNEL` where `CHANNEL` refers to the +current release channel (e.g, `stable` or `nightly`). + +Listed below are all possible directives: + +[XPath]: https://en.wikipedia.org/wiki/XPath ### `has` -Usage 1: `//@ has PATH` -Usage 2: `//@ has PATH XPATH PATTERN` +> Usage 1: `//@ has PATH` -In the first form, `has` checks that a given file exists. +Check that the file given by `PATH` exists. -In the second form, `has` is an alias for `matches`, -except `PATTERN` is a whitespace-normalized[^1] string instead of a regex. +> Usage 2: `//@ has PATH XPATH PATTERN` -### `matches` +Checks that the text of each element / attribute / text selected by `XPATH` in the +whitespace-normalized[^1] file given by `PATH` matches the +(also whitespace-normalized) string `PATTERN`. + +**Tip**: If you'd like to avoid whitespace normalization and/or if you'd like to match with a regex, +use `matches` instead. -Usage: `//@ matches PATH XPATH PATTERN` +### `hasraw` -Checks that the text of each element selected by `XPATH` in `PATH` matches the python-flavored regex `PATTERN`. +> Usage: `//@ hasraw PATH PATTERN` -### `matchesraw` +Checks that the contents of the whitespace-normalized[^1] file given by `PATH` +matches the (also whitespace-normalized) string `PATTERN`. -Usage: `//@ matchesraw PATH PATTERN` +**Tip**: If you'd like to avoid whitespace normalization and / or if you'd like to match with a +regex, use `matchesraw` instead. -Checks that the contents of the file `PATH` matches the regex `PATTERN`. +### `matches` -### `hasraw` +> Usage: `//@ matches PATH XPATH PATTERN` -Usage: `//@ hasraw PATH PATTERN` +Checks that the text of each element / attribute / text selected by `XPATH` in the +file given by `PATH` matches the Python-flavored[^2] regex `PATTERN`. -Same as `matchesraw`, except `PATTERN` is a whitespace-normalized[^1] string instead of a regex. +### `matchesraw` + +> Usage: `//@ matchesraw PATH PATTERN` + +Checks that the contents of the file given by `PATH` matches the +Python-flavored[^2] regex `PATTERN`. ### `count` -Usage: `//@ count PATH XPATH COUNT` +> Usage: `//@ count PATH XPATH COUNT` -Checks that there are exactly `COUNT` matches for `XPATH` within the file `PATH`. +Checks that there are exactly `COUNT` matches for `XPATH` within the file given by `PATH`. ### `snapshot` -Usage: `//@ snapshot NAME PATH XPATH` +> Usage: `//@ snapshot NAME PATH XPATH` -Creates a snapshot test named NAME. -A snapshot test captures a subtree of the DOM, at the location -determined by the XPath, and compares it to a pre-recorded value -in a file. The file's name is the test's name with the `.rs` extension -replaced with `.NAME.html`, where NAME is the snapshot's name. +Checks that the element / text selected by `XPATH` in the file given by `PATH` matches the +pre-recorded subtree or text (the "snapshot") in file `FILE_STEM.NAME.html` where `FILE_STEM` +is the file stem of the test file. -htmldocck supports the `--bless` option to accept the current subtree -as expected, saving it to the file determined by the snapshot's name. -compiletest's `--bless` flag is forwarded to htmldocck. +Pass the `--bless` option to `compiletest` to accept the current subtree/text as expected. +This will overwrite the aforementioned file (or create it if it doesn't exist). It will +automatically normalize the channel-dependent URL `https://doc.rust-lang.org/CHANNEL` to +the special string `{{channel}}`. ### `has-dir` -Usage: `//@ has-dir PATH` +> Usage: `//@ has-dir PATH` -Checks for the existence of directory `PATH`. +Checks for the existence of the directory given by `PATH`. ### `files` -Usage: `//@ files PATH ENTRIES` +> Usage: `//@ files PATH ENTRIES` + +Checks that the directory given by `PATH` contains exactly `ENTRIES`. +`ENTRIES` is a Python-like list of strings inside a quoted string. + +**Example**: `//@ files "foo/bar" '["index.html", "sidebar-items.js"]'` + +[^1]: Whitespace normalization means that all spans of consecutive whitespace are replaced with a single space. +[^2]: They are Unicode aware (flag `UNICODE` is set), match case-sensitively and in single-line mode. + +## Compiletest Directives (Brief) + +As mentioned in the introduction, you also have access to [compiletest directives]. +Most importantly, they allow you to register auxiliary crates and +to pass flags to the `rustdoc` binary under test. +It's *strongly recommended* to read that chapter if you don't know anything about them yet. + +Here are some details that are relevant to this test suite specifically: -Checks that the directory `PATH` contains exactly `ENTRIES`. -`ENTRIES` is a python list of strings inside a quoted string, -as if it were to be parsed by `eval`. -(note that the list is actually parsed by `shlex.split`, -so it cannot contain arbitrary python expressions). +* While you can use both `//@ compile-flags` and `//@ doc-flags` to pass flags to `rustdoc`, + prefer to user the latter to show intent. The former is meant for `rustc`. +* Add `//@ build-aux-docs` to the test file that has auxiliary crates to not only compile the + auxiliaries with `rustc` but to also document them with `rustdoc`. -Example: `//@ files "foo/bar" '["index.html", "sidebar-items.js"]'` +## Caveats -[^1]: Whitespace normalization means that all spans of consecutive whitespace are replaced with a single space. The files themselves are also whitespace-normalized. +Testing for the absence of an element or a piece of text is quite fragile and not very future proof. + +It's not unusual that the *shape* of the generated HTML document tree changes from time to time. +This includes for example renamings of CSS classes. + +Whenever that happens, *positive* checks will either continue to match the intended element / +attribute / text (if their XPath expression is general / loose enough) and +thus continue to test the correct thing or they won't in which case they would fail thereby +forcing the author of the change to look at them. + +Compare that to *negative* checks (e.g., `//@ !has PATH XPATH PATTERN`) which won't fail if their +XPath expression "no longer" matches. The author who changed "the shape" thus won't get notified and +as a result someone else can unintentionally reintroduce `PATTERN` into the generated docs without +the original negative check failing. + +**Note**: Please avoid the use of *negated* checks! + +**Tip**: If you can't avoid it, please **always** pair it with an analogous positive check in the +immediate vicinity, so people changing "the shape" have a chance to notice and to update the +negated check! ## Limitations -`htmldocck.py` uses the xpath implementation from the standard library. + +HtmlDocCk uses the XPath implementation from the Python standard library. This leads to several limitations: + * All `XPATH` arguments must start with `//` due to a flaw in the implementation. * Many XPath features (functions, axies, etc.) are not supported. * Only well-formed HTML can be parsed (hopefully rustdoc doesn't output mismatched tags). +Furthmore, compiletest [revisions] are not supported. + +[revisions]: ../tests/compiletest.md#revisions +[compiletest directives]: ../tests/directives.md diff --git a/src/doc/rustc-dev-guide/src/rustdoc.md b/src/doc/rustc-dev-guide/src/rustdoc.md index de70ba63823..52ae48c3735 100644 --- a/src/doc/rustc-dev-guide/src/rustdoc.md +++ b/src/doc/rustc-dev-guide/src/rustdoc.md @@ -67,43 +67,29 @@ does is call the `main()` that's in this crate's `lib.rs`, though.) ## Code structure -* All paths in this section are relative to `src/librustdoc` in the rust-lang/rust repository. +All paths in this section are relative to `src/librustdoc/` in the rust-lang/rust repository. + * Most of the HTML printing code is in `html/format.rs` and `html/render/mod.rs`. - It's in a bunch of `fmt::Display` implementations and supplementary - functions. -* The types that got `Display` impls above are defined in `clean/mod.rs`, right - next to the custom `Clean` trait used to process them out of the rustc HIR. + It's in a bunch of functions returning `impl std::fmt::Display`. +* The data types that get rendered by the functions mentioned above are defined in `clean/types.rs`. + The functions responsible for creating them from the `HIR` and the `rustc_middle::ty` IR + live in `clean/mod.rs`. * The bits specific to using rustdoc as a test harness are in `doctest.rs`. * The Markdown renderer is loaded up in `html/markdown.rs`, including functions for extracting doctests from a given block of Markdown. * Frontend CSS and JavaScript are stored in `html/static/`. + * Re. JavaScript, type annotations are written using [TypeScript-flavored JSDoc] +comments and an external `.d.ts` file. + This way, the code itself remains plain, valid JavaScript. + We only use `tsc` as a linter. -## Tests +[TypeScript-flavored JSDoc]: https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html -* Tests on search engine and index are located in `tests/rustdoc-js` and `tests/rustdoc-js-std`. - The format is specified - [in the search guide](rustdoc-internals/search.md#testing-the-search-engine). -* Tests on the "UI" of rustdoc (the terminal output it produces when run) are in - `tests/rustdoc-ui` -* Tests on the "GUI" of rustdoc (the HTML, JS, and CSS as rendered in a browser) - are in `tests/rustdoc-gui`. These use a [NodeJS tool called - browser-UI-test](https://github.com/GuillaumeGomez/browser-UI-test/) that uses - puppeteer to run tests in a headless browser and check rendering and - interactivity. For information on how to write this form of test, - see [`tests/rustdoc-gui/README.md`][rustdoc-gui-readme] - as well as [the description of the `.goml` format][goml-script] -* Tests on the structure of rustdoc HTML output are located in `tests/rustdoc`, - where they're handled by the test runner of bootstrap and - the supplementary script `src/etc/htmldocck.py`. - [These tests have several extra directives available to them](./rustdoc-internals/rustdoc-test-suite.md). -* Additionally, JavaScript type annotations are written using [TypeScript-flavored JSDoc] - comments and an external d.ts file. The code itself is plain, valid JavaScript; we only - use tsc as a linter. +## Tests -[TypeScript-flavored JSDoc]: https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html -[rustdoc-gui-readme]: https://github.com/rust-lang/rust/blob/master/tests/rustdoc-gui/README.md -[goml-script]: https://github.com/GuillaumeGomez/browser-UI-test/blob/master/goml-script.md +`rustdoc`'s integration tests are split across several test suites. +See [Rustdoc tests suites](tests/compiletest.md#rustdoc-test-suites) for more details. ## Constraints diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md index ee06ca3b698..20dd16c81df 100644 --- a/src/doc/rustc-dev-guide/src/tests/compiletest.md +++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md @@ -56,6 +56,9 @@ incremental compilation. The various suites are defined in The following test suites are available, with links for more information: +[`tests`]: https://github.com/rust-lang/rust/blob/master/tests +[`src/tools/compiletest/src/common.rs`]: https://github.com/rust-lang/rust/tree/master/src/tools/compiletest/src/common.rs + ### Compiler-specific test suites | Test suite | Purpose | @@ -71,6 +74,7 @@ The following test suites are available, with links for more information: | [`mir-opt`](#mir-opt-tests) | Check MIR generation and optimizations | | [`coverage`](#coverage-tests) | Check coverage instrumentation | | [`coverage-run-rustdoc`](#coverage-tests) | `coverage` tests that also run instrumented doctests | +| [`crashes`](#crashes-tests) | Check that the compiler ICEs/panics/crashes on certain inputs to catch accidental fixes | ### General purpose test suite @@ -78,19 +82,23 @@ The following test suites are available, with links for more information: ### Rustdoc test suites -See [Rustdoc tests](../rustdoc.md#tests) for more details. - -| Test suite | Purpose | -|------------------|--------------------------------------------------------------------------| -| `rustdoc` | Check `rustdoc` generated files contain the expected documentation | -| `rustdoc-gui` | Check `rustdoc`'s GUI using a web browser | -| `rustdoc-js` | Check `rustdoc` search is working as expected | -| `rustdoc-js-std` | Check rustdoc search is working as expected specifically on the std docs | -| `rustdoc-json` | Check JSON output of `rustdoc` | -| `rustdoc-ui` | Check terminal output of `rustdoc` | - -[`tests`]: https://github.com/rust-lang/rust/blob/master/tests -[`src/tools/compiletest/src/common.rs`]: https://github.com/rust-lang/rust/tree/master/src/tools/compiletest/src/common.rs +| Test suite | Purpose | +|--------------------------------------|--------------------------------------------------------------------------| +| [`rustdoc`][rustdoc-html-tests] | Check HTML output of `rustdoc` | +| [`rustdoc-gui`][rustdoc-gui-tests] | Check `rustdoc`'s GUI using a web browser | +| [`rustdoc-js`][rustdoc-js-tests] | Check `rustdoc`'s search engine and index | +| [`rustdoc-js-std`][rustdoc-js-tests] | Check `rustdoc`'s search engine and index on the std library docs | +| [`rustdoc-json`][rustdoc-json-tests] | Check JSON output of `rustdoc` | +| `rustdoc-ui` | Check terminal output of `rustdoc` ([see also](ui.md)) | + +Some rustdoc-specific tests can also be found in `ui/rustdoc/`. +These check rustdoc-related or -specific lints that (also) run as part of `rustc`, not (only) `rustdoc`. +Run-make tests pertaining to rustdoc are typically named `run-make/rustdoc-*/`. + +[rustdoc-html-tests]: ../rustdoc-internals/rustdoc-test-suite.md +[rustdoc-gui-tests]: ../rustdoc-internals/rustdoc-gui-test-suite.md +[rustdoc-js-tests]: ../rustdoc-internals/search.md#testing-the-search-engine +[rustdoc-json-tests]: ../rustdoc-internals/rustdoc-json-test-suite.md ### Pretty-printer tests diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index 2dff21ed61c..f73a2811d5a 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -261,7 +261,7 @@ Consider writing the test as a proper incremental test instead. | Directive | Explanation | Supported test suites | Possible values | |-------------|--------------------------------------------------------------|------------------------------------------|---------------------------| -| `doc-flags` | Flags passed to `rustdoc` when building the test or aux file | `rustdoc`, `rustdoc-js`, `rustdoc-json` | Any valid `rustdoc` flags | +| `doc-flags` | Flags passed to `rustdoc` when building the test or aux file | `rustdoc`, `rustdoc-js`, `rustdoc-json` | Any valid `rustdoc` flags | <!-- **FIXME(rustdoc)**: what does `check-test-line-numbers-match` do? @@ -269,6 +269,17 @@ Asked in <https://rust-lang.zulipchat.com/#narrow/stream/266220-t-rustdoc/topic/What.20is.20the.20.60check-test-line-numbers-match.60.20directive.3F>. --> +#### Test-suite-specific directives + +The test suites [`rustdoc`][rustdoc-html-tests], [`rustdoc-js`/`rustdoc-js-std`][rustdoc-js-tests] +and [`rustdoc-json`][rustdoc-json-tests] each feature an additional set of directives whose basic +syntax resembles the one of compiletest directives but which are ultimately read and checked by +separate tools. For more information, please read their respective chapters as linked above. + +[rustdoc-html-tests]: ../rustdoc-internals/rustdoc-test-suite.md +[rustdoc-js-tests]: ../rustdoc-internals/search.html#testing-the-search-engine +[rustdoc-json-tests]: ../rustdoc-internals/rustdoc-json-test-suite.md + ### Pretty printing See [Pretty-printer](compiletest.md#pretty-printer-tests). diff --git a/src/doc/rustc-dev-guide/src/tests/ui.md b/src/doc/rustc-dev-guide/src/tests/ui.md index 3402838da87..25d3efdbb82 100644 --- a/src/doc/rustc-dev-guide/src/tests/ui.md +++ b/src/doc/rustc-dev-guide/src/tests/ui.md @@ -220,8 +220,12 @@ negligible (i.e. there is no semantic difference between `//~ ERROR` and `//~ERROR` although the former is more common in the codebase). `~? <diagnostic kind>` (example being `~? ERROR`) -is used to match diagnostics without line information. -These can be placed on any line in the test file, but are conventionally placed at the end. +is used to match diagnostics _without_ line info at all, +or where the line info is outside the main test file[^main test file]. +These annotations can be placed on any line in the test file. + +[^main test file]: This is a file that has the `~?` annotations, +as distinct from aux files, or sources that we have no control over. ### Error annotation examples diff --git a/src/doc/rustc-dev-guide/triagebot.toml b/src/doc/rustc-dev-guide/triagebot.toml index 978802edf3f..b3f4c2d281c 100644 --- a/src/doc/rustc-dev-guide/triagebot.toml +++ b/src/doc/rustc-dev-guide/triagebot.toml @@ -72,6 +72,23 @@ days-threshold = 7 # Documentation at: https://forge.rust-lang.org/triagebot/pr-assignment.html [assign] +# NOTE: do not add `[assign.owners]` if we still wish to keep the opt-in +# reviewer model, as `[assign.owners]` will cause triagebot auto-reviewer +# assignment to kick in. + +# Custom PR welcome message for when no auto reviewer assignment is performed +# and no explicit manual reviewer selection is made. +# Documentation at: https://forge.rust-lang.org/triagebot/pr-assignment.html#custom-welcome-messages +[assign.custom_welcome_messages] +welcome-message = "" +welcome-message-no-reviewer = """\ +Thanks for the PR. If you have write access, feel free to merge this PR if it \ +does not need reviews. You can request a review using `r? rustc-dev-guide` or \ +`r? <username>`. +""" + +# Groups for `r? <group>`. +# Documentation at: https://forge.rust-lang.org/triagebot/pr-assignment.html#usage # Keep members alphanumerically sorted. [assign.adhoc_groups] rustc-dev-guide = [ diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index e7dfaaf4fd5..e2e2ad9ac3b 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -324,6 +324,8 @@ target | std | host | notes [`i686-win7-windows-msvc`](platform-support/win7-windows-msvc.md) | ✓ | | 32-bit Windows 7 support [^x86_32-floats-return-ABI] [^win32-msvc-alignment] [`i686-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | [^x86_32-floats-return-ABI] [`loongarch64-unknown-linux-ohos`](platform-support/openharmony.md) | ✓ | | LoongArch64 OpenHarmony +[`loongarch32-unknown-none`](platform-support/loongarch-none.md) | * | LoongArch32 Bare-metal (ILP32D ABI) +[`loongarch32-unknown-none-softfloat`](platform-support/loongarch-none.md) | * | LoongArch32 Bare-metal (ILP32S ABI) [`m68k-unknown-linux-gnu`](platform-support/m68k-unknown-linux-gnu.md) | ? | | Motorola 680x0 Linux [`m68k-unknown-none-elf`](platform-support/m68k-unknown-none-elf.md) | | | Motorola 680x0 `mips-unknown-linux-gnu` | ✓ | ✓ | MIPS Linux (kernel 4.4, glibc 2.23) diff --git a/src/doc/rustc/src/platform-support/loongarch-none.md b/src/doc/rustc/src/platform-support/loongarch-none.md index a2bd6e5734c..fd90b0a2763 100644 --- a/src/doc/rustc/src/platform-support/loongarch-none.md +++ b/src/doc/rustc/src/platform-support/loongarch-none.md @@ -1,18 +1,18 @@ # `loongarch*-unknown-none*` -**Tier: 2** +Freestanding/bare-metal LoongArch binaries in ELF format: firmware, kernels, etc. -Freestanding/bare-metal LoongArch64 binaries in ELF format: firmware, kernels, etc. - -| Target | Description | -|--------|-------------| -| `loongarch64-unknown-none` | LoongArch 64-bit, LP64D ABI (freestanding, hard-float) | -| `loongarch64-unknown-none-softfloat` | LoongArch 64-bit, LP64S ABI (freestanding, soft-float) | +| Target | Description | Tier | +|--------|-------------|------| +| `loongarch32-unknown-none` | LoongArch 32-bit, ILP32D ABI (freestanding, hard-float) | Tier 3 | +| `loongarch32-unknown-none-softfloat` | LoongArch 32-bit, ILP32S ABI (freestanding, soft-float) | Tier 3 | +| `loongarch64-unknown-none` | LoongArch 64-bit, LP64D ABI (freestanding, hard-float) | Tier 2 | +| `loongarch64-unknown-none-softfloat` | LoongArch 64-bit, LP64S ABI (freestanding, soft-float) | Tier 2 | ## Target maintainers -[@heiher](https://github.com/heiher) -[@xen0n](https://github.com/xen0n) +- [@heiher](https://github.com/heiher) +- [@xen0n](https://github.com/xen0n) ## Requirements @@ -29,13 +29,13 @@ additional CPU features via the `-C target-feature=` codegen options to rustc, o via the `#[target_feature]` mechanism within Rust code. By default, code generated with the soft-float target should run on any -LoongArch64 hardware, with the hard-float target additionally requiring an FPU; +LoongArch hardware, with the hard-float target additionally requiring an FPU; enabling additional target features may raise this baseline. Code generated with the targets will use the `medium` code model by default. You can change this using the `-C code-model=` option to rustc. -On `loongarch64-unknown-none*`, `extern "C"` uses the [architecture's standard calling convention][lapcs]. +On `loongarch*-unknown-none*`, `extern "C"` uses the [architecture's standard calling convention][lapcs]. [lapcs]: https://github.com/loongson/la-abi-specs/blob/release/lapcs.adoc @@ -52,6 +52,8 @@ list in `bootstrap.toml`: [build] build-stage = 1 target = [ + "loongarch32-unknown-none", + "loongarch32-unknown-none-softfloat", "loongarch64-unknown-none", "loongarch64-unknown-none-softfloat", ] @@ -64,13 +66,28 @@ As the targets support a variety of different environments and do not support ## Building Rust programs +### loongarch32-unknown-none* + +The `loongarch32-unknown-none*` targets are Tier 3, so you must build the Rust +compiler from source to use them. + +```sh +# target flag may be used with any cargo or rustc command +cargo build --target loongarch32-unknown-none +cargo build --target loongarch32-unknown-none-softfloat +``` + +### loongarch64-unknown-none* + Starting with Rust 1.74, precompiled artifacts are provided via `rustup`: ```sh # install cross-compile toolchain rustup target add loongarch64-unknown-none +rustup target add loongarch64-unknown-none-softfloat # target flag may be used with any cargo or rustc command cargo build --target loongarch64-unknown-none +cargo build --target loongarch64-unknown-none-softfloat ``` ## Cross-compilation toolchains and C code @@ -79,10 +96,10 @@ For cross builds, you will need an appropriate LoongArch C/C++ toolchain for linking, or if you want to compile C code along with Rust (such as for Rust crates with C dependencies). -Rust *may* be able to use an `loongarch64-unknown-linux-gnu-` toolchain with +Rust *may* be able to use an `loongarch{32,64}-unknown-linux-{gnu,musl}-` toolchain with appropriate standalone flags to build for this toolchain (depending on the assumptions of that toolchain, see below), or you may wish to use a separate -`loongarch64-unknown-none` toolchain. +`loongarch{32,64}-unknown-none` toolchain. On some LoongArch hosts that use ELF binaries, you *may* be able to use the host C toolchain, if it does not introduce assumptions about the host environment diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index ebc276b38fb..a3762e4117d 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -508,6 +508,7 @@ impl fmt::Display for Display<'_> { (sym::target_arch, Some(arch)) => match arch.as_str() { "aarch64" => "AArch64", "arm" => "ARM", + "loongarch32" => "LoongArch LA32", "loongarch64" => "LoongArch LA64", "m68k" => "M68k", "csky" => "CSKY", diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index f8209f2c8cd..4c53ea42793 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -113,6 +113,8 @@ static TARGETS: &[&str] = &[ "i686-unknown-uefi", "loongarch64-unknown-linux-gnu", "loongarch64-unknown-linux-musl", + "loongarch32-unknown-none", + "loongarch32-unknown-none-softfloat", "loongarch64-unknown-none", "loongarch64-unknown-none-softfloat", "m68k-unknown-linux-gnu", diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 4f93b498741..9b9d94bbead 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -495,6 +495,7 @@ impl Config { "arm64ec", "riscv32", "riscv64", + "loongarch32", "loongarch64", "s390x", // These targets require an additional asm_experimental_arch feature. diff --git a/src/tools/compiletest/src/directive-list.rs b/src/tools/compiletest/src/directive-list.rs index 5757e422ae2..1406553c9ea 100644 --- a/src/tools/compiletest/src/directive-list.rs +++ b/src/tools/compiletest/src/directive-list.rs @@ -73,6 +73,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "ignore-linux", "ignore-lldb", "ignore-llvm-version", + "ignore-loongarch32", "ignore-loongarch64", "ignore-macabi", "ignore-macos", @@ -196,6 +197,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "only-i686-unknown-linux-gnu", "only-ios", "only-linux", + "only-loongarch32", "only-loongarch64", "only-loongarch64-unknown-linux-gnu", "only-macos", diff --git a/src/tools/miri/cargo-miri/Cargo.lock b/src/tools/miri/cargo-miri/Cargo.lock index c1915ae617e..d37f8750bde 100644 --- a/src/tools/miri/cargo-miri/Cargo.lock +++ b/src/tools/miri/cargo-miri/Cargo.lock @@ -3,6 +3,15 @@ version = 4 [[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] name = "anyhow" version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -207,12 +216,42 @@ dependencies = [ ] [[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] name = "rustc-build-sysroot" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10edc2e4393515193bd766e2f6c050b0536a68e56f2b6d56c07ababfdc114ff0" +checksum = "16d115ad7e26e0d1337f64ae6598f758194696afc2e9f34c8a6f24582529c3dc" dependencies = [ "anyhow", + "regex", "rustc_version", "tempfile", "walkdir", diff --git a/src/tools/miri/cargo-miri/Cargo.toml b/src/tools/miri/cargo-miri/Cargo.toml index 5c579b2a77d..e08733959cc 100644 --- a/src/tools/miri/cargo-miri/Cargo.toml +++ b/src/tools/miri/cargo-miri/Cargo.toml @@ -18,7 +18,7 @@ directories = "6" rustc_version = "0.4" serde_json = "1.0.40" cargo_metadata = "0.19" -rustc-build-sysroot = "0.5.7" +rustc-build-sysroot = "0.5.8" # Enable some feature flags that dev-dependencies need but dependencies # do not. This makes `./miri install` after `./miri build` faster. diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index a4882a20148..9957e351ff1 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -3,17 +3,20 @@ mod atomic; mod simd; +use std::ops::Neg; + use rand::Rng; use rustc_abi::Size; -use rustc_apfloat::{Float, Round}; +use rustc_apfloat::ieee::{IeeeFloat, Semantics}; +use rustc_apfloat::{self, Float, Round}; use rustc_middle::mir; -use rustc_middle::ty::{self, FloatTy}; +use rustc_middle::ty::{self, FloatTy, ScalarInt}; use rustc_span::{Symbol, sym}; use self::atomic::EvalContextExt as _; use self::helpers::{ToHost, ToSoft, check_intrinsic_arg_count}; use self::simd::EvalContextExt as _; -use crate::math::apply_random_float_error_to_imm; +use crate::math::{IeeeExt, apply_random_float_error_ulp}; use crate::*; impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} @@ -187,31 +190,39 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { => { let [f] = check_intrinsic_arg_count(args)?; let f = this.read_scalar(f)?.to_f32()?; - // Using host floats (but it's fine, these operations do not have - // guaranteed precision). - let host = f.to_host(); - let res = match intrinsic_name { - "sinf32" => host.sin(), - "cosf32" => host.cos(), - "expf32" => host.exp(), - "exp2f32" => host.exp2(), - "logf32" => host.ln(), - "log10f32" => host.log10(), - "log2f32" => host.log2(), - _ => bug!(), - }; - let res = res.to_soft(); - // Apply a relative error of 16ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - // FIXME: temporarily disabled as it breaks std tests. - // let res = apply_random_float_error_ulp( - // this, - // res, - // 4, // log2(16) - // ); + + let res = fixed_float_value(intrinsic_name, &[f]).unwrap_or_else(||{ + // Using host floats (but it's fine, these operations do not have + // guaranteed precision). + let host = f.to_host(); + let res = match intrinsic_name { + "sinf32" => host.sin(), + "cosf32" => host.cos(), + "expf32" => host.exp(), + "exp2f32" => host.exp2(), + "logf32" => host.ln(), + "log10f32" => host.log10(), + "log2f32" => host.log2(), + _ => bug!(), + }; + let res = res.to_soft(); + + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + let res = apply_random_float_error_ulp( + this, + res, + 2, // log2(4) + ); + + // Clamp the result to the guaranteed range of this function according to the C standard, + // if any. + clamp_float_value(intrinsic_name, res) + }); let res = this.adjust_nan(res, &[f]); this.write_scalar(res, dest)?; } + #[rustfmt::skip] | "sinf64" | "cosf64" @@ -223,28 +234,35 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { => { let [f] = check_intrinsic_arg_count(args)?; let f = this.read_scalar(f)?.to_f64()?; - // Using host floats (but it's fine, these operations do not have - // guaranteed precision). - let host = f.to_host(); - let res = match intrinsic_name { - "sinf64" => host.sin(), - "cosf64" => host.cos(), - "expf64" => host.exp(), - "exp2f64" => host.exp2(), - "logf64" => host.ln(), - "log10f64" => host.log10(), - "log2f64" => host.log2(), - _ => bug!(), - }; - let res = res.to_soft(); - // Apply a relative error of 16ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - // FIXME: temporarily disabled as it breaks std tests. - // let res = apply_random_float_error_ulp( - // this, - // res, - // 4, // log2(16) - // ); + + let res = fixed_float_value(intrinsic_name, &[f]).unwrap_or_else(||{ + // Using host floats (but it's fine, these operations do not have + // guaranteed precision). + let host = f.to_host(); + let res = match intrinsic_name { + "sinf64" => host.sin(), + "cosf64" => host.cos(), + "expf64" => host.exp(), + "exp2f64" => host.exp2(), + "logf64" => host.ln(), + "log10f64" => host.log10(), + "log2f64" => host.log2(), + _ => bug!(), + }; + let res = res.to_soft(); + + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + let res = apply_random_float_error_ulp( + this, + res, + 2, // log2(4) + ); + + // Clamp the result to the guaranteed range of this function according to the C standard, + // if any. + clamp_float_value(intrinsic_name, res) + }); let res = this.adjust_nan(res, &[f]); this.write_scalar(res, dest)?; } @@ -302,43 +320,75 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "powf32" => { - // FIXME: apply random relative error but without altering behaviour of powf let [f1, f2] = check_intrinsic_arg_count(args)?; let f1 = this.read_scalar(f1)?.to_f32()?; let f2 = this.read_scalar(f2)?.to_f32()?; - // Using host floats (but it's fine, this operation does not have guaranteed precision). - let res = f1.to_host().powf(f2.to_host()).to_soft(); + + let res = fixed_float_value(intrinsic_name, &[f1, f2]).unwrap_or_else(|| { + // Using host floats (but it's fine, this operation does not have guaranteed precision). + let res = f1.to_host().powf(f2.to_host()).to_soft(); + + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + apply_random_float_error_ulp( + this, res, 2, // log2(4) + ) + }); let res = this.adjust_nan(res, &[f1, f2]); this.write_scalar(res, dest)?; } "powf64" => { - // FIXME: apply random relative error but without altering behaviour of powf let [f1, f2] = check_intrinsic_arg_count(args)?; let f1 = this.read_scalar(f1)?.to_f64()?; let f2 = this.read_scalar(f2)?.to_f64()?; - // Using host floats (but it's fine, this operation does not have guaranteed precision). - let res = f1.to_host().powf(f2.to_host()).to_soft(); + + let res = fixed_float_value(intrinsic_name, &[f1, f2]).unwrap_or_else(|| { + // Using host floats (but it's fine, this operation does not have guaranteed precision). + let res = f1.to_host().powf(f2.to_host()).to_soft(); + + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + apply_random_float_error_ulp( + this, res, 2, // log2(4) + ) + }); let res = this.adjust_nan(res, &[f1, f2]); this.write_scalar(res, dest)?; } "powif32" => { - // FIXME: apply random relative error but without altering behaviour of powi let [f, i] = check_intrinsic_arg_count(args)?; let f = this.read_scalar(f)?.to_f32()?; let i = this.read_scalar(i)?.to_i32()?; - // Using host floats (but it's fine, this operation does not have guaranteed precision). - let res = f.to_host().powi(i).to_soft(); + + let res = fixed_powi_float_value(f, i).unwrap_or_else(|| { + // Using host floats (but it's fine, this operation does not have guaranteed precision). + let res = f.to_host().powi(i).to_soft(); + + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + apply_random_float_error_ulp( + this, res, 2, // log2(4) + ) + }); let res = this.adjust_nan(res, &[f]); this.write_scalar(res, dest)?; } "powif64" => { - // FIXME: apply random relative error but without altering behaviour of powi let [f, i] = check_intrinsic_arg_count(args)?; let f = this.read_scalar(f)?.to_f64()?; let i = this.read_scalar(i)?.to_i32()?; - // Using host floats (but it's fine, this operation does not have guaranteed precision). - let res = f.to_host().powi(i).to_soft(); + + let res = fixed_powi_float_value(f, i).unwrap_or_else(|| { + // Using host floats (but it's fine, this operation does not have guaranteed precision). + let res = f.to_host().powi(i).to_soft(); + + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + apply_random_float_error_ulp( + this, res, 2, // log2(4) + ) + }); let res = this.adjust_nan(res, &[f]); this.write_scalar(res, dest)?; } @@ -425,3 +475,97 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(EmulateItemResult::NeedsReturn) } } + +/// Applies a random ULP floating point error to `val` and returns the new value. +/// So if you want an X ULP error, `ulp_exponent` should be log2(X). +/// +/// Will fail if `val` is not a floating point number. +fn apply_random_float_error_to_imm<'tcx>( + ecx: &mut MiriInterpCx<'tcx>, + val: ImmTy<'tcx>, + ulp_exponent: u32, +) -> InterpResult<'tcx, ImmTy<'tcx>> { + let scalar = val.to_scalar_int()?; + let res: ScalarInt = match val.layout.ty.kind() { + ty::Float(FloatTy::F16) => + apply_random_float_error_ulp(ecx, scalar.to_f16(), ulp_exponent).into(), + ty::Float(FloatTy::F32) => + apply_random_float_error_ulp(ecx, scalar.to_f32(), ulp_exponent).into(), + ty::Float(FloatTy::F64) => + apply_random_float_error_ulp(ecx, scalar.to_f64(), ulp_exponent).into(), + ty::Float(FloatTy::F128) => + apply_random_float_error_ulp(ecx, scalar.to_f128(), ulp_exponent).into(), + _ => bug!("intrinsic called with non-float input type"), + }; + + interp_ok(ImmTy::from_scalar_int(res, val.layout)) +} + +/// For the intrinsics: +/// - sinf32, sinf64 +/// - cosf32, cosf64 +/// - expf32, expf64, exp2f32, exp2f64 +/// - logf32, logf64, log2f32, log2f64, log10f32, log10f64 +/// - powf32, powf64 +/// +/// Returns `Some(output)` if the `intrinsic` results in a defined fixed `output` specified in the C standard +/// (specifically, C23 annex F.10) when given `args` as arguments. Outputs that are unaffected by a relative error +/// (such as INF and zero) are not handled here, they are assumed to be handled by the underlying +/// implementation. Returns `None` if no specific value is guaranteed. +fn fixed_float_value<S: Semantics>( + intrinsic_name: &str, + args: &[IeeeFloat<S>], +) -> Option<IeeeFloat<S>> { + let one = IeeeFloat::<S>::one(); + match (intrinsic_name, args) { + // cos(+- 0) = 1 + ("cosf32" | "cosf64", [input]) if input.is_zero() => Some(one), + + // e^0 = 1 + ("expf32" | "expf64" | "exp2f32" | "exp2f64", [input]) if input.is_zero() => Some(one), + + // 1^y = 1 for any y, even a NaN. + ("powf32" | "powf64", [base, _]) if *base == one => Some(one), + + // (-1)^(±INF) = 1 + ("powf32" | "powf64", [base, exp]) if *base == -one && exp.is_infinite() => Some(one), + + // FIXME(#4286): The C ecosystem is inconsistent with handling sNaN's, some return 1 others propogate + // the NaN. We should return either 1 or the NaN non-deterministically here. + // But for now, just handle them all the same. + // x^(±0) = 1 for any x, even a NaN + ("powf32" | "powf64", [_, exp]) if exp.is_zero() => Some(one), + + // There are a lot of cases for fixed outputs according to the C Standard, but these are mainly INF or zero + // which are not affected by the applied error. + _ => None, + } +} + +/// Returns `Some(output)` if `powi` (called `pown` in C) results in a fixed value specified in the C standard +/// (specifically, C23 annex F.10.4.6) when doing `base^exp`. Otherwise, returns `None`. +fn fixed_powi_float_value<S: Semantics>(base: IeeeFloat<S>, exp: i32) -> Option<IeeeFloat<S>> { + match (base.category(), exp) { + // x^0 = 1, if x is not a Signaling NaN + // FIXME(#4286): The C ecosystem is inconsistent with handling sNaN's, some return 1 others propogate + // the NaN. We should return either 1 or the NaN non-deterministically here. + // But for now, just handle them all the same. + (_, 0) => Some(IeeeFloat::<S>::one()), + + _ => None, + } +} + +/// Given an floating-point operation and a floating-point value, clamps the result to the output +/// range of the given operation. +fn clamp_float_value<S: Semantics>(intrinsic_name: &str, val: IeeeFloat<S>) -> IeeeFloat<S> { + match intrinsic_name { + // sin and cos: [-1, 1] + "sinf32" | "cosf32" | "sinf64" | "cosf64" => + val.clamp(IeeeFloat::<S>::one().neg(), IeeeFloat::<S>::one()), + // exp: [0, +INF] + "expf32" | "exp2f32" | "expf64" | "exp2f64" => + IeeeFloat::<S>::maximum(val, IeeeFloat::<S>::ZERO), + _ => val, + } +} diff --git a/src/tools/miri/src/math.rs b/src/tools/miri/src/math.rs index 2ff29c7ac1a..d1355a21684 100644 --- a/src/tools/miri/src/math.rs +++ b/src/tools/miri/src/math.rs @@ -151,6 +151,20 @@ pub(crate) fn sqrt<S: rustc_apfloat::ieee::Semantics>(x: IeeeFloat<S>) -> IeeeFl } } +/// Extend functionality of rustc_apfloat softfloats +pub trait IeeeExt: rustc_apfloat::Float { + #[inline] + fn one() -> Self { + Self::from_u128(1).value + } + + #[inline] + fn clamp(self, min: Self, max: Self) -> Self { + self.maximum(min).minimum(max) + } +} +impl<S: rustc_apfloat::ieee::Semantics> IeeeExt for IeeeFloat<S> {} + #[cfg(test)] mod tests { use rustc_apfloat::ieee::{DoubleS, HalfS, IeeeFloat, QuadS, SingleS}; diff --git a/src/tools/miri/src/shims/alloc.rs b/src/tools/miri/src/shims/alloc.rs index 323b95d5f5f..d7bb16f0858 100644 --- a/src/tools/miri/src/shims/alloc.rs +++ b/src/tools/miri/src/shims/alloc.rs @@ -13,10 +13,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // alignment requirement and size less than or equal to the size requested." // So first we need to figure out what the limits are for "fundamental alignment". // This is given by `alignof(max_align_t)`. The following list is taken from - // `library/std/src/sys/pal/common/alloc.rs` (where this is called `MIN_ALIGN`) and should + // `library/std/src/sys/alloc/mod.rs` (where this is called `MIN_ALIGN`) and should // be kept in sync. let max_fundamental_align = match this.tcx.sess.target.arch.as_ref() { - "x86" | "arm" | "mips" | "mips32r6" | "powerpc" | "powerpc64" | "wasm32" => 8, + "x86" | "arm" | "loongarch32" | "mips" | "mips32r6" | "powerpc" | "powerpc64" + | "wasm32" => 8, "x86_64" | "aarch64" | "mips64" | "mips64r6" | "s390x" | "sparc64" | "loongarch64" => 16, arch => bug!("unsupported target architecture for malloc: `{}`", arch), diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs index 98a88cfd62d..7ce0bc88517 100644 --- a/src/tools/miri/tests/pass/float.rs +++ b/src/tools/miri/tests/pass/float.rs @@ -45,6 +45,31 @@ macro_rules! assert_approx_eq { }; } + +/// From IEEE 754 a Signaling NaN for single precision has the following representation: +/// ``` +/// s | 1111 1111 | 0x..x +/// ```` +/// Were at least one `x` is a 1. +/// +/// This sNaN has the following representation and is used for testing purposes.: +/// ``` +/// 0 | 1111111 | 01..0 +/// ``` +const SNAN_F32: f32 = f32::from_bits(0x7fa00000); + +/// From IEEE 754 a Signaling NaN for double precision has the following representation: +/// ``` +/// s | 1111 1111 111 | 0x..x +/// ```` +/// Were at least one `x` is a 1. +/// +/// This sNaN has the following representation and is used for testing purposes.: +/// ``` +/// 0 | 1111 1111 111 | 01..0 +/// ``` +const SNAN_F64: f64 = f64::from_bits(0x7ff4000000000000); + fn main() { basic(); casts(); @@ -1008,17 +1033,84 @@ pub fn libm() { assert_approx_eq!(25f32.powf(-2f32), 0.0016f32); assert_approx_eq!(400f64.powf(0.5f64), 20f64); + // Some inputs to powf and powi result in fixed outputs + // and thus must be exactly equal to that value. + // C standard says: + // 1^y = 1 for any y, even a NaN. + assert_eq!(1f32.powf(10.0), 1.0); + assert_eq!(1f64.powf(100.0), 1.0); + assert_eq!(1f32.powf(f32::INFINITY), 1.0); + assert_eq!(1f64.powf(f64::INFINITY), 1.0); + assert_eq!(1f32.powf(f32::NAN), 1.0); + assert_eq!(1f64.powf(f64::NAN), 1.0); + + // f*::NAN is a quiet NAN and should return 1 as well. + assert_eq!(f32::NAN.powf(0.0), 1.0); + assert_eq!(f64::NAN.powf(0.0), 1.0); + + assert_eq!(42f32.powf(0.0), 1.0); + assert_eq!(42f64.powf(0.0), 1.0); + assert_eq!(f32::INFINITY.powf(0.0), 1.0); + assert_eq!(f64::INFINITY.powf(0.0), 1.0); + + // f*::NAN is a quiet NAN and should return 1 as well. + assert_eq!(f32::NAN.powi(0), 1.0); + assert_eq!(f64::NAN.powi(0), 1.0); + + assert_eq!(10.0f32.powi(0), 1.0); + assert_eq!(10.0f64.powi(0), 1.0); + assert_eq!(f32::INFINITY.powi(0), 1.0); + assert_eq!(f64::INFINITY.powi(0), 1.0); + + assert_eq!((-1f32).powf(f32::INFINITY), 1.0); + assert_eq!((-1f64).powf(f64::INFINITY), 1.0); + assert_eq!((-1f32).powf(f32::NEG_INFINITY), 1.0); + assert_eq!((-1f64).powf(f64::NEG_INFINITY), 1.0); + + // For pow (powf in rust) the C standard says: + // x^0 = 1 for all x even a sNaN + // FIXME(#4286): this does not match the behavior of all implementations. + assert_eq!(SNAN_F32.powf(0.0), 1.0); + assert_eq!(SNAN_F64.powf(0.0), 1.0); + + // For pown (powi in rust) the C standard says: + // x^0 = 1 for all x even a sNaN + // FIXME(#4286): this does not match the behavior of all implementations. + assert_eq!(SNAN_F32.powi(0), 1.0); + assert_eq!(SNAN_F64.powi(0), 1.0); + + assert_eq!(0f32.powi(10), 0.0); + assert_eq!(0f64.powi(100), 0.0); + assert_eq!(0f32.powi(9), 0.0); + assert_eq!(0f64.powi(99), 0.0); + + assert_biteq((-0f32).powf(10.0), 0.0, "-0^x = +0 where x is positive"); + assert_biteq((-0f64).powf(100.0), 0.0, "-0^x = +0 where x is positive"); + assert_biteq((-0f32).powf(9.0), -0.0, "-0^x = -0 where x is negative"); + assert_biteq((-0f64).powf(99.0), -0.0, "-0^x = -0 where x is negative"); + + assert_biteq((-0f32).powi(10), 0.0, "-0^x = +0 where x is positive"); + assert_biteq((-0f64).powi(100), 0.0, "-0^x = +0 where x is positive"); + assert_biteq((-0f32).powi(9), -0.0, "-0^x = -0 where x is negative"); + assert_biteq((-0f64).powi(99), -0.0, "-0^x = -0 where x is negative"); + assert_approx_eq!(1f32.exp(), f32::consts::E); assert_approx_eq!(1f64.exp(), f64::consts::E); + assert_eq!(0f32.exp(), 1.0); + assert_eq!(0f64.exp(), 1.0); assert_approx_eq!(1f32.exp_m1(), f32::consts::E - 1.0); assert_approx_eq!(1f64.exp_m1(), f64::consts::E - 1.0); assert_approx_eq!(10f32.exp2(), 1024f32); assert_approx_eq!(50f64.exp2(), 1125899906842624f64); + assert_eq!(0f32.exp2(), 1.0); + assert_eq!(0f64.exp2(), 1.0); assert_approx_eq!(f32::consts::E.ln(), 1f32); - assert_approx_eq!(1f64.ln(), 0f64); + assert_approx_eq!(f64::consts::E.ln(), 1f64); + assert_eq!(1f32.ln(), 0.0); + assert_eq!(1f64.ln(), 0.0); assert_approx_eq!(0f32.ln_1p(), 0f32); assert_approx_eq!(0f64.ln_1p(), 0f64); @@ -1047,7 +1139,8 @@ pub fn libm() { // Trigonometric functions. - assert_approx_eq!(0f32.sin(), 0f32); + assert_eq!(0f32.sin(), 0f32); + assert_eq!(0f64.sin(), 0f64); assert_approx_eq!((f64::consts::PI / 2f64).sin(), 1f64); assert_approx_eq!(f32::consts::FRAC_PI_6.sin(), 0.5); assert_approx_eq!(f64::consts::FRAC_PI_6.sin(), 0.5); @@ -1059,7 +1152,23 @@ pub fn libm() { assert_approx_eq!(2.0f32.asinh(), 1.443635475178810342493276740273105f32); assert_approx_eq!((-2.0f64).asinh(), -1.443635475178810342493276740273105f64); - assert_approx_eq!(0f32.cos(), 1f32); + // Ensure `sin` always returns something that is a valid input for `asin`, and same for + // `cos` and `acos`. + let halve_pi_f32 = std::f32::consts::FRAC_PI_2; + let halve_pi_f64 = std::f64::consts::FRAC_PI_2; + let pi_f32 = std::f32::consts::PI; + let pi_f64 = std::f64::consts::PI; + for _ in 0..64 { + // sin() should be clamped to [-1, 1] so asin() can never return NaN + assert!(!halve_pi_f32.sin().asin().is_nan()); + assert!(!halve_pi_f64.sin().asin().is_nan()); + // cos() should be clamped to [-1, 1] so acos() can never return NaN + assert!(!pi_f32.cos().acos().is_nan()); + assert!(!pi_f64.cos().acos().is_nan()); + } + + assert_eq!(0f32.cos(), 1f32); + assert_eq!(0f64.cos(), 1f64); assert_approx_eq!((f64::consts::PI * 2f64).cos(), 1f64); assert_approx_eq!(f32::consts::FRAC_PI_3.cos(), 0.5); assert_approx_eq!(f64::consts::FRAC_PI_3.cos(), 0.5); |
