From 5f623260818ba453bd969bf3f259e1e1e506dd45 Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Mon, 24 Nov 2025 04:18:20 +0000 Subject: [PATCH 01/25] Prepare for merging from rust-lang/rust This updates the rust-version file to d3e1ccdf40ae7b7a6dc81edc073d80dad7b66f75. --- src/doc/rustc-dev-guide/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index 8c841aac8eb60..08324790e3113 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -cc328c12382f05d8ddf6ffc8139deb7985270ad8 +d3e1ccdf40ae7b7a6dc81edc073d80dad7b66f75 From 7593f9d70612de27d8db97260ad247a8fa675a82 Mon Sep 17 00:00:00 2001 From: Redddy Date: Tue, 25 Nov 2025 01:18:01 +0900 Subject: [PATCH 02/25] Fix link to README in ui test documentation Updated link from SUMMARY.md to README.md in ui.md --- src/doc/rustc-dev-guide/src/tests/ui.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/tests/ui.md b/src/doc/rustc-dev-guide/src/tests/ui.md index 497e6109446ef..759029e671322 100644 --- a/src/doc/rustc-dev-guide/src/tests/ui.md +++ b/src/doc/rustc-dev-guide/src/tests/ui.md @@ -12,7 +12,7 @@ the resulting program](#controlling-passfail-expectations) to verify its behavior. For a survey of each subdirectory's purpose under `tests/ui`, consult the -[SUMMARY.md](https://github.com/rust-lang/rust/tree/HEAD/tests/ui/SUMMARY.md). +[README.md](https://github.com/rust-lang/rust/tree/HEAD/tests/ui/README.md). This is useful if you write a new test, and are looking for a category to place it in. From 4aa5ee99c848e1da81ddac872d9c82dc3faa6943 Mon Sep 17 00:00:00 2001 From: Redddy Date: Tue, 25 Nov 2025 13:42:45 +0900 Subject: [PATCH 03/25] Update contributing.md with Rust Book reference Added a reference to The Rust Book for more information on Rust's release channels. --- src/doc/rustc-dev-guide/src/contributing.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/doc/rustc-dev-guide/src/contributing.md b/src/doc/rustc-dev-guide/src/contributing.md index 8c764c31dbfa3..4476ed842daea 100644 --- a/src/doc/rustc-dev-guide/src/contributing.md +++ b/src/doc/rustc-dev-guide/src/contributing.md @@ -52,6 +52,7 @@ the CI to build and test their PR (e.g. when developing on a slow machine). Rust has strong backwards-compatibility guarantees. Thus, new features can't just be implemented directly in stable Rust. Instead, we have 3 release channels: stable, beta, and nightly. +See [The Rust Book] for more details on Rust’s train release model. - **Stable**: this is the latest stable release for general usage. - **Beta**: this is the next release (will be stable within 6 weeks). @@ -62,6 +63,8 @@ Instead, we have 3 release channels: stable, beta, and nightly. See [this chapter on implementing new features](./implementing_new_features.md) for more information. +[The Rust Book]: https://doc.rust-lang.org/book/appendix-07-nightly-rust.html + ### Breaking changes Breaking changes have a [dedicated section][Breaking Changes] in the dev-guide. From b881bd29fdcd5c906396977358ef9b308f982aee Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Tue, 25 Nov 2025 15:43:23 -0500 Subject: [PATCH 04/25] add a high-level design description --- src/doc/rustc-dev-guide/src/offload/internals.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/doc/rustc-dev-guide/src/offload/internals.md b/src/doc/rustc-dev-guide/src/offload/internals.md index 28857a6e78bff..77a4cadbcb986 100644 --- a/src/doc/rustc-dev-guide/src/offload/internals.md +++ b/src/doc/rustc-dev-guide/src/offload/internals.md @@ -7,3 +7,10 @@ also offer more advanced, possibly unsafe, interfaces which allow a higher degre The implementation is based on LLVM's "offload" project, which is already used by OpenMP to run Fortran or C++ code on GPUs. While the project is under development, users will need to call other compilers like clang to finish the compilation process. + +## High-level design: +We use a single-source, two-pass compilation approach. + +First we compile all functions that should be offloaded for the device (e.g nvptx64, amdgcn-amd-amdhsa, intel in the future). Currently we require cumbersome `#cfg(target_os="")` annotations, but we intend to recognize those in the future based on our offload intrinsic. + +We then compile the code for the host (e.g. x86-64), where most of the offloading logic happens. On the host side, we generate calls to the openmp offload runtime, to inform it about the layout of the types (a simplified version of the autodiff TypeTrees). We also use the type system to figure out whether kernel arguments have to be moved only to the device (e.g. `&[f32;1024]`), from the device, or both (e.g. `&mut [f64]`). We then launched the kernel, after which we inform the runtime to end this environment and move data back (as far as needed). From 2aa9360c359fb9fcf2e9ecf3acb66d5c936ace01 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Thu, 27 Nov 2025 06:41:59 +0100 Subject: [PATCH 05/25] Give an overview of our stability guarantees --- src/doc/rustc-dev-guide/src/SUMMARY.md | 1 + .../src/stability-guarantees.md | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 src/doc/rustc-dev-guide/src/stability-guarantees.md diff --git a/src/doc/rustc-dev-guide/src/SUMMARY.md b/src/doc/rustc-dev-guide/src/SUMMARY.md index 249140956c09a..37ab3ff56979e 100644 --- a/src/doc/rustc-dev-guide/src/SUMMARY.md +++ b/src/doc/rustc-dev-guide/src/SUMMARY.md @@ -52,6 +52,7 @@ - [Mastering @rustbot](./rustbot.md) - [Walkthrough: a typical contribution](./walkthrough.md) - [Implementing new language features](./implementing_new_features.md) +- [Stability guarantees](./stability-guarantees.md) - [Stability attributes](./stability.md) - [Stabilizing language features](./stabilization_guide.md) - [Stabilization report template](./stabilization_report_template.md) diff --git a/src/doc/rustc-dev-guide/src/stability-guarantees.md b/src/doc/rustc-dev-guide/src/stability-guarantees.md new file mode 100644 index 0000000000000..21c4f3594d84e --- /dev/null +++ b/src/doc/rustc-dev-guide/src/stability-guarantees.md @@ -0,0 +1,26 @@ +# Stability guarantees + +This page gives an overview of our stability guarantees. + +## RFCs + +* [RFC 1105 api evolution](https://github.com/rust-lang/rfcs/blob/master/text/1105-api-evolution.md) +* [RFC 1122 language semver](https://github.com/rust-lang/rfcs/blob/master/text/1122-language-semver.md) + +## Blog posts + +* [Stability as a Deliverable](https://blog.rust-lang.org/2014/10/30/Stability/) + +## rustc-dev-guide links + +* [Stabilizing library features](./stability.md) +* [Stabilizing language features](./stabilization_guide.md) +* [What qualifies as a bug fix?](./bug-fix-procedure.md#what-qualifies-as-a-bug-fix) + +## Exemptions + +Even if some of our infrastructure can be used by others, it is still considered +internal and comes without stability guarantees. This is a non-exhaustive list +of components without stability guarantees: + +* The CLIs and environment variables used by `remote-test-client` / `remote-test-server` From 430ec291a0ba420c187a5ca22ac05811551efedd Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Thu, 27 Nov 2025 06:58:36 +0000 Subject: [PATCH 06/25] Prepare for merging from rust-lang/rust This updates the rust-version file to 1be6b13be73dc12e98e51b403add4c41a0b77759. --- src/doc/rustc-dev-guide/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index 08324790e3113..bddb68a06b02c 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -d3e1ccdf40ae7b7a6dc81edc073d80dad7b66f75 +1be6b13be73dc12e98e51b403add4c41a0b77759 From 9289323fe2f081ca3caa43c9f384288fde9b2e42 Mon Sep 17 00:00:00 2001 From: Mattias Petersson Date: Thu, 27 Nov 2025 20:22:51 +0100 Subject: [PATCH 07/25] Specify toolchain part in building and running There was an issue raised that the toolchain explanation for building and running the compiler needed some refinement. This patch aims to remedy that by specifying what "first" and "second" refer to, and what the toolchain does. Small change to the language to specify that the stage2 toolchain is not built, and how one would build it. --- .../src/building/how-to-build-and-run.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md index c924216b8cd30..b15d77111e192 100644 --- a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md +++ b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md @@ -277,18 +277,20 @@ default). Once you have successfully built `rustc`, you will have created a bunch of files in your `build` directory. In order to actually run the resulting `rustc`, we recommend creating rustup toolchains. The first -one will run the stage1 compiler (which we built above). The second -will execute the stage2 compiler (which we did not build, but which -you will likely need to build at some point; for example, if you want -to run the entire test suite). +command listed below runs the stage1 compiler, which was built in the +steps above, with the name `stage1`. The second command runs the stage2 +compiler using the stage1 compiler. This will be needed in the future +if running the entire test suite, but will not be built in this page. +Building stage2 is done with the same `./x build` command as for stage1, +specifying that the stage is 2 instead. ```bash rustup toolchain link stage1 build/host/stage1 rustup toolchain link stage2 build/host/stage2 ``` -Now you can run the `rustc` you built with. If you run with `-vV`, you -should see a version number ending in `-dev`, indicating a build from +Now you can run the `rustc` you built with via the toolchain. If you run with +`-vV`, you should see a version number ending in `-dev`, indicating a build from your local environment: ```bash From 16a47d97267748f98fbc084afcff67a6ff962530 Mon Sep 17 00:00:00 2001 From: Mattias Petersson <61464624+Mattias-Petersson@users.noreply.github.com> Date: Fri, 28 Nov 2025 07:44:24 +0100 Subject: [PATCH 08/25] Update src/building/how-to-build-and-run.md Co-authored-by: Tshepang Mbambo --- .../rustc-dev-guide/src/building/how-to-build-and-run.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md index b15d77111e192..d0a38b12c550f 100644 --- a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md +++ b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md @@ -277,9 +277,9 @@ default). Once you have successfully built `rustc`, you will have created a bunch of files in your `build` directory. In order to actually run the resulting `rustc`, we recommend creating rustup toolchains. The first -command listed below runs the stage1 compiler, which was built in the -steps above, with the name `stage1`. The second command runs the stage2 -compiler using the stage1 compiler. This will be needed in the future +command listed below creates the stage1 toolchain, which was built in the +steps above, with the name `stage1`. The second command creates the stage2 +toolchain using the stage1 compiler. This will be needed in the future if running the entire test suite, but will not be built in this page. Building stage2 is done with the same `./x build` command as for stage1, specifying that the stage is 2 instead. From bd211952823c79c382bf0f6a8ace3fddee860c1e Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 22 Nov 2025 13:29:55 -0800 Subject: [PATCH 09/25] Update to mdbook 0.5 Changelog: https://github.com/rust-lang/mdBook/blob/master/CHANGELOG.md#mdbook-051 --- src/doc/rustc-dev-guide/.github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/rustc-dev-guide/.github/workflows/ci.yml b/src/doc/rustc-dev-guide/.github/workflows/ci.yml index 76e1d0a8f7cf3..fe92bc876cf71 100644 --- a/src/doc/rustc-dev-guide/.github/workflows/ci.yml +++ b/src/doc/rustc-dev-guide/.github/workflows/ci.yml @@ -14,9 +14,9 @@ jobs: if: github.repository == 'rust-lang/rustc-dev-guide' runs-on: ubuntu-latest env: - MDBOOK_VERSION: 0.4.52 - MDBOOK_LINKCHECK2_VERSION: 0.9.1 - MDBOOK_MERMAID_VERSION: 0.12.6 + MDBOOK_VERSION: 0.5.1 + MDBOOK_LINKCHECK2_VERSION: 0.11.0 + MDBOOK_MERMAID_VERSION: 0.17.0 MDBOOK_OUTPUT__LINKCHECK__FOLLOW_WEB_LINKS: ${{ github.event_name != 'pull_request' }} DEPLOY_DIR: book/html BASE_SHA: ${{ github.event.pull_request.base.sha }} From 5d8e7ae8057f8278784420f3925ebe4e99b79a2e Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Fri, 28 Nov 2025 11:47:54 -0800 Subject: [PATCH 10/25] Fix some broken links `P` was removed in https://github.com/rust-lang/rust/pull/145146 (replaced with plain Box). `CrateLoader` was inlined to `CStore` in https://github.com/rust-lang/rust/pull/144059. `elaborate_drops.rs` was moved in https://github.com/rust-lang/rust/pull/137008. --- src/doc/rustc-dev-guide/src/appendix/bibliography.md | 2 +- src/doc/rustc-dev-guide/src/appendix/code-index.md | 1 - src/doc/rustc-dev-guide/src/backend/libs-and-metadata.md | 7 +++---- src/doc/rustc-dev-guide/src/contributing.md | 4 ++-- .../rustc-dev-guide/src/llvm-coverage-instrumentation.md | 4 ++-- src/doc/rustc-dev-guide/src/mir/drop-elaboration.md | 2 +- 6 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/appendix/bibliography.md b/src/doc/rustc-dev-guide/src/appendix/bibliography.md index 93426b645a61e..3729194f5fa1a 100644 --- a/src/doc/rustc-dev-guide/src/appendix/bibliography.md +++ b/src/doc/rustc-dev-guide/src/appendix/bibliography.md @@ -32,7 +32,7 @@ Rust, as well as publications about Rust. * [Scheduling techniques for concurrent systems](https://www.stanford.edu/~ouster/cgi-bin/papers/coscheduling.pdf) * [Singularity: rethinking the software stack](https://research.microsoft.com/pubs/69431/osr2007_rethinkingsoftwarestack.pdf) * [The data locality of work stealing](http://www.aladdin.cs.cmu.edu/papers/pdfs/y2000/locality_spaa00.pdf) -* [Thread scheduling for multiprogramming multiprocessors](https://www.eecis.udel.edu/%7Ecavazos/cisc879-spring2008/papers/arora98thread.pdf) +* [Thread scheduling for multiprogramming multiprocessors](https://dl.acm.org/doi/10.1145/277651.277678) * [Three layer cake for shared-memory programming](https://dl.acm.org/doi/10.1145/1953611.1953616) * [Work-first and help-first scheduling policies for async-finish task parallelism](https://dl.acm.org/doi/10.1109/IPDPS.2009.5161079) - More general than fully-strict work stealing diff --git a/src/doc/rustc-dev-guide/src/appendix/code-index.md b/src/doc/rustc-dev-guide/src/appendix/code-index.md index 0795d83b2e9ad..22b3396ed1ff9 100644 --- a/src/doc/rustc-dev-guide/src/appendix/code-index.md +++ b/src/doc/rustc-dev-guide/src/appendix/code-index.md @@ -16,7 +16,6 @@ Item | Kind | Short description | Chapter | `HirId` | struct | One of four types of HIR node identifiers | [Identifiers in the HIR] | [compiler/rustc_hir_id/src/lib.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/struct.HirId.html) `Lexer` | struct | This is the lexer used during parsing. It consumes characters from the raw source code being compiled and produces a series of tokens for use by the rest of the parser | [The parser] | [compiler/rustc_parse/src/lexer/mod.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/lexer/struct.Lexer.html) `NodeId` | struct | One of four types of HIR node identifiers. Being phased out | [Identifiers in the HIR] | [compiler/rustc_ast/src/ast.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/node_id/struct.NodeId.html) -`P` | struct | An owned immutable smart pointer. By contrast, `&T` is not owned, and `Box` is not immutable. | None | [compiler/rustc_ast/src/ptr.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ptr/struct.P.html) `ParamEnv` | struct | Information about generic parameters or `Self`, useful for working with associated or generic items | [Parameter Environment] | [compiler/rustc_middle/src/ty/mod.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html) `ParseSess` | struct | This struct contains information about a parsing session | [The parser] | [compiler/rustc_session/src/parse/parse.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/parse/struct.ParseSess.html) `Rib` | struct | Represents a single scope of names | [Name resolution] | [compiler/rustc_resolve/src/lib.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_resolve/late/struct.Rib.html) diff --git a/src/doc/rustc-dev-guide/src/backend/libs-and-metadata.md b/src/doc/rustc-dev-guide/src/backend/libs-and-metadata.md index aa1d644703a0c..44b647d28a1e2 100644 --- a/src/doc/rustc-dev-guide/src/backend/libs-and-metadata.md +++ b/src/doc/rustc-dev-guide/src/backend/libs-and-metadata.md @@ -133,12 +133,12 @@ crate name, so they need to be disambiguated). Crate loading can have quite a few subtle complexities. During [name resolution], when an external crate is referenced (via an `extern crate` or -path), the resolver uses the [`CrateLoader`] which is responsible for finding +path), the resolver uses the [`CStore`] which is responsible for finding the crate libraries and loading the [metadata] for them. After the dependency -is loaded, the `CrateLoader` will provide the information the resolver needs +is loaded, the `CStore` will provide the information the resolver needs to perform its job (such as expanding macros, resolving paths, etc.). -To load each external crate, the `CrateLoader` uses a [`CrateLocator`] to +To load each external crate, the `CStore` uses a [`CrateLocator`] to actually find the correct files for one specific crate. There is some great documentation in the [`locator`] module that goes into detail on how loading works, and I strongly suggest reading it to get the full picture. @@ -157,7 +157,6 @@ wrapped in the [`CrateMetadata`] struct. After resolution and expansion, the compilation. [name resolution]: ../name-resolution.md -[`CrateLoader`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_metadata/creader/struct.CrateLoader.html [`CrateLocator`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_metadata/locator/struct.CrateLocator.html [`locator`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_metadata/locator/index.html [`CStore`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_metadata/creader/struct.CStore.html diff --git a/src/doc/rustc-dev-guide/src/contributing.md b/src/doc/rustc-dev-guide/src/contributing.md index 4476ed842daea..40ad58e025c17 100644 --- a/src/doc/rustc-dev-guide/src/contributing.md +++ b/src/doc/rustc-dev-guide/src/contributing.md @@ -468,7 +468,7 @@ Just a few things to keep in mind: #### ⚠️ Note: Where to contribute `rustc-dev-guide` changes For detailed information about where to contribute rustc-dev-guide changes and the benefits of doing so, -see [the rustc-dev-guide working group documentation]. +see [the rustc-dev-guide team documentation]. ## Issue triage @@ -485,7 +485,7 @@ Please see . [regression-]: https://github.com/rust-lang/rust/labels?q=regression [relnotes]: https://github.com/rust-lang/rust/labels/relnotes [S-tracking-]: https://github.com/rust-lang/rust/labels?q=s-tracking -[the rustc-dev-guide working group documentation]: https://forge.rust-lang.org/wg-rustc-dev-guide/index.html#where-to-contribute-rustc-dev-guide-changes +[the rustc-dev-guide team documentation]: https://forge.rust-lang.org/rustc-dev-guide/index.html#where-to-contribute-rustc-dev-guide-changes ### rfcbot labels diff --git a/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md b/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md index 30a1c5ebc7ef4..d71e51d5f61bf 100644 --- a/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md +++ b/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md @@ -84,11 +84,11 @@ Note that when building `rustc`, `profiler_builtins` is only included when `build.profiler = true` is set in `bootstrap.toml`. When compiling with `-C instrument-coverage`, -[`CrateLoader::postprocess()`][crate-loader-postprocess] dynamically loads +[`CStore::postprocess()`][crate-loader-postprocess] dynamically loads `profiler_builtins` by calling `inject_profiler_runtime()`. [compiler-rt-profile]: https://github.com/llvm/llvm-project/tree/main/compiler-rt/lib/profile -[crate-loader-postprocess]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_metadata/creader/struct.CrateLoader.html#method.postprocess +[crate-loader-postprocess]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_metadata/creader/struct.CStore.html#method.postprocess ## Testing coverage instrumentation diff --git a/src/doc/rustc-dev-guide/src/mir/drop-elaboration.md b/src/doc/rustc-dev-guide/src/mir/drop-elaboration.md index 93e21e2b227fc..7ef60f4cca00b 100644 --- a/src/doc/rustc-dev-guide/src/mir/drop-elaboration.md +++ b/src/doc/rustc-dev-guide/src/mir/drop-elaboration.md @@ -188,6 +188,6 @@ known to be uninitialized) to run these checks. [RFC 320]: https://rust-lang.github.io/rfcs/0320-nonzeroing-dynamic-drop.html [reference-drop]: https://doc.rust-lang.org/reference/destructors.html -[drops]: https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_mir_dataflow/src/elaborate_drops.rs +[drops]: https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_mir_transform/src/elaborate_drops.rs [drops-shim]: https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_mir_transform/src/shim.rs [drops-transform]: https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_mir_transform/src/elaborate_drops.rs From 05b8b5ae92f32e551b8fe189140cc0a9d43c2455 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Sat, 29 Nov 2025 02:05:46 +0200 Subject: [PATCH 11/25] sembr backend/libs-and-metadata.md --- .../src/backend/libs-and-metadata.md | 146 +++++++++--------- 1 file changed, 77 insertions(+), 69 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/backend/libs-and-metadata.md b/src/doc/rustc-dev-guide/src/backend/libs-and-metadata.md index 44b647d28a1e2..4cf0dfa6a6da1 100644 --- a/src/doc/rustc-dev-guide/src/backend/libs-and-metadata.md +++ b/src/doc/rustc-dev-guide/src/backend/libs-and-metadata.md @@ -1,36 +1,41 @@ # Libraries and metadata When the compiler sees a reference to an external crate, it needs to load some -information about that crate. This chapter gives an overview of that process, +information about that crate. +This chapter gives an overview of that process, and the supported file formats for crate libraries. ## Libraries -A crate dependency can be loaded from an `rlib`, `dylib`, or `rmeta` file. A -key point of these file formats is that they contain `rustc`-specific -[*metadata*](#metadata). This metadata allows the compiler to discover enough +A crate dependency can be loaded from an `rlib`, `dylib`, or `rmeta` file. +A key point of these file formats is that they contain `rustc`-specific +[*metadata*](#metadata). +This metadata allows the compiler to discover enough information about the external crate to understand the items it contains, which macros it exports, and *much* more. ### rlib -An `rlib` is an [archive file], which is similar to a tar file. This file -format is specific to `rustc`, and may change over time. This file contains: +An `rlib` is an [archive file], which is similar to a tar file. +This file format is specific to `rustc`, and may change over time. +This file contains: -* Object code, which is the result of code generation. This is used during - regular linking. There is a separate `.o` file for each [codegen unit]. The - codegen step can be skipped with the [`-C +* Object code, which is the result of code generation. + This is used during regular linking. + There is a separate `.o` file for each [codegen unit]. + The codegen step can be skipped with the [`-C linker-plugin-lto`][linker-plugin-lto] CLI option, which means each `.o` file will only contain LLVM bitcode. * [LLVM bitcode], which is a binary representation of LLVM's intermediate - representation, which is embedded as a section in the `.o` files. This can - be used for [Link Time Optimization] (LTO). This can be removed with the + representation, which is embedded as a section in the `.o` files. + This can be used for [Link Time Optimization] (LTO). + This can be removed with the [`-C embed-bitcode=no`][embed-bitcode] CLI option to improve compile times and reduce disk space if LTO is not needed. * `rustc` [metadata], in a file named `lib.rmeta`. * A symbol table, which is essentially a list of symbols with offsets to the - object files that contain that symbol. This is pretty standard for archive - files. + object files that contain that symbol. + This is pretty standard for archive files. [archive file]: https://en.wikipedia.org/wiki/Ar_(Unix) [LLVM bitcode]: https://llvm.org/docs/BitCodeFormat.html @@ -41,46 +46,46 @@ format is specific to `rustc`, and may change over time. This file contains: ### dylib -A `dylib` is a platform-specific shared library. It includes the `rustc` -[metadata] in a special link section called `.rustc`. +A `dylib` is a platform-specific shared library. +It includes the `rustc` [metadata] in a special link section called `.rustc`. ### rmeta -An `rmeta` file is a custom binary format that contains the [metadata] for the -crate. This file can be used for fast "checks" of a project by skipping all code +An `rmeta` file is a custom binary format that contains the [metadata] for the crate. +This file can be used for fast "checks" of a project by skipping all code generation (as is done with `cargo check`), collecting enough information for documentation (as is done with `cargo doc`), or for [pipelining](#pipelining). This file is created if the [`--emit=metadata`][emit] CLI option is used. -`rmeta` files do not support linking, since they do not contain compiled -object files. +`rmeta` files do not support linking, since they do not contain compiled object files. [emit]: https://doc.rust-lang.org/rustc/command-line-arguments.html#option-emit ## Metadata -The metadata contains a wide swath of different elements. This guide will not go -into detail about every field it contains. You are encouraged to browse the +The metadata contains a wide swath of different elements. +This guide will not go into detail about every field it contains. +You are encouraged to browse the [`CrateRoot`] definition to get a sense of the different elements it contains. -Everything about metadata encoding and decoding is in the [`rustc_metadata`] -package. +Everything about metadata encoding and decoding is in the [`rustc_metadata`] package. Here are a few highlights of things it contains: -* The version of the `rustc` compiler. The compiler will refuse to load files - from any other version. -* The [Strict Version Hash](#strict-version-hash) (SVH). This helps ensure the - correct dependency is loaded. -* The [Stable Crate Id](#stable-crate-id). This is a hash used - to identify crates. -* Information about all the source files in the library. This can be used for - a variety of things, such as diagnostics pointing to sources in a +* The version of the `rustc` compiler. + The compiler will refuse to load files from any other version. +* The [Strict Version Hash](#strict-version-hash) (SVH). + This helps ensure the correct dependency is loaded. +* The [Stable Crate Id](#stable-crate-id). + This is a hash used to identify crates. +* Information about all the source files in the library. + This can be used for a variety of things, such as diagnostics pointing to sources in a dependency. -* Information about exported macros, traits, types, and items. Generally, - anything that's needed to be known when a path references something inside a - crate dependency. -* Encoded [MIR]. This is optional, and only encoded if needed for code - generation. `cargo check` skips this for performance reasons. +* Information about exported macros, traits, types, and items. + Generally, + anything that's needed to be known when a path references something inside a crate dependency. +* Encoded [MIR]. + This is optional, and only encoded if needed for code generation. + `cargo check` skips this for performance reasons. [`CrateRoot`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_metadata/rmeta/struct.CrateRoot.html [`rustc_metadata`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_metadata/index.html @@ -89,10 +94,10 @@ Here are a few highlights of things it contains: ### Strict Version Hash The Strict Version Hash ([SVH], also known as the "crate hash") is a 64-bit -hash that is used to ensure that the correct crate dependencies are loaded. It -is possible for a directory to contain multiple copies of the same dependency -built with different settings, or built from different sources. The crate -loader will skip any crates that have the wrong SVH. +hash that is used to ensure that the correct crate dependencies are loaded. +It is possible for a directory to contain multiple copies of the same dependency +built with different settings, or built from different sources. +The crate loader will skip any crates that have the wrong SVH. The SVH is also used for the [incremental compilation] session filename, though that usage is mostly historic. @@ -114,14 +119,15 @@ See [`compute_hir_hash`] for where the hash is actually computed. ### Stable Crate Id The [`StableCrateId`] is a 64-bit hash used to identify different crates with -potentially the same name. It is a hash of the crate name and all the -[`-C metadata`] CLI options computed in [`StableCrateId::new`]. It is -used in a variety of places, such as symbol name mangling, crate loading, and +potentially the same name. +It is a hash of the crate name and all the +[`-C metadata`] CLI options computed in [`StableCrateId::new`]. +It is used in a variety of places, such as symbol name mangling, crate loading, and much more. By default, all Rust symbols are mangled and incorporate the stable crate id. -This allows multiple versions of the same crate to be included together. Cargo -automatically generates `-C metadata` hashes based on a variety of factors, like +This allows multiple versions of the same crate to be included together. +Cargo automatically generates `-C metadata` hashes based on a variety of factors, like the package version, source, and target kind (a lib and test can have the same crate name, so they need to be disambiguated). @@ -131,30 +137,31 @@ crate name, so they need to be disambiguated). ## Crate loading -Crate loading can have quite a few subtle complexities. During [name -resolution], when an external crate is referenced (via an `extern crate` or +Crate loading can have quite a few subtle complexities. +During [name resolution], when an external crate is referenced (via an `extern crate` or path), the resolver uses the [`CStore`] which is responsible for finding -the crate libraries and loading the [metadata] for them. After the dependency -is loaded, the `CStore` will provide the information the resolver needs +the crate libraries and loading the [metadata] for them. +After the dependency is loaded, the `CStore` will provide the information the resolver needs to perform its job (such as expanding macros, resolving paths, etc.). To load each external crate, the `CStore` uses a [`CrateLocator`] to -actually find the correct files for one specific crate. There is some great -documentation in the [`locator`] module that goes into detail on how loading +actually find the correct files for one specific crate. +There is some great documentation in the [`locator`] module that goes into detail on how loading works, and I strongly suggest reading it to get the full picture. -The location of a dependency can come from several different places. Direct -dependencies are usually passed with `--extern` flags, and the loader can look -at those directly. Direct dependencies often have references to their own -dependencies, which need to be loaded, too. These are usually found by +The location of a dependency can come from several different places. +Direct dependencies are usually passed with `--extern` flags, and the loader can look +at those directly. +Direct dependencies often have references to their own dependencies, which need to be loaded, too. +These are usually found by scanning the directories passed with the `-L` flag for any file whose metadata -contains a matching crate name and [SVH](#strict-version-hash). The loader -will also look at the [sysroot] to find dependencies. +contains a matching crate name and [SVH](#strict-version-hash). +The loader will also look at the [sysroot] to find dependencies. As crates are loaded, they are kept in the [`CStore`] with the crate metadata -wrapped in the [`CrateMetadata`] struct. After resolution and expansion, the -`CStore` will make its way into the [`GlobalCtxt`] for the rest of the -compilation. +wrapped in the [`CrateMetadata`] struct. +After resolution and expansion, the +`CStore` will make its way into the [`GlobalCtxt`] for the rest of the compilation. [name resolution]: ../name-resolution.md [`CrateLocator`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_metadata/locator/struct.CrateLocator.html @@ -167,20 +174,21 @@ compilation. ## Pipelining One trick to improve compile times is to start building a crate as soon as the -metadata for its dependencies is available. For a library, there is no need to -wait for the code generation of dependencies to finish. Cargo implements this -technique by telling `rustc` to emit an [`rmeta`](#rmeta) file for each -dependency as well as an [`rlib`](#rlib). As early as it can, `rustc` will -save the `rmeta` file to disk before it continues to the code generation -phase. The compiler sends a JSON message to let the build tool know that it +metadata for its dependencies is available. +For a library, there is no need to wait for the code generation of dependencies to finish. +Cargo implements this technique by telling `rustc` to emit an [`rmeta`](#rmeta) file for each +dependency as well as an [`rlib`](#rlib). +As early as it can, `rustc` will +save the `rmeta` file to disk before it continues to the code generation phase. +The compiler sends a JSON message to let the build tool know that it can start building the next crate if possible. The [crate loading](#crate-loading) system is smart enough to know when it -sees an `rmeta` file to use that if the `rlib` is not there (or has only been -partially written). +sees an `rmeta` file to use that if the `rlib` is not there (or has only been partially written). This pipelining isn't possible for binaries, because the linking phase will -require the code generation of all its dependencies. In the future, it may be +require the code generation of all its dependencies. +In the future, it may be possible to further improve this scenario by splitting linking into a separate command (see [#64191]). From 9e61149e8e40538b2de23db5979ad54d034403b0 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Sat, 29 Nov 2025 02:24:02 +0200 Subject: [PATCH 12/25] link text spanning separate lines is awkward --- src/doc/rustc-dev-guide/src/backend/libs-and-metadata.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/backend/libs-and-metadata.md b/src/doc/rustc-dev-guide/src/backend/libs-and-metadata.md index 4cf0dfa6a6da1..4a0b75c694317 100644 --- a/src/doc/rustc-dev-guide/src/backend/libs-and-metadata.md +++ b/src/doc/rustc-dev-guide/src/backend/libs-and-metadata.md @@ -23,9 +23,8 @@ This file contains: * Object code, which is the result of code generation. This is used during regular linking. There is a separate `.o` file for each [codegen unit]. - The codegen step can be skipped with the [`-C - linker-plugin-lto`][linker-plugin-lto] CLI option, which means each `.o` - file will only contain LLVM bitcode. + The codegen step can be skipped with the [`-C linker-plugin-lto`][linker-plugin-lto] CLI option, + which means each `.o` file will only contain LLVM bitcode. * [LLVM bitcode], which is a binary representation of LLVM's intermediate representation, which is embedded as a section in the `.o` files. This can be used for [Link Time Optimization] (LTO). From 74da3a26c60ddf6fbdf12bedba409a12c51789da Mon Sep 17 00:00:00 2001 From: Boxy Uwu Date: Sat, 29 Nov 2025 14:29:58 +0000 Subject: [PATCH 13/25] add coercions chapter and split out non hir typeck stuff --- src/doc/rustc-dev-guide/src/SUMMARY.md | 17 +- .../src/appendix/code-index.md | 2 +- .../rustc-dev-guide/src/appendix/glossary.md | 2 +- .../src/hir-typeck/coercions.md | 284 ++++++++++++++++++ .../src/{ => hir-typeck}/method-lookup.md | 0 .../summary.md} | 8 +- src/doc/rustc-dev-guide/src/overview.md | 2 +- src/doc/rustc-dev-guide/src/thir.md | 2 +- .../src/traits/canonical-queries.md | 2 +- 9 files changed, 302 insertions(+), 17 deletions(-) create mode 100644 src/doc/rustc-dev-guide/src/hir-typeck/coercions.md rename src/doc/rustc-dev-guide/src/{ => hir-typeck}/method-lookup.md (100%) rename src/doc/rustc-dev-guide/src/{type-checking.md => hir-typeck/summary.md} (94%) diff --git a/src/doc/rustc-dev-guide/src/SUMMARY.md b/src/doc/rustc-dev-guide/src/SUMMARY.md index 37ab3ff56979e..c136d37160c53 100644 --- a/src/doc/rustc-dev-guide/src/SUMMARY.md +++ b/src/doc/rustc-dev-guide/src/SUMMARY.md @@ -186,14 +186,15 @@ - [Opaque types](./solve/opaque-types.md) - [Significant changes and quirks](./solve/significant-changes.md) - [`Unsize` and `CoerceUnsized` traits](./traits/unsize.md) -- [Type checking](./type-checking.md) - - [Method lookup](./method-lookup.md) - - [Variance](./variance.md) - - [Coherence checking](./coherence.md) - - [Opaque types](./opaque-types-type-alias-impl-trait.md) - - [Inference details](./opaque-types-impl-trait-inference.md) - - [Return Position Impl Trait In Trait](./return-position-impl-trait-in-trait.md) - - [Region inference restrictions][opaque-infer] +- [Variance](./variance.md) +- [Coherence checking](./coherence.md) +- [HIR Type checking](./hir-typeck/summary.md) + - [Coercions](./hir-typeck/coercions.md) + - [Method lookup](./hir-typeck/method-lookup.md) +- [Opaque types](./opaque-types-type-alias-impl-trait.md) + - [Inference details](./opaque-types-impl-trait-inference.md) + - [Return Position Impl Trait In Trait](./return-position-impl-trait-in-trait.md) + - [Region inference restrictions][opaque-infer] - [Const condition checking](./effects.md) - [Pattern and exhaustiveness checking](./pat-exhaustive-checking.md) - [Unsafety checking](./unsafety-checking.md) diff --git a/src/doc/rustc-dev-guide/src/appendix/code-index.md b/src/doc/rustc-dev-guide/src/appendix/code-index.md index 22b3396ed1ff9..bf9d3bd465645 100644 --- a/src/doc/rustc-dev-guide/src/appendix/code-index.md +++ b/src/doc/rustc-dev-guide/src/appendix/code-index.md @@ -33,7 +33,7 @@ Item | Kind | Short description | Chapter | [Identifiers in the HIR]: ../hir.html#hir-id [The parser]: ../the-parser.html [The Rustc Driver and Interface]: ../rustc-driver/intro.html -[Type checking]: ../type-checking.html +[Type checking]: ../hir-typeck/summary.html [The `ty` modules]: ../ty.html [Rustdoc]: ../rustdoc.html [Emitting Diagnostics]: ../diagnostics.html diff --git a/src/doc/rustc-dev-guide/src/appendix/glossary.md b/src/doc/rustc-dev-guide/src/appendix/glossary.md index 21162f8ee7d08..901fb68c0513f 100644 --- a/src/doc/rustc-dev-guide/src/appendix/glossary.md +++ b/src/doc/rustc-dev-guide/src/appendix/glossary.md @@ -88,7 +88,7 @@ Term | Meaning trans | Short for _translation_, the code to translate MIR into LLVM IR. Renamed to [codegen](#codegen). `Ty` | The internal representation of a type. ([see more](../ty.md)) `TyCtxt` | The data structure often referred to as [`tcx`](#tcx) in code which provides access to session data and the query system. -UFCS | Short for _universal function call syntax_, this is an unambiguous syntax for calling a method. **Term no longer in use!** Prefer _fully-qualified path/syntax_. ([see more](../type-checking.md), [see the reference](https://doc.rust-lang.org/reference/expressions/call-expr.html#disambiguating-function-calls)) +UFCS | Short for _universal function call syntax_, this is an unambiguous syntax for calling a method. **Term no longer in use!** Prefer _fully-qualified path/syntax_. ([see more](../hir-typeck/summary.md), [see the reference](https://doc.rust-lang.org/reference/expressions/call-expr.html#disambiguating-function-calls)) uninhabited type | A type which has _no_ values. This is not the same as a ZST, which has exactly 1 value. An example of an uninhabited type is `enum Foo {}`, which has no variants, and so, can never be created. The compiler can treat code that deals with uninhabited types as dead code, since there is no such value to be manipulated. `!` (the never type) is an uninhabited type. Uninhabited types are also called _empty types_. upvar | A variable captured by a closure from outside the closure. variance | Determines how changes to a generic parameter affect subtyping; for example, if `T` is a subtype of `U`, then `Vec` is a subtype `Vec` because `Vec` is _covariant_ in its generic parameter. See [the background chapter](./background.md#variance) for a more general explanation. See the [variance chapter](../variance.md) for an explanation of how type checking handles variance. diff --git a/src/doc/rustc-dev-guide/src/hir-typeck/coercions.md b/src/doc/rustc-dev-guide/src/hir-typeck/coercions.md new file mode 100644 index 0000000000000..384ea9bcf0306 --- /dev/null +++ b/src/doc/rustc-dev-guide/src/hir-typeck/coercions.md @@ -0,0 +1,284 @@ +# Coercions + +Coercions are implicit operations which transform a value into a different type. A coercion *site* is a position where a coercion is able to be implicitly performed. There are two kinds of coercion sites: +- one-to-one +- LUB (Least-Uppper-Bound) + +```rust +let one_to_one_coercion: &u32 = &mut 8; + +let lub_coercion = match my_bool { + true => &mut 10, + false => &12, +}; +``` + +See the Reference page on coercions for descriptions of what coercions exist and what expressions are coercion sites: + +## one-to-one coercions + +With a one-to-one coercion we coerce from one singular type to a known target type. In the above example this would be the coercion from `&mut u32` to `&u32`. + +A one-to-one coercion can be performed by calling [`FnCtxt::coerce`][fnctxt_coerce]. + +## LUB coercions + +With a LUB coercion we coerce a set of source types to some unknown target type. Unlike one-to-one coercions, a LUB coercion *produces* the target type that all of the source types coerce to. + +In the above example this would be the LUB coercion of both `&mut i32` and `&i32`, where we produce the target type `&i32`. + +The name "LUB coercion" (Least-Upper-Bound coercion) comes from how this coercion takes a set of types and computes the least coerced/subtyped type that both source types are coercable/subtypeable into. + +The general process for performing a LUB coercion is as follows: + +```rust ignore +// * 1 +let mut coerce = CoerceMany::new(intial_lub_ty); +for expr in exprs { + // * 2 + let expr_ty = fcx.check_expr_with_expectation(expr, expectation); + coerce.coerce(fcx, &cause, expr, expr_ty); +} +// * 3 +let final_ty = coerce.complete(fcx); +``` + +There are a few key steps here: +1. Creating the [`CoerceMany`][coerce_many] value and picking an initial lub +2. Typechecking each expression and registering its type as part of the LUB coercion +3. Completing the LUB coercion to get the resulting lubbed type + +### Step 1 + +First we create a [`CoerceMany`][coerce_many] value, this stores all of the state required for the LUB coercion. Unlike one-to-one coercions, a LUB coercion isn't a single function call as we want to intermix typechecking with advancing the LUB coercion. + +Creating a `CoerceMany` takes some `initial_lub` type. This is different from the *target* of the coercion which is an output of a LUB coercion rather than an input (unlike a one-to-one coercion). + +The initial lub ty should be derived from the [`Expectation`][expectation] for whatever expression this LUB coercion is for. It allows for inference constraints from computing the LUB coercion to propagate into the `Expectation`s used for type checking later expressions participating in the LUB coercion. + +See the ["unnecessary inference constraints"][unnecessary_inference_constraints] header for some more information about the effects this has. + +If there's no `Expectation` to use then some new infer var should be made for the initial lub ty. + +### Step 2 + +Next, for each expression participating in the LUB coercion, we typecheck it then invoke [`CoerceMany::coerce`][coerce_many_coerce] with its type. + +In some cases the expression participating in the LUB coercion doesn't actually exist in the HIR. For example when handling an operand-less `break` or `return` expression we need `()` to participate in the LUB coercion. + +In these cases the [`CoerceMany::coerce_forced_unit`][coerce_many_coerce_forced_unit] method can be used. + +The `CoerceMany::coerce` and `coerce_forced_unit` methods will both emit errors if the new type causes the LUB coercion to be unsatisfiable. In this case the final type of the LUB coercion will be an error type. + +### Step 3 + +Finally once all expressions have been coerced the final type of the LUB coercion can be obtained by calling [`CoerceMany::complete`][coerce_many_complete]. + +The resulting type of the LUB coercion is meaningfully different from the initial lub type passed in when constructing the [`CoerceMany`][coerce_many]. You should always take the resulting type of the LUB coercion and perform any necessary checks on it. + +## Implementation nuances + +### Adjustments + +When a coerce operation succeeds we record what kind of coercion it was, for example an unsize coercion or an autoderef etc. This is handled as part of the coerce operation by writing a list of *adjustments* into the in-progress [`TypeckResults`][typeck_results]. + +When building THIR we take the adjustments stored in the `TypeckResults` and make all of the coercion steps explicit. After this point in the compiler there isn't really a notion of coercions, only explicit casts and subtyping in the MIR. + +TODO: write and link to an adjustments chapter here + +### How does `CoerceMany` work + +[`CoerceMany`][coerce_many] works by repeatedly taking the current lub ty and some new source type, and computing a new lub ty which both types can coerce to. The core logic of taking a pair of types and computing some new third type can be found in [`try_find_coercion_lub`][try_find_coercion_lub]. + +```rust +fn foo() {} +fn bar() {} + +let a = match my_bool { + true => foo, + true if other_bool => foo, + false => bar, +} +``` + +In this example when type checking the `match` expression a LUB coercion is performed. This LUB coercion starts out with an initial lub ty of some inference variable `?x` due to the let statement having no known type. + +There are three expressions that participate in this LUB coercion. The first expression of a LUB coercion is special, instead of computing a new type with the existing initial lub ty, we coerce directly from the first expression to the initial lub ty. + +1. After type checking `true => foo,` we wind up with the type `FnDef(Foo)`. We then call [`CoerceMany::coerce`][coerce_many_coerce] which will perform a one-to-one coercion of `FnDef(Foo)` to `?x`. This infers `?x=FnDef(Foo)` giving us a new lub ty for the LUB coercion. +2. After type checking `true if other_bool => foo,` we once again wind up with the type `FnDef(Foo)`. We'll then call `CoerceMany::coerce` which will attempt to compute a new lub ty from our previous lub ty (`FnDef(Foo)`) and the type of this expression (`FnDef(Foo)`). This gives us a lub ty of `FnDef(Foo)`. +3. After type checking `false => bar,` we'll wind up with the type `FnDef(Bar)`. We'll then call `CoerceMany::coerce` which will attempt to compute a new lub ty from our previous lub ty (`FnDef(Foo)`) and the type of this expression (`FnDef(Bar)`). In this case we get the type `fn() -> ()` as we choose to coerce both function item types to a function pointer. + +This gives us a final type for the LUB coercion of `fn() -> ()`. + +### Transitive coercions + +[`CoerceMany`][coerce_many]'s algorithm of repeatedly attempting to coerce the currrent target type to the new type currently results in "Transitive Coercions". It's possible for a step in a LUB coercion to coerce an expression, and then a later step to coerce that expression further. + +```rust +struct Foo; + +use std::ops::Deref; + +impl Deref for Foo { + type Target = [u8; 2]; + + fn deref(&self) -> &[u8; 2] { + &[1; _] + } +} + +fn main() { + match () { + _ if true => &Foo, + _ if true => &[1_u8; 2], + _ => &[1_u8; 2] as &[u8], + }; +} +``` + +Here we have a LUB coercion with an initial lub ty of `?x`. In the first step we do a one-to-one coercion of `&Foo` to `?x` (reminder the first step is special). + +In the second step we compute a new lub ty from the current lub ty of `&Foo` and the new type of `&[u8; 2]`. This new lub ty would be `&[u8; 2]` by performing a deref coercion of `&Foo` to `&[u8; 2]` on the first expression. + +In the third step we compute a new lub ty from the current lub ty of `&[u8; 2]` and the new type of `&[u8]`. This new lub ty would be `&[u8]` by performing an unsizing coercion of `&[u8; 2]` to `&[u8]` on the first two expressions. + +Note how the first expression is coerced twice. Once a deref coercion from `&Foo` to `&[u8; 2]`, and then an unsizing coercion from `&[u8; 2]` to `&[u8]`. + +The current implementation of transitive coercions is broken, the previous example actually ICEs on stable. While the logic for performing a LUB coercion can produce transitive coercions just fine, the rest of the compiler is not set up to handle them. + +One-to-one coercions are also not capable of producing a lot of the kinds of transitive coercions that LUB coercions can. For example if we take the previous example and turn it into a one-to-one coercion we get a compile error: +```rust +struct Foo; + +use std::ops::Deref; + +impl Deref for Foo { + type Target = [u8; 2]; + + fn deref(&self) -> &[u8; 2] { + &[1; _] + } +} + +fn main() { + let a: &[u8] = &Foo; +} +``` + +Here we try to perform a one-to-one coercion from `&Foo` to `&[u8]` which fails as we can only perform a deref coercion *or* an unsizing coercion, we can't compose the two. + +### How does `try_find_coercion_lub` work + +There are three ways that we can compute a new lub ty for a LUB coercion: +1. Coerce both the current lub ty and the new type to a function pointer +2. Coerce the current lub ty to the new type (or vice versa) +3. Compute a mutual supertype of the current lub ty and the new type + +Unfortunately the actual implementation obsfucates this a fair amount. + +Computing a mutual supertype happens implicitly due to reusing the logic for one-to-one coercions which already handles subtyping if coercing fails. + +Additionally when trying to coerce both the current lub ty and the new type to function pointers we eagerly try to compute a mutual supertype to avoid unnecessary coercions. + +There is likely room for improving the structure of this function to make it more closely align with the conceptual model. + +### `use_lub` field in one-to-one coercions + +The implementation of one-to-one coercions is reused as part of LUB coercions. + +It would be wrong for LUB coercions to use one way subtyping when relating signatures or falling back to subtyping in the case of no coercions being possible. Instead we want to compute a mutual supertype of the two types. + +The `use_lub` field on [`Coerce`][coerce_ty] exists to toggle whether to perform normal subtyping (in the case of a one-to-one coercion), or whether to compute a mutual supertype (in the case of a LUB coercion). + +### Lubbing + +In theory computing a mutual supertype should be as simple as creating some new infer var `?mutual_sup` and then requiring `lub_ty <: ?mutual_sup` and `new_ty <: ?mutual_sup`. In reality LUB coercions use a special [`TypeRelation`][type_relation], [`LatticeOp`][lattice_op]. + +This is primarily to work around subtyping/generalization for higher ranked types being fairly broken. Unlike normal subtyping, when encountering higher ranked types the lub type relation will switch to invariance. + +This enforces that the binders of the higher ranked types are equivalent which avoids the need to pick a "most general" binder, which would be quite difficult to do. + +It also avoids the process of computing a mutual supertype being *order dependent*. Given the types `a` and `b`, it may be nice if computing the mutual supertype of `a` and `b` would yield the same result as computing the mutual supertype of `b` and `a`. + +The current issues with higher ranked types and subtyping would cause this property to not hold if we were to use the naive method of computing a mutual supertype. + +Coercions being turned into explicit MIR operations during MIR building means that the process of computing the final type of a LUB coercion only occurs during HIR typeck. This also means the behaviour of computing a mutual supertype only matters for type inference, and is not soundness relevant. + +## Cautionary notes + +### Probes + +Care should be taken when coercing from inside of a probe as both one-to-one coercions and LUB coercions have side effects that can't be rolled back by a probe. + +LUB coercions will emit error when a coercion step fails, this makes it entirely suitable for use inside of probes. + +1-to-1 and LUB coercions will both apply *adjustments* to the coerced expressions on success. This means that if inside of a probe and an attempt to coerce succeeds, then the probe must not rollback anything. + +It's therefore correct to wrap a [`FnCtxt::coerce`][fnctxt_coerce] call inside of a [`commit_if_ok`][commit_if_ok], but would be wrong to do so if returning `Err` after the coerce call. It would also be wrong to call `FnCtxt::coerce` from within a [`probe`][probe]. + +[`CoerceMany`][coerce_many] should never be used from within a `probe` or `commit_if_ok`. + +### Never-to-Any coercions + +Coercing from the never type (`!`) to an inference variable will result in a [`NeverToAny`][never_to_any] coercion with a target type of the inference variable. This is subtly different from *unifying* the inference variable with the never type. + +Unifying some infer var `?x` with `!` requires that `?x` actually be *equal* to `!`. However, a `NeverToAny` coercion allows for `?x` to be inferred to any possible type. + +This distinction means that in cases where the initial lub ty of a coercion is an inference variable (e.g. there's no [`Expectation`][expectation] to use for the initial lub ty), it's still important to use a coercion instead of subtyping. + +See PR [#147834](https://github.com/rust-lang/rust/pull/147834) which fixes a bug where we were incorrectly inferring things to the never type instead of going through a coercion. + +### Fallback to subtyping + +Even though subtyping is not a coercion, both [`FnCtxt::coerce`][fnctxt_coerce] and [`CoerceMany::coerce`][coerce_many_coerce]/[`coerce_forced_unit`][coerce_many_coerce_forced_unit] are able to succeed due to subtyping. + +For one-to-one coercions we will try to enforce the source type is a subtype of the target type. For LUB coercions we will try to compute a type that is a supertype of all the existing types. + +For example performing a one-to-one coercion of `?x` to `u32` will fallback to subtyping, inferring `?x eq u32`. This means that when a coercion fails there's no need to attempt subtyping afterwards. + +### Unnecessary inference constraints + +Using types from [`Expectation`][expectation]s as the initial lub ty can cause infer vars to be constrained by the types of the expressions participating in the LUB coercion. This is not always desirable as these infer vars actually only need to be constrained by the final type of the LUB coercion. + +```rust +fn foo(_: T) {} + +fn a() {} +fn b() {} + +foo::(match my_bool { + true => a, + false => b, +}) +``` + +Here we have a LUB coercion with the first expression being of type `FnDef(a)` and the second expression being of type `FnDef(b)`. If we use `?x` as the initial lub ty of the LUB coercion then we would get the following behaviour: +- expression 1: infer `?x=FnDef(a)` +- expression 2: find a coercion lub between `FnDef(a), FnDef(b)` resulting in `fn() -> ()` +- the final type of the LUB coercion is `fn() -> ()`. equate `?x eq fn() -> ()`, where `?x` actually already has been inferred to `FnDef(a)`, so this is actually equating `FnDef(a) eq fn() -> ()` which does not hold + +To avoid some (but not all) of these undesirable inference constraints, if the `Expectation` for the LUB coercion is an inference variable then we won't use it as the initial lub ty. Instead we create a new infer var, for example in the above code snippet we would actually make some new infer var `?y` for the initial lub ty instead of using `?x`. +- expression 1: infer `?y=FnDef(a)` +- expression 2: find a coercion lub between `FnDef(a), FnDef(b)` resulting in `fn() -> ()` +- the final type of the LUB coercion is `fn() -> ()`, infer `?x=fn() -> ()` + +See [#140283](https://github.com/rust-lang/rust/pull/140283) for a case where we had undesirable inference constraints caused by not creating a new infer var. + +This doesn't avoid unnecessary constraints in *all* cases, only the most common case of having an infer var as our `Expectation`. In theory it would be desirable to avoid these constraints in all cases but it would be quite involved to do so. + +[coerce_many]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/coercion/struct.CoerceMany.html +[coerce_many_coerce]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/coercion/struct.CoerceMany.html#method.coerce +[coerce_many_coerce_forced_unit]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/coercion/struct.CoerceMany.html#method.coerce_forced_unit +[coerce_many_complete]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/coercion/struct.CoerceMany.html#method.complete +[try_find_coercion_lub]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/fn_ctxt/struct.FnCtxt.html#method.try_find_coercion_lub +[expectation]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/expectation/enum.Expectation.html +[unnecessary_inference_constraints]: #unnecessary-inference-constraints +[typeck_results]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html +[type_relation]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_infer/infer/canonical/ir/relate/trait.TypeRelation.html +[lattice_op]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_infer/infer/relate/lattice/struct.LatticeOp.html +[fnctxt_coerce]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/fn_ctxt/struct.FnCtxt.html#method.coerce +[coerce_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/coercion/struct.Coerce.html +[commit_if_ok]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_infer/infer/struct.InferCtxt.html#method.commit_if_ok +[probe]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_infer/infer/struct.InferCtxt.html#method.probe +[never_to_any]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/adjustment/enum.Adjust.html#variant.NeverToAny diff --git a/src/doc/rustc-dev-guide/src/method-lookup.md b/src/doc/rustc-dev-guide/src/hir-typeck/method-lookup.md similarity index 100% rename from src/doc/rustc-dev-guide/src/method-lookup.md rename to src/doc/rustc-dev-guide/src/hir-typeck/method-lookup.md diff --git a/src/doc/rustc-dev-guide/src/type-checking.md b/src/doc/rustc-dev-guide/src/hir-typeck/summary.md similarity index 94% rename from src/doc/rustc-dev-guide/src/type-checking.md rename to src/doc/rustc-dev-guide/src/hir-typeck/summary.md index 4e8b30b19fc7b..23df97a9cf833 100644 --- a/src/doc/rustc-dev-guide/src/type-checking.md +++ b/src/doc/rustc-dev-guide/src/hir-typeck/summary.md @@ -1,4 +1,4 @@ -# Type checking +# HIR Type checking The [`hir_analysis`] crate contains the source for "type collection" as well as a bunch of related functionality. @@ -7,8 +7,8 @@ These crates draw heavily on the [type inference] and [trait solving]. [`hir_analysis`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/index.html [`hir_typeck`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/index.html -[type inference]: ./type-inference.md -[trait solving]: ./traits/resolution.md +[type inference]: ./../type-inference.md +[trait solving]: ./../traits/resolution.md ## Type collection @@ -40,7 +40,7 @@ type *checking*). For more details, see the [`collect`][collect] module. -[queries]: ./query.md +[queries]: ../query.md [collect]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/collect/index.html **TODO**: actually talk about type checking... [#1161](https://github.com/rust-lang/rustc-dev-guide/issues/1161) diff --git a/src/doc/rustc-dev-guide/src/overview.md b/src/doc/rustc-dev-guide/src/overview.md index f83b5b2e181c8..23cc94d41846d 100644 --- a/src/doc/rustc-dev-guide/src/overview.md +++ b/src/doc/rustc-dev-guide/src/overview.md @@ -130,7 +130,7 @@ with additional low-level types and annotations added (e.g. an ELF object or the final binary. [*trait solving*]: traits/resolution.md -[*type checking*]: type-checking.md +[*type checking*]: hir-typeck/summary.md [*type inference*]: type-inference.md [`bump`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/parser/struct.Parser.html#method.bump [`check`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/parser/struct.Parser.html#method.check diff --git a/src/doc/rustc-dev-guide/src/thir.md b/src/doc/rustc-dev-guide/src/thir.md index 3d3dafaef49ba..d5f7d8b5fcb70 100644 --- a/src/doc/rustc-dev-guide/src/thir.md +++ b/src/doc/rustc-dev-guide/src/thir.md @@ -5,7 +5,7 @@ The THIR ("Typed High-Level Intermediate Representation"), previously called HAI [type checking]. It is (as of January 2024) used for [MIR construction], [exhaustiveness checking], and [unsafety checking]. -[type checking]: ./type-checking.md +[type checking]: ./hir-typeck/summary.md [MIR construction]: ./mir/construction.md [exhaustiveness checking]: ./pat-exhaustive-checking.md [unsafety checking]: ./unsafety-checking.md diff --git a/src/doc/rustc-dev-guide/src/traits/canonical-queries.md b/src/doc/rustc-dev-guide/src/traits/canonical-queries.md index 792858bd2af8e..389f380e4b8de 100644 --- a/src/doc/rustc-dev-guide/src/traits/canonical-queries.md +++ b/src/doc/rustc-dev-guide/src/traits/canonical-queries.md @@ -214,7 +214,7 @@ As a result of this assignment, the type of `u` is forced to be `Option>`, where `?V` represents the element type of the vector. This in turn implies that `?U` is [unified] to `Vec`. -[unified]: ../type-checking.html +[unified]: ../hir-typeck/summary.md Let's suppose that the type checker decides to revisit the "as-yet-unproven" trait obligation we saw before, `Vec: From c3c14018cf98d2944b206716b8184e2caa9a59ac Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Sat, 29 Nov 2025 19:41:44 +0200 Subject: [PATCH 14/25] do not mess with a &[u8; 2] --- src/doc/rustc-dev-guide/ci/sembr/src/main.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/rustc-dev-guide/ci/sembr/src/main.rs b/src/doc/rustc-dev-guide/ci/sembr/src/main.rs index b74dbbe56bfe6..7ace34aed984a 100644 --- a/src/doc/rustc-dev-guide/ci/sembr/src/main.rs +++ b/src/doc/rustc-dev-guide/ci/sembr/src/main.rs @@ -27,7 +27,7 @@ static REGEX_IGNORE_END: LazyLock = static REGEX_IGNORE_LINK_TARGETS: LazyLock = LazyLock::new(|| Regex::new(r"^\[.+\]: ").unwrap()); static REGEX_SPLIT: LazyLock = - LazyLock::new(|| Regex::new(r"([^\.\d\-\*]\.|[^r]\?|;|!)\s").unwrap()); + LazyLock::new(|| Regex::new(r"([^\.\d\-\*]\.|[^r]\?|!)\s").unwrap()); // list elements, numbered (1.) or not (- and *) static REGEX_LIST_ENTRY: LazyLock = LazyLock::new(|| Regex::new(r"^\s*(\d\.|\-|\*)\s+").unwrap()); @@ -196,7 +196,7 @@ fn lengthen_lines(content: &str, limit: usize) -> String { fn test_sembr() { let original = " # some. heading -must! be; split? +must! be. split? 1. ignore a dot after number. but no further ignore | tables ignore e.g. and @@ -214,7 +214,7 @@ git log main.. compiler let expected = " # some. heading must! -be; +be. split? 1. ignore a dot after number. but no further From e26128a3c71a553e2c00e58253a48ab95dbb4dc8 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Sat, 29 Nov 2025 19:42:53 +0200 Subject: [PATCH 15/25] sembr tests/crater.md --- src/doc/rustc-dev-guide/src/tests/crater.md | 41 +++++++++++---------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tests/crater.md b/src/doc/rustc-dev-guide/src/tests/crater.md index 96bb5a4f2ae67..df7e8ef0b7ed1 100644 --- a/src/doc/rustc-dev-guide/src/tests/crater.md +++ b/src/doc/rustc-dev-guide/src/tests/crater.md @@ -1,43 +1,46 @@ # Crater [Crater](https://github.com/rust-lang/crater) is a tool for compiling and -running tests for _every_ crate on [crates.io](https://crates.io) (and a few on -GitHub). It is mainly used for checking the extent of breakage when implementing +running tests for _every_ crate on [crates.io](https://crates.io) (and a few on GitHub). +It is mainly used for checking the extent of breakage when implementing potentially breaking changes and ensuring lack of breakage by running beta vs stable compiler versions. ## When to run Crater You should request a Crater run if your PR makes large changes to the compiler -or could cause breakage. If you are unsure, feel free to ask your PR's reviewer. +or could cause breakage. +If you are unsure, feel free to ask your PR's reviewer. ## Requesting Crater Runs The Rust team maintains a few machines that can be used for Crater runs -on the changes introduced by a PR. If your PR needs a Crater run, leave a -comment for the triage team in the PR thread. Please inform the team whether you -require a "check-only" Crater run, a "build only" Crater run, or a -"build-and-test" Crater run. The difference is primarily in time; -if you're not sure, go for the build-and-test run. If +on the changes introduced by a PR. +If your PR needs a Crater run, leave a comment for the triage team in the PR thread. +Please inform the team whether you +require a "check-only" Crater run, a "build only" Crater run, or a "build-and-test" Crater run. +The difference is primarily in time; +if you're not sure, go for the build-and-test run. +If making changes that will only have an effect at compile-time (e.g., implementing a new trait), then you only need a check run. -Your PR will be enqueued by the triage team and the results will be posted when -they are ready. Check runs will take around ~3-4 days, and the other two taking -5-6 days on average. +Your PR will be enqueued by the triage team and the results will be posted when they are ready. +Check runs will take around ~3-4 days, and the other two taking 5-6 days on average. -While Crater is really useful, it is also important to be aware of a few -caveats: +While Crater is really useful, it is also important to be aware of a few caveats: -- Not all code is on crates.io! There is a lot of code in repos on GitHub and - elsewhere. Also, companies may not wish to publish their code. Thus, a - successful Crater run does not mean there will be no +- Not all code is on crates.io! + There is a lot of code in repos on GitHub and elsewhere. + Also, companies may not wish to publish their code. + Thus, a successful Crater run does not mean there will be no breakage; you still need to be careful. -- Crater only runs Linux builds on x86_64. Thus, other architectures and - platforms are not tested. Critically, this includes Windows. +- Crater only runs Linux builds on x86_64. Thus, other architectures and platforms are not tested. + Critically, this includes Windows. -- Many crates are not tested. This could be for a lot of reasons, including that +- Many crates are not tested. + This could be for a lot of reasons, including that the crate doesn't compile any more (e.g. used old nightly features), has broken or flaky tests, requires network access, or other reasons. From c58288a411123f898a61b1b1e7d90b6fa040bbaa Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Sat, 29 Nov 2025 20:00:25 +0200 Subject: [PATCH 16/25] manual formatting improvement --- src/doc/rustc-dev-guide/src/tests/crater.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tests/crater.md b/src/doc/rustc-dev-guide/src/tests/crater.md index df7e8ef0b7ed1..beea10aab7706 100644 --- a/src/doc/rustc-dev-guide/src/tests/crater.md +++ b/src/doc/rustc-dev-guide/src/tests/crater.md @@ -21,9 +21,8 @@ Please inform the team whether you require a "check-only" Crater run, a "build only" Crater run, or a "build-and-test" Crater run. The difference is primarily in time; if you're not sure, go for the build-and-test run. -If -making changes that will only have an effect at compile-time (e.g., implementing -a new trait), then you only need a check run. +If making changes that will only have an effect at compile-time +(e.g., implementing a new trait), then you only need a check run. Your PR will be enqueued by the triage team and the results will be posted when they are ready. Check runs will take around ~3-4 days, and the other two taking 5-6 days on average. From c5e928d5ac9a65e237aa8158a30c0cca35d30356 Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Mon, 1 Dec 2025 04:28:14 +0000 Subject: [PATCH 17/25] Prepare for merging from rust-lang/rust This updates the rust-version file to dfe1b8c97bcde283102f706d5dcdc3649e5e12e3. --- src/doc/rustc-dev-guide/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index bddb68a06b02c..7a84872f266d1 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -1be6b13be73dc12e98e51b403add4c41a0b77759 +dfe1b8c97bcde283102f706d5dcdc3649e5e12e3 From b55796facaf80bc5f7ba2855672280ca22c1766d Mon Sep 17 00:00:00 2001 From: Boxy Date: Mon, 1 Dec 2025 16:22:27 +0000 Subject: [PATCH 18/25] Uppper typo Co-authored-by: Santiago Pastorino --- src/doc/rustc-dev-guide/src/hir-typeck/coercions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/hir-typeck/coercions.md b/src/doc/rustc-dev-guide/src/hir-typeck/coercions.md index 384ea9bcf0306..158ac0885d323 100644 --- a/src/doc/rustc-dev-guide/src/hir-typeck/coercions.md +++ b/src/doc/rustc-dev-guide/src/hir-typeck/coercions.md @@ -2,7 +2,7 @@ Coercions are implicit operations which transform a value into a different type. A coercion *site* is a position where a coercion is able to be implicitly performed. There are two kinds of coercion sites: - one-to-one -- LUB (Least-Uppper-Bound) +- LUB (Least-Upper-Bound) ```rust let one_to_one_coercion: &u32 = &mut 8; From 5e0b1533469354abe4f2e447598cd08aea1c002c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Tue, 2 Dec 2025 11:47:22 +0100 Subject: [PATCH 19/25] Fix example which wrongly implied that the upper bound of edition ranges was inclusive And explicitly state the fact that the upper bound is exclusive since only giving an example is too implicit. --- src/doc/rustc-dev-guide/src/tests/directives.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index 4226c1750ef2c..7cf5869b719cc 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -273,7 +273,9 @@ This affects which edition is used by `./x test` to run the test. For example: - A test with the `//@ edition: 2018` directive will only run under the 2018 edition. -- A test with the `//@ edition: 2015..2021` directive can be run under the 2015, 2018, and 2021 editions. +- A test with the `//@ edition: 2015..2021` directive can be run under the 2015 and the 2018 edition, + so the upper bound is exclusive just like in Rust + (note that there's no equivalent to Rust's `..=` where the upper bound is inclusive). However, CI will only run the test with the lowest edition in the range (which is 2015 in this example). - A test with the `//@ edition: 2018..` directive will run under 2018 edition or greater. However, CI will only run the test with the lowest edition in the range (which is 2018 in this example). From f113df337548ae54d83657fdb54ba887fbfd5c31 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 2 Dec 2025 16:44:15 -0800 Subject: [PATCH 20/25] Remove pagetoc This has been subsumed by the update to mdbook 0.5. I just forgot to remove this in https://github.com/rust-lang/rustc-dev-guide/pull/2652. --- src/doc/rustc-dev-guide/README.md | 5 -- src/doc/rustc-dev-guide/book.toml | 2 - src/doc/rustc-dev-guide/pagetoc.css | 84 ---------------------- src/doc/rustc-dev-guide/pagetoc.js | 104 ---------------------------- 4 files changed, 195 deletions(-) delete mode 100644 src/doc/rustc-dev-guide/pagetoc.css delete mode 100644 src/doc/rustc-dev-guide/pagetoc.js diff --git a/src/doc/rustc-dev-guide/README.md b/src/doc/rustc-dev-guide/README.md index 1ad895aeda2e1..fee7cf042e1f8 100644 --- a/src/doc/rustc-dev-guide/README.md +++ b/src/doc/rustc-dev-guide/README.md @@ -65,11 +65,6 @@ following example. ENABLE_LINKCHECK=1 mdbook serve ``` -### Table of Contents - -Each page has a TOC that is automatically generated by `pagetoc.js`. -There is an associated `pagetoc.css`, for styling. - ## Synchronizing josh subtree with rustc This repository is linked to `rust-lang/rust` as a [josh](https://josh-project.github.io/josh/intro.html) subtree. You can use the [rustc-josh-sync](https://github.com/rust-lang/josh-sync) tool to perform synchronization. diff --git a/src/doc/rustc-dev-guide/book.toml b/src/doc/rustc-dev-guide/book.toml index efb13101c8de2..15a597e5addbe 100644 --- a/src/doc/rustc-dev-guide/book.toml +++ b/src/doc/rustc-dev-guide/book.toml @@ -15,9 +15,7 @@ edit-url-template = "https://github.com/rust-lang/rustc-dev-guide/edit/main/{pat additional-js = [ "mermaid.min.js", "mermaid-init.js", - "pagetoc.js", ] -additional-css = ["pagetoc.css"] [output.html.search] use-boolean-and = true diff --git a/src/doc/rustc-dev-guide/pagetoc.css b/src/doc/rustc-dev-guide/pagetoc.css deleted file mode 100644 index fa709194f3755..0000000000000 --- a/src/doc/rustc-dev-guide/pagetoc.css +++ /dev/null @@ -1,84 +0,0 @@ -/* Inspired by https://github.com/JorelAli/mdBook-pagetoc/tree/98ee241 (under WTFPL) */ - -:root { - --toc-width: 270px; - --center-content-toc-shift: calc(-1 * var(--toc-width) / 2); -} - -.nav-chapters { - /* adjust width of buttons that bring to the previous or the next page */ - min-width: 50px; -} - -@media only screen { - @media (max-width: 1179px) { - .sidebar-hidden #sidetoc { - display: none; - } - } - - @media (max-width: 1439px) { - .sidebar-visible #sidetoc { - display: none; - } - } - - @media (1180px <= width <= 1439px) { - .sidebar-hidden main { - position: relative; - left: var(--center-content-toc-shift); - } - } - - @media (1440px <= width <= 1700px) { - .sidebar-visible main { - position: relative; - left: var(--center-content-toc-shift); - } - } - - #sidetoc { - margin-left: calc(100% + 20px); - } - #pagetoc { - position: fixed; - /* adjust TOC width */ - width: var(--toc-width); - height: calc(100vh - var(--menu-bar-height) - 0.67em * 4); - overflow: auto; - } - #pagetoc a { - border-left: 1px solid var(--sidebar-bg); - color: var(--fg); - display: block; - padding-bottom: 5px; - padding-top: 5px; - padding-left: 10px; - text-align: left; - text-decoration: none; - } - #pagetoc a:hover, - #pagetoc a.active { - background: var(--sidebar-bg); - color: var(--sidebar-active) !important; - } - #pagetoc .active { - background: var(--sidebar-bg); - color: var(--sidebar-active); - } - #pagetoc .pagetoc-H2 { - padding-left: 20px; - } - #pagetoc .pagetoc-H3 { - padding-left: 40px; - } - #pagetoc .pagetoc-H4 { - padding-left: 60px; - } -} - -@media print { - #sidetoc { - display: none; - } -} diff --git a/src/doc/rustc-dev-guide/pagetoc.js b/src/doc/rustc-dev-guide/pagetoc.js deleted file mode 100644 index 927a5b10749b5..0000000000000 --- a/src/doc/rustc-dev-guide/pagetoc.js +++ /dev/null @@ -1,104 +0,0 @@ -// Inspired by https://github.com/JorelAli/mdBook-pagetoc/tree/98ee241 (under WTFPL) - -let activeHref = location.href; -function updatePageToc(elem = undefined) { - let selectedPageTocElem = elem; - const pagetoc = document.getElementById("pagetoc"); - - function getRect(element) { - return element.getBoundingClientRect(); - } - - function overflowTop(container, element) { - return getRect(container).top - getRect(element).top; - } - - function overflowBottom(container, element) { - return getRect(container).bottom - getRect(element).bottom; - } - - // We've not selected a heading to highlight, and the URL needs updating - // so we need to find a heading based on the URL - if (selectedPageTocElem === undefined && location.href !== activeHref) { - activeHref = location.href; - for (const pageTocElement of pagetoc.children) { - if (pageTocElement.href === activeHref) { - selectedPageTocElem = pageTocElement; - } - } - } - - // We still don't have a selected heading, let's try and find the most - // suitable heading based on the scroll position - if (selectedPageTocElem === undefined) { - const margin = window.innerHeight / 3; - - const headers = document.getElementsByClassName("header"); - for (let i = 0; i < headers.length; i++) { - const header = headers[i]; - if (selectedPageTocElem === undefined && getRect(header).top >= 0) { - if (getRect(header).top < margin) { - selectedPageTocElem = header; - } else { - selectedPageTocElem = headers[Math.max(0, i - 1)]; - } - } - // a very long last section's heading is over the screen - if (selectedPageTocElem === undefined && i === headers.length - 1) { - selectedPageTocElem = header; - } - } - } - - // Remove the active flag from all pagetoc elements - for (const pageTocElement of pagetoc.children) { - pageTocElement.classList.remove("active"); - } - - // If we have a selected heading, set it to active and scroll to it - if (selectedPageTocElem !== undefined) { - for (const pageTocElement of pagetoc.children) { - if (selectedPageTocElem.href.localeCompare(pageTocElement.href) === 0) { - pageTocElement.classList.add("active"); - if (overflowTop(pagetoc, pageTocElement) > 0) { - pagetoc.scrollTop = pageTocElement.offsetTop; - } - if (overflowBottom(pagetoc, pageTocElement) < 0) { - pagetoc.scrollTop -= overflowBottom(pagetoc, pageTocElement); - } - } - } - } -} - -if (document.getElementById("sidetoc") === null && - document.getElementsByClassName("header").length > 0) { - // The sidetoc element doesn't exist yet, let's create it - - // Create the empty sidetoc and pagetoc elements - const sidetoc = document.createElement("div"); - const pagetoc = document.createElement("div"); - sidetoc.id = "sidetoc"; - pagetoc.id = "pagetoc"; - sidetoc.appendChild(pagetoc); - - // And append them to the current DOM - const main = document.querySelector('main'); - main.insertBefore(sidetoc, main.firstChild); - - // Populate sidebar on load - window.addEventListener("load", () => { - for (const header of document.getElementsByClassName("header")) { - const link = document.createElement("a"); - link.innerHTML = header.innerHTML; - link.href = header.hash; - link.classList.add("pagetoc-" + header.parentElement.tagName); - document.getElementById("pagetoc").appendChild(link); - link.onclick = () => updatePageToc(link); - } - updatePageToc(); - }); - - // Update page table of contents selected heading on scroll - window.addEventListener("scroll", () => updatePageToc()); -} From 0ab78c152300499d0c17344e4e12d299313910e7 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 3 Dec 2025 12:21:03 +0000 Subject: [PATCH 21/25] `is_const_default_method` is completely handled by the `constness` query --- compiler/rustc_const_eval/src/check_consts/mod.rs | 6 ++++-- compiler/rustc_metadata/src/rmeta/encoder.rs | 3 +-- compiler/rustc_middle/src/hir/map.rs | 1 - compiler/rustc_middle/src/ty/mod.rs | 5 ----- compiler/rustc_mir_transform/src/lib.rs | 3 +-- 5 files changed, 6 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_const_eval/src/check_consts/mod.rs b/compiler/rustc_const_eval/src/check_consts/mod.rs index 4a88d039ef3c4..e9824400ab7a1 100644 --- a/compiler/rustc_const_eval/src/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/check_consts/mod.rs @@ -95,8 +95,10 @@ pub fn rustc_allow_const_fn_unstable( /// unstable features, not even recursively), and those that are not. pub fn is_fn_or_trait_safe_to_expose_on_stable(tcx: TyCtxt<'_>, def_id: DefId) -> bool { // A default body in a `const trait` is const-stable when the trait is const-stable. - if tcx.is_const_default_method(def_id) { - return is_fn_or_trait_safe_to_expose_on_stable(tcx, tcx.parent(def_id)); + if let Some(trait_id) = tcx.trait_of_assoc(def_id) + && tcx.is_const_trait(trait_id) + { + return is_fn_or_trait_safe_to_expose_on_stable(tcx, trait_id); } match tcx.lookup_const_stability(def_id) { diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 9fac68039f52c..4085fd5e70f16 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1132,8 +1132,7 @@ fn should_encode_mir( && (generics.requires_monomorphization(tcx) || tcx.cross_crate_inlinable(def_id))); // The function has a `const` modifier or is in a `const trait`. - let is_const_fn = tcx.is_const_fn(def_id.to_def_id()) - || tcx.is_const_default_method(def_id.to_def_id()); + let is_const_fn = tcx.is_const_fn(def_id.to_def_id()); (is_const_fn, opt) } // The others don't have MIR. diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs index 430cd329408f5..bf3192d9df173 100644 --- a/compiler/rustc_middle/src/hir/map.rs +++ b/compiler/rustc_middle/src/hir/map.rs @@ -321,7 +321,6 @@ impl<'tcx> TyCtxt<'tcx> { BodyOwnerKind::Fn | BodyOwnerKind::Closure if self.is_const_fn(def_id) => { ConstContext::ConstFn } - BodyOwnerKind::Fn if self.is_const_default_method(def_id) => ConstContext::ConstFn, BodyOwnerKind::Fn | BodyOwnerKind::Closure | BodyOwnerKind::GlobalAsm => return None, }; diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 61b3059ab4253..d3e0fbb955c4d 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2224,11 +2224,6 @@ impl<'tcx> TyCtxt<'tcx> { self.trait_def(def_id).constness == hir::Constness::Const } - #[inline] - pub fn is_const_default_method(self, def_id: DefId) -> bool { - matches!(self.trait_of_assoc(def_id), Some(trait_id) if self.is_const_trait(trait_id)) - } - pub fn impl_method_has_trait_impl_trait_tys(self, def_id: DefId) -> bool { if self.def_kind(def_id) != DefKind::AssocFn { return false; diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 18b798c01faab..9a964d5e01bb5 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -429,8 +429,7 @@ fn mir_promoted( let const_qualifs = match tcx.def_kind(def) { DefKind::Fn | DefKind::AssocFn | DefKind::Closure - if tcx.constness(def) == hir::Constness::Const - || tcx.is_const_default_method(def.to_def_id()) => + if tcx.constness(def) == hir::Constness::Const => { tcx.mir_const_qualif(def) } From 8d70cfecbeee900ca8f7040362dcd3532fb3b895 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 2 Dec 2025 21:18:26 +0100 Subject: [PATCH 22/25] Add a section about `rustc_clean` --- .../rustc-dev-guide/src/tests/compiletest.md | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md index c2aee82bdd744..a691380234cd1 100644 --- a/src/doc/rustc-dev-guide/src/tests/compiletest.md +++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md @@ -187,8 +187,26 @@ still pass. cause an Internal Compiler Error (ICE). This is a highly specialized directive to check that the incremental cache continues to work after an ICE. -[`tests/incremental`]: https://github.com/rust-lang/rust/tree/HEAD/tests/incremental - +Incremental tests may use the attribute `#[rustc_clean(...)]` attribute. This attribute compares +the fingerprint from the current compilation session with the previous one. +The first revision should never have an active `rustc_clean` attribute, since it will always be dirty. + +In the default mode, it asserts that the fingerprints must be the same. +The attribute takes the following arguments: + +* `cfg=""` — checks the cfg condition ``, and only runs the check if the config condition evaluates to true. + This can be used to only run the `rustc_clean` attribute in a specific revision. +* `except=",,..."` — asserts that the query results for the listed queries must be different, + rather than the same. +* `loaded_from_disk=",,..."` — asserts that the query results for the listed queries + were actually loaded from disk (not just marked green). + This can be useful to ensure that a test is actually exercising the deserialization + logic for a particular query result. This can be combined with `except`. + +A simple example of a test using `rustc_clean` is the [hello_world test]. + +[`tests/incremental`]: https://github.com/rust-lang/rust/tree/7b42543/tests/incremental +[hello_world test]: https://github.com/rust-lang/rust/blob/646a3f8c15baefb98dc6e0c1c1ba3356db702d2a/tests/incremental/hello_world.rs ### Debuginfo tests From ce050d6571c0bf4e37af08a6f45e92a54c7a22eb Mon Sep 17 00:00:00 2001 From: Redddy Date: Fri, 5 Dec 2025 02:02:48 +0900 Subject: [PATCH 23/25] Add Zed to quickstart --- src/doc/rustc-dev-guide/src/building/quickstart.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/building/quickstart.md b/src/doc/rustc-dev-guide/src/building/quickstart.md index 97314d8036953..42058246cd05f 100644 --- a/src/doc/rustc-dev-guide/src/building/quickstart.md +++ b/src/doc/rustc-dev-guide/src/building/quickstart.md @@ -66,6 +66,6 @@ questions, [the full chapter](./how-to-build-and-run.md) might contain the answers, and if it doesn't, feel free to ask for help on [Zulip](https://rust-lang.zulipchat.com/#narrow/stream/182449-t-compiler.2Fhelp). -If you use VSCode, Vim, Emacs or Helix, `./x setup` will ask you if you want to +If you use VSCode, Vim, Emacs, Helix or Zed, `./x setup` will ask you if you want to set up the editor config. For more information, check out [suggested workflows](./suggested.md). From 8f59eb017753e4e72f0d2b0a736ba9172002e321 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Thu, 4 Dec 2025 20:59:53 +0100 Subject: [PATCH 24/25] Move attribute lints to `rustc_lint` --- Cargo.lock | 3 +- compiler/rustc_attr_parsing/messages.ftl | 31 ----- .../src/attributes/inline.rs | 5 +- .../src/attributes/link_attrs.rs | 5 +- .../src/attributes/macro_attrs.rs | 15 +-- .../src/attributes/prelude.rs | 2 - .../src/attributes/test_attrs.rs | 13 +- compiler/rustc_attr_parsing/src/context.rs | 20 ++- compiler/rustc_attr_parsing/src/interface.rs | 17 ++- compiler/rustc_attr_parsing/src/lib.rs | 2 - compiler/rustc_attr_parsing/src/lints.rs | 114 ------------------ compiler/rustc_attr_parsing/src/safety.rs | 3 + .../src/session_diagnostics.rs | 65 +--------- .../rustc_attr_parsing/src/target_checking.rs | 31 ++++- .../rustc_attr_parsing/src/validate_attr.rs | 5 +- compiler/rustc_errors/src/lib.rs | 16 +-- compiler/rustc_hir/Cargo.toml | 1 + compiler/rustc_hir/src/lints.rs | 44 +------ compiler/rustc_hir_analysis/Cargo.toml | 2 +- compiler/rustc_hir_analysis/src/lib.rs | 14 ++- compiler/rustc_lint/messages.ftl | 34 ++++++ compiler/rustc_lint/src/early/diagnostics.rs | 54 ++++++++- compiler/rustc_lint/src/lib.rs | 2 +- compiler/rustc_lint/src/lints.rs | 65 ++++++++++ compiler/rustc_lint_defs/src/lib.rs | 32 +++++ compiler/rustc_middle/src/ty/context.rs | 16 +-- compiler/rustc_session/src/session.rs | 17 +-- 27 files changed, 284 insertions(+), 344 deletions(-) delete mode 100644 compiler/rustc_attr_parsing/src/lints.rs diff --git a/Cargo.lock b/Cargo.lock index 2cc2e094e9f9c..003bc6adb0e88 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3945,6 +3945,7 @@ dependencies = [ "rustc_hashes", "rustc_hir_id", "rustc_index", + "rustc_lint_defs", "rustc_macros", "rustc_serialize", "rustc_span", @@ -3962,7 +3963,6 @@ dependencies = [ "rustc_abi", "rustc_arena", "rustc_ast", - "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", "rustc_feature", @@ -3970,6 +3970,7 @@ dependencies = [ "rustc_hir", "rustc_index", "rustc_infer", + "rustc_lint", "rustc_lint_defs", "rustc_macros", "rustc_middle", diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index a2a5f8ab14236..0d53ed9d97a54 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -14,18 +14,6 @@ attr_parsing_deprecated_item_suggestion = .help = add `#![feature(deprecated_suggestion)]` to the crate root .note = see #94785 for more details -attr_parsing_empty_attribute = - unused attribute - .suggestion = {$valid_without_list -> - [true] remove these parentheses - *[other] remove this attribute - } - .note = {$valid_without_list -> - [true] using `{$attr_path}` with an empty list is equivalent to not using a list at all - *[other] using `{$attr_path}` with an empty list has no effect - } - - attr_parsing_empty_confusables = expected at least one confusable name attr_parsing_empty_link_name = @@ -119,19 +107,9 @@ attr_parsing_invalid_repr_hint_no_value = attr_parsing_invalid_since = 'since' must be a Rust version number, such as "1.31.0" -attr_parsing_invalid_style = {$is_used_as_inner -> - [false] crate-level attribute should be an inner attribute: add an exclamation mark: `#![{$name}]` - *[other] the `#![{$name}]` attribute can only be used at the crate root - } - .note = This attribute does not have an `!`, which means it is applied to this {$target} - attr_parsing_invalid_target = `#[{$name}]` attribute cannot be used on {$target} .help = `#[{$name}]` can {$only}be applied to {$applied} .suggestion = remove the attribute -attr_parsing_invalid_target_lint = `#[{$name}]` attribute cannot be used on {$target} - .warn = {-attr_parsing_previously_accepted} - .help = `#[{$name}]` can {$only}be applied to {$applied} - .suggestion = remove the attribute attr_parsing_limit_invalid = `limit` must be a non-negative integer @@ -250,19 +228,10 @@ attr_parsing_unsupported_literal_generic = attr_parsing_unsupported_literal_suggestion = consider removing the prefix -attr_parsing_unused_duplicate = - unused attribute - .suggestion = remove this attribute - .note = attribute also specified here - .warn = {-attr_parsing_previously_accepted} - attr_parsing_unused_multiple = multiple `{$name}` attributes .suggestion = remove this attribute .note = attribute also specified here --attr_parsing_previously_accepted = - this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - attr_parsing_whole_archive_needs_static = linking modifier `whole-archive` is only compatible with `static` linking kind diff --git a/compiler/rustc_attr_parsing/src/attributes/inline.rs b/compiler/rustc_attr_parsing/src/attributes/inline.rs index eda272fb7f2b7..fba1a663c0575 100644 --- a/compiler/rustc_attr_parsing/src/attributes/inline.rs +++ b/compiler/rustc_attr_parsing/src/attributes/inline.rs @@ -3,6 +3,7 @@ // SingleAttributeParser which is what we have two of here. use rustc_hir::attrs::{AttributeKind, InlineAttr}; +use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT; use super::prelude::*; @@ -56,9 +57,7 @@ impl SingleAttributeParser for InlineParser { } } ArgParser::NameValue(_) => { - let suggestions = cx.suggestions(); - let span = cx.attr_span; - cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span); + cx.warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT); return None; } } diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 5a4a4e7599101..46fa8ee713438 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -2,6 +2,7 @@ use rustc_feature::Features; use rustc_hir::attrs::AttributeKind::{LinkName, LinkOrdinal, LinkSection}; use rustc_hir::attrs::*; use rustc_session::Session; +use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT; use rustc_session::parse::feature_err; use rustc_span::kw; use rustc_target::spec::{Arch, BinaryFormat}; @@ -71,9 +72,7 @@ impl CombineAttributeParser for LinkParser { // Specifically `#[link = "dl"]` is accepted with a FCW // For more information, see https://github.com/rust-lang/rust/pull/143193 ArgParser::NameValue(nv) if nv.value_as_str().is_some_and(|v| v == sym::dl) => { - let suggestions = cx.suggestions(); - let span = cx.attr_span; - cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span); + cx.warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT); return None; } _ => { diff --git a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs index 787003519e78b..d2fa1d440f407 100644 --- a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs @@ -1,5 +1,6 @@ use rustc_errors::DiagArgValue; use rustc_hir::attrs::MacroUseArgs; +use rustc_session::lint::builtin::INVALID_MACRO_EXPORT_ARGUMENTS; use super::prelude::*; use crate::session_diagnostics::IllFormedAttributeInputLint; @@ -152,23 +153,13 @@ impl SingleAttributeParser for MacroExportParser { ArgParser::NoArgs => false, ArgParser::List(list) => { let Some(l) = list.single() else { - let span = cx.attr_span; - let suggestions = cx.suggestions(); - cx.emit_lint( - AttributeLintKind::InvalidMacroExportArguments { suggestions }, - span, - ); + cx.warn_ill_formed_attribute_input(INVALID_MACRO_EXPORT_ARGUMENTS); return None; }; match l.meta_item().and_then(|i| i.path().word_sym()) { Some(sym::local_inner_macros) => true, _ => { - let span = cx.attr_span; - let suggestions = cx.suggestions(); - cx.emit_lint( - AttributeLintKind::InvalidMacroExportArguments { suggestions }, - span, - ); + cx.warn_ill_formed_attribute_input(INVALID_MACRO_EXPORT_ARGUMENTS); return None; } } diff --git a/compiler/rustc_attr_parsing/src/attributes/prelude.rs b/compiler/rustc_attr_parsing/src/attributes/prelude.rs index 980366b5c372f..65c408fa6358c 100644 --- a/compiler/rustc_attr_parsing/src/attributes/prelude.rs +++ b/compiler/rustc_attr_parsing/src/attributes/prelude.rs @@ -4,8 +4,6 @@ pub(super) use rustc_feature::{AttributeTemplate, template}; #[doc(hidden)] pub(super) use rustc_hir::attrs::AttributeKind; #[doc(hidden)] -pub(super) use rustc_hir::lints::AttributeLintKind; -#[doc(hidden)] pub(super) use rustc_hir::{MethodKind, Target}; #[doc(hidden)] pub(super) use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym}; diff --git a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs index 23ecc0bf7d29f..e0b006030758b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs @@ -1,3 +1,5 @@ +use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT; + use super::prelude::*; pub(crate) struct IgnoreParser; @@ -20,20 +22,13 @@ impl SingleAttributeParser for IgnoreParser { ArgParser::NoArgs => None, ArgParser::NameValue(name_value) => { let Some(str_value) = name_value.value_as_str() else { - let suggestions = cx.suggestions(); - let span = cx.attr_span; - cx.emit_lint( - AttributeLintKind::IllFormedAttributeInput { suggestions }, - span, - ); + cx.warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT); return None; }; Some(str_value) } ArgParser::List(_) => { - let suggestions = cx.suggestions(); - let span = cx.attr_span; - cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span); + cx.warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT); return None; } }, diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index dcb7341b4b5ec..bc74eaad50bc4 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -11,6 +11,7 @@ use rustc_hir::attrs::AttributeKind; use rustc_hir::lints::{AttributeLint, AttributeLintKind}; use rustc_hir::{AttrPath, CRATE_HIR_ID, HirId}; use rustc_session::Session; +use rustc_session::lint::{Lint, LintId}; use rustc_span::{ErrorGuaranteed, Span, Symbol}; use crate::AttributeParser; @@ -381,7 +382,7 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> { /// Emit a lint. This method is somewhat special, since lints emitted during attribute parsing /// must be delayed until after HIR is built. This method will take care of the details of /// that. - pub(crate) fn emit_lint(&mut self, lint: AttributeLintKind, span: Span) { + pub(crate) fn emit_lint(&mut self, lint: &'static Lint, kind: AttributeLintKind, span: Span) { if !matches!( self.stage.should_emit(), ShouldEmit::ErrorsAndLints | ShouldEmit::EarlyFatal { also_emit_lints: true } @@ -389,11 +390,12 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> { return; } let id = self.target_id; - (self.emit_lint)(AttributeLint { id, span, kind: lint }); + (self.emit_lint)(AttributeLint { lint_id: LintId::of(lint), id, span, kind }); } pub(crate) fn warn_unused_duplicate(&mut self, used_span: Span, unused_span: Span) { self.emit_lint( + rustc_session::lint::builtin::UNUSED_ATTRIBUTES, AttributeLintKind::UnusedDuplicate { this: unused_span, other: used_span, @@ -409,6 +411,7 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> { unused_span: Span, ) { self.emit_lint( + rustc_session::lint::builtin::UNUSED_ATTRIBUTES, AttributeLintKind::UnusedDuplicate { this: unused_span, other: used_span, @@ -632,14 +635,25 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { } pub(crate) fn warn_empty_attribute(&mut self, span: Span) { - let attr_path = self.attr_path.clone(); + let attr_path = self.attr_path.clone().to_string(); let valid_without_list = self.template.word; self.emit_lint( + rustc_session::lint::builtin::UNUSED_ATTRIBUTES, AttributeLintKind::EmptyAttribute { first_span: span, attr_path, valid_without_list }, span, ); } + pub(crate) fn warn_ill_formed_attribute_input(&mut self, lint: &'static Lint) { + let suggestions = self.suggestions(); + let span = self.attr_span; + self.emit_lint( + lint, + AttributeLintKind::IllFormedAttributeInput { suggestions, docs: None }, + span, + ); + } + pub(crate) fn suggestions(&self) -> Vec { let style = match self.parsed_description { // If the outer and inner spans are equal, we are parsing an embedded attribute diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index 87e29b7b0de6e..b26a4a29cd2e2 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -8,6 +8,7 @@ use rustc_hir::attrs::AttributeKind; use rustc_hir::lints::AttributeLint; use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, Target}; use rustc_session::Session; +use rustc_session::lint::BuiltinLintDiag; use rustc_span::{DUMMY_SP, Span, Symbol, sym}; use crate::context::{AcceptContext, FinalizeContext, SharedContext, Stage}; @@ -115,7 +116,12 @@ impl<'sess> AttributeParser<'sess, Early> { OmitDoc::Skip, std::convert::identity, |lint| { - crate::lints::emit_attribute_lint(&lint, sess); + sess.psess.buffer_lint( + lint.lint_id.lint, + lint.span, + lint.id, + BuiltinLintDiag::AttributeLint(lint.kind), + ) }, ) } @@ -183,8 +189,13 @@ impl<'sess> AttributeParser<'sess, Early> { sess, stage: Early { emit_errors }, }; - let mut emit_lint = |lint| { - crate::lints::emit_attribute_lint(&lint, sess); + let mut emit_lint = |lint: AttributeLint| { + sess.psess.buffer_lint( + lint.lint_id.lint, + lint.span, + lint.id, + BuiltinLintDiag::AttributeLint(lint.kind), + ) }; if let Some(safety) = attr_safety { parser.check_attribute_safety( diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index 7a7f2555287a2..7cef70f88e1ca 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -97,7 +97,6 @@ mod interface; /// like lists or name-value pairs. pub mod parser; -mod lints; mod safety; mod session_diagnostics; mod target_checking; @@ -111,7 +110,6 @@ pub use attributes::cfg_select::*; pub use attributes::util::{is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version}; pub use context::{Early, Late, OmitDoc, ShouldEmit}; pub use interface::AttributeParser; -pub use lints::emit_attribute_lint; pub use session_diagnostics::ParsedDescription; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_attr_parsing/src/lints.rs b/compiler/rustc_attr_parsing/src/lints.rs deleted file mode 100644 index a23884d7f71eb..0000000000000 --- a/compiler/rustc_attr_parsing/src/lints.rs +++ /dev/null @@ -1,114 +0,0 @@ -use std::borrow::Cow; - -use rustc_errors::{DiagArgValue, LintEmitter}; -use rustc_hir::Target; -use rustc_hir::lints::{AttributeLint, AttributeLintKind}; -use rustc_span::sym; - -use crate::session_diagnostics; - -pub fn emit_attribute_lint(lint: &AttributeLint, lint_emitter: L) { - let AttributeLint { id, span, kind } = lint; - - match kind { - &AttributeLintKind::UnusedDuplicate { this, other, warning } => lint_emitter - .emit_node_span_lint( - rustc_session::lint::builtin::UNUSED_ATTRIBUTES, - *id, - *span, - session_diagnostics::UnusedDuplicate { this, other, warning }, - ), - AttributeLintKind::IllFormedAttributeInput { suggestions } => { - lint_emitter.emit_node_span_lint( - rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT, - *id, - *span, - session_diagnostics::IllFormedAttributeInput { - num_suggestions: suggestions.len(), - suggestions: DiagArgValue::StrListSepByAnd( - suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), - ), - }, - ); - } - AttributeLintKind::InvalidMacroExportArguments { suggestions } => lint_emitter - .emit_node_span_lint( - rustc_session::lint::builtin::INVALID_MACRO_EXPORT_ARGUMENTS, - *id, - *span, - session_diagnostics::IllFormedAttributeInput { - num_suggestions: suggestions.len(), - suggestions: DiagArgValue::StrListSepByAnd( - suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), - ), - }, - ), - AttributeLintKind::EmptyAttribute { first_span, attr_path, valid_without_list } => { - lint_emitter.emit_node_span_lint( - rustc_session::lint::builtin::UNUSED_ATTRIBUTES, - *id, - *first_span, - session_diagnostics::EmptyAttributeList { - attr_span: *first_span, - attr_path: attr_path.clone(), - valid_without_list: *valid_without_list, - }, - ) - } - AttributeLintKind::InvalidTarget { name, target, applied, only } => lint_emitter - .emit_node_span_lint( - // This check is here because `deprecated` had its own lint group and removing this would be a breaking change - if name.segments[0].name == sym::deprecated - && ![ - Target::Closure, - Target::Expression, - Target::Statement, - Target::Arm, - Target::MacroCall, - ] - .contains(target) - { - rustc_session::lint::builtin::USELESS_DEPRECATED - } else { - rustc_session::lint::builtin::UNUSED_ATTRIBUTES - }, - *id, - *span, - session_diagnostics::InvalidTargetLint { - name: name.clone(), - target: target.plural_name(), - applied: DiagArgValue::StrListSepByAnd( - applied.into_iter().map(|i| Cow::Owned(i.to_string())).collect(), - ), - only, - attr_span: *span, - }, - ), - - &AttributeLintKind::InvalidStyle { ref name, is_used_as_inner, target, target_span } => { - lint_emitter.emit_node_span_lint( - rustc_session::lint::builtin::UNUSED_ATTRIBUTES, - *id, - *span, - session_diagnostics::InvalidAttrStyle { - name: name.clone(), - is_used_as_inner, - target_span: (!is_used_as_inner).then_some(target_span), - target, - }, - ) - } - &AttributeLintKind::UnsafeAttrOutsideUnsafe { - attribute_name_span, - sugg_spans: (left, right), - } => lint_emitter.emit_node_span_lint( - rustc_session::lint::builtin::UNSAFE_ATTR_OUTSIDE_UNSAFE, - *id, - *span, - session_diagnostics::UnsafeAttrOutsideUnsafeLint { - span: attribute_name_span, - suggestion: session_diagnostics::UnsafeAttrOutsideUnsafeSuggestion { left, right }, - }, - ), - } -} diff --git a/compiler/rustc_attr_parsing/src/safety.rs b/compiler/rustc_attr_parsing/src/safety.rs index ff385bf13aaa9..52baf2136173d 100644 --- a/compiler/rustc_attr_parsing/src/safety.rs +++ b/compiler/rustc_attr_parsing/src/safety.rs @@ -2,6 +2,8 @@ use rustc_ast::Safety; use rustc_feature::{AttributeSafety, BUILTIN_ATTRIBUTE_MAP}; use rustc_hir::AttrPath; use rustc_hir::lints::{AttributeLint, AttributeLintKind}; +use rustc_session::lint::LintId; +use rustc_session::lint::builtin::UNSAFE_ATTR_OUTSIDE_UNSAFE; use rustc_span::{Span, sym}; use crate::context::Stage; @@ -74,6 +76,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { ); } else { emit_lint(AttributeLint { + lint_id: LintId::of(UNSAFE_ATTR_OUTSIDE_UNSAFE), id: target_id, span: path_span, kind: AttributeLintKind::UnsafeAttrOutsideUnsafe { diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index f94f0867451f0..c4f6f9c6a38cb 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -6,8 +6,8 @@ use rustc_errors::{ Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, }; use rustc_feature::AttributeTemplate; -use rustc_hir::{AttrPath, Target}; -use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; +use rustc_hir::AttrPath; +use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; use crate::fluent_generated as fluent; @@ -417,25 +417,6 @@ pub(crate) struct UnusedMultiple { pub name: Symbol, } -#[derive(LintDiagnostic)] -#[diag(attr_parsing_unused_duplicate)] -pub(crate) struct UnusedDuplicate { - #[suggestion(code = "", applicability = "machine-applicable")] - pub this: Span, - #[note] - pub other: Span, - #[warning] - pub warning: bool, -} - -// FIXME(jdonszelmann): duplicated in rustc_lints, should be moved here completely. -#[derive(LintDiagnostic)] -#[diag(attr_parsing_ill_formed_attribute_input)] -pub(crate) struct IllFormedAttributeInput { - pub num_suggestions: usize, - pub suggestions: DiagArgValue, -} - #[derive(Diagnostic)] #[diag(attr_parsing_ill_formed_attribute_input)] pub(crate) struct IllFormedAttributeInputLint { @@ -501,29 +482,6 @@ pub(crate) struct EmptyConfusables { pub span: Span, } -#[derive(LintDiagnostic)] -#[diag(attr_parsing_empty_attribute)] -#[note] -pub(crate) struct EmptyAttributeList { - #[suggestion(code = "", applicability = "machine-applicable")] - pub attr_span: Span, - pub attr_path: AttrPath, - pub valid_without_list: bool, -} - -#[derive(LintDiagnostic)] -#[diag(attr_parsing_invalid_target_lint)] -#[warning] -#[help] -pub(crate) struct InvalidTargetLint { - pub name: AttrPath, - pub target: &'static str, - pub applied: DiagArgValue, - pub only: &'static str, - #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")] - pub attr_span: Span, -} - #[derive(Diagnostic)] #[help] #[diag(attr_parsing_invalid_target)] @@ -803,15 +761,6 @@ pub(crate) struct UnsafeAttrOutsideUnsafe { pub suggestion: UnsafeAttrOutsideUnsafeSuggestion, } -#[derive(LintDiagnostic)] -#[diag(attr_parsing_unsafe_attr_outside_unsafe)] -pub(crate) struct UnsafeAttrOutsideUnsafeLint { - #[label] - pub span: Span, - #[subdiagnostic] - pub suggestion: UnsafeAttrOutsideUnsafeSuggestion, -} - #[derive(Subdiagnostic)] #[multipart_suggestion( attr_parsing_unsafe_attr_outside_unsafe_suggestion, @@ -881,16 +830,6 @@ pub(crate) struct SuffixedLiteralInAttribute { pub span: Span, } -#[derive(LintDiagnostic)] -#[diag(attr_parsing_invalid_style)] -pub(crate) struct InvalidAttrStyle { - pub name: AttrPath, - pub is_used_as_inner: bool, - #[note] - pub target_span: Option, - pub target: Target, -} - #[derive(Diagnostic)] #[diag(attr_parsing_empty_link_name, code = E0454)] pub(crate) struct EmptyLinkName { diff --git a/compiler/rustc_attr_parsing/src/target_checking.rs b/compiler/rustc_attr_parsing/src/target_checking.rs index fabd364d3d7f8..88efb910c1601 100644 --- a/compiler/rustc_attr_parsing/src/target_checking.rs +++ b/compiler/rustc_attr_parsing/src/target_checking.rs @@ -5,6 +5,7 @@ use rustc_errors::DiagArgValue; use rustc_feature::Features; use rustc_hir::lints::AttributeLintKind; use rustc_hir::{MethodKind, Target}; +use rustc_span::sym; use crate::AttributeParser; use crate::context::{AcceptContext, Stage}; @@ -102,13 +103,31 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { let allowed_targets = allowed_targets.allowed_targets(); let (applied, only) = allowed_targets_applied(allowed_targets, target, cx.features); let name = cx.attr_path.clone(); + + let lint = if name.segments[0].name == sym::deprecated + && ![ + Target::Closure, + Target::Expression, + Target::Statement, + Target::Arm, + Target::MacroCall, + ] + .contains(&target) + { + rustc_session::lint::builtin::USELESS_DEPRECATED + } else { + rustc_session::lint::builtin::UNUSED_ATTRIBUTES + }; + let attr_span = cx.attr_span; cx.emit_lint( + lint, AttributeLintKind::InvalidTarget { - name, - target, + name: name.to_string(), + target: target.plural_name(), only: if only { "only " } else { "" }, applied, + attr_span, }, attr_span, ); @@ -145,15 +164,15 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { return; } - let lint = AttributeLintKind::InvalidStyle { - name: cx.attr_path.clone(), + let kind = AttributeLintKind::InvalidStyle { + name: cx.attr_path.to_string(), is_used_as_inner: cx.attr_style == AttrStyle::Inner, - target, + target: target.name(), target_span: cx.target_span, }; let attr_span = cx.attr_span; - cx.emit_lint(lint, attr_span); + cx.emit_lint(rustc_session::lint::builtin::UNUSED_ATTRIBUTES, kind, attr_span); } } diff --git a/compiler/rustc_attr_parsing/src/validate_attr.rs b/compiler/rustc_attr_parsing/src/validate_attr.rs index c57e0baea05f6..cd28677b6a8fd 100644 --- a/compiler/rustc_attr_parsing/src/validate_attr.rs +++ b/compiler/rustc_attr_parsing/src/validate_attr.rs @@ -11,6 +11,7 @@ use rustc_ast::{ use rustc_errors::{Applicability, FatalError, PResult}; use rustc_feature::{AttributeTemplate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute}; use rustc_hir::AttrPath; +use rustc_hir::lints::AttributeLintKind; use rustc_parse::parse_in; use rustc_session::errors::report_lit_error; use rustc_session::lint::BuiltinLintDiag; @@ -202,10 +203,10 @@ fn emit_malformed_attribute( ILL_FORMED_ATTRIBUTE_INPUT, span, ast::CRATE_NODE_ID, - BuiltinLintDiag::IllFormedAttributeInput { + BuiltinLintDiag::AttributeLint(AttributeLintKind::IllFormedAttributeInput { suggestions: suggestions.clone(), docs: template.docs, - }, + }), ); } else { suggestions.sort(); diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 5a7a178582e34..12e6700e0b8d6 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -64,8 +64,8 @@ pub use rustc_error_messages::{ fallback_fluent_bundle, fluent_bundle, into_diag_arg_using_display, }; use rustc_hashes::Hash128; +use rustc_lint_defs::LintExpectationId; pub use rustc_lint_defs::{Applicability, listify, pluralize}; -use rustc_lint_defs::{Lint, LintExpectationId}; use rustc_macros::{Decodable, Encodable}; pub use rustc_span::ErrorGuaranteed; pub use rustc_span::fatal_error::{FatalError, FatalErrorMarker}; @@ -106,20 +106,6 @@ rustc_data_structures::static_assert_size!(PResult<'_, ()>, 24); #[cfg(target_pointer_width = "64")] rustc_data_structures::static_assert_size!(PResult<'_, bool>, 24); -/// Used to avoid depending on `rustc_middle` in `rustc_attr_parsing`. -/// Always the `TyCtxt`. -pub trait LintEmitter: Copy { - type Id: Copy; - #[track_caller] - fn emit_node_span_lint( - self, - lint: &'static Lint, - hir_id: Self::Id, - span: impl Into, - decorator: impl for<'a> LintDiagnostic<'a, ()> + DynSend + 'static, - ); -} - #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Encodable, Decodable)] pub enum SuggestionStyle { /// Hide the suggested code when displaying this suggestion inline. diff --git a/compiler/rustc_hir/Cargo.toml b/compiler/rustc_hir/Cargo.toml index 1008a3e787d01..13e73acf07375 100644 --- a/compiler/rustc_hir/Cargo.toml +++ b/compiler/rustc_hir/Cargo.toml @@ -16,6 +16,7 @@ rustc_error_messages = { path = "../rustc_error_messages" } rustc_hashes = { path = "../rustc_hashes" } rustc_hir_id = { path = "../rustc_hir_id" } rustc_index = { path = "../rustc_index" } +rustc_lint_defs = { path = "../rustc_lint_defs" } rustc_macros = { path = "../rustc_macros" } rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_hir/src/lints.rs b/compiler/rustc_hir/src/lints.rs index a4c60fd2cc1ad..eba2d182d2c48 100644 --- a/compiler/rustc_hir/src/lints.rs +++ b/compiler/rustc_hir/src/lints.rs @@ -1,8 +1,10 @@ use rustc_data_structures::fingerprint::Fingerprint; +pub use rustc_lint_defs::AttributeLintKind; +use rustc_lint_defs::LintId; use rustc_macros::HashStable_Generic; use rustc_span::Span; -use crate::{AttrPath, HirId, Target}; +use crate::HirId; #[derive(Debug)] pub struct DelayedLints { @@ -24,46 +26,8 @@ pub enum DelayedLint { #[derive(Debug, HashStable_Generic)] pub struct AttributeLint { + pub lint_id: LintId, pub id: Id, pub span: Span, pub kind: AttributeLintKind, } - -#[derive(Debug, HashStable_Generic)] -pub enum AttributeLintKind { - /// Copy of `IllFormedAttributeInput` - /// specifically for the `invalid_macro_export_arguments` lint until that is removed, - /// see - InvalidMacroExportArguments { - suggestions: Vec, - }, - UnusedDuplicate { - this: Span, - other: Span, - warning: bool, - }, - IllFormedAttributeInput { - suggestions: Vec, - }, - EmptyAttribute { - first_span: Span, - attr_path: AttrPath, - valid_without_list: bool, - }, - InvalidTarget { - name: AttrPath, - target: Target, - applied: Vec, - only: &'static str, - }, - InvalidStyle { - name: AttrPath, - is_used_as_inner: bool, - target: Target, - target_span: Span, - }, - UnsafeAttrOutsideUnsafe { - attribute_name_span: Span, - sugg_spans: (Span, Span), - }, -} diff --git a/compiler/rustc_hir_analysis/Cargo.toml b/compiler/rustc_hir_analysis/Cargo.toml index e5017794d8f29..8d114862b3fe1 100644 --- a/compiler/rustc_hir_analysis/Cargo.toml +++ b/compiler/rustc_hir_analysis/Cargo.toml @@ -13,7 +13,6 @@ itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } -rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } @@ -21,6 +20,7 @@ rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_infer = { path = "../rustc_infer" } +rustc_lint = { path = "../rustc_lint" } rustc_lint_defs = { path = "../rustc_lint_defs" } rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 1d2a456b555e7..538fb8c7df1ea 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -158,7 +158,19 @@ pub fn provide(providers: &mut Providers) { fn emit_delayed_lint(lint: &DelayedLint, tcx: TyCtxt<'_>) { match lint { DelayedLint::AttributeParsing(attribute_lint) => { - rustc_attr_parsing::emit_attribute_lint(attribute_lint, tcx) + tcx.node_span_lint( + attribute_lint.lint_id.lint, + attribute_lint.id, + attribute_lint.span, + |diag| { + rustc_lint::decorate_attribute_lint( + tcx.sess, + Some(tcx), + &attribute_lint.kind, + diag, + ); + }, + ); } } } diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 75e7af4c11733..1bcdda96e13a2 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -245,6 +245,19 @@ lint_dropping_copy_types = calls to `std::mem::drop` with a value that implement lint_dropping_references = calls to `std::mem::drop` with a reference instead of an owned value does nothing .label = argument has type `{$arg_ty}` +lint_empty_attribute = + unused attribute + .suggestion = {$valid_without_list -> + [true] remove these parentheses + *[other] remove this attribute + } + .note = {$valid_without_list -> + [true] using `{$attr_path}` with an empty list is equivalent to not using a list at all + *[other] using `{$attr_path}` with an empty list has no effect + } + +-lint_previously_accepted = + this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! lint_enum_intrinsics_mem_discriminant = the return value of `mem::discriminant` is unspecified when called with a non-enum type .note = the argument to `discriminant` should be a reference to an enum, but it was passed a reference to a `{$ty_param}`, which is not an enum @@ -458,6 +471,17 @@ lint_invalid_reference_casting_note_book = for more information, visit + [false] crate-level attribute should be an inner attribute: add an exclamation mark: `#![{$name}]` + *[other] the `#![{$name}]` attribute can only be used at the crate root + } + .note = This attribute does not have an `!`, which means it is applied to this {$target} + +lint_invalid_target = `#[{$name}]` attribute cannot be used on {$target} + .warn = {-lint_previously_accepted} + .help = `#[{$name}]` can {$only}be applied to {$applied} + .suggestion = remove the attribute + lint_lintpass_by_hand = implementing `LintPass` by hand .help = try using `declare_lint_pass!` or `impl_lint_pass!` instead @@ -890,6 +914,10 @@ lint_unpredictable_fn_pointer_comparisons = function pointer comparisons do not lint_unqualified_local_imports = `use` of a local item without leading `self::`, `super::`, or `crate::` +lint_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe + .label = usage of unsafe attribute +lint_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)` + lint_unsupported_group = `{$lint_group}` lint group is not supported with ´--force-warn´ lint_untranslatable_diag = diagnostics should be created using translatable messages @@ -922,6 +950,12 @@ lint_unused_def = unused {$pre}`{$def}`{$post} that must be used lint_unused_delim = unnecessary {$delim} around {$item} .suggestion = remove these {$delim} +lint_unused_duplicate = + unused attribute + .suggestion = remove this attribute + .note = attribute also specified here + .warn = {-lint_previously_accepted} + lint_unused_import_braces = braces around {$node} is unnecessary lint_unused_imports = {$num_snippets -> diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 87ccd114ee97e..b654bc848ecfc 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -4,6 +4,7 @@ use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS; use rustc_errors::{ Applicability, Diag, DiagArgValue, LintDiagnostic, elided_lifetime_in_path_suggestion, }; +use rustc_hir::lints::AttributeLintKind; use rustc_middle::middle::stability; use rustc_middle::ty::TyCtxt; use rustc_session::Session; @@ -301,7 +302,21 @@ pub fn decorate_builtin_lint( BuiltinLintDiag::UnusedCrateDependency { extern_crate, local_crate } => { lints::UnusedCrateDependency { extern_crate, local_crate }.decorate_lint(diag) } - BuiltinLintDiag::IllFormedAttributeInput { suggestions, docs } => { + BuiltinLintDiag::AttributeLint(kind) => decorate_attribute_lint(sess, tcx, &kind, diag), + } +} + +pub fn decorate_attribute_lint( + _sess: &Session, + _tcx: Option>, + kind: &AttributeLintKind, + diag: &mut Diag<'_, ()>, +) { + match kind { + &AttributeLintKind::UnusedDuplicate { this, other, warning } => { + lints::UnusedDuplicate { this, other, warning }.decorate_lint(diag) + } + AttributeLintKind::IllFormedAttributeInput { suggestions, docs } => { lints::IllFormedAttributeInput { num_suggestions: suggestions.len(), suggestions: DiagArgValue::StrListSepByAnd( @@ -312,5 +327,42 @@ pub fn decorate_builtin_lint( } .decorate_lint(diag) } + AttributeLintKind::EmptyAttribute { first_span, attr_path, valid_without_list } => { + lints::EmptyAttributeList { + attr_span: *first_span, + attr_path: attr_path.clone(), + valid_without_list: *valid_without_list, + } + .decorate_lint(diag) + } + AttributeLintKind::InvalidTarget { name, target, applied, only, attr_span } => { + lints::InvalidTargetLint { + name: name.clone(), + target, + applied: DiagArgValue::StrListSepByAnd( + applied.into_iter().map(|i| Cow::Owned(i.to_string())).collect(), + ), + only, + attr_span: *attr_span, + } + .decorate_lint(diag) + } + &AttributeLintKind::InvalidStyle { ref name, is_used_as_inner, target, target_span } => { + lints::InvalidAttrStyle { + name: name.clone(), + is_used_as_inner, + target_span: (!is_used_as_inner).then_some(target_span), + target, + } + .decorate_lint(diag) + } + &AttributeLintKind::UnsafeAttrOutsideUnsafe { + attribute_name_span, + sugg_spans: (left, right), + } => lints::UnsafeAttrOutsideUnsafeLint { + span: attribute_name_span, + suggestion: lints::UnsafeAttrOutsideUnsafeSuggestion { left, right }, + } + .decorate_lint(diag), } } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 78b76e083d416..23afbf6c0d7b7 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -129,7 +129,7 @@ use unused::*; #[rustfmt::skip] pub use builtin::{MissingDoc, SoftLints}; pub use context::{CheckLintNameResult, EarlyContext, LateContext, LintContext, LintStore}; -pub use early::diagnostics::decorate_builtin_lint; +pub use early::diagnostics::{decorate_attribute_lint, decorate_builtin_lint}; pub use early::{EarlyCheckNode, check_ast_node}; pub use late::{check_crate, late_lint_mod, unerased_lint_store}; pub use levels::LintLevelsBuilder; diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 51708bc045830..4f28d503af3c3 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3126,3 +3126,68 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion { } } } + +#[derive(LintDiagnostic)] +#[diag(lint_empty_attribute)] +#[note] +pub(crate) struct EmptyAttributeList { + #[suggestion(code = "", applicability = "machine-applicable")] + pub attr_span: Span, + pub attr_path: String, + pub valid_without_list: bool, +} + +#[derive(LintDiagnostic)] +#[diag(lint_invalid_target)] +#[warning] +#[help] +pub(crate) struct InvalidTargetLint { + pub name: String, + pub target: &'static str, + pub applied: DiagArgValue, + pub only: &'static str, + #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")] + pub attr_span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(lint_invalid_style)] +pub(crate) struct InvalidAttrStyle { + pub name: String, + pub is_used_as_inner: bool, + #[note] + pub target_span: Option, + pub target: &'static str, +} + +#[derive(LintDiagnostic)] +#[diag(lint_unused_duplicate)] +pub(crate) struct UnusedDuplicate { + #[suggestion(code = "", applicability = "machine-applicable")] + pub this: Span, + #[note] + pub other: Span, + #[warning] + pub warning: bool, +} + +#[derive(LintDiagnostic)] +#[diag(lint_unsafe_attr_outside_unsafe)] +pub(crate) struct UnsafeAttrOutsideUnsafeLint { + #[label] + pub span: Span, + #[subdiagnostic] + pub suggestion: UnsafeAttrOutsideUnsafeSuggestion, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + lint_unsafe_attr_outside_unsafe_suggestion, + applicability = "machine-applicable" +)] +pub(crate) struct UnsafeAttrOutsideUnsafeSuggestion { + #[suggestion_part(code = "unsafe(")] + pub left: Span, + #[suggestion_part(code = ")")] + pub right: Span, +} diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index abdc41eb57c21..3c6e7d04a29dd 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -696,10 +696,42 @@ pub enum BuiltinLintDiag { extern_crate: Symbol, local_crate: Symbol, }, + AttributeLint(AttributeLintKind), +} + +#[derive(Debug, HashStable_Generic)] +pub enum AttributeLintKind { + UnusedDuplicate { + this: Span, + other: Span, + warning: bool, + }, IllFormedAttributeInput { suggestions: Vec, docs: Option<&'static str>, }, + EmptyAttribute { + first_span: Span, + attr_path: String, + valid_without_list: bool, + }, + InvalidTarget { + name: String, + target: &'static str, + applied: Vec, + only: &'static str, + attr_span: Span, + }, + InvalidStyle { + name: String, + is_used_as_inner: bool, + target: &'static str, + target_span: Span, + }, + UnsafeAttrOutsideUnsafe { + attribute_name_span: Span, + sugg_spans: (Span, Span), + }, } pub type RegisteredTools = FxIndexSet; diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 0cd36d5e971de..a16c2232c3a68 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -30,7 +30,7 @@ use rustc_data_structures::sync::{ self, DynSend, DynSync, FreezeReadGuard, Lock, RwLock, WorkerLocal, }; use rustc_errors::{ - Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, LintDiagnostic, LintEmitter, MultiSpan, + Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, LintDiagnostic, MultiSpan, }; use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{CtorKind, CtorOf, DefKind}; @@ -1534,20 +1534,6 @@ pub struct TyCtxt<'tcx> { gcx: &'tcx GlobalCtxt<'tcx>, } -impl<'tcx> LintEmitter for TyCtxt<'tcx> { - type Id = HirId; - - fn emit_node_span_lint( - self, - lint: &'static Lint, - hir_id: HirId, - span: impl Into, - decorator: impl for<'a> LintDiagnostic<'a, ()>, - ) { - self.emit_node_span_lint(lint, hir_id, span, decorator); - } -} - // Explicitly implement `DynSync` and `DynSend` for `TyCtxt` to short circuit trait resolution. Its // field are asserted to implement these traits below, so this is trivially safe, and it greatly // speeds-up compilation of this crate and its dependents. diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index e52475cdd5bde..acc65fc11a2a2 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -6,7 +6,6 @@ use std::sync::atomic::AtomicBool; use std::{env, io}; use rand::{RngCore, rng}; -use rustc_ast::NodeId; use rustc_data_structures::base_n::{CASE_INSENSITIVE, ToBaseN}; use rustc_data_structures::flock; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; @@ -22,7 +21,7 @@ use rustc_errors::timings::TimingSectionHandler; use rustc_errors::translation::Translator; use rustc_errors::{ Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, Diagnostic, ErrorGuaranteed, FatalAbort, - LintEmitter, TerminalUrl, fallback_fluent_bundle, + TerminalUrl, fallback_fluent_bundle, }; use rustc_hir::limit::Limit; use rustc_macros::HashStable_Generic; @@ -160,20 +159,6 @@ pub struct Session { pub invocation_temp: Option, } -impl LintEmitter for &'_ Session { - type Id = NodeId; - - fn emit_node_span_lint( - self, - lint: &'static rustc_lint_defs::Lint, - node_id: Self::Id, - span: impl Into, - decorator: impl for<'a> rustc_errors::LintDiagnostic<'a, ()> + DynSend + 'static, - ) { - self.psess.buffer_lint(lint, span, node_id, decorator); - } -} - #[derive(Clone, Copy)] pub enum CodegenUnits { /// Specified by the user. In this case we try fairly hard to produce the From 24219203940952557b66e5f8e532e4a78ade256e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 2 Dec 2025 22:40:12 +0100 Subject: [PATCH 25/25] f*::min/max: fix comparing with libm and IEEE operations --- library/core/src/intrinsics/mod.rs | 48 +++++++++++++++--------------- library/core/src/num/f128.rs | 30 ++++++++++--------- library/core/src/num/f16.rs | 30 ++++++++++--------- library/core/src/num/f32.rs | 30 ++++++++++--------- library/core/src/num/f64.rs | 30 ++++++++++--------- 5 files changed, 88 insertions(+), 80 deletions(-) diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 2115c5c9a85d8..a9735a7e6f315 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2949,9 +2949,9 @@ pub const unsafe fn write_bytes(dst: *mut T, val: u8, count: usize); /// Returns the minimum of two `f16` values, ignoring NaN. /// -/// This behaves like IEEE 754-2008 minNum. In particular: -/// If one of the arguments is NaN, then the other argument is returned. If the inputs compare equal -/// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. +/// If one of the arguments is NaN (quiet or signaling), then the other argument is returned. If +/// both arguments are NaN, returns NaN. If the inputs compare equal (such as for the case of `+0.0` +/// and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -2965,9 +2965,9 @@ pub const fn minnumf16(x: f16, y: f16) -> f16; /// Returns the minimum of two `f32` values, ignoring NaN. /// -/// This behaves like IEEE 754-2008 minNum. In particular: -/// If one of the arguments is NaN, then the other argument is returned. If the inputs compare equal -/// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. +/// If one of the arguments is NaN (quiet or signaling), then the other argument is returned. If +/// both arguments are NaN, returns NaN. If the inputs compare equal (such as for the case of `+0.0` +/// and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -2982,9 +2982,9 @@ pub const fn minnumf32(x: f32, y: f32) -> f32; /// Returns the minimum of two `f64` values, ignoring NaN. /// -/// This behaves like IEEE 754-2008 minNum. In particular: -/// If one of the arguments is NaN, then the other argument is returned. If the inputs compare equal -/// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. +/// If one of the arguments is NaN (quiet or signaling), then the other argument is returned. If +/// both arguments are NaN, returns NaN. If the inputs compare equal (such as for the case of `+0.0` +/// and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -2999,9 +2999,9 @@ pub const fn minnumf64(x: f64, y: f64) -> f64; /// Returns the minimum of two `f128` values, ignoring NaN. /// -/// This behaves like IEEE 754-2008 minNum. In particular: -/// If one of the arguments is NaN, then the other argument is returned. If the inputs compare equal -/// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. +/// If one of the arguments is NaN (quiet or signaling), then the other argument is returned. If +/// both arguments are NaN, returns NaN. If the inputs compare equal (such as for the case of `+0.0` +/// and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3115,9 +3115,9 @@ pub const fn minimumf128(x: f128, y: f128) -> f128 { /// Returns the maximum of two `f16` values, ignoring NaN. /// -/// This behaves like IEEE 754-2008 maxNum. In particular: -/// If one of the arguments is NaN, then the other argument is returned. If the inputs compare equal -/// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. +/// If one of the arguments is NaN (quiet or signaling), then the other argument is returned. If +/// both arguments are NaN, returns NaN. If the inputs compare equal (such as for the case of `+0.0` +/// and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3131,9 +3131,9 @@ pub const fn maxnumf16(x: f16, y: f16) -> f16; /// Returns the maximum of two `f32` values, ignoring NaN. /// -/// This behaves like IEEE 754-2008 maxNum. In particular: -/// If one of the arguments is NaN, then the other argument is returned. If the inputs compare equal -/// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. +/// If one of the arguments is NaN (quiet or signaling), then the other argument is returned. If +/// both arguments are NaN, returns NaN. If the inputs compare equal (such as for the case of `+0.0` +/// and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3148,9 +3148,9 @@ pub const fn maxnumf32(x: f32, y: f32) -> f32; /// Returns the maximum of two `f64` values, ignoring NaN. /// -/// This behaves like IEEE 754-2008 maxNum. In particular: -/// If one of the arguments is NaN, then the other argument is returned. If the inputs compare equal -/// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. +/// If one of the arguments is NaN (quiet or signaling), then the other argument is returned. If +/// both arguments are NaN, returns NaN. If the inputs compare equal (such as for the case of `+0.0` +/// and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3165,9 +3165,9 @@ pub const fn maxnumf64(x: f64, y: f64) -> f64; /// Returns the maximum of two `f128` values, ignoring NaN. /// -/// This behaves like IEEE 754-2008 maxNum. In particular: -/// If one of the arguments is NaN, then the other argument is returned. If the inputs compare equal -/// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. +/// If one of the arguments is NaN (quiet or signaling), then the other argument is returned. If +/// both arguments are NaN, returns NaN. If the inputs compare equal (such as for the case of `+0.0` +/// and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 9b9cc80a606c6..3d1f48ca6e1ab 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -694,14 +694,15 @@ impl f128 { /// Returns the maximum of the two numbers, ignoring NaN. /// - /// If exactly one of the arguments is NaN, then the other argument is returned. If both - /// arguments are NaN, the return value is NaN, with the bit pattern picked using the usual - /// [rules for arithmetic operations](f32#nan-bit-patterns). If the inputs compare equal (such - /// as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. + /// If exactly one of the arguments is NaN (quiet or signaling), then the other argument is + /// returned. If both arguments are NaN, the return value is NaN, with the bit pattern picked + /// using the usual [rules for arithmetic operations](f32#nan-bit-patterns). If the inputs + /// compare equal (such as for the case of `+0.0` and `-0.0`), either input may be returned + /// non-deterministically. /// - /// This follows the IEEE 754-2008 semantics for `maxNum`, except for handling of signaling NaNs; - /// this function handles all NaNs the same way and avoids `maxNum`'s problems with associativity. - /// This also matches the behavior of libm’s `fmax`. + /// The handling of NaNs follows the IEEE 754-2019 semantics for `maximumNumber`, treating all + /// NaNs the same way to ensure the operation is associative. The handling of signed zeros + /// follows the IEEE 754-2008 semantics for `maxNum`. /// /// ``` /// #![feature(f128)] @@ -725,14 +726,15 @@ impl f128 { /// Returns the minimum of the two numbers, ignoring NaN. /// - /// If exactly one of the arguments is NaN, then the other argument is returned. If both - /// arguments are NaN, the return value is NaN, with the bit pattern picked using the usual - /// [rules for arithmetic operations](f32#nan-bit-patterns). If the inputs compare equal (such - /// as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. + /// If exactly one of the arguments is NaN (quiet or signaling), then the other argument is + /// returned. If both arguments are NaN, the return value is NaN, with the bit pattern picked + /// using the usual [rules for arithmetic operations](f32#nan-bit-patterns). If the inputs + /// compare equal (such as for the case of `+0.0` and `-0.0`), either input may be returned + /// non-deterministically. /// - /// This follows the IEEE 754-2008 semantics for `minNum`, except for handling of signaling NaNs; - /// this function handles all NaNs the same way and avoids `minNum`'s problems with associativity. - /// This also matches the behavior of libm’s `fmin`. + /// The handling of NaNs follows the IEEE 754-2019 semantics for `minimumNumber`, treating all + /// NaNs the same way to ensure the operation is associative. The handling of signed zeros + /// follows the IEEE 754-2008 semantics for `minNum`. /// /// ``` /// #![feature(f128)] diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index ab765ebcb7fa7..937394b339cc8 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -687,14 +687,15 @@ impl f16 { /// Returns the maximum of the two numbers, ignoring NaN. /// - /// If exactly one of the arguments is NaN, then the other argument is returned. If both - /// arguments are NaN, the return value is NaN, with the bit pattern picked using the usual - /// [rules for arithmetic operations](f32#nan-bit-patterns). If the inputs compare equal (such - /// as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. + /// If exactly one of the arguments is NaN (quiet or signaling), then the other argument is + /// returned. If both arguments are NaN, the return value is NaN, with the bit pattern picked + /// using the usual [rules for arithmetic operations](f32#nan-bit-patterns). If the inputs + /// compare equal (such as for the case of `+0.0` and `-0.0`), either input may be returned + /// non-deterministically. /// - /// This follows the IEEE 754-2008 semantics for `maxNum`, except for handling of signaling NaNs; - /// this function handles all NaNs the same way and avoids `maxNum`'s problems with associativity. - /// This also matches the behavior of libm’s `fmax`. + /// The handling of NaNs follows the IEEE 754-2019 semantics for `maximumNumber`, treating all + /// NaNs the same way to ensure the operation is associative. The handling of signed zeros + /// follows the IEEE 754-2008 semantics for `maxNum`. /// /// ``` /// #![feature(f16)] @@ -717,14 +718,15 @@ impl f16 { /// Returns the minimum of the two numbers, ignoring NaN. /// - /// If exactly one of the arguments is NaN, then the other argument is returned. If both - /// arguments are NaN, the return value is NaN, with the bit pattern picked using the usual - /// [rules for arithmetic operations](f32#nan-bit-patterns). If the inputs compare equal (such - /// as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. + /// If exactly one of the arguments is NaN (quiet or signaling), then the other argument is + /// returned. If both arguments are NaN, the return value is NaN, with the bit pattern picked + /// using the usual [rules for arithmetic operations](f32#nan-bit-patterns). If the inputs + /// compare equal (such as for the case of `+0.0` and `-0.0`), either input may be returned + /// non-deterministically. /// - /// This follows the IEEE 754-2008 semantics for `minNum`, except for handling of signaling NaNs; - /// this function handles all NaNs the same way and avoids `minNum`'s problems with associativity. - /// This also matches the behavior of libm’s `fmin`. + /// The handling of NaNs follows the IEEE 754-2019 semantics for `minimumNumber`, treating all + /// NaNs the same way to ensure the operation is associative. The handling of signed zeros + /// follows the IEEE 754-2008 semantics for `minNum`. /// /// ``` /// #![feature(f16)] diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 361f273ead019..04760ddc1d67e 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -897,14 +897,15 @@ impl f32 { /// Returns the maximum of the two numbers, ignoring NaN. /// - /// If exactly one of the arguments is NaN, then the other argument is returned. If both - /// arguments are NaN, the return value is NaN, with the bit pattern picked using the usual - /// [rules for arithmetic operations](f32#nan-bit-patterns). If the inputs compare equal (such - /// as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. + /// If exactly one of the arguments is NaN (quiet or signaling), then the other argument is + /// returned. If both arguments are NaN, the return value is NaN, with the bit pattern picked + /// using the usual [rules for arithmetic operations](f32#nan-bit-patterns). If the inputs + /// compare equal (such as for the case of `+0.0` and `-0.0`), either input may be returned + /// non-deterministically. /// - /// This follows the IEEE 754-2008 semantics for `maxNum`, except for handling of signaling NaNs; - /// this function handles all NaNs the same way and avoids `maxNum`'s problems with associativity. - /// This also matches the behavior of libm’s `fmax`. + /// The handling of NaNs follows the IEEE 754-2019 semantics for `maximumNumber`, treating all + /// NaNs the same way to ensure the operation is associative. The handling of signed zeros + /// follows the IEEE 754-2008 semantics for `maxNum`. /// /// ``` /// let x = 1.0f32; @@ -923,14 +924,15 @@ impl f32 { /// Returns the minimum of the two numbers, ignoring NaN. /// - /// If exactly one of the arguments is NaN, then the other argument is returned. If both - /// arguments are NaN, the return value is NaN, with the bit pattern picked using the usual - /// [rules for arithmetic operations](f32#nan-bit-patterns). If the inputs compare equal (such - /// as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. + /// If exactly one of the arguments is NaN (quiet or signaling), then the other argument is + /// returned. If both arguments are NaN, the return value is NaN, with the bit pattern picked + /// using the usual [rules for arithmetic operations](f32#nan-bit-patterns). If the inputs + /// compare equal (such as for the case of `+0.0` and `-0.0`), either input may be returned + /// non-deterministically. /// - /// This follows the IEEE 754-2008 semantics for `minNum`, except for handling of signaling NaNs; - /// this function handles all NaNs the same way and avoids `minNum`'s problems with associativity. - /// This also matches the behavior of libm’s `fmin`. + /// The handling of NaNs follows the IEEE 754-2019 semantics for `minimumNumber`, treating all + /// NaNs the same way to ensure the operation is associative. The handling of signed zeros + /// follows the IEEE 754-2008 semantics for `minNum`. /// /// ``` /// let x = 1.0f32; diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 17a908643a41f..e7cab39dca447 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -915,14 +915,15 @@ impl f64 { /// Returns the maximum of the two numbers, ignoring NaN. /// - /// If exactly one of the arguments is NaN, then the other argument is returned. If both - /// arguments are NaN, the return value is NaN, with the bit pattern picked using the usual - /// [rules for arithmetic operations](f32#nan-bit-patterns). If the inputs compare equal (such - /// as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. + /// If exactly one of the arguments is NaN (quiet or signaling), then the other argument is + /// returned. If both arguments are NaN, the return value is NaN, with the bit pattern picked + /// using the usual [rules for arithmetic operations](f32#nan-bit-patterns). If the inputs + /// compare equal (such as for the case of `+0.0` and `-0.0`), either input may be returned + /// non-deterministically. /// - /// This follows the IEEE 754-2008 semantics for `maxNum`, except for handling of signaling NaNs; - /// this function handles all NaNs the same way and avoids `maxNum`'s problems with associativity. - /// This also matches the behavior of libm’s `fmax`. + /// The handling of NaNs follows the IEEE 754-2019 semantics for `maximumNumber`, treating all + /// NaNs the same way to ensure the operation is associative. The handling of signed zeros + /// follows the IEEE 754-2008 semantics for `maxNum`. /// /// ``` /// let x = 1.0_f64; @@ -941,14 +942,15 @@ impl f64 { /// Returns the minimum of the two numbers, ignoring NaN. /// - /// If exactly one of the arguments is NaN, then the other argument is returned. If both - /// arguments are NaN, the return value is NaN, with the bit pattern picked using the usual - /// [rules for arithmetic operations](f32#nan-bit-patterns). If the inputs compare equal (such - /// as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. + /// If exactly one of the arguments is NaN (quiet or signaling), then the other argument is + /// returned. If both arguments are NaN, the return value is NaN, with the bit pattern picked + /// using the usual [rules for arithmetic operations](f32#nan-bit-patterns). If the inputs + /// compare equal (such as for the case of `+0.0` and `-0.0`), either input may be returned + /// non-deterministically. /// - /// This follows the IEEE 754-2008 semantics for `minNum`, except for handling of signaling NaNs; - /// this function handles all NaNs the same way and avoids `minNum`'s problems with associativity. - /// This also matches the behavior of libm’s `fmin`. + /// The handling of NaNs follows the IEEE 754-2019 semantics for `minimumNumber`, treating all + /// NaNs the same way to ensure the operation is associative. The handling of signed zeros + /// follows the IEEE 754-2008 semantics for `minNum`. /// /// ``` /// let x = 1.0_f64;