diff --git a/.github/ISSUE_TEMPLATE/tracking_issue.md b/.github/ISSUE_TEMPLATE/tracking_issue.md index 0e0fdc5ca864e..e4bdcd212f381 100644 --- a/.github/ISSUE_TEMPLATE/tracking_issue.md +++ b/.github/ISSUE_TEMPLATE/tracking_issue.md @@ -1,6 +1,6 @@ --- name: Tracking Issue -about: A tracking issue for an accepted feature or RFC in Rust. +about: A tracking issue for an unstable feature in Rust. title: Tracking Issue for XXX labels: C-tracking-issue --- @@ -8,19 +8,27 @@ labels: C-tracking-issue NOTE: For library features, please use the "Library Tracking Issue" template instead. Thank you for creating a tracking issue! 📜 Tracking issues are for tracking a -feature from implementation to stabilisation. Make sure to include the relevant -RFC for the feature if it has one. Otherwise provide a short summary of the -feature and link any relevant PRs or issues, and remove any sections that are -not relevant to the feature. +feature from implementation to stabilisation. Remember to add team labels to the tracking issue. -For a language team feature, this would e.g., be `T-lang`. +For a language team feature, this would e.g., be `T-lang`: + +> @rustbot label T-lang + Such a feature should also be labeled with e.g., `F-my_feature`. This label is used to associate issues (e.g., bugs and design questions) to the feature. --> -This is a tracking issue for the RFC "XXX" (rust-lang/rfcs#NNN). -The feature gate for the issue is `#![feature(FFF)]`. +This is a tracking issue for `#![feature(FFF)]`. +The RFC for this feature is rust-lang/rfcs#NNN. + + ### About tracking issues @@ -38,8 +46,8 @@ implementing a feature, followed by a PR that stabilises the feature. However for larger features an implementation could be broken up into multiple PRs. --> -- [ ] Implement the RFC (cc @rust-lang/XXX -- can anyone write up mentoring - instructions?) +- [ ] Create the feature gate +- [ ] Implement the feature - [ ] Adjust documentation ([see instructions on rustc-dev-guide][doc-guide]) - [ ] Style updates for any new syntax ([nightly-style-procedure]) - [ ] Style team decision on new formatting @@ -59,8 +67,8 @@ stabilised. It is useful to link related discussions and conculsions as they develop. --> -- [ ] list all the "unresolved questions" found in the RFC to ensure they are - not forgotten. +- [ ] List any unresolved questions, including those listed in the RFC if there is one, + to ensure they are not forgotten. ### Implementation history diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f6f663477bc06..c0f896724ae56 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,7 +46,7 @@ jobs: # If you want to modify CI jobs, take a look at src/ci/github-actions/jobs.yml. calculate_matrix: name: Calculate job matrix - runs-on: ubuntu-24.04-arm + runs-on: ubuntu-26.04-arm outputs: jobs: ${{ steps.jobs.outputs.jobs }} run_type: ${{ steps.jobs.outputs.run_type }} @@ -309,7 +309,7 @@ jobs: # This job is used to publish toolstate for successful auto builds. outcome: name: publish toolstate - runs-on: ubuntu-24.04 + runs-on: ubuntu-26.04 needs: [ calculate_matrix, job ] if: ${{ needs.calculate_matrix.outputs.run_type == 'auto' }} environment: ${{ (github.repository == 'rust-lang/rust' && 'bors') || '' }} diff --git a/.github/workflows/ghcr.yml b/.github/workflows/ghcr.yml index 2cc67b822ccfc..f0f0ece750b9f 100644 --- a/.github/workflows/ghcr.yml +++ b/.github/workflows/ghcr.yml @@ -23,7 +23,7 @@ on: jobs: mirror: name: DockerHub mirror - runs-on: ubuntu-24.04 + runs-on: ubuntu-26.04 if: github.repository == 'rust-lang/rust' permissions: # Needed to write to the ghcr.io registry diff --git a/.github/workflows/post-merge.yml b/.github/workflows/post-merge.yml index 0d78d0774185f..7200aa22a4162 100644 --- a/.github/workflows/post-merge.yml +++ b/.github/workflows/post-merge.yml @@ -10,7 +10,7 @@ on: jobs: analysis: - runs-on: ubuntu-24.04 + runs-on: ubuntu-26.04 if: github.repository == 'rust-lang/rust' permissions: pull-requests: write diff --git a/.mailmap b/.mailmap index 32fbff098127d..33000e11dcefa 100644 --- a/.mailmap +++ b/.mailmap @@ -64,6 +64,7 @@ Arthur Cohen Arthur Silva Arthur Woimbée Artyom Pavlov +Austin K. Austin Seipp Ayaz Hafiz Aydin Kim aydin.kim @@ -144,6 +145,9 @@ Christian Poveda <31802960+christianpoveda@users.noreply.github. Christian Poveda Christian Vallentin Christoffer Buchholz +Clar Fon +Clar Fon +Clar Fon Clark Gaebel Clement Miao Clément Renault @@ -459,6 +463,8 @@ Mateusz Mikuła Mateusz Mikuła Mateusz Mikuła Mateusz Mikuła +Matilde Morrone +Matilde Morrone Matt Brubeck Matthew Auld Matthew Jasper diff --git a/Cargo.lock b/Cargo.lock index 5e57835c9cfef..71086e7203470 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -285,12 +285,6 @@ dependencies = [ "windows-targets", ] -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - [[package]] name = "base64" version = "0.22.1" @@ -659,7 +653,7 @@ checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" [[package]] name = "clippy" -version = "0.1.100" +version = "0.1.101" dependencies = [ "anstream 0.6.21", "askama", @@ -700,7 +694,7 @@ dependencies = [ [[package]] name = "clippy_config" -version = "0.1.100" +version = "0.1.101" dependencies = [ "arrayvec", "clippy_utils", @@ -711,7 +705,7 @@ dependencies = [ [[package]] name = "clippy_lints" -version = "0.1.100" +version = "0.1.101" dependencies = [ "arrayvec", "cargo_metadata 0.23.1", @@ -741,7 +735,7 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.100" +version = "0.1.101" dependencies = [ "arrayvec", "itertools", @@ -1154,7 +1148,7 @@ checksum = "a0afaad2b26fa326569eb264b1363e8ae3357618c43982b3f285f0774ce76b69" [[package]] name = "declare_clippy_lint" -version = "0.1.100" +version = "0.1.101" [[package]] name = "derive-where" @@ -2135,7 +2129,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bdc5a74b0feeb5e6a1dc2dd08c34280a61e37668d10a6a3b27ad69d0fb9ce2e" dependencies = [ "aho-corasick", - "base64 0.22.1", + "base64", "bstr", "jaq-core", "jiff", @@ -2564,8 +2558,10 @@ dependencies = [ "serde", "serde_json", "smallvec", + "socket2", "tempfile", "ui_test", + "windows-sys 0.61.2", ] [[package]] @@ -3446,7 +3442,6 @@ name = "replace-version-placeholder" version = "0.1.0" dependencies = [ "tidy", - "walkdir", ] [[package]] @@ -3498,9 +3493,9 @@ dependencies = [ [[package]] name = "rustc-build-sysroot" -version = "0.5.13" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "569d545953ee9a1ab9d3e9112e961739ff0cfd50bce06b126937395940be69c9" +checksum = "0d0ed7817483b026efc0492f4ccca177f00fb711537f37943cc412bbf12682b4" dependencies = [ "anyhow", "rustc_version", @@ -3633,6 +3628,7 @@ dependencies = [ name = "rustc_ast_lowering" version = "0.0.0" dependencies = [ + "bitflags", "rustc_abi", "rustc_ast", "rustc_attr_ir", @@ -3741,6 +3737,7 @@ dependencies = [ "itertools", "polonius-engine", "rustc_abi", + "rustc_attr_ir", "rustc_data_structures", "rustc_errors", "rustc_graphviz", @@ -3878,6 +3875,7 @@ dependencies = [ "rustc_abi", "rustc_apfloat", "rustc_ast", + "rustc_attr_ir", "rustc_data_structures", "rustc_errors", "rustc_hir", @@ -4135,6 +4133,7 @@ dependencies = [ "rustc_abi", "rustc_arena", "rustc_ast", + "rustc_attr_ir", "rustc_data_structures", "rustc_errors", "rustc_feature", @@ -4174,6 +4173,7 @@ dependencies = [ "rustc_abi", "rustc_ast", "rustc_ast_pretty", + "rustc_attr_ir", "rustc_hir", "rustc_span", ] @@ -4185,6 +4185,7 @@ dependencies = [ "itertools", "rustc_abi", "rustc_ast", + "rustc_attr_ir", "rustc_data_structures", "rustc_errors", "rustc_hir", @@ -4209,6 +4210,7 @@ name = "rustc_incremental" version = "0.0.0" dependencies = [ "rand 0.9.3", + "rustc_attr_ir", "rustc_data_structures", "rustc_errors", "rustc_fs_util", @@ -4269,6 +4271,7 @@ dependencies = [ "rustc_ast_lowering", "rustc_ast_passes", "rustc_ast_pretty", + "rustc_attr_ir", "rustc_attr_parsing", "rustc_borrowck", "rustc_builtin_macros", @@ -4292,6 +4295,7 @@ dependencies = [ "rustc_metadata", "rustc_middle", "rustc_mir_build", + "rustc_mir_dataflow", "rustc_mir_transform", "rustc_monomorphize", "rustc_parse", @@ -4330,6 +4334,7 @@ dependencies = [ "rustc_apfloat", "rustc_ast", "rustc_ast_pretty", + "rustc_attr_ir", "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", @@ -4408,6 +4413,7 @@ dependencies = [ "odht", "rustc_abi", "rustc_ast", + "rustc_attr_ir", "rustc_attr_parsing", "rustc_crate_store", "rustc_data_structures", @@ -4415,6 +4421,7 @@ dependencies = [ "rustc_expand", "rustc_feature", "rustc_fs_util", + "rustc_hashes", "rustc_hir", "rustc_hir_pretty", "rustc_index", @@ -4429,6 +4436,7 @@ dependencies = [ "rustc_target", "tempfile", "tracing", + "twox-hash 2.1.2", ] [[package]] @@ -4477,6 +4485,7 @@ dependencies = [ "rustc_apfloat", "rustc_arena", "rustc_ast", + "rustc_attr_ir", "rustc_data_structures", "rustc_errors", "rustc_hir", @@ -4498,10 +4507,10 @@ dependencies = [ "polonius-engine", "regex", "rustc_abi", + "rustc_attr_ir", "rustc_data_structures", "rustc_errors", "rustc_graphviz", - "rustc_hir", "rustc_index", "rustc_macros", "rustc_middle", @@ -4519,6 +4528,7 @@ dependencies = [ "rustc_abi", "rustc_arena", "rustc_ast", + "rustc_attr_ir", "rustc_const_eval", "rustc_data_structures", "rustc_errors", @@ -4544,6 +4554,7 @@ version = "0.0.0" dependencies = [ "rustc_abi", "rustc_ast", + "rustc_attr_ir", "rustc_data_structures", "rustc_errors", "rustc_hir", @@ -4661,6 +4672,7 @@ name = "rustc_privacy" version = "0.0.0" dependencies = [ "rustc_ast", + "rustc_attr_ir", "rustc_data_structures", "rustc_errors", "rustc_hir", @@ -4701,6 +4713,7 @@ name = "rustc_public_bridge" version = "0.0.0" dependencies = [ "rustc_abi", + "rustc_attr_ir", "rustc_crate_store", "rustc_data_structures", "rustc_hir", @@ -4738,6 +4751,7 @@ dependencies = [ "rustc_arena", "rustc_ast", "rustc_ast_pretty", + "rustc_attr_ir", "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", @@ -4764,6 +4778,7 @@ dependencies = [ "bitflags", "libc", "rustc_abi", + "rustc_attr_ir", "rustc_data_structures", "rustc_hir", "rustc_middle", @@ -4816,7 +4831,6 @@ version = "0.0.0" dependencies = [ "bitflags", "blake3", - "derive-where", "indexmap", "itoa", "md-5", @@ -4854,6 +4868,7 @@ dependencies = [ "punycode", "rustc-demangle", "rustc_abi", + "rustc_attr_ir", "rustc_data_structures", "rustc_hashes", "rustc_hir", @@ -4916,6 +4931,7 @@ dependencies = [ "itertools", "rustc_abi", "rustc_ast", + "rustc_attr_ir", "rustc_crate_store", "rustc_data_structures", "rustc_errors", @@ -4953,8 +4969,8 @@ version = "0.0.0" dependencies = [ "itertools", "rustc_abi", + "rustc_attr_ir", "rustc_data_structures", - "rustc_hir", "rustc_middle", "rustc_span", "smallvec", @@ -4967,6 +4983,7 @@ version = "0.0.0" dependencies = [ "itertools", "rustc_abi", + "rustc_attr_ir", "rustc_data_structures", "rustc_errors", "rustc_hashes", @@ -5051,7 +5068,6 @@ version = "0.0.0" dependencies = [ "arrayvec", "askama", - "base64 0.21.7", "expect-test", "indexmap", "itertools", @@ -5131,7 +5147,7 @@ dependencies = [ [[package]] name = "rustfmt-nightly" -version = "1.10.0" +version = "1.11.0" dependencies = [ "annotate-snippets 0.12.16", "anyhow", @@ -5459,9 +5475,9 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", "windows-sys 0.61.2", diff --git a/RELEASES.md b/RELEASES.md index 34984a29eff51..b4dc100f5d5f1 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,134 @@ +Version 1.99.0 (2026-10-01) +========================== + + + +Language +-------- +- [Add allow-by-default `raw_borrows_via_references` lint that checks for references that decay immediately into raw borrows](https://github.com/rust-lang/rust/pull/138230) +- [Extend `unconditional_panic` lint to function calls that panic when the chunks/windows size is zero](https://github.com/rust-lang/rust/pull/153563) +- [Stabilize C-variadic function definitions](https://github.com/rust-lang/rust/pull/155697) +- [Stabilize the ability to use `#[unsafe(naked)]` functions to define C-variadic functions (`#![feature(c_variadic_naked_functions)]`).](https://github.com/rust-lang/rust/pull/159746) +- [Trait methods are now resolved on an adjusted never type (producing a FCW)](https://github.com/rust-lang/rust/pull/156047) +- [Coerce from inference variables to trait objects if the inference variable is related via subtyping to a type that is known to be `Sized`](https://github.com/rust-lang/rust/pull/157820) +- [Stabilize `#[my_macro] mod foo;`](https://github.com/rust-lang/rust/pull/157857). This allows outlined modules (`mod foo;`) anywhere in the body of a custom attribute or derive macro. +- [Fix the `overflowing_literals` lint with repeated negation](https://github.com/rust-lang/rust/pull/158302). For instance, it will now no longer lint on `--128_i8`, which is already detected by the `arithmetic_overflow` lint. +- [Add POSIX symbols to the `invalid_runtime_symbol_definitions` and `suspicious_runtime_symbol_definitions` lints](https://github.com/rust-lang/rust/pull/158522) +- [Lint unused `#[path]` attributes on inline modules](https://github.com/rust-lang/rust/pull/158835) +- [Enable `unreachable_cfg_select_predicates` lint as part of `unused` lint group](https://github.com/rust-lang/rust/pull/159179) +- [Stabilize passing 128-bit integers via vector registers with `asm!` on x86](https://github.com/rust-lang/rust/pull/159525) +- [Explicitly document that some allocations are allowed to grow in-place (but none are allowed to shrink)](https://github.com/rust-lang/rust/pull/159729) +- We now [guarantee](https://github.com/rust-lang/rust/pull/159730) that the contents of an `UnsafeCell` can be accessed without going through `get` + - [The `invalid_reference_casting` lint was adjusted accordingly](https://github.com/rust-lang/rust/pull/159960) +- [Account for globally enabled target features in `global_asm!`](https://github.com/rust-lang/rust/pull/160594) +- [Warn if an invalid `doc` attribute is used on a macro invocation](https://github.com/rust-lang/rust/pull/161003) + + + + +Compiler +-------- +- [Convert `-Ctarget-cpu` into a target-modifier for AVR, AMDGCN and NVPTX](https://github.com/rust-lang/rust/pull/150732) +- [Enable `static_position_independent_executables` on all gnu targets](https://github.com/rust-lang/rust/pull/158510) +- When providing a suggestion about a missing method, rustc now prefers an exactly matching name from a [doc alias attribute](https://doc.rust-lang.org/rustdoc/advanced-features.html#add-aliases-for-an-item-in-documentation-search) over a similarity search from other method names. If your new users sometimes expect a method under a different name, adding a doc alias will now help them find it via rustc suggestions, in addition to helping them find it via rustdoc search: [When suggesting method names, prefer *exact* doc aliases over similar names](https://github.com/rust-lang/rust/pull/160369) + + + + +Platform Support +---------------- +- [Promote `riscv64-unknown-linux-musl` to Tier 2 with host tools](https://github.com/rust-lang/rust/pull/158766) + + +Refer to Rust's [platform support page][platform-support-doc] +for more information on Rust's tiered platform support. + +[platform-support-doc]: https://doc.rust-lang.org/rustc/platform-support.html + + + +Libraries +--------- +- Iteration on `RangeInclusive` (`a..=b` ranges) is now [optimized better in some circumstances](https://github.com/rust-lang/rust/pull/155114). As a side effect of this, the behavior of `RangeInclusive` values that has already been exhausted (as an iterator) has changed. For example, the return values of `start()` and `end()` on such ranges may return different values, and using such ranges as slice indexes may have different behavior. These behaviors were not guaranteed to be stable, so these changes are considered to not be breaking changes. +- [Relax `transmute_copy` to accept `?Sized` types](https://github.com/rust-lang/rust/pull/155989) +- [Update `transmute_copy` to use a non-unwinding panic](https://github.com/rust-lang/rust/pull/155989) +- [Don't escape U+FF9E and U+FF9F in `escape_debug_ext`](https://github.com/rust-lang/rust/pull/158057) +- [Re-export `core::fmt::NumBuffer` in `alloc` (and `std`)](https://github.com/rust-lang/rust/pull/161430) + + + + +Stabilized APIs +--------------- + +- [`IntoIterator` for `Box<[T; N]>`](https://doc.rust-lang.org/stable/std/iter/trait.IntoIterator.html#impl-IntoIterator-for-Box%3C%5BT;+N%5D,+A%3E) +- [`IntoIterator` for `&Box<[T; N]>`](https://doc.rust-lang.org/stable/std/iter/trait.IntoIterator.html#impl-IntoIterator-for-%26Box%3C%5BT;+N%5D,+A%3E) +- [`IntoIterator` for `&mut Box<[T; N]>`](https://doc.rust-lang.org/stable/std/iter/trait.IntoIterator.html#impl-IntoIterator-for-%26mut+Box%3C%5BT;+N%5D,+A%3E) +- [`VecDeque::retain_back`](https://doc.rust-lang.org/stable/std/collections/struct.VecDeque.html#method.retain_back) +- [`core::ffi::VaList`](https://doc.rust-lang.org/stable/core/ffi/struct.VaList.html) +- [`Box::into_non_null`](https://doc.rust-lang.org/stable/std/boxed/struct.Box.html#method.into_non_null) +- [`Box::from_non_null`](https://doc.rust-lang.org/stable/std/boxed/struct.Box.html#method.from_non_null) +- [`Vec::into_parts`](https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.into_parts) +- [`Vec::from_parts`](https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.from_parts) +- [`core::mem::size_of_val_raw`](https://doc.rust-lang.org/stable/core/mem/fn.size_of_val_raw.html) +- [`core::mem::align_of_val_raw`](https://doc.rust-lang.org/stable/core/mem/fn.align_of_val_raw.html) +- [`core::alloc::Layout::for_value_raw`](https://doc.rust-lang.org/stable/core/alloc/struct.Layout.html#method.for_value_raw) +- [`String::from_utf8_lossy_owned`](https://doc.rust-lang.org/stable/std/string/struct.String.html#method.from_utf8_lossy_owned) +- [`string::FromUtf8Error::into_utf8_lossy`](https://doc.rust-lang.org/stable/std/string/struct.FromUtf8Error.html#method.into_utf8_lossy) +- [`FusedIterator for StepBy`](https://doc.rust-lang.org/stable/std/iter/struct.StepBy.html#impl-FusedIterator-for-StepBy%3CI%3E) +- [`std::fs::set_times`](https://doc.rust-lang.org/stable/std/fs/fn.set_times.html) +- [`std::fs::set_times_nofollow`](https://doc.rust-lang.org/stable/std/fs/fn.set_times_nofollow.html) + + + +Cargo +----- +- Add a new built-in profile `debug`. This is a preparation for transitioning the `dev` profile away from debugging to give a saner default for faster development iterations. Currently there is no difference between `dev` and `debug` profiles. [docs](https://doc.rust-lang.org/nightly/cargo/reference/profiles.html#debug-1) [#17214](https://github.com/rust-lang/cargo/pull/17214) +- Workspace members on edition 2024 or later can now override an inherited workspace dependency's `default-features` field. For example, `serde = { workspace = true, default-features = false }` now turns off default features even when the workspace definition enables them. On earlier editions, `default-features = false` is ignored with a warning. ([RFC 3945](https://github.com/rust-lang/rfcs/pull/3945)) [#17126](https://github.com/rust-lang/cargo/pull/17126) + +See also the [full Cargo changelog](https://doc.rust-lang.org/nightly/cargo/CHANGELOG.html#cargo-199-2026-10-01) + + + +Rustdoc +----- +- [Add new `unused_footnote_definition` rustdoc lint](https://github.com/rust-lang/rust/pull/137858) +- Smarter filtering of trait impls yields performance improvements of 20% on average and up to 40% on some real-world crates. ([1](https://github.com/rust-lang/rust/pull/159623), [2](https://github.com/rust-lang/rust/pull/159721), [3](https://github.com/rust-lang/rust/pull/159779), [4](https://github.com/rust-lang/rust/pull/159854), [5](https://github.com/rust-lang/rust/pull/159091)) + + + +Compatibility Notes +------------------- +- [Fully deprecate the legacy integral modules](https://github.com/rust-lang/rust/pull/146882). For example, `std::i32::MAX` should be accessed via `i32::MAX` instead. +- [Upgrade `no_mangle_generic_items` into hard error](https://github.com/rust-lang/rust/pull/154585) +- [The `Pin::new_unchecked` has had its safety invariants changed slightly](https://github.com/rust-lang/rust/pull/156935) +- [Do not promote references to extern statics](https://github.com/rust-lang/rust/pull/157641) +- [Ensure that the inferred types of `let` patterns typecheck](https://github.com/rust-lang/rust/pull/157841) +- [hermit/fs: Return `unsupported()` instead of `from_raw_os_error(22)`](https://github.com/rust-lang/rust/pull/158247) +- [Fixed a bug where `#[repr(simd)]` was accidentally allowed on macro invocations on stable Rust](https://github.com/rust-lang/rust/pull/158523) +- [Abort const-eval when there are generics in the type of the value being produced](https://github.com/rust-lang/rust/pull/159504) +- [Attributes not applying to anything are now an error in code blocks in doc comments](https://github.com/rust-lang/rust/pull/159849) +- [`Box::leak`: tell people to avoid unleaking](https://github.com/rust-lang/rust/pull/160323) +- [PowerPC inline ASM: Fix scalar floats being in the wrong vector lane on little endian](https://github.com/rust-lang/rust/pull/160441) +- [Do not take `doc(cfg())` into account when filtering doctests](https://github.com/rust-lang/rust/pull/159014) +- [Infer anonymous lifetimes in the types of associated consts as `'static`](https://github.com/rust-lang/rust/pull/156508) +- Macros that expand to a semicolon now produce a warning lint (`semicolon_in_expressions_from_non_local_macros`) even when the macro comes from another crate. Previously, such warnings only appeared for macros from the same crate, to avoid showing warnings that can't be fixed locally; however, this masked problems, as integration tests from the crate providing the macro get compiled as a separate crate, so tests often wouldn't reveal this issue. If you encounter a lint like this, please make sure to report it to the crate providing the macro so they can fix it; don't just silence it in your own crate. + - [`semicolon_in_expressions_from_macros`: Lint on non-local macros too](https://github.com/rust-lang/rust/pull/159222) + - [Split non-local `semicolon_in_expressions_from_macros` into a separate lint](https://github.com/rust-lang/rust/pull/159700) +- In Cargo, incremental compilation is now disabled by default when running in CI. CI is detected via the `CI` environment variable. [#17220](https://github.com/rust-lang/cargo/pull/17220) +- [The Rust Reference no longer recommends using a single pattern to match a union alongside another value, such as in a manually-written tagged union](https://github.com/rust-lang/reference/pull/2303). This was because it was found that such code would, in some circumstances, result in the compiler reading the contents of the union before checking if the rest of the scrutinee matches the rest of the pattern. This can cause undefined behavior in code similar to what the reference previously recommended. + + + +Internal Changes +---------------- + +These changes do not affect any public interfaces of Rust, but they represent +significant improvements to the performance or internals of rustc and related +tools. + +- [Update to LLVM 23](https://github.com/rust-lang/rust/pull/158734) + Version 1.98.1 (2026-09-03) =========================== @@ -1406,7 +1537,7 @@ for more information on Rust's tiered platform support. Libraries --------- - [Stabilize the anonymous pipe API](https://github.com/rust-lang/rust/issues/127154) -- [Add support for unbounded left/right shift operations](https://github.com/rust-lang/rust/issues/129375) +- [Add support for unbounded left/right shift operations](https://github.com/rust-lang/rust/issues/129375) - [Print pointer metadata in `Debug` impl of raw pointers](https://github.com/rust-lang/rust/pull/135080) - [`Vec::with_capacity` guarantees it allocates with the amount requested, even if `Vec::capacity` returns a different number.](https://github.com/rust-lang/rust/pull/135933) - Most `std::arch` intrinsics which don't take pointer arguments can now be called from safe code if the caller has the appropriate target features already enabled (https://github.com/rust-lang/stdarch/pull/1714, https://github.com/rust-lang/stdarch/pull/1716, https://github.com/rust-lang/stdarch/pull/1717) @@ -1480,7 +1611,7 @@ These previously stable APIs are now stable in const contexts: - [`char::is_digit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_digit) - [`char::is_whitespace`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_whitespace) - [`<[[T; N]]>::as_flattened`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.as_flattened) -- [`<[[T; N]]>::as_flattened_mut`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.as_flattened_mut) +- [`<[[T; N]]>::as_flattened_mut`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.as_flattened_mut) - [`String::into_bytes`](https://doc.rust-lang.org/stable/std/string/struct.String.html#method.into_bytes) - [`String::as_str`](https://doc.rust-lang.org/stable/std/string/struct.String.html#method.as_str) - [`String::capacity`](https://doc.rust-lang.org/stable/std/string/struct.String.html#method.capacity) diff --git a/bootstrap.example.toml b/bootstrap.example.toml index 0470b9151ef34..34ff4eb623917 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -431,7 +431,7 @@ # "rust-analyzer", # "rust-analyzer-proc-macro-srv", # "analysis", -# "src", +# "rust-src", # "wasm-component-ld", # "miri", "cargo-miri" # for dev/nightly channels #] @@ -1038,6 +1038,11 @@ # Instrument LLVM, so that when executed, it will gather profiles # Note: for LLVM specifically, this should be a directory, not a file path. #pgo.llvm.generate = "/tmp/profiles" +# Use the following profile to PGO optimize the Cranelift codegen backend. +#pgo.cranelift.use = "/tmp/profiles/foo.profraw" +# Instrument the Cranelift codegen backend, so that when executed, it will gather profiles +# to this path. +#pgo.cranelift.generate = "/tmp/profiles/foo.profraw" # ============================================================================= # Options for specific targets diff --git a/compiler/rustc/src/main.rs b/compiler/rustc/src/main.rs index 17f768f6dc54a..6609a19fa9e34 100644 --- a/compiler/rustc/src/main.rs +++ b/compiler/rustc/src/main.rs @@ -1,7 +1,5 @@ // We need this feature as it changes `dylib` linking behavior and allows us to link to `rustc_driver`. #![feature(rustc_private)] -// Several crates are depended upon but unused so that they are present in the sysroot -#![expect(unused_crate_dependencies)] use std::process::ExitCode; diff --git a/compiler/rustc_abi/src/callconv.rs b/compiler/rustc_abi/src/callconv.rs index 4fda4735b613c..1bab82667232b 100644 --- a/compiler/rustc_abi/src/callconv.rs +++ b/compiler/rustc_abi/src/callconv.rs @@ -58,6 +58,10 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { /// /// Note: We generally ignore 1-ZST fields when computing this value (see #56877). /// + /// Note: The fields of a union are merged as if it were a struct, so the result does not + /// distinguish the two. `powerpc64::is_or_contains_union` in `rustc_target` walks the layout + /// again to do that, mirroring the array handling here. + /// /// This is public so that it can be used in unit tests, but /// should generally only be relevant to the ABI details of /// specific targets. diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index e8779d6ee6869..fa03fb87d02d1 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -11,8 +11,9 @@ use tracing::{debug, trace}; use crate::{ AbiAlign, Align, BackendLaneCount, BackendRepr, FieldsShape, HasDataLayout, IndexSlice, - IndexVec, Integer, LayoutData, Niche, NumScalableVectors, Primitive, ReprOptions, Scalar, Size, - StructKind, TagEncoding, TargetDataLayout, VariantLayout, Variants, WrappingRange, + IndexVec, Integer, LayoutData, Niche, NicheOptimizations, NumScalableVectors, Primitive, + ReprOptions, Scalar, Size, StructKind, TagEncoding, TargetDataLayout, VariantLayout, Variants, + WrappingRange, }; mod coroutine; @@ -178,7 +179,7 @@ impl LayoutCalculator { Self { cx } } - pub fn array_like( + pub fn layout_of_array_like( &self, element: &LayoutData, count_if_sized: Option, // None for slices @@ -201,7 +202,7 @@ impl LayoutCalculator { }) } - pub fn scalable_vector_type( + pub fn layout_of_scalable_vector_type( &self, element: F, count: u64, @@ -220,7 +221,7 @@ impl LayoutCalculator { ) } - pub fn simd_type( + pub fn layout_of_simd_type( &self, element: F, count: u64, @@ -239,20 +240,20 @@ impl LayoutCalculator { /// /// This uses dedicated code instead of [`Self::layout_of_struct_or_enum`], as coroutine /// fields may be shared between multiple variants (see the [`coroutine`] module for details). - pub fn coroutine< - 'a, - F: Deref> + fmt::Debug + Copy, - VariantIdx: Idx, - FieldIdx: Idx, - LocalIdx: Idx, - >( + pub fn layout_of_coroutine<'a, F, VariantIdx, FieldIdx, LocalIdx>( &self, local_layouts: &IndexSlice, prefix_layouts: IndexVec, variant_fields: &IndexSlice>, storage_conflicts: &BitMatrix, tag_to_layout: impl Fn(Scalar) -> F, - ) -> LayoutCalculatorResult { + ) -> LayoutCalculatorResult + where + F: Deref> + fmt::Debug + Copy, + VariantIdx: Idx, + FieldIdx: Idx, + LocalIdx: Idx, + { coroutine::layout( self, local_layouts, @@ -263,96 +264,96 @@ impl LayoutCalculator { ) } - pub fn univariant< - 'a, - FieldIdx: Idx, - VariantIdx: Idx, - F: Deref> + fmt::Debug + Copy, - >( + /// Compute the layout for a univariant (see [`Variants::Single`]). + /// + /// As a consumer of `rustc_abi`, you should only use this method for non-ADTs. + /// For structs and univariant enums, use [`Self::layout_of_struct`] instead + /// (it uses this function internally). + pub fn layout_of_univariant<'a, FieldIdx, VariantIdx, F>( &self, fields: &IndexSlice, repr: &ReprOptions, kind: StructKind, - ) -> LayoutCalculatorResult { + ) -> LayoutCalculatorResult + where + FieldIdx: Idx, + VariantIdx: Idx, + F: Deref> + fmt::Debug + Copy, + { let dl = self.cx.data_layout(); - let layout = self.univariant_biased(fields, repr, kind, NicheBias::Start); + let layout = self.layout_of_univariant_biased(fields, repr, kind, NicheBias::Start); // Enums prefer niches close to the beginning or the end of the variants so that other // (smaller) data-carrying variants can be packed into the space after/before the niche. // If the default field ordering does not give us a niche at the front then we do a second // run and bias niches to the right and then check which one is closer to one of the // struct's edges. - if let Ok(layout) = &layout { + if let Ok(layout) = &layout // Don't try to calculate an end-biased layout for unsizable structs, // otherwise we could end up with different layouts for // Foo and Foo which would break unsizing. - if !matches!(kind, StructKind::MaybeUnsized) { - if let Some(niche) = layout.largest_niche { - let head_space = niche.offset.bytes(); - let niche_len = niche.value.size(dl).bytes(); - let tail_space = layout.size.bytes() - head_space - niche_len; - - // This may end up doing redundant work if the niche is already in the last - // field (e.g. a trailing bool) and there is tail padding. But it's non-trivial - // to get the unpadded size so we try anyway. - if fields.len() > 1 && head_space != 0 && tail_space > 0 { - let alt_layout = self - .univariant_biased(fields, repr, kind, NicheBias::End) - .expect("alt layout should always work"); - let alt_niche = alt_layout - .largest_niche - .expect("alt layout should have a niche like the regular one"); - let alt_head_space = alt_niche.offset.bytes(); - let alt_niche_len = alt_niche.value.size(dl).bytes(); - let alt_tail_space = - alt_layout.size.bytes() - alt_head_space - alt_niche_len; - - debug_assert_eq!(layout.size.bytes(), alt_layout.size.bytes()); - - let prefer_alt_layout = - alt_head_space > head_space && alt_head_space > tail_space; - - debug!( - "sz: {}, default_niche_at: {}+{}, default_tail_space: {}, alt_niche_at/head_space: {}+{}, alt_tail: {}, num_fields: {}, better: {}\n\ - layout: {}\n\ - alt_layout: {}\n", - layout.size.bytes(), - head_space, - niche_len, - tail_space, - alt_head_space, - alt_niche_len, - alt_tail_space, - layout.fields.count(), - prefer_alt_layout, - self.format_field_niches(layout, fields), - self.format_field_niches(&alt_layout, fields), - ); - - if prefer_alt_layout { - return Ok(alt_layout); - } - } - } + && !matches!(kind, StructKind::MaybeUnsized) + && let Some(niche) = layout.largest_niche + && let head_space = niche.offset.bytes() + && let niche_len = niche.value.size(dl).bytes() + && let tail_space = layout.size.bytes() - head_space - niche_len + // This may end up doing redundant work if the niche is already in the last + // field (e.g. a trailing bool) and there is tail padding. But it's non-trivial + // to get the unpadded size so we try anyway. + && (fields.len() > 1 && head_space != 0 && tail_space > 0) + { + let alt_layout = self + .layout_of_univariant_biased(fields, repr, kind, NicheBias::End) + .expect("alt layout should always work"); + let alt_niche = alt_layout + .largest_niche + .expect("alt layout should have a niche like the regular one"); + let alt_head_space = alt_niche.offset.bytes(); + let alt_niche_len = alt_niche.value.size(dl).bytes(); + let alt_tail_space = alt_layout.size.bytes() - alt_head_space - alt_niche_len; + + debug_assert_eq!(layout.size.bytes(), alt_layout.size.bytes()); + + let prefer_alt_layout = alt_head_space > head_space && alt_head_space > tail_space; + + debug!( + "sz: {}, default_niche_at: {}+{}, default_tail_space: {}, alt_niche_at/head_space: {}+{}, alt_tail: {}, num_fields: {}, better: {}\n\ + layout: {}\n\ + alt_layout: {}\n", + layout.size.bytes(), + head_space, + niche_len, + tail_space, + alt_head_space, + alt_niche_len, + alt_tail_space, + layout.fields.count(), + prefer_alt_layout, + self.format_field_niches(layout, fields), + self.format_field_niches(&alt_layout, fields), + ); + + if prefer_alt_layout { + return Ok(alt_layout); } } layout } - pub fn layout_of_struct_or_enum< - 'a, - FieldIdx: Idx, - VariantIdx: Idx, - F: Deref> + fmt::Debug + Copy, - >( + pub fn layout_of_struct_or_enum<'a, FieldIdx, VariantIdx, F>( &self, repr: &ReprOptions, variants: &IndexSlice>, is_enum: bool, - is_special_no_niche: bool, + niche_optimizations: NicheOptimizations, discr_range_of_repr: impl Fn(RangeFrom, RangeToInclusive) -> (Integer, bool), discriminants: impl Iterator, always_sized: bool, - ) -> LayoutCalculatorResult { + ) -> LayoutCalculatorResult + where + FieldIdx: Idx, + VariantIdx: Idx, + F: Deref> + fmt::Debug + Copy, + { let (present_first, present_second) = { let mut present_variants = variants.iter_enumerated().filter_map(|(i, v)| { if !repr.inhibit_enum_layout_opt() && absent(v) { None } else { Some(i) } @@ -378,10 +379,10 @@ impl LayoutCalculator { self.layout_of_struct( repr, variants, + present_first, is_enum, - is_special_no_niche, + niche_optimizations, always_sized, - present_first, ) } else { // At this point, we have handled all unions and @@ -392,16 +393,16 @@ impl LayoutCalculator { } } - pub fn layout_of_union< - 'a, - FieldIdx: Idx, - VariantIdx: Idx, - F: Deref> + fmt::Debug + Copy, - >( + pub fn layout_of_union<'a, FieldIdx, VariantIdx, F>( &self, repr: &ReprOptions, variants: &IndexSlice>, - ) -> LayoutCalculatorResult { + ) -> LayoutCalculatorResult + where + FieldIdx: Idx, + VariantIdx: Idx, + F: Deref> + fmt::Debug + Copy, + { let dl = self.cx.data_layout(); let mut align = if repr.pack.is_some() { dl.i8_align } else { dl.aggregate_align }; let mut max_repr_align = repr.align; @@ -519,36 +520,36 @@ impl LayoutCalculator { }) } - /// single-variant enums are just structs, if you think about it - fn layout_of_struct< - 'a, - FieldIdx: Idx, - VariantIdx: Idx, - F: Deref> + fmt::Debug + Copy, - >( + /// Calculate the layout for a struct, or a single-variant enum. + /// + /// They are the same thing, if you think about it + /// (Typechecking will reject discriminant-sizing attrs.) + fn layout_of_struct<'a, FieldIdx, VariantIdx, F>( &self, repr: &ReprOptions, variants: &IndexSlice>, + variant_idx: VariantIdx, is_enum: bool, - is_special_no_niche: bool, + niche_optimizations: NicheOptimizations, always_sized: bool, - present_first: VariantIdx, - ) -> LayoutCalculatorResult { - // Struct, or univariant enum equivalent to a struct. - // (Typechecking will reject discriminant-sizing attrs.) - + ) -> LayoutCalculatorResult + where + FieldIdx: Idx, + VariantIdx: Idx, + F: Deref> + fmt::Debug + Copy, + { let dl = self.cx.data_layout(); - let v = present_first; + let v = variant_idx; let kind = if is_enum || variants[v].is_empty() || always_sized { StructKind::AlwaysSized } else { StructKind::MaybeUnsized }; - let mut st = self.univariant(&variants[v], repr, kind)?; + let mut st = self.layout_of_univariant(&variants[v], repr, kind)?; st.variants = Variants::Single { index: v }; - if is_special_no_niche { + if niche_optimizations == NicheOptimizations::Disabled { let hide_niches = |scalar: &mut _| match scalar { Scalar::Initialized { value, valid_range } => { *valid_range = WrappingRange::full(value.size(dl)) @@ -573,18 +574,18 @@ impl LayoutCalculator { Ok(st) } - fn layout_of_enum< - 'a, - FieldIdx: Idx, - VariantIdx: Idx, - F: Deref> + fmt::Debug + Copy, - >( + fn layout_of_enum<'a, FieldIdx, VariantIdx, F>( &self, repr: &ReprOptions, variants: &IndexSlice>, discr_range_of_repr: impl Fn(RangeFrom, RangeToInclusive) -> (Integer, bool), discriminants: impl Iterator, - ) -> LayoutCalculatorResult { + ) -> LayoutCalculatorResult + where + FieldIdx: Idx, + VariantIdx: Idx, + F: Deref> + fmt::Debug + Copy, + { let dl = self.cx.data_layout(); // bail if the enum has an incoherent repr that cannot be computed if repr.packed() { @@ -613,7 +614,7 @@ impl LayoutCalculator { let mut variant_layouts = variants .iter() .map(|v| { - let st = self.univariant(v, repr, StructKind::AlwaysSized).ok()?; + let st = self.layout_of_univariant(v, repr, StructKind::AlwaysSized).ok()?; variants_info.push(VariantLayoutInfo { align_abi: st.align.abi }); @@ -815,7 +816,7 @@ impl LayoutCalculator { let mut layout_variants = variants .iter() .map(|field_layouts| { - let st = self.univariant( + let st = self.layout_of_univariant( field_layouts, repr, StructKind::Prefixed(min_ity.size(), prefix_align), @@ -1078,18 +1079,18 @@ impl LayoutCalculator { Ok(best_layout) } - fn univariant_biased< - 'a, - FieldIdx: Idx, - VariantIdx: Idx, - F: Deref> + fmt::Debug + Copy, - >( + fn layout_of_univariant_biased<'a, FieldIdx, VariantIdx, F>( &self, fields: &IndexSlice, repr: &ReprOptions, kind: StructKind, niche_bias: NicheBias, - ) -> LayoutCalculatorResult { + ) -> LayoutCalculatorResult + where + FieldIdx: Idx, + VariantIdx: Idx, + F: Deref> + fmt::Debug + Copy, + { let dl = self.cx.data_layout(); let pack = repr.pack; let mut align = if pack.is_some() { dl.i8_align } else { dl.aggregate_align }; @@ -1424,16 +1425,16 @@ impl LayoutCalculator { }) } - fn format_field_niches< - 'a, - FieldIdx: Idx, - VariantIdx: Idx, - F: Deref> + fmt::Debug, - >( + fn format_field_niches<'a, FieldIdx, VariantIdx, F>( &self, layout: &LayoutData, fields: &IndexSlice, - ) -> String { + ) -> String + where + FieldIdx: Idx, + VariantIdx: Idx, + F: Deref> + fmt::Debug, + { let dl = self.cx.data_layout(); let mut s = String::new(); for i in layout.fields.index_by_increasing_offset() { @@ -1484,15 +1485,13 @@ where }; // Compute the size and alignment of the vector - let size = elt - .size - .checked_mul(count.as_u64(), dl) - .ok_or_else(|| LayoutCalculatorError::SizeOverflow)?; + let size = + elt.size.checked_mul(count.as_u64(), dl).ok_or(LayoutCalculatorError::SizeOverflow)?; let (repr, size, align) = match kind { SimdVectorKind::Scalable(number_of_vectors) => ( BackendRepr::SimdScalableVector { element, count, number_of_vectors }, size.checked_mul(number_of_vectors.0 as u64, dl) - .ok_or_else(|| LayoutCalculatorError::SizeOverflow)?, + .ok_or(LayoutCalculatorError::SizeOverflow)?, dl.rust_vector_align(size), ), // Non-power-of-two vectors have padding up to the next power-of-two. diff --git a/compiler/rustc_abi/src/layout/coroutine.rs b/compiler/rustc_abi/src/layout/coroutine.rs index fd68d06c93829..c102fbb3e843c 100644 --- a/compiler/rustc_abi/src/layout/coroutine.rs +++ b/compiler/rustc_abi/src/layout/coroutine.rs @@ -136,20 +136,20 @@ fn coroutine_saved_local_eligibility> + core::fmt::Debug + Copy, - VariantIdx: Idx, - FieldIdx: Idx, - LocalIdx: Idx, ->( +pub(super) fn layout<'a, F, VariantIdx, FieldIdx, LocalIdx>( calc: &super::LayoutCalculator, local_layouts: &IndexSlice, mut prefix_layouts: IndexVec, variant_fields: &IndexSlice>, storage_conflicts: &BitMatrix, tag_to_layout: impl Fn(Scalar) -> F, -) -> super::LayoutCalculatorResult { +) -> super::LayoutCalculatorResult +where + F: core::ops::Deref> + core::fmt::Debug + Copy, + VariantIdx: Idx, + FieldIdx: Idx, + LocalIdx: Idx, +{ use SavedLocalEligibility::*; let (ineligible_locals, assignments) = @@ -171,8 +171,11 @@ pub(super) fn layout< let promoted_layouts = ineligible_locals.iter().map(|local| local_layouts[local]); prefix_layouts.push(tag_to_layout(tag)); prefix_layouts.extend(promoted_layouts); - let prefix = - calc.univariant(&prefix_layouts, &ReprOptions::default(), StructKind::AlwaysSized)?; + let prefix = calc.layout_of_univariant( + &prefix_layouts, + &ReprOptions::default(), + StructKind::AlwaysSized, + )?; let (prefix_size, prefix_align) = (prefix.size, prefix.align); @@ -225,7 +228,7 @@ pub(super) fn layout< }) .map(|local| local_layouts[*local]); - let mut variant = calc.univariant( + let mut variant = calc.layout_of_univariant( &variant_only_tys.collect::>(), &ReprOptions::default(), StructKind::Prefixed(prefix_size, prefix_align.abi), diff --git a/compiler/rustc_abi/src/layout/ty.rs b/compiler/rustc_abi/src/layout/ty.rs index 5033f887a60f9..b8928aecf0cc5 100644 --- a/compiler/rustc_abi/src/layout/ty.rs +++ b/compiler/rustc_abi/src/layout/ty.rs @@ -233,8 +233,8 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { } /// If this method returns `true`, then this type should always have a `PassMode` of - /// `Indirect { on_stack: false, .. }` when being used as the argument type of a function with a - /// non-Rustic ABI (this is true for structs annotated with the + /// `Indirect { mode: IndirectMode::Pointer, .. }` when being used as the argument type of a + /// function with a non-Rustic ABI (this is true for structs annotated with the /// `#[rustc_pass_indirectly_in_non_rustic_abis]` attribute). /// /// This is used to replicate some of the behaviour of C array-to-pointer decay; however unlike @@ -342,7 +342,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { Primitive::Float(float @ (Float::F16 | Float::F32 | Float::F64 | Float::F128)) => { Some(Numeric::Float(float)) } - Primitive::Pointer(..) => None, + Primitive::Pointer(..) | Primitive::Float(Float::F16B) => None, } } diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index b056fdc73d40b..3804f4281a9bb 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -399,8 +399,8 @@ pub enum TargetDataLayoutError<'a> { } #[cfg(feature = "nightly")] -impl Diagnostic<'_, G> for TargetDataLayoutError<'_> { - fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { +impl Diagnostic<'_> for TargetDataLayoutError<'_> { + fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_> { match self { TargetDataLayoutError::InvalidAddressSpace { addr_space, err, cause } => { Diag::new(dcx, level, msg!("invalid address space `{$addr_space}` for `{$cause}` in \"data-layout\": {$err}")) @@ -1420,6 +1420,10 @@ impl Integer { #[cfg_attr(feature = "nightly", derive(StableHash))] pub enum Float { F16, + /// `f16b`. This is not a builtin type in Rust (it is exposed as a lang item), + /// but it is a builtin type in LLVM so needs to be explicitly represented + /// in the backend. + F16B, F32, F64, F128, @@ -1431,6 +1435,7 @@ impl Float { match self { F16 => Size::from_bits(16), + F16B => Size::from_bits(16), F32 => Size::from_bits(32), F64 => Size::from_bits(64), F128 => Size::from_bits(128), @@ -1442,7 +1447,7 @@ impl Float { let dl = cx.data_layout(); AbiAlign::new(match self { - F16 => dl.f16_align, + F16 | F16B => dl.f16_align, F32 => dl.f32_align, F64 => dl.f64_align, F128 => dl.f128_align, @@ -1454,6 +1459,7 @@ impl Float { match self { F16 => "f16", + F16B => "f16b", F32 => "f32", F64 => "f64", F128 => "f128", @@ -1772,6 +1778,10 @@ pub struct AddressSpace(pub u32); impl AddressSpace { /// LLVM's `0` address space. pub const ZERO: Self = AddressSpace(0); + /// The address space for constant memory on nvptx and amdgpu. + /// This address space is used e.g. for kernel arguments that are constant throughout the + /// execution. + pub const GPU_CONSTANT: Self = AddressSpace(4); /// The address space for workgroup memory on nvptx and amdgpu. /// See e.g. the `gpu_launch_sized_workgroup_mem` intrinsic for details. pub const GPU_WORKGROUP: Self = AddressSpace(3); @@ -2005,24 +2015,40 @@ impl BackendRepr { } } +/// Describes the variants of a type. // NOTE: This struct is generic over the FieldIdx and VariantIdx for rust-analyzer usage. #[derive(PartialEq, Eq, Hash, Clone, Debug)] #[cfg_attr(feature = "nightly", derive(StableHash))] pub enum Variants { - /// A type with no valid variants. Must be uninhabited. + /// The type has no valid variants. Must be uninhabited. + /// + /// This is the case for: + /// 1. enums with no inhabited variants + /// 2. the never type Empty, - /// Single enum variants, structs/tuples, unions, and all non-ADTs. + /// The type has a single valid variant. Such types are called "univariant". + /// + /// This is the case for: + /// 1. enums with a single inhabited variant, aka. "univariant enums" + /// 2. structs, unions, and non-ADTs (except coroutines; see below), + /// as those can't have multiple variants Single { - /// Always `0` for types that cannot have multiple variants. + /// - for case 1, this is the index of the inhabited variant + /// - for case 2, this is always `0` (a dummy value) index: VariantIdx, }, - /// Enum-likes with more than one variant: each variant comes with - /// a *discriminant* (usually the same as the variant index but the user can - /// assign explicit discriminant values). That discriminant is encoded - /// as a *tag* on the machine. The layout of each variant is - /// a struct, and they all have space reserved for the tag. + /// The type has multiple valid variants. + /// + /// This is the case for: + /// 1. enums with multiple inhabited variants + /// 2. coroutines + /// + /// Each variant comes with a *discriminant* (usually the same as the + /// variant index but the user can assign explicit discriminant values). + /// That discriminant is encoded as a *tag* on the machine. The layout of + /// each variant is a struct, and they all have space reserved for the tag. /// For enums, the tag is the sole field of the layout. Multiple { tag: Scalar, @@ -2169,6 +2195,18 @@ impl Niche { } } +/// Whether niche optimizations should be performed during layout calculation. +/// +/// [`UnsafeCell`] and [`UnsafePinned`] both disable niche optimizations. +/// +/// [`UnsafeCell`]: std::cell::UnsafeCell +/// [`UnsafePinned`]: std::pin::UnsafePinned +#[derive(PartialEq, Eq, Clone, Copy)] +pub enum NicheOptimizations { + Enabled, + Disabled, +} + // NOTE: This struct is generic over the FieldIdx and VariantIdx for rust-analyzer usage. #[derive(PartialEq, Eq, Hash, Clone)] #[cfg_attr(feature = "nightly", derive(StableHash))] @@ -2316,9 +2354,12 @@ pub struct PointeeInfo { /// If `size` is not zero, then the pointer is either null or dereferenceable for this many bytes /// (independent of `safe`). /// - /// On a function argument, "dereferenceable" here means "dereferenceable for the entire duration - /// of this function call", i.e. it is UB for the memory that this pointer points to be freed - /// while this function is still running. + /// "Dereferenceable" means that it's sound to add a speculative (spurious) read of the pointer. + /// The backend is free to add such reads at will. (This is useful to hoist pointer reads out of + /// loops without proving that the loop executes at least once, for example.) + /// + /// This can be used in both argument and return position, in both cases it means + /// "dereferenceable right now" (function entry/return) (but could be freed any time later). pub size: Size, /// The pointer is guaranteed to be aligned this much (independent of `safe`). pub align: Align, @@ -2424,12 +2465,22 @@ pub enum AbiFromStrErr { NoExplicitUnwind, } +/// The layout information for a variant. +/// +/// For items with multiple variants ([`Variants::Multiple`]), the layout information of each +/// variant largely matches that of the overall item. So, instead of giving each one a new [`LayoutData`], +/// we use this struct, which stores only the information that differs between the variants. +/// +/// See for more context. // NOTE: This struct is generic over the FieldIdx for rust-analyzer usage. #[derive(PartialEq, Eq, Hash, Clone, Debug)] #[cfg_attr(feature = "nightly", derive(StableHash))] pub struct VariantLayout { + // FIXME: ideally we'd remove these as variants should not have their own + // size or backend_repr. pub size: Size, pub backend_repr: BackendRepr, + pub field_offsets: IndexVec, fields_in_memory_order: IndexVec, largest_niche: Option, diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs index 0fc1f96dfe469..4c9258b753d8f 100644 --- a/compiler/rustc_arena/src/lib.rs +++ b/compiler/rustc_arena/src/lib.rs @@ -10,14 +10,13 @@ // tidy-alphabetical-start #![allow(clippy::mut_from_ref)] // Arena allocators are one place where this pattern is fine. #![allow(internal_features)] -#![cfg_attr(bootstrap, feature(never_type))] +#![cfg_attr(bootstrap, feature(unwrap_infallible))] #![cfg_attr(test, feature(test))] #![deny(unsafe_op_in_unsafe_fn)] #![doc(test(no_crate_inject, attr(deny(warnings))))] #![feature(decl_macro)] #![feature(dropck_eyepatch)] #![feature(rustc_attrs)] -#![feature(unwrap_infallible)] // tidy-alphabetical-end use std::alloc::Layout; diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 5b9f3231fc744..6a86e9af71826 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -651,8 +651,8 @@ impl Pat { PatKind::MacCall(mac) => TyKind::MacCall(mac.clone()), // `&mut? P` can be reinterpreted as `&mut? T` where `T` is `P` reparsed as a type. PatKind::Ref(pat, pinned, mutbl) => pat.to_ty().map(|ty| match pinned { - Pinnedness::Not => TyKind::Ref(None, MutTy { ty, mutbl: *mutbl }), - Pinnedness::Pinned => TyKind::PinnedRef(None, MutTy { ty, mutbl: *mutbl }), + Pinnedness::Not => TyKind::Ref(None, ty, *mutbl), + Pinnedness::Pinned => TyKind::PinnedRef(None, ty, *mutbl), })?, // A slice/array pattern `[P]` can be reparsed as `[T]`, an unsized array, // when `P` can be reparsed as a type `T`. @@ -1507,7 +1507,7 @@ impl Expr { ExprKind::Paren(expr) => expr.to_ty().map(TyKind::Paren)?, ExprKind::AddrOf(BorrowKind::Ref, mutbl, expr) => { - expr.to_ty().map(|ty| TyKind::Ref(None, MutTy { ty, mutbl: *mutbl }))? + expr.to_ty().map(|ty| TyKind::Ref(None, ty, *mutbl))? } ExprKind::Repeat(expr, expr_len) => { @@ -1623,7 +1623,7 @@ impl Expr { | ExprKind::UnsafeBinderCast(..) | ExprKind::While(..) | ExprKind::Yield(YieldKind::Postfix(..)) - | ExprKind::DirectConstArg(..) + | ExprKind::GcaMacro(..) | ExprKind::Err(_) | ExprKind::Dummy => prefix_attrs_precedence(&self.attrs), } @@ -1920,8 +1920,8 @@ pub enum ExprKind { UnsafeBinderCast(UnsafeBinderCastKind, Box, Option>), - /// An mGCA `direct_const_arg!()` expression. - DirectConstArg(Box), + /// An mGCA `gca!()` expression. + GcaMacro(Box), /// Placeholder for an expression that wasn't syntactically well formed in some way. Err(ErrorGuaranteed), @@ -2336,14 +2336,6 @@ impl LitKind { } } -// N.B., If you change this, you'll probably want to change the corresponding -// type structure in `middle/ty.rs` as well. -#[derive(Clone, Encodable, Decodable, Debug, Walkable)] -pub struct MutTy { - pub ty: Box, - pub mutbl: Mutability, -} - /// Represents a function's signature in a trait declaration, /// trait implementation, or free function. #[derive(Clone, Encodable, Decodable, Debug)] @@ -2402,7 +2394,7 @@ pub struct BorrowedFnSig<'a> { /// * the `G = Ty` in `Trait = Ty>` /// * the `A: Bound` in `Trait` /// * the `RetTy` in `Trait(ArgTy, ArgTy) -> RetTy` -/// * the `C = { Ct }` in `Trait` (feature `min_generic_const_args`) +/// * the `C = { Ct }` in `Trait` (feature `gca_min_const_items`) /// * the `f(..): Bound` in `Trait` (feature `return_type_notation`) #[derive(Clone, Encodable, Decodable, Debug, Walkable)] pub struct AssocItemConstraint { @@ -2470,8 +2462,7 @@ impl From> for Ty { impl Ty { pub fn peel_refs(&self) -> &Self { let mut final_ty = self; - while let TyKind::Ref(_, MutTy { ty, .. }) | TyKind::Ptr(MutTy { ty, .. }) = &final_ty.kind - { + while let TyKind::Ref(_, ty, _) | TyKind::Ptr(ty, ..) = &final_ty.kind { final_ty = ty; } final_ty @@ -2528,13 +2519,13 @@ pub enum TyKind { /// A fixed length array (`[T; n]`). Array(Box, AnonConst), /// A raw pointer (`*const T` or `*mut T`). - Ptr(MutTy), + Ptr(Box, Mutability), /// A reference (`&'a T` or `&'a mut T`). - Ref(#[visitable(extra = LifetimeCtxt::Ref)] Option, MutTy), + Ref(#[visitable(extra = LifetimeCtxt::Ref)] Option, Box, Mutability), /// A pinned reference (`&'a pin const T` or `&'a pin mut T`). /// /// Desugars into `Pin<&'a T>` or `Pin<&'a mut T>`. - PinnedRef(#[visitable(extra = LifetimeCtxt::Ref)] Option, MutTy), + PinnedRef(#[visitable(extra = LifetimeCtxt::Ref)] Option, Box, Mutability), /// A function pointer type (e.g., `fn(usize) -> bool`). FnPtr(Box), /// An unsafe existential lifetime binder (e.g., `unsafe<'a> &'a ()`). @@ -2579,8 +2570,8 @@ pub enum TyKind { FieldOf(Box, Option, Ident), /// A view of a type. `T.{ field_1, field_2 }`. View(Box, #[visitable(ignore)] ThinVec), - /// An mGCA `direct_const_arg!()` expression. - DirectConstArg(Box), + /// An mGCA `gca!()` expression. + GcaMacro(Box), /// Sometimes we need a dummy value when no error has occurred. Dummy, /// Placeholder for a kind that has failed to be defined. @@ -2979,12 +2970,10 @@ impl Param { if ident.name == kw::SelfLower { return match self.ty.kind { TyKind::ImplicitSelf => Some(respan(self.pat.span, SelfKind::Value(mutbl))), - TyKind::Ref(lt, MutTy { ref ty, mutbl }) if ty.kind.is_implicit_self() => { + TyKind::Ref(lt, ref ty, mutbl) if ty.kind.is_implicit_self() => { Some(respan(self.pat.span, SelfKind::Region(lt, mutbl))) } - TyKind::PinnedRef(lt, MutTy { ref ty, mutbl }) - if ty.kind.is_implicit_self() => - { + TyKind::PinnedRef(lt, ref ty, mutbl) if ty.kind.is_implicit_self() => { Some(respan(self.pat.span, SelfKind::Pinned(lt, mutbl))) } _ => Some(respan( @@ -3016,17 +3005,13 @@ impl Param { SelfKind::Value(mutbl) => (mutbl, infer_ty), SelfKind::Region(lt, mutbl) => ( Mutability::Not, - Box::new(Ty { - id: DUMMY_NODE_ID, - kind: TyKind::Ref(lt, MutTy { ty: infer_ty, mutbl }), - span, - }), + Box::new(Ty { id: DUMMY_NODE_ID, kind: TyKind::Ref(lt, infer_ty, mutbl), span }), ), SelfKind::Pinned(lt, mutbl) => ( Mutability::Not, Box::new(Ty { id: DUMMY_NODE_ID, - kind: TyKind::PinnedRef(lt, MutTy { ty: infer_ty, mutbl }), + kind: TyKind::PinnedRef(lt, infer_ty, mutbl), span, }), ), diff --git a/compiler/rustc_ast/src/expand/autodiff_attrs.rs b/compiler/rustc_ast/src/expand/autodiff_attrs.rs index c9a3275c4c97f..9db7802f1739f 100644 --- a/compiler/rustc_ast/src/expand/autodiff_attrs.rs +++ b/compiler/rustc_ast/src/expand/autodiff_attrs.rs @@ -160,7 +160,7 @@ pub fn valid_ty_for_activity(ty: &Box, activity: DiffActivity) -> bool { if matches!(activity, Active | ActiveOnly) { return true; } - matches!(ty.kind, TyKind::Ptr(_) | TyKind::Ref(..)) + matches!(ty.kind, TyKind::Ptr(..) | TyKind::Ref(..)) && matches!(activity, Duplicated | DuplicatedOnly) } pub fn valid_input_activity(mode: DiffMode, activity: DiffActivity) -> bool { diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index c5addc5926901..20079dd039f6f 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -231,7 +231,9 @@ impl Lit { /// `Parser::eat_token_lit` (excluding unary negation). pub fn from_token(token: &Token) -> Option { match token.uninterpolate().kind { - Ident(name, IdentIsRaw::No) if name.is_bool_lit() => Some(Lit::new(Bool, name, None)), + Ident(name, IdentKind::Normal | IdentKind::ForcedKeyword) if name.is_bool_lit() => { + Some(Lit::new(Bool, name, None)) + } Literal(token_lit) => Some(token_lit), OpenInvisible(InvisibleOrigin::MetaVar( MetaVarKind::Literal | MetaVarKind::Expr { .. }, @@ -307,11 +309,11 @@ impl LitKind { } } -pub fn ident_can_begin_expr(name: Symbol, span: Span, is_raw: IdentIsRaw) -> bool { +pub fn ident_can_begin_expr(name: Symbol, span: Span, kind: IdentKind) -> bool { // WARNING: Take care when modifying this function! It will change the stable(!) set of // tokens that are allowed to match an `expr` nonterminal which is user observable. - let ident_token = Token::new(Ident(name, is_raw), span); + let ident_token = Token::new(Ident(name, kind), span); // FIXME: Remove `box` from this list given we officially no longer support box expressions // (#108471) (needs lang FCP as it affects stable macro matching behavior). @@ -344,11 +346,11 @@ pub fn ident_can_begin_expr(name: Symbol, span: Span, is_raw: IdentIsRaw) -> boo .contains(&name) } -fn ident_can_begin_type(name: Symbol, span: Span, is_raw: IdentIsRaw) -> bool { +fn ident_can_begin_type(name: Symbol, span: Span, kind: IdentKind) -> bool { // WARNING: Take care when modifying this function! It will change the stable(!) set of // tokens that are allowed to match an `ty` nonterminal which is user observable. - let ident_token = Token::new(Ident(name, is_raw), span); + let ident_token = Token::new(Ident(name, kind), span); !ident_token.is_reserved_ident() || ident_token.is_path_segment_keyword() @@ -357,32 +359,30 @@ fn ident_can_begin_type(name: Symbol, span: Span, is_raw: IdentIsRaw) -> bool { } #[derive(PartialEq, Eq, Encodable, Decodable, Hash, Debug, Copy, Clone, StableHash)] -pub enum IdentIsRaw { - No, - Yes, +pub enum IdentKind { + Normal, + Raw, + ForcedKeyword, } -impl IdentIsRaw { +impl IdentKind { pub fn to_print_mode_ident(self) -> IdentPrintMode { match self { - IdentIsRaw::No => IdentPrintMode::Normal, - IdentIsRaw::Yes => IdentPrintMode::RawIdent, + IdentKind::Normal => IdentPrintMode::Normal, + IdentKind::Raw => IdentPrintMode::RawIdent, + IdentKind::ForcedKeyword => IdentPrintMode::ForcedKeywordIdent, } } + pub fn to_print_mode_lifetime(self) -> IdentPrintMode { match self { - IdentIsRaw::No => IdentPrintMode::Normal, - IdentIsRaw::Yes => IdentPrintMode::RawLifetime, + IdentKind::Normal => IdentPrintMode::Normal, + IdentKind::Raw => IdentPrintMode::RawLifetime, + IdentKind::ForcedKeyword => unreachable!(), } } } -impl From for IdentIsRaw { - fn from(b: bool) -> Self { - if b { Self::Yes } else { Self::No } - } -} - #[derive(Clone, Copy, PartialEq, Eq, Hash, Encodable, Decodable, Debug, StableHash)] pub enum TokenKind { /* Expression-operator symbols. */ @@ -507,22 +507,22 @@ pub enum TokenKind { /// It's recommended to use `Token::{ident,uninterpolate}` and /// `Parser::token_uninterpolated_span` to treat regular and interpolated /// identifiers in the same way. - Ident(Symbol, IdentIsRaw), + Ident(Symbol, IdentKind), /// This identifier (and its span) is the identifier passed to the /// declarative macro. The span in the surrounding `Token` is the span of /// the `ident` metavariable in the macro's RHS. - NtIdent(sp::Ident, IdentIsRaw), + NtIdent(sp::Ident, IdentKind), /// Lifetime identifier token. /// Do not forget about `NtLifetime` when you want to match on lifetime identifiers. /// It's recommended to use `Token::{ident,uninterpolate}` and /// `Parser::token_uninterpolated_span` to treat regular and interpolated /// identifiers in the same way. - Lifetime(Symbol, IdentIsRaw), + Lifetime(Symbol, IdentKind), /// This identifier (and its span) is the lifetime passed to the /// declarative macro. The span in the surrounding `Token` is the span of /// the `lifetime` metavariable in the macro's RHS. - NtLifetime(sp::Ident, IdentIsRaw), + NtLifetime(sp::Ident, IdentKind), /// A doc comment token. /// `Symbol` is the doc comment's data excluding its "quotes" (`///`, `/**`, etc) @@ -641,9 +641,13 @@ impl Token { Token::new(TokenKind::Question, DUMMY_SP) } - /// Recovers a `Token` from an `Ident`. This creates a raw identifier if necessary. + /// Recovers a `Token` from an `Ident`. + /// + /// This creates a raw identifier if necessary. + /// It will never create a forced keyword. pub fn from_ast_ident(ident: sp::Ident) -> Self { - Token::new(Ident(ident.name, ident.is_raw_guess().into()), ident.span) + let kind = if ident.is_raw_guess() { IdentKind::Raw } else { IdentKind::Normal }; + Token::new(Ident(ident.name, kind), ident.span) } pub fn is_range_separator(&self) -> bool { @@ -674,8 +678,8 @@ impl Token { // tokens that are allowed to match an `expr` nonterminal which is user observable. match self.uninterpolate().kind { - Ident(name, is_raw) => - ident_can_begin_expr(name, self.span, is_raw), // value name or keyword + Ident(name, kind) => + ident_can_begin_expr(name, self.span, kind), // value name or keyword OpenParen | // tuple OpenBrace | // block OpenBracket | // array @@ -743,8 +747,8 @@ impl Token { // object types (consider `use<>+` and `use + Trait` for example). match self.uninterpolate().kind { - Ident(name, is_raw) => - ident_can_begin_type(name, self.span, is_raw), // type name or keyword + Ident(name, kind) => + ident_can_begin_type(name, self.span, kind), // type name or keyword OpenParen // tuple | OpenBracket // array | Bang // never @@ -766,7 +770,7 @@ impl Token { pub fn can_begin_const_arg(&self) -> bool { match self.kind { OpenBrace | Literal(..) | Minus => true, - Ident(name, IdentIsRaw::No) if name.is_bool_lit() => true, + Ident(name, IdentKind::Normal | IdentKind::ForcedKeyword) if name.is_bool_lit() => true, OpenInvisible(InvisibleOrigin::MetaVar( MetaVarKind::Expr { .. } | MetaVarKind::Block | MetaVarKind::Literal, )) => true, @@ -815,7 +819,7 @@ impl Token { pub fn can_begin_literal_maybe_minus(&self) -> bool { match self.uninterpolate().kind { Literal(..) | Minus => true, - Ident(name, IdentIsRaw::No) if name.is_bool_lit() => true, + Ident(name, IdentKind::Normal | IdentKind::ForcedKeyword) if name.is_bool_lit() => true, OpenInvisible(InvisibleOrigin::MetaVar(mv_kind)) => match mv_kind { MetaVarKind::Literal => true, MetaVarKind::Expr { can_begin_literal_maybe_minus, .. } => { @@ -845,9 +849,9 @@ impl Token { /// otherwise returns the original token. pub fn uninterpolate(&self) -> Cow<'_, Token> { match self.kind { - NtIdent(ident, is_raw) => Cow::Owned(Token::new(Ident(ident.name, is_raw), ident.span)), - NtLifetime(ident, is_raw) => { - Cow::Owned(Token::new(Lifetime(ident.name, is_raw), ident.span)) + NtIdent(ident, kind) => Cow::Owned(Token::new(Ident(ident.name, kind), ident.span)), + NtLifetime(ident, kind) => { + Cow::Owned(Token::new(Lifetime(ident.name, kind), ident.span)) } _ => Cow::Borrowed(self), } @@ -855,22 +859,22 @@ impl Token { /// Returns an identifier if this token is an identifier. #[inline] - pub fn ident(&self) -> Option<(sp::Ident, IdentIsRaw)> { + pub fn ident(&self) -> Option<(sp::Ident, IdentKind)> { // We avoid using `Token::uninterpolate` here because it's slow. match self.kind { - Ident(name, is_raw) => Some((sp::Ident::new(name, self.span), is_raw)), - NtIdent(ident, is_raw) => Some((ident, is_raw)), + Ident(name, kind) => Some((sp::Ident::new(name, self.span), kind)), + NtIdent(ident, kind) => Some((ident, kind)), _ => None, } } /// Returns a lifetime identifier if this token is a lifetime. #[inline] - pub fn lifetime(&self) -> Option<(sp::Ident, IdentIsRaw)> { + pub fn lifetime(&self) -> Option<(sp::Ident, IdentKind)> { // We avoid using `Token::uninterpolate` here because it's slow. match self.kind { - Lifetime(name, is_raw) => Some((sp::Ident::new(name, self.span), is_raw)), - NtLifetime(ident, is_raw) => Some((ident, is_raw)), + Lifetime(name, kind) => Some((sp::Ident::new(name, self.span), kind)), + NtLifetime(ident, kind) => Some((ident, kind)), _ => None, } } @@ -929,7 +933,7 @@ impl Token { /// Returns `true` if the token is a given keyword, `kw`. pub fn is_keyword(&self, kw: Symbol) -> bool { - self.is_non_raw_ident_where(|id| id.name == kw) + self.non_raw_ident().is_some_and(|id| id.name == kw) } /// Returns `true` if the token is a given keyword, `kw` or if `case` is `Insensitive` and this @@ -937,35 +941,35 @@ impl Token { pub fn is_keyword_case(&self, kw: Symbol, case: Case) -> bool { self.is_keyword(kw) || (case == Case::Insensitive - && self.is_non_raw_ident_where(|id| { + && self.non_raw_ident().is_some_and(|id| { // Do an ASCII case-insensitive match, because all keywords are ASCII. id.name.as_str().eq_ignore_ascii_case(kw.as_str()) })) } pub fn is_path_segment_keyword(&self) -> bool { - self.is_non_raw_ident_where(sp::Ident::is_path_segment_keyword) + self.non_raw_ident().is_some_and(sp::Ident::is_path_segment_keyword) } /// Returns true for reserved identifiers used internally for elided lifetimes, /// unnamed method parameters, crate root module, error recovery etc. pub fn is_special_ident(&self) -> bool { - self.is_non_raw_ident_where(sp::Ident::is_special) + self.non_raw_ident().is_some_and(sp::Ident::is_special) } /// Returns `true` if the token is a keyword used in the language. pub fn is_used_keyword(&self) -> bool { - self.is_non_raw_ident_where(sp::Ident::is_used_keyword) + self.non_raw_ident().is_some_and(sp::Ident::is_used_keyword) } /// Returns `true` if the token is a keyword reserved for possible future use. pub fn is_unused_keyword(&self) -> bool { - self.is_non_raw_ident_where(sp::Ident::is_unused_keyword) + self.non_raw_ident().is_some_and(sp::Ident::is_unused_keyword) } /// Returns `true` if the token is either a special identifier or a keyword. pub fn is_reserved_ident(&self) -> bool { - self.is_non_raw_ident_where(sp::Ident::is_reserved) + self.ident().is_some_and(|(id, kind)| ident_of_kind_is_reserved(id, kind)) } pub fn is_non_reserved_ident(&self) -> bool { @@ -973,14 +977,20 @@ impl Token { } pub fn non_reserved_ident(&self) -> Option { - self.ident() - .filter(|&(id, raw)| raw == IdentIsRaw::Yes || !sp::Ident::is_reserved(id)) - .map(|(id, _)| id) + self.ident().filter(|&(id, kind)| !ident_of_kind_is_reserved(id, kind)).map(|(id, _)| id) + } + + /// If this token is a non-raw identifier, return the identifier in question. + pub fn non_raw_ident(&self) -> Option { + match self.ident() { + Some((id, IdentKind::Normal | IdentKind::ForcedKeyword)) => Some(id), + _ => None, + } } /// Returns `true` if the token is the identifier `true` or `false`. pub fn is_bool_lit(&self) -> bool { - self.is_non_raw_ident_where(|id| id.name.is_bool_lit()) + self.non_raw_ident().is_some_and(|id| id.name.is_bool_lit()) } pub fn is_numeric_lit(&self) -> bool { @@ -995,14 +1005,6 @@ impl Token { matches!(self.kind, Literal(Lit { kind: LitKind::Integer, .. })) } - /// Returns `true` if the token is a non-raw identifier for which `pred` holds. - pub fn is_non_raw_ident_where(&self, pred: impl FnOnce(sp::Ident) -> bool) -> bool { - match self.ident() { - Some((id, IdentIsRaw::No)) => pred(id), - _ => false, - } - } - /// Is this an invisible open delimiter at the start of a token sequence /// from an expanded metavar? pub fn is_metavar_seq(&self) -> Option { @@ -1076,8 +1078,8 @@ impl Token { (Colon, Colon) => PathSep, (Colon, _) => return None, - (SingleQuote, Ident(name, is_raw)) => { - Lifetime(Symbol::intern(&format!("'{name}")), *is_raw) + (SingleQuote, Ident(name, kind)) => { + Lifetime(Symbol::intern(&format!("'{name}")), *kind) } (SingleQuote, _) => return None, @@ -1105,6 +1107,14 @@ impl PartialEq for Token { } } +pub fn ident_of_kind_is_reserved(id: sp::Ident, kind: IdentKind) -> bool { + match kind { + IdentKind::Normal => id.is_reserved(), + IdentKind::Raw => false, + IdentKind::ForcedKeyword => true, + } +} + #[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, StableHash)] pub enum NtPatKind { // Matches or-patterns. Was written using `pat` in edition 2021 or later. diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index df71aad0111cd..164d1cb8fd731 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -793,7 +793,7 @@ impl TokenStream { DelimSpacing::new(Spacing::JointHidden, Spacing::Alone), Delimiter::Bracket, [ - TokenTree::token_alone(token::Ident(sym::doc, token::IdentIsRaw::No), span), + TokenTree::token_alone(token::Ident(sym::doc, token::IdentKind::Normal), span), TokenTree::token_alone(token::Eq, span), TokenTree::token_alone( TokenKind::lit(token::StrRaw(num_of_hashes), data, None), diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs index e799f73ff544f..86a4073bd100f 100644 --- a/compiler/rustc_ast/src/util/classify.rs +++ b/compiler/rustc_ast/src/util/classify.rs @@ -158,7 +158,7 @@ pub fn leading_labeled_expr(mut expr: &ast::Expr) -> bool { | Yeet(..) | Yield(..) | UnsafeBinderCast(..) - | DirectConstArg(..) + | GcaMacro(..) | Err(..) | Dummy => return false, } @@ -244,7 +244,7 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option> { | Try(_) | Yeet(None) | UnsafeBinderCast(..) - | DirectConstArg(..) + | GcaMacro(..) | Err(_) | Dummy => { break None; @@ -262,10 +262,10 @@ fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> { break (mac.args.delim == Delimiter::Brace).then_some(mac); } - ast::TyKind::Ptr(mut_ty) - | ast::TyKind::Ref(_, mut_ty) - | ast::TyKind::PinnedRef(_, mut_ty) => { - ty = &mut_ty.ty; + ast::TyKind::Ptr(inner_ty, _) + | ast::TyKind::Ref(_, inner_ty, _) + | ast::TyKind::PinnedRef(_, inner_ty, _) => { + ty = inner_ty; } ast::TyKind::UnsafeBinder(binder) => { @@ -307,7 +307,7 @@ fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> { | ast::TyKind::Pat(..) | ast::TyKind::FieldOf(..) | ast::TyKind::View(..) - | ast::TyKind::DirectConstArg(..) + | ast::TyKind::GcaMacro(..) | ast::TyKind::Dummy | ast::TyKind::Err(..) => break None, } diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs index 1430d04dcca6a..bc5f693a29b7b 100644 --- a/compiler/rustc_ast/src/util/parser.rs +++ b/compiler/rustc_ast/src/util/parser.rs @@ -110,32 +110,6 @@ impl AssocOp { Cast | Binary(_) | Range(_) => false, } } - - /// This operator could be used to follow a block unambiguously. - /// - /// This is used for error recovery at the moment, providing a suggestion to wrap blocks with - /// parentheses while having a high degree of confidence on the correctness of the suggestion. - pub fn can_continue_expr_unambiguously(&self) -> bool { - use AssocOp::*; - use BinOpKind::*; - matches!( - self, - Assign | // `{ 42 } = { 42 }` - Binary( - BitXor | // `{ 42 } ^ 3` - Div | // `{ 42 } / 42` - Rem | // `{ 42 } % 2` - Shr | // `{ 42 } >> 2` - Le | // `{ 42 } <= 3` - Gt | // `{ 42 } > 3` - Ge // `{ 42 } >= 3` - ) | - AssignOp(_) | // `{ 42 } +=` - // Equal | // `{ 42 } == { 42 }` Accepting these here would regress incorrect - // NotEqual | // `{ 42 } != { 42 } struct literals parser recovery. - Cast // `{ 42 } as usize` - ) - } } #[derive(Clone, Copy, PartialEq, PartialOrd)] diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 4270ac0656deb..e4813b9588409 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -542,7 +542,6 @@ macro_rules! common_visitor_and_walkers { MethodCall, ModKind, ModSpans, - MutTy, NormalAttr, Parens, ParenthesizedArgs, @@ -1112,7 +1111,7 @@ macro_rules! common_visitor_and_walkers { visit_visitable!(vis, bytes), ExprKind::UnsafeBinderCast(kind, expr, ty) => visit_visitable!(vis, kind, expr, ty), - ExprKind::DirectConstArg(expr) => + ExprKind::GcaMacro(expr) => visit_visitable!(vis, expr), ExprKind::Err(_guar) => {} ExprKind::Dummy => {} diff --git a/compiler/rustc_ast_ir/src/lib.rs b/compiler/rustc_ast_ir/src/lib.rs index e919e7df99ee0..dc2bf67d338e7 100644 --- a/compiler/rustc_ast_ir/src/lib.rs +++ b/compiler/rustc_ast_ir/src/lib.rs @@ -7,7 +7,6 @@ // tidy-alphabetical-start #![cfg_attr(feature = "nightly", allow(internal_features))] -#![cfg_attr(feature = "nightly", cfg_attr(bootstrap, feature(never_type)))] #![cfg_attr(feature = "nightly", feature(rustc_attrs))] // tidy-alphabetical-end diff --git a/compiler/rustc_ast_lowering/Cargo.toml b/compiler/rustc_ast_lowering/Cargo.toml index 9dc5f81581e87..350687ba299c7 100644 --- a/compiler/rustc_ast_lowering/Cargo.toml +++ b/compiler/rustc_ast_lowering/Cargo.toml @@ -8,6 +8,7 @@ doctest = false [dependencies] # tidy-alphabetical-start +bitflags = "2.4.1" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_attr_ir = { path = "../rustc_attr_ir" } diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index fd3a00d56fe0b..71bcdb0e27e8f 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -140,8 +140,8 @@ impl<'hir> LoweringContext<'_, 'hir> { .iter() .map(|(op, op_sp)| { let lower_reg = |®: &_| match reg { - InlineAsmRegOrRegClass::Reg(reg) => { - asm::InlineAsmRegOrRegClass::Reg(if let Some(asm_arch) = asm_arch { + InlineAsmRegOrRegClass::Reg(reg) => hir::InlineAsmRegOrRegClass::Reg { + reg: if let Some(asm_arch) = asm_arch { asm::InlineAsmReg::parse(asm_arch, reg).unwrap_or_else(|error| { self.dcx().emit_err(InvalidRegister { op_span: *op_sp, @@ -152,10 +152,11 @@ impl<'hir> LoweringContext<'_, 'hir> { }) } else { asm::InlineAsmReg::Err - }) - } + }, + source_name: Some(reg), + }, InlineAsmRegOrRegClass::RegClass(reg_class) => { - asm::InlineAsmRegOrRegClass::RegClass(if let Some(asm_arch) = asm_arch { + hir::InlineAsmRegOrRegClass::RegClass(if let Some(asm_arch) = asm_arch { asm::InlineAsmRegClass::parse(asm_arch, reg_class).unwrap_or_else( |supported_register_classes| { self.dcx().emit_err(InvalidRegisterClass { @@ -344,7 +345,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } // Check for conflicts between explicit register operands. - if let asm::InlineAsmRegOrRegClass::Reg(reg) = reg { + if let hir::InlineAsmRegOrRegClass::Reg { reg, .. } = reg { let (input, output) = match op { hir::InlineAsmOperand::In { .. } => (true, false), @@ -393,24 +394,21 @@ impl<'hir> LoweringContext<'_, 'hir> { } _ => None, }; - let reg_str = |idx| -> &str { - // HIR asm doesn't preserve the original alias string of the explicit register, - // so we have to retrieve it from AST - let (op, _): &(InlineAsmOperand, Span) = &asm.operands[idx]; - if let Some(ast::InlineAsmRegOrRegClass::Reg(reg_sym)) = - op.reg() - { - reg_sym.as_str() - } else { - unreachable!("{op:?} is not a register operand"); - } - }; + let reg1_name = op + .reg() + .and_then(|reg| reg.source_name()) + .expect("{op:?} has no source register name"); + + let reg2_name = op2 + .reg() + .and_then(|reg| reg.source_name()) + .expect("{op2:?} has no source register name"); self.dcx().emit_err(RegisterConflict { op_span1: op_sp, op_span2: op_sp2, - reg1_name: reg_str(idx), - reg2_name: reg_str(idx2), + reg1_name: reg1_name.as_str(), + reg2_name: reg2_name.as_str(), in_out, }); } @@ -457,7 +455,10 @@ impl<'hir> LoweringContext<'_, 'hir> { if !output_used { operands.push(( hir::InlineAsmOperand::Out { - reg: asm::InlineAsmRegOrRegClass::Reg(clobber), + reg: hir::InlineAsmRegOrRegClass::Reg { + reg: clobber, + source_name: None, + }, late: true, expr: None, }, diff --git a/compiler/rustc_ast_lowering/src/block.rs b/compiler/rustc_ast_lowering/src/block.rs index b52af8fd3715a..b2cb1718b86a3 100644 --- a/compiler/rustc_ast_lowering/src/block.rs +++ b/compiler/rustc_ast_lowering/src/block.rs @@ -1,6 +1,6 @@ use rustc_ast::{Block, BlockCheckMode, Local, LocalKind, Stmt, StmtKind}; +use rustc_attr_ir::target::Target; use rustc_hir as hir; -use rustc_hir::Target; use rustc_span::sym; use smallvec::SmallVec; diff --git a/compiler/rustc_ast_lowering/src/contract.rs b/compiler/rustc_ast_lowering/src/contract.rs index eaebff521cb68..cbfeda33040e7 100644 --- a/compiler/rustc_ast_lowering/src/contract.rs +++ b/compiler/rustc_ast_lowering/src/contract.rs @@ -1,6 +1,8 @@ use std::sync::Arc; -use rustc_hir::attrs::lang_items::LangItem; +use rustc_attr_ir::lang_items::LangItem; +use rustc_attr_ir::target::Target; +use rustc_span::sym; use thin_vec::thin_vec; use crate::LoweringContext; @@ -209,7 +211,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let postcond_checker = self.arena.alloc(self.expr_enum_variant_lang_item( postcond_checker.span, - rustc_hir::attrs::lang_items::LangItem::OptionSome, + LangItem::OptionSome, &*arena_vec![self; *postcond_checker], )); let then_block_stmts = self.block_all(span, stmts, Some(postcond_checker)); @@ -217,7 +219,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let none_expr = self.arena.alloc(self.expr_enum_variant_lang_item( postcond_checker.span, - rustc_hir::attrs::lang_items::LangItem::OptionNone, + LangItem::OptionNone, Default::default(), )); let else_block = self.block_expr(none_expr); @@ -238,7 +240,7 @@ impl<'hir> LoweringContext<'_, 'hir> { postcond_span: rustc_span::Span, ) -> &'hir rustc_hir::Block<'hir> { let check_ident: rustc_span::Ident = - rustc_span::Ident::from_str_and_span("__ensures_checker", postcond_span); + rustc_span::Ident::new(sym::__ensures_checker, postcond_span); let (check_hir_id, postcond_decl) = { // Set up the postcondition `let` statement. let (checker_pat, check_hir_id) = self.pat_ident_binding_mode_mut( @@ -308,7 +310,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // ret // } // } - let ret_ident: rustc_span::Ident = rustc_span::Ident::from_str_and_span("__ret", span); + let ret_ident: rustc_span::Ident = rustc_span::Ident::new(sym::__ret, span); // Set up the return `let` statement. let (ret_pat, ret_hir_id) = @@ -350,7 +352,7 @@ impl<'hir> LoweringContext<'_, 'hir> { )); let attrs: rustc_ast::AttrVec = thin_vec![self.unreachable_code_attr(span)]; - self.lower_attrs(contract_check.hir_id, &attrs, span, rustc_hir::Target::Expression); + self.lower_attrs(contract_check.hir_id, &attrs, span, Target::Expression); let ret_block = self.block_all(span, arena_vec![self; ret_stmt], Some(contract_check)); self.arena.alloc(self.expr_block(self.arena.alloc(ret_block))) diff --git a/compiler/rustc_ast_lowering/src/delegation/attributes.rs b/compiler/rustc_ast_lowering/src/delegation/attributes.rs index 834f85450a2cd..686f5bbf06acc 100644 --- a/compiler/rustc_ast_lowering/src/delegation/attributes.rs +++ b/compiler/rustc_ast_lowering/src/delegation/attributes.rs @@ -1,5 +1,5 @@ +use rustc_attr_ir::{AttributeKind, InlineAttr}; use rustc_hir as hir; -use rustc_hir::attrs::{AttributeKind, InlineAttr}; use rustc_span::Span; use rustc_span::def_id::DefId; @@ -7,33 +7,37 @@ use crate::LoweringContext; use crate::delegation::DelegationResolution; struct AdditionInfo { - pub equals: fn(&hir::Attribute) -> bool, + pub equals: fn(&rustc_attr_ir::Attribute) -> bool, pub kind: AdditionKind, } enum AdditionKind { - Default { factory: fn(Span) -> hir::Attribute }, - Inherit { factory: fn(Span, &hir::Attribute) -> hir::Attribute }, + Default { factory: fn(Span) -> rustc_attr_ir::Attribute }, + Inherit { factory: fn(Span, &rustc_attr_ir::Attribute) -> rustc_attr_ir::Attribute }, } static ADDITIONS: &[AdditionInfo] = &[ AdditionInfo { - equals: |a| matches!(a, hir::Attribute::Parsed(AttributeKind::MustUse { .. })), + equals: |a| matches!(a, rustc_attr_ir::Attribute::Parsed(AttributeKind::MustUse { .. })), kind: AdditionKind::Inherit { factory: |span, original_attr| { let reason = match original_attr { - hir::Attribute::Parsed(AttributeKind::MustUse { reason, .. }) => *reason, + rustc_attr_ir::Attribute::Parsed(AttributeKind::MustUse { reason, .. }) => { + *reason + } _ => None, }; - hir::Attribute::Parsed(AttributeKind::MustUse { span, reason }) + rustc_attr_ir::Attribute::Parsed(AttributeKind::MustUse { span, reason }) }, }, }, AdditionInfo { - equals: |a| matches!(a, hir::Attribute::Parsed(AttributeKind::Inline(..))), + equals: |a| matches!(a, rustc_attr_ir::Attribute::Parsed(AttributeKind::Inline(..))), kind: AdditionKind::Default { - factory: |span| hir::Attribute::Parsed(AttributeKind::Inline(InlineAttr::Hint, span)), + factory: |span| { + rustc_attr_ir::Attribute::Parsed(AttributeKind::Inline(InlineAttr::Hint, span)) + }, }, }, ]; @@ -61,8 +65,8 @@ impl<'hir> LoweringContext<'_, 'hir> { &self, span: Span, sig_id: DefId, - existing: Option<&&[hir::Attribute]>, - ) -> Vec { + existing: Option<&&[rustc_attr_ir::Attribute]>, + ) -> Vec { ADDITIONS .iter() .filter_map(|addition| { diff --git a/compiler/rustc_ast_lowering/src/delegation/generics.rs b/compiler/rustc_ast_lowering/src/delegation/generics.rs index 836bf7df2bbd5..cdec2f5312ae2 100644 --- a/compiler/rustc_ast_lowering/src/delegation/generics.rs +++ b/compiler/rustc_ast_lowering/src/delegation/generics.rs @@ -35,6 +35,7 @@ pub(super) struct DelegationGenerics { data: T, pos: GenericsPosition, trait_impl: bool, + has_implicit_self: bool, } type TyGenerics<'hir> = Vec>; @@ -44,11 +45,13 @@ impl<'hir> DelegationGenerics> { params: &'hir [ty::GenericParamDef], pos: GenericsPosition, trait_impl: bool, + has_implicit_self: bool, ) -> Self { DelegationGenerics { data: params.iter().map(|p| GenericArgSlot::Generate(p, None)).collect(), pos, trait_impl, + has_implicit_self, } } } @@ -92,6 +95,7 @@ pub(super) struct GenericsGenerationResults<'hir> { pub(super) struct DelegationGenericArgsIterator<'hir> { index: usize = Default::default(), params: &'hir [hir::GenericParam<'hir>], + has_implicit_self: bool, } /// During generic args propagation we need to create generic args @@ -112,10 +116,10 @@ impl<'hir> DelegationGenericArgsIterator<'hir> { } let p = self.params[self.index]; + let is_implicit_self = self.has_implicit_self && self.index == 0; self.index += 1; - // Skip self generic arg, we do not need to propagate it. - if p.name.ident().name == kw::SelfUpper || p.is_impl_trait() { + if is_implicit_self || p.is_impl_trait() { continue; } @@ -173,6 +177,7 @@ impl<'hir> HirOrTyGenerics<'hir> { data: params, pos: ty.pos, trait_impl: ty.trait_impl, + has_implicit_self: ty.has_implicit_self, }); } } @@ -189,9 +194,11 @@ impl<'hir> HirOrTyGenerics<'hir> { HirOrTyGenerics::Ty(_) => { bug!("attempting to get generic args before uplifting to HIR") } - HirOrTyGenerics::Hir(hir) => { - DelegationGenericArgsIterator { params: hir.data.params, .. } - } + HirOrTyGenerics::Hir(hir) => DelegationGenericArgsIterator { + params: hir.data.params, + has_implicit_self: hir.has_implicit_self, + .. + }, } } @@ -380,13 +387,21 @@ impl<'hir> DelegationResolver<'_, 'hir> { if trait_impl { // Considering parent generics, during signature inheritance // we will take those args that are in trait impl header trait ref. - let parent = - DelegationGenerics { data: vec![], pos: GenericsPosition::Child, trait_impl: true }; + let parent = DelegationGenerics { + data: vec![], + pos: GenericsPosition::Child, + trait_impl: true, + has_implicit_self: false, + }; let parent = GenericsGenerationResult::new(parent); - let child = - DelegationGenerics::generate_all(sig_child_params, GenericsPosition::Child, true); + let child = DelegationGenerics::generate_all( + sig_child_params, + GenericsPosition::Child, + true, + false, + ); let child = GenericsGenerationResult::new(child); @@ -411,15 +426,20 @@ impl<'hir> DelegationResolver<'_, 'hir> { ), pos: GenericsPosition::Parent, trait_impl, + has_implicit_self: generate_free_to_trait_self, }, ParentSegmentArgs::NotSpecified => DelegationGenerics::generate_all( &sig_parent_params[usize::from(skip_self)..], GenericsPosition::Parent, trait_impl, + generate_free_to_trait_self, ), - ParentSegmentArgs::Invalid => { - DelegationGenerics { data: vec![], pos: GenericsPosition::Parent, trait_impl } - } + ParentSegmentArgs::Invalid => DelegationGenerics { + data: vec![], + pos: GenericsPosition::Parent, + trait_impl, + has_implicit_self: false, + }, }; let child_generics = if let Some(args) = res.child_args { @@ -439,9 +459,19 @@ impl<'hir> DelegationResolver<'_, 'hir> { slots.push(GenericArgSlot::Generate(synth_param, None)); } - DelegationGenerics { data: slots, pos: GenericsPosition::Child, trait_impl } + DelegationGenerics { + data: slots, + pos: GenericsPosition::Child, + trait_impl, + has_implicit_self: false, + } } else { - DelegationGenerics::generate_all(sig_child_params, GenericsPosition::Child, trait_impl) + DelegationGenerics::generate_all( + sig_child_params, + GenericsPosition::Child, + trait_impl, + false, + ) }; Ok(GenericsGenerationResults { diff --git a/compiler/rustc_ast_lowering/src/delegation/mod.rs b/compiler/rustc_ast_lowering/src/delegation/mod.rs index 0e94016963c78..b2c0e1ffa81b1 100644 --- a/compiler/rustc_ast_lowering/src/delegation/mod.rs +++ b/compiler/rustc_ast_lowering/src/delegation/mod.rs @@ -45,7 +45,7 @@ use hir::def::Res; use rustc_abi::ExternAbi; use rustc_ast as ast; use rustc_ast::*; -use rustc_hir::attrs::lang_items::LangItem; +use rustc_attr_ir::lang_items::LangItem; use rustc_hir::def::DefKind; use rustc_hir::{self as hir, FnDeclFlags, QPath}; use rustc_middle::ty::Asyncness; diff --git a/compiler/rustc_ast_lowering/src/delegation/resolution.rs b/compiler/rustc_ast_lowering/src/delegation/resolution.rs index 0e4992267b250..7678418c47e56 100644 --- a/compiler/rustc_ast_lowering/src/delegation/resolution.rs +++ b/compiler/rustc_ast_lowering/src/delegation/resolution.rs @@ -343,7 +343,9 @@ impl<'tcx> DelegationResolver<'_, 'tcx> { match tcx.resolve_type_relative_delegations(()).get(&def_id) { Some(res) => match *res { TypeRelativeDelegationRes::Ok(sig_id) => Ok(sig_id), - TypeRelativeDelegationRes::Error(err) => Err(err), + TypeRelativeDelegationRes::Error(_) => { + Err(tcx.dcx().emit_err(UnresolvedDelegationCallee { span })) + } TypeRelativeDelegationRes::Ambig(_) => { Err(tcx.dcx().emit_err(AmbiguousDelegationToInherentImpl { span })) } diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 1c1b9a247f7a2..f4bfec24e3b1b 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -544,8 +544,8 @@ impl<'hir> LoweringContext<'_, 'hir> { ExprKind::MacCall(_) => panic!("{:?} shouldn't exist here", e.span), - ExprKind::DirectConstArg(expr) => { - let e = self.emit_bad_direct_const_arg(e.span, expr, "expression"); + ExprKind::GcaMacro(expr) => { + let e = self.emit_bad_gca_macro(e.span, expr, "expression"); hir::ExprKind::Err(e) } }; @@ -1018,7 +1018,7 @@ impl<'hir> LoweringContext<'_, 'hir> { expr.span, hir::ExprKind::Err(self.dcx().emit_err(AwaitOnlyInAsyncFnAndBlocks { await_kw_span, - item_span: self.current_item, + item_span: self.current_item_span, })), ); return hir::ExprKind::Block( @@ -1712,7 +1712,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } Some(hir::CoroutineKind::Coroutine(_)) => false, None => { - let suggestion = self.current_item.map(|s| s.shrink_to_lo()); + let suggestion = self.current_item_span.map(|s| s.shrink_to_lo()); self.dcx().emit_err(YieldInClosure { span, suggestion }); self.coroutine_kind = Some(hir::CoroutineKind::Coroutine(Movability::Movable)); diff --git a/compiler/rustc_ast_lowering/src/expr/closure.rs b/compiler/rustc_ast_lowering/src/expr/closure.rs index 1b8740948d726..5924d53dd11e4 100644 --- a/compiler/rustc_ast_lowering/src/expr/closure.rs +++ b/compiler/rustc_ast_lowering/src/expr/closure.rs @@ -1,11 +1,13 @@ use rustc_ast::*; +use rustc_attr_ir::find_attr; +use rustc_attr_ir::target::Target; use rustc_hir as hir; -use rustc_hir::{HirId, Target, find_attr}; +use rustc_hir::HirId; use rustc_span::{Span, span_bug}; use super::{LoweringContext, MoveExprState}; -use crate::FnDeclKind; use crate::diagnostics::{ClosureCannotBeStatic, CoroutineTooManyParameters}; +use crate::{DiscardParams, FnDeclKind}; impl<'hir> LoweringContext<'_, 'hir> { // Entry point for `ExprKind::Closure`. Plain closures go through @@ -55,7 +57,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_expr_coroutine_closure_with_move_exprs( &mut self, expr_hir_id: HirId, - attrs: &[hir::Attribute], + attrs: &[rustc_attr_ir::Attribute], binder: &ClosureBinder, capture_clause: CaptureBy, closure_id: NodeId, @@ -114,7 +116,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_expr_plain_closure_with_move_exprs( &mut self, expr_hir_id: HirId, - attrs: &[hir::Attribute], + attrs: &[rustc_attr_ir::Attribute], binder: &ClosureBinder, capture_clause: CaptureBy, closure_id: NodeId, @@ -131,6 +133,7 @@ impl<'hir> LoweringContext<'_, 'hir> { binder, capture_clause, closure_id, + expr_hir_id, constness, movability, decl, @@ -153,10 +156,11 @@ impl<'hir> LoweringContext<'_, 'hir> { // local uses and the caller can later add the matching initializers. fn lower_expr_closure( &mut self, - attrs: &[hir::Attribute], + attrs: &[rustc_attr_ir::Attribute], binder: &ClosureBinder, capture_clause: CaptureBy, closure_id: NodeId, + closure_hir_id: HirId, constness: Const, movability: Movability, decl: &FnDecl, @@ -203,7 +207,14 @@ impl<'hir> LoweringContext<'_, 'hir> { let bound_generic_params = self.lower_lifetime_binder(closure_id, generic_params); // Lower outside new scope to preserve `is_in_loop_condition`. - let fn_decl = self.lower_fn_decl(decl, closure_id, FnDeclKind::Closure, None); + let fn_decl = self.lower_fn_decl( + decl, + closure_id, + closure_hir_id, + FnDeclKind::Closure, + None, + DiscardParams::No, + ); let c = self.arena.alloc(hir::Closure { def_id: closure_def_id, @@ -283,7 +294,7 @@ impl<'hir> LoweringContext<'_, 'hir> { body: &Expr, fn_decl_span: Span, fn_arg_span: Span, - attrs: &[hir::Attribute], + attrs: &[rustc_attr_ir::Attribute], ) -> hir::ExprKind<'hir> { let closure_def_id = self.local_def_id(closure_id); let (binder_clause, generic_params) = self.lower_closure_binder(binder); @@ -326,7 +337,14 @@ impl<'hir> LoweringContext<'_, 'hir> { // We need to lower the declaration outside the new scope, because we // have to conserve the state of being inside a loop condition for the // closure argument types. - let fn_decl = self.lower_fn_decl(&decl, closure_id, FnDeclKind::Closure, None); + let fn_decl = self.lower_fn_decl( + &decl, + closure_id, + closure_hir_id, + FnDeclKind::Closure, + None, + DiscardParams::No, + ); if let Const::Yes(span) = constness { self.dcx().span_err(span, "const coroutines are not supported"); diff --git a/compiler/rustc_ast_lowering/src/format.rs b/compiler/rustc_ast_lowering/src/format.rs index b9974edea71a4..9da42511c0d7f 100644 --- a/compiler/rustc_ast_lowering/src/format.rs +++ b/compiler/rustc_ast_lowering/src/format.rs @@ -1,9 +1,9 @@ use std::borrow::Cow; use rustc_ast::*; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::fx::FxIndexMap; use rustc_hir as hir; -use rustc_hir::attrs::lang_items::LangItem; use rustc_session::config::FmtDebug; use rustc_span::{ByteSymbol, DesugaringKind, Ident, Span, Symbol, sym}; diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index f1b6e196fe413..3795673d53ba1 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -6,6 +6,7 @@ use rustc_hir::intravisit::Visitor; use rustc_hir::*; use rustc_index::IndexVec; use rustc_middle::ty::TyCtxt; +use rustc_span::def_id::CRATE_MOD_ID; use rustc_span::{DUMMY_SP, Span, span_bug}; use tracing::{debug, instrument}; @@ -48,9 +49,7 @@ pub(super) fn index_hir<'hir>( }; match item { - OwnerNode::Crate(citem) => { - collector.visit_mod(citem, citem.spans.inner_span, hir::CRATE_HIR_ID) - } + OwnerNode::Crate(citem) => collector.visit_mod(citem, citem.spans.inner_span, CRATE_MOD_ID), OwnerNode::Item(item) => collector.visit_item(item), OwnerNode::TraitItem(item) => collector.visit_trait_item(item), OwnerNode::ImplItem(item) => collector.visit_impl_item(item), @@ -151,6 +150,13 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { self.visit_body(body); } + fn visit_use(&mut self, tree: &'hir UseTree<'hir>, hir_id: HirId, _def_id: LocalDefId) { + if !hir_id.is_owner() { + self.insert(tree.prefix.span, hir_id, Node::NestedUseTree(tree)); + } + intravisit::walk_use(self, tree, hir_id); + } + fn visit_param(&mut self, param: &'hir Param<'hir>) { let node = Node::Param(param); self.insert(param.pat.span, param.hir_id, node); diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index edf184f568b22..9698ab0aedb83 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1,19 +1,15 @@ use rustc_abi::ExternAbi; use rustc_ast::visit::AssocCtxt; use rustc_ast::*; +use rustc_attr_ir::target::Target; +use rustc_attr_ir::{AttributeKind, EiiImplResolution, find_attr}; use rustc_errors::{E0570, ErrorGuaranteed, struct_span_code_err}; -use rustc_hir::attrs::{AttributeKind, EiiImplResolution}; -use rustc_hir::def::{DefKind, PerNS, Res}; -use rustc_hir::{ - self as hir, CRATE_OWNER_ID, HirId, ImplItemImplKind, LifetimeSource, PredicateOrigin, Target, - find_attr, -}; -use rustc_middle::middle::resolve::ResolverAstLowering; -use rustc_middle::ty::TyCtxt; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{self as hir, HirId, ImplItemImplKind, LifetimeSource, PredicateOrigin}; use rustc_middle::ty::data_structures::IndexMap; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::edit_distance::find_best_match_for_name; -use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, span_bug, sym}; +use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym}; use smallvec::SmallVec; use thin_vec::ThinVec; use tracing::instrument; @@ -23,19 +19,14 @@ use super::diagnostics::{ }; use super::stability::{enabled_names, gate_unstable_abi}; use super::{ - FnDeclKind, GenericArgsMode, ImplTraitContext, ImplTraitPosition, LoweringContext, ParamMode, - RelaxedBoundForbiddenReason, RelaxedBoundPolicy, + DiscardParams, FnDeclKind, GenericArgsMode, ImplTraitContext, ImplTraitPosition, + LoweringContext, ParamMode, RelaxedBoundForbiddenReason, RelaxedBoundPolicy, }; use crate::diagnostics::{ConstComptimeFn, ResolvingRestrictionKind, RestrictionAncestorOnly}; -pub(super) struct ItemLowerer<'a, 'hir> { - pub(super) tcx: TyCtxt<'hir>, - pub(super) resolver: &'a ResolverAstLowering<'hir>, -} - -/// When we have a ty alias we *may* have two where clauses. To give the best diagnostics, we set the span -/// to the where clause that is preferred, if it exists. Otherwise, it sets the span to the other where -/// clause if it exists. +/// When we have a ty alias we *may* have two where clauses. To give the best diagnostics, we set +/// the span to the where clause that is preferred, if it exists. Otherwise, it sets the span to +/// the other where clause if it exists. fn add_ty_alias_where_clause( generics: &mut ast::Generics, after_where_clause: &ast::WhereClause, @@ -52,48 +43,6 @@ fn add_ty_alias_where_clause( if before.0 || !after.0 { before } else { after }; } -impl<'hir> ItemLowerer<'_, 'hir> { - fn with_lctx( - &mut self, - owner: NodeId, - f: impl FnOnce(&mut LoweringContext<'_, 'hir>) -> hir::OwnerNode<'hir>, - ) -> hir::MaybeOwner<'hir> { - let mut lctx = LoweringContext::new(self.tcx, self.resolver, owner); - - let item = f(&mut lctx); - - let info = lctx.curr_owner.into_owner_info(self.tcx, item); - hir::MaybeOwner::Owner(lctx.arena.alloc(info)) - } - - #[instrument(level = "debug", skip(self, c))] - pub(super) fn lower_crate(&mut self, c: &Crate) -> hir::MaybeOwner<'hir> { - self.with_lctx(CRATE_NODE_ID, |lctx| { - debug_assert_eq!(lctx.curr_owner.owner_id(), CRATE_OWNER_ID); - let module = lctx.lower_mod(&c.items, &c.spans); - lctx.lower_attrs(hir::CRATE_HIR_ID, &c.attrs, c.spans.inner_span, Target::Crate); - hir::OwnerNode::Crate(module) - }) - } - - #[instrument(level = "debug", skip(self))] - pub(super) fn lower_item(&mut self, item: &Item) -> hir::MaybeOwner<'hir> { - self.with_lctx(item.id, |lctx| hir::OwnerNode::Item(lctx.lower_item(item))) - } - - pub(super) fn lower_trait_item(&mut self, item: &AssocItem) -> hir::MaybeOwner<'hir> { - self.with_lctx(item.id, |lctx| hir::OwnerNode::TraitItem(lctx.lower_trait_item(item))) - } - - pub(super) fn lower_impl_item(&mut self, item: &AssocItem) -> hir::MaybeOwner<'hir> { - self.with_lctx(item.id, |lctx| hir::OwnerNode::ImplItem(lctx.lower_impl_item(item))) - } - - pub(super) fn lower_foreign_item(&mut self, item: &ForeignItem) -> hir::MaybeOwner<'hir> { - self.with_lctx(item.id, |lctx| hir::OwnerNode::ForeignItem(lctx.lower_foreign_item(item))) - } -} - impl<'hir> LoweringContext<'_, 'hir> { pub(super) fn lower_mod( &mut self, @@ -118,8 +67,8 @@ impl<'hir> LoweringContext<'_, 'hir> { id: NodeId, name: Ident, EiiDecl { foreign_item, impl_unsafe }: &EiiDecl, - ) -> Option { - self.lower_path_simple_eii(id, foreign_item).map(|did| hir::attrs::EiiDecl { + ) -> Option { + self.lower_path_simple_eii(id, foreign_item).map(|did| rustc_attr_ir::EiiDecl { foreign_item: did, impl_unsafe: *impl_unsafe, name, @@ -137,7 +86,7 @@ impl<'hir> LoweringContext<'_, 'hir> { is_default, known_eii_macro_resolution, }: &EiiImpl, - ) -> hir::attrs::EiiImpl { + ) -> rustc_attr_ir::EiiImpl { let resolution = if let Some(target) = known_eii_macro_resolution && let Some(foreign_item_did) = self.lower_path_simple_eii(*node_id, target) { @@ -150,7 +99,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ) }; - hir::attrs::EiiImpl { + rustc_attr_ir::EiiImpl { span: self.lower_span(*span), inner_span: self.lower_span(*inner_span), impl_unsafe_span: match *impl_safety { @@ -166,19 +115,21 @@ impl<'hir> LoweringContext<'_, 'hir> { &mut self, id: NodeId, i: &ItemKind, - ) -> Vec { + ) -> Vec { match i { ItemKind::Fn(Fn { eii_impl: None, .. }) | ItemKind::Static(StaticItem { eii_impl: None, .. }) => Vec::new(), ItemKind::Fn(Fn { eii_impl: Some(eii_impl), .. }) | ItemKind::Static(StaticItem { eii_impl: Some(eii_impl), .. }) => { - vec![hir::Attribute::Parsed(AttributeKind::EiiImpl(Box::new( + vec![rustc_attr_ir::Attribute::Parsed(AttributeKind::EiiImpl(Box::new( self.lower_eii_impl(eii_impl), )))] } ItemKind::MacroDef(name, MacroDef { eii_declaration: Some(target), .. }) => self .lower_eii_decl(id, *name, target) - .map(|decl| vec![hir::Attribute::Parsed(AttributeKind::EiiDeclaration(decl))]) + .map(|decl| { + vec![rustc_attr_ir::Attribute::Parsed(AttributeKind::EiiDeclaration(decl))] + }) .unwrap_or_default(), ItemKind::ExternCrate(..) @@ -203,7 +154,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } } - fn lower_item(&mut self, i: &Item) -> &'hir hir::Item<'hir> { + pub(super) fn lower_item(&mut self, i: &Item) -> &'hir hir::Item<'hir> { let owner_id = self.curr_owner.owner_id(); let hir_id: HirId = owner_id.into(); let vis_span = self.lower_span(i.vis.span); @@ -234,7 +185,7 @@ impl<'hir> LoweringContext<'_, 'hir> { span: Span, id: NodeId, hir_id: hir::HirId, - attrs: &'hir [hir::Attribute], + attrs: &'hir [rustc_attr_ir::Attribute], vis_span: Span, i: &ItemKind, ) -> hir::ItemKind<'hir> { @@ -244,11 +195,7 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ItemKind::ExternCrate(*orig_name, ident) } ItemKind::Use(use_tree) => { - // Start with an empty prefix. - let prefix = - Path { segments: ThinVec::new(), span: use_tree.prefix.span.shrink_to_lo() }; - - self.lower_use_tree(use_tree, &prefix, id, vis_span, attrs) + hir::ItemKind::Use(self.lower_use_tree(use_tree, id, vis_span, attrs)) } ItemKind::Static(ast::StaticItem { ident, @@ -331,7 +278,14 @@ impl<'hir> LoweringContext<'_, 'hir> { let itctx = ImplTraitContext::Universal; let (generics, decl) = this.lower_generics(generics, itctx, |this| { - this.lower_fn_decl(decl, id, FnDeclKind::Fn, coroutine_marker) + this.lower_fn_decl( + decl, + id, + hir_id, + FnDeclKind::Fn, + coroutine_marker, + if body.is_none() { DiscardParams::Yes } else { DiscardParams::No }, + ) }); let sig = hir::FnSig { decl, @@ -544,7 +498,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } ItemKind::MacroDef(ident, MacroDef { body, macro_rules, eii_declaration: _ }) => { let ident = self.lower_ident(*ident); - let body = Box::new(self.lower_delim_args(body)); + let body = body.clone(); let def_id = self.curr_owner.owner.def_id; let def_kind = self.tcx.def_kind(def_id); let DefKind::Macro(macro_kinds) = def_kind else { @@ -598,13 +552,12 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_use_tree( &mut self, tree: &UseTree, - prefix: &Path, id: NodeId, vis_span: Span, - attrs: &'hir [hir::Attribute], - ) -> hir::ItemKind<'hir> { + attrs: &'hir [rustc_attr_ir::Attribute], + ) -> hir::UseTree<'hir> { let path = &tree.prefix; - let segments = prefix.segments.iter().chain(path.segments.iter()).cloned().collect(); + let segments = path.segments.iter().cloned().collect(); match tree.kind { UseTreeKind::Simple(rename) => { @@ -626,111 +579,39 @@ impl<'hir> LoweringContext<'_, 'hir> { let res = self.lower_import_res(id, path.span); let path = self.lower_use_path(res, &path, ParamMode::Explicit); let ident = self.lower_ident(ident); - hir::ItemKind::Use(path, hir::UseKind::Single(ident)) + hir::UseTree { prefix: path, kind: hir::UseKind::Single(ident) } } UseTreeKind::Glob(_) => { let res = self.expect_full_res(id); let res = self.lower_res(res); // Put the result in the appropriate namespace. - let res = match res { - Res::Def(DefKind::Mod | DefKind::Trait, _) => { - PerNS { type_ns: Some(res), value_ns: None, macro_ns: None } - } - Res::Def(DefKind::Enum, _) => { - PerNS { type_ns: None, value_ns: Some(res), macro_ns: None } - } - Res::Err => { - // Propagate the error to all namespaces, just to be sure. - let err = Some(Res::Err); - PerNS { type_ns: err, value_ns: err, macro_ns: err } - } - _ => span_bug!(path.span, "bad glob res {:?}", res), - }; + let res = res.in_namespace(); let path = Path { segments, span: path.span }; let path = self.lower_use_path(res, &path, ParamMode::Explicit); - hir::ItemKind::Use(path, hir::UseKind::Glob) + hir::UseTree { prefix: path, kind: hir::UseKind::Glob } } UseTreeKind::Nested { items: ref trees, .. } => { - // Nested imports are desugared into simple imports. - // So, if we start with - // - // ``` - // pub(x) use foo::{a, b}; - // ``` - // - // we will create three items: - // - // ``` - // pub(x) use foo::a; - // pub(x) use foo::b; - // pub(x) use foo::{}; // <-- this is called the `ListStem` - // ``` - // - // The first two are produced by recursively invoking - // `lower_use_tree` (and indeed there may be things - // like `use foo::{a::{b, c}}` and so forth). They - // wind up being directly added to - // `self.items`. However, the structure of this - // function also requires us to return one item, and - // for that we return the `{}` import (called the - // `ListStem`). - - let span = prefix.span.to(path.span); - let prefix = Path { segments, span }; - - // Add all the nested `PathListItem`s to the HIR. - for use_tree in trees { + let res = self.expect_full_res(id); + let res = self.lower_res(res); + // Put the result in the appropriate namespace. + let res = res.in_namespace(); + let prefix = self.lower_use_path(res, &path, ParamMode::Explicit); + let items = self.arena.alloc_from_iter(trees.iter().map(|use_tree| { let id = use_tree.id; - let owner_id = self.owner_id(id); - - // Each `use` import is an item and thus are owners of the - // names in the path. Up to this point the nested import is - // the current owner, since we want each desugared import to - // own its own names, we have to adjust the owner before - // lowering the rest of the import. - self.with_hir_id_owner(id, |this| { - // `prefix` is lowered multiple times, but in different HIR owners. - // So each segment gets renewed `HirId` with the same - // `ItemLocalId` and the new owner. (See `lower_node_id`) - let kind = - this.lower_use_tree(&use_tree.inner, &prefix, id, vis_span, attrs); - if !attrs.is_empty() { - this.curr_owner.attrs.insert(hir::ItemLocalId::ZERO, attrs); - } - - let item = hir::Item { - owner_id, - kind, - vis_span, - span: this.lower_span(use_tree.inner.span()), - eii: find_attr!(attrs, EiiImpl(..) | EiiDeclaration(..)), - }; - hir::OwnerNode::Item(this.arena.alloc(item)) - }); - } + let hir_id = self.lower_node_id(id); + let def_id = self.curr_owner.owner.node_id_to_def_id[&id]; + if !attrs.is_empty() { + self.curr_owner.attrs.insert(hir_id.local_id, attrs); + } + (self.lower_use_tree(&use_tree.inner, id, vis_span, attrs), hir_id, def_id) + })); - // Condition should match `build_reduced_graph_for_use_tree`. - let path = if trees.is_empty() - && !(prefix.segments.is_empty() - || prefix.segments.len() == 1 - && prefix.segments[0].ident.name == kw::PathRoot) - { - // For empty lists we need to lower the prefix so it is checked for things - // like stability later. - let res = self.lower_import_res(id, span); - self.lower_use_path(res, &prefix, ParamMode::Explicit) - } else { - // For non-empty lists we can just drop all the data, the prefix is already - // present in HIR as a part of nested imports. - let span = self.lower_span(span); - self.arena.alloc(hir::UsePath { res: PerNS::default(), segments: &[], span }) - }; - hir::ItemKind::Use(path, hir::UseKind::ListStem) + hir::UseTree { prefix, kind: hir::UseKind::Nested { items } } } } } - fn lower_foreign_item(&mut self, i: &ForeignItem) -> &'hir hir::ForeignItem<'hir> { + pub(super) fn lower_foreign_item(&mut self, i: &ForeignItem) -> &'hir hir::ForeignItem<'hir> { let owner_id = self.curr_owner.owner_id(); let hir_id: HirId = owner_id.into(); let attrs = @@ -742,7 +623,15 @@ impl<'hir> LoweringContext<'_, 'hir> { let (generics, (decl, fn_args)) = self.lower_generics(generics, itctx, |this| { ( // Disallow `impl Trait` in foreign items. - this.lower_fn_decl(fdec, i.id, FnDeclKind::ExternFn, None), + // Parameters are discarded because foreign functions don't have a body + this.lower_fn_decl( + fdec, + i.id, + hir_id, + FnDeclKind::ExternFn, + None, + DiscardParams::Yes, + ), this.lower_fn_params_to_idents(fdec), ) }); @@ -911,7 +800,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } } - fn lower_trait_item(&mut self, i: &AssocItem) -> &'hir hir::TraitItem<'hir> { + pub(super) fn lower_trait_item(&mut self, i: &AssocItem) -> &'hir hir::TraitItem<'hir> { let trait_item_def_id = self.curr_owner.owner_id(); let hir_id: HirId = trait_item_def_id.into(); let attrs = self.lower_attrs( @@ -964,9 +853,12 @@ impl<'hir> LoweringContext<'_, 'hir> { generics, sig, i.id, + hir_id, FnDeclKind::Trait, sig.header.coroutine_marker, attrs, + // Parameters are discarded for functions without a body + DiscardParams::Yes, ); if define_opaque.is_some() { self.dcx().span_err( @@ -1004,9 +896,11 @@ impl<'hir> LoweringContext<'_, 'hir> { generics, sig, i.id, + hir_id, FnDeclKind::Trait, sig.header.coroutine_marker, attrs, + DiscardParams::No, ); self.lower_define_opaque(hir_id, &define_opaque); ( @@ -1160,7 +1054,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ident } - fn lower_impl_item(&mut self, i: &AssocItem) -> &'hir hir::ImplItem<'hir> { + pub(super) fn lower_impl_item(&mut self, i: &AssocItem) -> &'hir hir::ImplItem<'hir> { let owner_id = self.curr_owner.owner_id(); let hir_id: HirId = owner_id.into(); let parent_id = self.tcx.local_parent(owner_id.def_id); @@ -1214,9 +1108,11 @@ impl<'hir> LoweringContext<'_, 'hir> { generics, sig, i.id, + hir_id, if is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent }, sig.header.coroutine_marker, attrs, + if body.is_none() { DiscardParams::Yes } else { DiscardParams::No }, ); self.lower_define_opaque(hir_id, &define_opaque); @@ -1401,7 +1297,7 @@ impl<'hir> LoweringContext<'_, 'hir> { decl: &FnDecl, coroutine_marker: Option, body: Option<&Block>, - attrs: &'hir [hir::Attribute], + attrs: &'hir [rustc_attr_ir::Attribute], contract: Option<&FnContract>, ) -> hir::BodyId { let Some(body) = body else { @@ -1659,15 +1555,17 @@ impl<'hir> LoweringContext<'_, 'hir> { &mut self, generics: &Generics, sig: &FnSig, - id: NodeId, + node_id: NodeId, + hir_id: HirId, kind: FnDeclKind, coroutine_marker: Option, - attrs: &[hir::Attribute], + attrs: &[rustc_attr_ir::Attribute], + discard_params: DiscardParams, ) -> (&'hir hir::Generics<'hir>, hir::FnSig<'hir>) { let header = self.lower_fn_header(sig.header, hir::Safety::Safe, attrs); let itctx = ImplTraitContext::Universal; let (generics, decl) = self.lower_generics(generics, itctx, |this| { - this.lower_fn_decl(&sig.decl, id, kind, coroutine_marker) + this.lower_fn_decl(&sig.decl, node_id, hir_id, kind, coroutine_marker, discard_params) }); (generics, hir::FnSig { header, decl, span: self.lower_span(sig.span) }) } @@ -1676,7 +1574,7 @@ impl<'hir> LoweringContext<'_, 'hir> { &mut self, h: FnHeader, default_safety: hir::Safety, - attrs: &[hir::Attribute], + attrs: &[rustc_attr_ir::Attribute], ) -> hir::FnHeader { let asyncness = if let Some(coroutine_marker) = h.coroutine_marker && let CoroutineKind::Async = coroutine_marker.kind @@ -1763,7 +1661,11 @@ impl<'hir> LoweringContext<'_, 'hir> { /// Lowers constness or comptime attribute. /// Whether `const` is allowed here is checked by ast validation. /// Whether `comptime` is allowed here is checked by the `comptime` attribute parser. - pub(super) fn lower_constness(&mut self, attrs: &[hir::Attribute], c: Const) -> hir::Constness { + pub(super) fn lower_constness( + &mut self, + attrs: &[rustc_attr_ir::Attribute], + c: Const, + ) -> hir::Constness { let mut constness = match c { Const::Yes(_) => hir::Constness::Const { always: false }, Const::No => hir::Constness::NotConst, @@ -1809,12 +1711,10 @@ impl<'hir> LoweringContext<'_, 'hir> { if !self.tcx.is_descendant_of(parent_module, did) { // If the restriction path is not an ancestor of the item, // emit an error and recover by lowering the restriction to `Unrestricted`. - self.dcx() - .create_err(RestrictionAncestorOnly { - span: path.span, - kind: resolving_kind, - }) - .emit(); + self.dcx().emit_err(RestrictionAncestorOnly { + span: path.span, + kind: resolving_kind, + }); hir::RestrictionKind::Unrestricted } else { hir::RestrictionKind::Restricted(self.arena.alloc(hir::Path { @@ -2222,7 +2122,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ); hir::TestBinderBoundTypeConstraint { - span: *span, + span: self.lower_span(*span), hir_id: self.lower_node_id(*node_id), params: generics.params, lhs, diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 19c37f4a76065..b6d1017665ec6 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -45,23 +45,24 @@ use rustc_ast::mut_visit::{self, MutVisitor}; use rustc_ast::node_id::NodeMap; use rustc_ast::visit::{self, Visitor}; use rustc_ast::{self as ast, *}; +use rustc_attr_ir::find_attr; +use rustc_attr_ir::lang_items::LangItem; +use rustc_attr_ir::target::Target; use rustc_attr_parsing::{AttributeParser, Recovery, ShouldEmit}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::stable_hash::{StableHash, StableHasher}; use rustc_data_structures::steal::Steal; use rustc_data_structures::tagged_ptr::TaggedRef; -use rustc_data_structures::unord::ExtendUnord; use rustc_errors::codes::*; use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle, ErrorGuaranteed}; -use rustc_hir::attrs::lang_items::LangItem; -use rustc_hir::def::{DefKind, Namespace, PerNS, Res}; +use rustc_hir::def::{CtorKind, DefKind, Namespace, PerNS, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId, LocalDefIdMap}; use rustc_hir::definitions::PerParentDisambiguatorState; use rustc_hir::lints::DelayedLint; use rustc_hir::{ - self as hir, AngleBrackets, ConstArg, GenericArg, HirId, ItemLocalMap, LifetimeSource, - LifetimeSyntax, MissingLifetimeKind, ParamName, Target, TraitCandidate, find_attr, + self as hir, AngleBrackets, CRATE_OWNER_ID, ConstArg, GenericArg, HirId, ItemLocalMap, + LifetimeSource, LifetimeSyntax, MissingLifetimeKind, ParamName, TraitCandidate, }; use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_macros::extension; @@ -168,7 +169,7 @@ struct PerOwnerLoweringState<'a, 'hir> { // -- Accumulated outputs -- /// Attributes inside the owner being lowered. - attrs: SortedMap, + attrs: SortedMap, /// Bodies inside the owner being lowered. bodies: Vec<(hir::ItemLocalId, &'hir hir::Body<'hir>)>, /// `#[define_opaque]` attributes @@ -252,7 +253,7 @@ impl<'a, 'hir> PerOwnerLoweringState<'a, 'hir> { let nodes = hir::OwnerNodes { opt_hash: bodies_hash, nodes, bodies }; let attrs = hir::AttributeMap { map: attrs, opt_hash: attrs_hash, define_opaque }; - let opt_hash = tcx.needs_hir_hash().then(|| { + let opt_hash = tcx.needs_owner_info_hash().then(|| { tcx.with_stable_hashing_context(|mut hcx| { let mut stable_hasher = StableHasher::new(); bodies_hash.unwrap().stable_hash(&mut hcx, &mut stable_hasher); @@ -296,7 +297,7 @@ struct LoweringContext<'a, 'hir> { /// Used to get the current `fn`'s def span to point to when using `await` /// outside of an `async fn`. - current_item: Option, + current_item_span: Option, try_block_scope: TryBlockScope, loop_scope: Option, @@ -366,7 +367,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { is_in_dyn_type: false, coroutine_kind: None, task_context: None, - current_item: None, + current_item_span: None, move_expr_bindings: Vec::new(), lowering_move_expr_initializer: false, @@ -664,28 +665,6 @@ fn index_ast<'tcx>( let item = mem::replace(item, *dummy); self.insert(item.id, node(Box::new(item))); } - - #[tracing::instrument(level = "trace", skip(self))] - fn visit_item_id_use_tree( - &mut self, - tree: &UseTree, - parent: LocalDefId, - items: &mut SmallVec<[Box; 1]>, - ) { - match tree.kind { - UseTreeKind::Glob(_) | UseTreeKind::Simple(_) => {} - UseTreeKind::Nested { items: ref nested_vec, span } => { - for nested in nested_vec { - let id = nested.id; - self.insert(id, AstOwner::NestedUseTree(parent)); - items.push(self.make_dummy(id, span, ItemKind::MacCall)); - - let def_id = self.owners[&id].def_id; - self.visit_item_id_use_tree(&nested.inner, def_id, items); - } - } - } - } } impl MutVisitor for Indexer<'_, '_> { @@ -695,37 +674,13 @@ fn index_ast<'tcx>( } fn flat_map_item(&mut self, mut item: Box) -> SmallVec<[Box; 1]> { - let def_id = self.owners[&item.id].def_id; mut_visit::walk_item(self, &mut *item); let dummy = self.make_dummy(item.id, item.span, ItemKind::MacCall); - let mut items = smallvec![dummy]; - if let ItemKind::Use(ref use_tree) = item.kind { - self.visit_item_id_use_tree(use_tree, def_id, &mut items); - } + let items = smallvec![dummy]; self.insert(item.id, AstOwner::Item(item)); items } - fn flat_map_stmt(&mut self, stmt: Stmt) -> SmallVec<[Stmt; 1]> { - let Stmt { id, span, kind } = stmt; - let mut id = Some(id); - mut_visit::walk_flat_map_stmt_kind(self, kind) - .into_iter() - .map(|kind| { - // Expanding the current statement is a nested `use` item, - // it is expanded into several flat `use` items. - // Create new NodeIds for the corresponding statements - // as two statements cannot have the same. - let id = id.take().unwrap_or_else(|| { - let next = self.next_node_id; - self.next_node_id.increment_by(1); - next - }); - Stmt { id, kind, span } - }) - .collect() - } - fn visit_assoc_item(&mut self, item: &mut AssocItem, ctxt: visit::AssocCtxt) { mut_visit::walk_assoc_item(self, item, ctxt); match ctxt { @@ -752,12 +707,12 @@ fn lower_to_hir(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::MaybeOwner<'_> { let ast_index = tcx.index_ast(()); let resolver_and_node = ast_index.get(def_id).map(Steal::steal); - let fallback_to_ancestor = |parent_id| { + let fallback_to_ancestor = || { // The item did not exist in the AST, it was created while lowering another item. - // `parent_id` may be different from the direct parent of `def_id`, - // for instance use-trees are lowered by the first sibling. + + let parent_id = tcx.local_parent(def_id); let mut parent_info = tcx.lower_to_hir(parent_id); - if let hir::MaybeOwner::NonOwner(hir_id) = parent_info { + while let hir::MaybeOwner::NonOwner(hir_id) = parent_info { // `parent_id` could also not be a owner either. // For instance if `def_id` is an enum variant field, // the direct parent is the enum variant. @@ -768,7 +723,8 @@ fn lower_to_hir(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::MaybeOwner<'_> { let parent_info = parent_info.unwrap(); *parent_info.children.get(&def_id).unwrap_or_else(|| { - panic!( + span_bug!( + tcx.source_span(def_id), "{:?} does not appear in children of {:?}", def_id, parent_info.nodes.node().def_id() @@ -780,22 +736,43 @@ fn lower_to_hir(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::MaybeOwner<'_> { // `ast_index` does not contain all definitions, only up-to the highest // `LocalDefId` which has a non-trivial `AstOwner`. Gracefully handle // other definitions, in particular those nested inside this highest definition. - return fallback_to_ancestor(tcx.local_parent(def_id)); + return fallback_to_ancestor(); }; - let mut item_lowerer = item::ItemLowerer { tcx, resolver: &*resolver }; + fn with_lctx<'hir>( + tcx: TyCtxt<'hir>, + resolver: &ResolverAstLowering<'hir>, + owner: NodeId, + f: impl FnOnce(&mut LoweringContext<'_, 'hir>) -> hir::OwnerNode<'hir>, + ) -> hir::MaybeOwner<'hir> { + let mut lctx = LoweringContext::new(tcx, resolver, owner); + let item = f(&mut lctx); + hir::MaybeOwner::Owner(lctx.curr_owner.into_owner_info(tcx, item)) + } let item = match &node { // The item existed in the AST. - AstOwner::Crate(c) => item_lowerer.lower_crate(&c), - AstOwner::Item(item) => item_lowerer.lower_item(&item), - AstOwner::TraitItem(item) => item_lowerer.lower_trait_item(&item), - AstOwner::ImplItem(item) => item_lowerer.lower_impl_item(&item), - AstOwner::ForeignItem(item) => item_lowerer.lower_foreign_item(&item), - AstOwner::NestedUseTree(owner_id) => fallback_to_ancestor(*owner_id), + AstOwner::Crate(c) => with_lctx(tcx, &*resolver, CRATE_NODE_ID, |lctx| { + debug_assert_eq!(lctx.curr_owner.owner_id(), CRATE_OWNER_ID); + let module = lctx.lower_mod(&c.items, &c.spans); + lctx.lower_attrs(hir::CRATE_HIR_ID, &c.attrs, c.spans.inner_span, Target::Crate); + hir::OwnerNode::Crate(module) + }), + AstOwner::Item(item) => { + with_lctx(tcx, &*resolver, item.id, |lctx| hir::OwnerNode::Item(lctx.lower_item(item))) + } + AstOwner::TraitItem(item) => with_lctx(tcx, &*resolver, item.id, |lctx| { + hir::OwnerNode::TraitItem(lctx.lower_trait_item(item)) + }), + AstOwner::ImplItem(item) => with_lctx(tcx, &*resolver, item.id, |lctx| { + hir::OwnerNode::ImplItem(lctx.lower_impl_item(item)) + }), + AstOwner::ForeignItem(item) => with_lctx(tcx, &*resolver, item.id, |lctx| { + hir::OwnerNode::ForeignItem(lctx.lower_foreign_item(item)) + }), // The item existed in the AST, but is not a HIR owner. // Fetch the correct information from its parent. - AstOwner::NonOwner => fallback_to_ancestor(tcx.local_parent(def_id)), + AstOwner::NonOwner => fallback_to_ancestor(), }; tcx.sess.time("drop_ast", || mem::drop(node)); @@ -824,12 +801,20 @@ enum GenericArgsMode { ParenSugar, /// Allow RTN, don't allow paren sugar. ReturnTypeNotation, - // Error if parenthesized generics or RTN are encountered. + /// Error if parenthesized generics or RTN are encountered. Err, /// Silence errors when lowering generics. Only used with `Res::Err`. Silence, } +#[derive(Debug, Copy, Clone)] +enum DiscardParams { + /// Should be used for functions without a body. Lowers the attributes on the parameter and then discards them. + Yes, + /// Should be used for functions with a body. Does not lower the attributes, as lowering of the parameters is done by `lower_body`. + No, +} + impl<'hir> LoweringContext<'_, 'hir> { fn create_def( &mut self, @@ -904,43 +889,6 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::OwnerId { def_id: self.resolver.owners[&node].def_id } } - /// Freshen the `LoweringContext` and ready it to lower a nested item. - /// The lowered item is registered into `self.curr_owner.children`. - /// - /// This function sets up `HirId` lowering infrastructure, - /// and stashes the per-owner state to avoid pollution by the closure. - #[instrument(level = "debug", skip(self, f))] - fn with_hir_id_owner( - &mut self, - owner: NodeId, - f: impl FnOnce(&mut Self) -> hir::OwnerNode<'hir>, - ) { - let child_owner = PerOwnerLoweringState::new(self.resolver, owner); - let parent_owner = mem::replace(&mut self.curr_owner, child_owner); - - // Do not reset `next_node_id` and `node_id_to_def_id`: - // we want `f` to be able to refer to the `LocalDefId`s that the caller created. - // and the caller to refer to some of the subdefinitions' nodes' `LocalDefId`s. - - // Always allocate the first `HirId` for the owner itself. - #[cfg(debug_assertions)] - self.curr_owner - .relowering_checker - .assert_node_is_not_relowered(owner, hir::ItemLocalId::ZERO); - - let item = f(self); - let completed_child_owner = mem::replace(&mut self.curr_owner, parent_owner); - let owner_id = completed_child_owner.owner_id(); - let info = completed_child_owner.into_owner_info(self.tcx, item); - - self.curr_owner - .children - .extend_unord(info.children.items().map(|(&def_id, &info)| (def_id, info))); - - debug_assert!(!self.curr_owner.children.contains_key(&owner_id.def_id)); - self.curr_owner.children.insert(owner_id.def_id, hir::MaybeOwner::Owner(info)); - } - /// This method allocates a new `HirId` for the given `NodeId`. /// Take care not to call this method if the resulting `HirId` is then not /// actually used in the HIR, as that would trigger an assertion in the @@ -982,7 +930,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } #[instrument(level = "trace", skip(self))] - fn lower_res(&mut self, res: Res) -> Res { + fn lower_res(&self, res: Res) -> Res { let res: Result = res.apply_id(|id| { let owner = self.curr_owner.owner_id(); let local_id = @@ -999,13 +947,27 @@ impl<'hir> LoweringContext<'_, 'hir> { res.unwrap_or(Res::Err) } - fn expect_full_res(&mut self, id: NodeId) -> Res { + fn expect_full_res(&self, id: NodeId) -> Res { self.get_partial_res(id).map_or(Res::Err, |pr| pr.expect_full_res()) } - fn lower_import_res(&mut self, id: NodeId, span: Span) -> PerNS> { - debug_assert_eq!(id, self.curr_owner.owner.id); - let per_ns = self.curr_owner.owner.import_res.map(|res| res.map(|res| self.lower_res(res))); + fn lower_import_res(&self, id: NodeId, span: Span) -> PerNS> { + let per_ns = self + .curr_owner + .owner + .import_res + .get(&id) + .unwrap_or_else(|| { + let sp = self.tcx.source_span(self.curr_owner.owner.def_id); + self.tcx.dcx().span_delayed_bug( + sp, + "no import_res entry for import, \ + this should only happen if it already errored in resolve", + ); + &PerNS { value_ns: None, type_ns: None, macro_ns: None } + }) + .map(|res| res.map(|res| self.lower_res(res))); + if per_ns.is_empty() { // Propagate the error to all namespaces, just to be sure. self.dcx().span_delayed_bug(span, "no resolution for an import"); @@ -1154,8 +1116,8 @@ impl<'hir> LoweringContext<'_, 'hir> { } fn with_new_scopes(&mut self, scope_span: Span, f: impl FnOnce(&mut Self) -> T) -> T { - let current_item = self.current_item; - self.current_item = Some(scope_span); + let current_item_span = self.current_item_span; + self.current_item_span = Some(scope_span); let was_in_loop_condition = self.is_in_loop_condition; self.is_in_loop_condition = false; @@ -1172,7 +1134,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.is_in_loop_condition = was_in_loop_condition; - self.current_item = current_item; + self.current_item_span = current_item_span; ret } @@ -1183,7 +1145,7 @@ impl<'hir> LoweringContext<'_, 'hir> { attrs: &[Attribute], target_span: Span, target: Target, - ) -> &'hir [hir::Attribute] { + ) -> &'hir [rustc_attr_ir::Attribute] { self.lower_attrs_with_extra(id, attrs, target_span, target, None, &[]) } @@ -1194,8 +1156,8 @@ impl<'hir> LoweringContext<'_, 'hir> { target_span: Span, target: Target, target_item: Option<&ast::Item>, - extra_hir_attributes: &[hir::Attribute], - ) -> &'hir [hir::Attribute] { + extra_hir_attributes: &[rustc_attr_ir::Attribute], + ) -> &'hir [rustc_attr_ir::Attribute] { if attrs.is_empty() && extra_hir_attributes.is_empty() { &[] } else { @@ -1228,7 +1190,7 @@ impl<'hir> LoweringContext<'_, 'hir> { target_hir_id: HirId, target: Target, target_item: Option<&ast::Item>, - ) -> Vec { + ) -> Vec { let l = self.span_lowerer(); self.attribute_parser.parse_attribute_list( attrs, @@ -1261,10 +1223,6 @@ impl<'hir> LoweringContext<'_, 'hir> { } } - fn lower_delim_args(&self, args: &DelimArgs) -> DelimArgs { - args.clone() - } - /// Lower an associated item constraint. #[instrument(level = "debug", skip_all)] fn lower_assoc_item_constraint( @@ -1453,7 +1411,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // type and value namespaces. If we resolved the path in the value namespace, we // transform it into a generic const argument. // - // Note that even under `#![feature(min_generic_const_args)]`, only plain paths + // Note that even under `#![feature(gca_min_const_items)]`, only plain paths // to constants are allowed - e.g. `A::` and // `A::>` are disallowed (they must be wrapped in `{ }`). // @@ -1470,12 +1428,10 @@ impl<'hir> LoweringContext<'_, 'hir> { let ct = self.arena.alloc(ct); return GenericArg::Const(ct.try_as_ambig_ct().unwrap()); } - TyKind::DirectConstArg(expr) - if self.tcx.features().min_generic_const_args() => - { + TyKind::GcaMacro(expr) if self.tcx.features().gca() => { let ct = match self.can_lower_expr_to_const_arg_direct( expr, - DirectConstArgContext::MacrolessMinGenericConstArgs, + self.direct_const_arg_context_enabled_by_gca_macro(), ) { Ok(()) => self.lower_expr_to_const_arg_direct(expr, None), Err(e) => e.emit(self), @@ -1579,14 +1535,14 @@ impl<'hir> LoweringContext<'_, 'hir> { TyKind::Infer => hir::TyKind::Infer(()), TyKind::Err(guar) => hir::TyKind::Err(*guar), TyKind::Slice(ty) => hir::TyKind::Slice(self.lower_ty_alloc(ty, itctx)), - TyKind::Ptr(mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)), - TyKind::Ref(region, mt) => { + TyKind::Ptr(ty, mutbl) => hir::TyKind::Ptr(self.lower_ty_alloc(ty, itctx), *mutbl), + TyKind::Ref(region, ty, mutbl) => { let lifetime = self.lower_ty_direct_lifetime(t, *region); - hir::TyKind::Ref(lifetime, self.lower_mt(mt, itctx)) + hir::TyKind::Ref(lifetime, self.lower_ty_alloc(ty, itctx), *mutbl) } - TyKind::PinnedRef(region, mt) => { + TyKind::PinnedRef(region, ty, mutbl) => { let lifetime = self.lower_ty_direct_lifetime(t, *region); - let kind = hir::TyKind::Ref(lifetime, self.lower_mt(mt, itctx)); + let kind = hir::TyKind::Ref(lifetime, self.lower_ty_alloc(ty, itctx), *mutbl); let span = self.lower_span(t.span); let arg = hir::Ty { kind, span, hir_id: self.next_id() }; let args = self.arena.alloc(hir::GenericArgs { @@ -1599,14 +1555,23 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::TyKind::Path(path) } TyKind::FnPtr(f) => { + let hir_id = self.lower_node_id(t.id); let generic_params = self.lower_lifetime_binder(t.id, &f.generic_params); - hir::TyKind::FnPtr(self.arena.alloc(hir::FnPtrTy { + let kind = hir::TyKind::FnPtr(self.arena.alloc(hir::FnPtrTy { generic_params, safety: self.lower_safety(f.safety, hir::Safety::Safe), abi: self.lower_extern(f.ext), - decl: self.lower_fn_decl(&f.decl, t.id, FnDeclKind::Pointer, None), + decl: self.lower_fn_decl( + &f.decl, + t.id, + hir_id, + FnDeclKind::Pointer, + None, + DiscardParams::Yes, + ), param_idents: self.lower_fn_params_to_idents(&f.decl), - })) + })); + return hir::Ty { kind, span: self.lower_span(t.span), hir_id }; } TyKind::UnsafeBinder(f) => { let generic_params = self.lower_lifetime_binder(t.id, &f.generic_params); @@ -1647,8 +1612,8 @@ impl<'hir> LoweringContext<'_, 'hir> { self.lower_array_length_to_const_arg(length), ), TyKind::TraitObject(bounds, kind) => { - let mut lifetime_bound = None; let (bounds, lifetime_bound) = self.with_dyn_type_scope(true, |this| { + let mut lifetime_bound = None; let bounds = this.arena.alloc_from_iter(bounds.iter().filter_map(|bound| match bound { // We can safely ignore constness here since AST validation @@ -1681,9 +1646,7 @@ impl<'hir> LoweringContext<'_, 'hir> { None } })); - let lifetime_bound = - lifetime_bound.unwrap_or_else(|| this.elided_dyn_bound(t.span)); - (bounds, lifetime_bound) + (bounds, lifetime_bound.unwrap_or_else(|| this.elided_dyn_bound(t.span))) }); hir::TyKind::TraitObject(bounds, TaggedRef::new(lifetime_bound, *kind)) } @@ -1734,7 +1697,7 @@ impl<'hir> LoweringContext<'_, 'hir> { }, feature, ) - .emit(); + .emit_err(); hir::TyKind::Err(guar) } ImplTraitContext::Disallowed(position) => { @@ -1780,8 +1743,8 @@ impl<'hir> LoweringContext<'_, 'hir> { let fields = self.arena.alloc_slice(fields); hir::TyKind::View(ty, fields) } - TyKind::DirectConstArg(expr) => { - let e = self.emit_bad_direct_const_arg(t.span, expr, "type"); + TyKind::GcaMacro(expr) => { + let e = self.emit_bad_gca_macro(t.span, expr, "type"); hir::TyKind::Err(e) } TyKind::Dummy => panic!("`TyKind::Dummy` should never be lowered"), @@ -1790,16 +1753,16 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::Ty { kind, span: self.lower_span(t.span), hir_id: self.lower_node_id(t.id) } } - pub(crate) fn emit_bad_direct_const_arg( + pub(crate) fn emit_bad_gca_macro( &mut self, span: Span, expr: &Expr, expected: &'static str, ) -> ErrorGuaranteed { - let msg = format!("expected {expected}, found `direct_const_arg!()` constant"); + let msg = format!("expected {expected}, found `gca!()` constant"); if expr::WillCreateDefIdsVisitor.visit_expr(expr).is_break() { // FIXME(mgca): make this non-fatal once we have a better way to handle - // nested items in invalid `direct_const_arg!()` arguments. + // nested items in invalid `gca!()` arguments. self.dcx().span_fatal(span, msg) } else { self.dcx().span_err(span, msg) @@ -1954,18 +1917,26 @@ impl<'hir> LoweringContext<'_, 'hir> { /// /// `decl`: the unlowered (AST) function declaration. /// - /// `fn_node_id`: `impl Trait` arguments are lowered into generic parameters on the given - /// `NodeId`. + /// `fn_node_id`: Node Id of the function. /// - /// `transform_return_type`: if `Some`, applies some conversion to the return type, such as is - /// needed for `async fn` and `gen fn`. See [`CoroutineKind`] for more details. + /// `fn_hir_id`: Hir Id of the function. Used for attribute parsing. + /// + /// `kind`: The kind of function. + /// + /// `coro`: If the function is a coroutine, information about the coroutine. + /// + /// `discard_params`: if `DiscardParams::Yes`, the parameters are lowered and then discarded. Set this to `DiscardParams::Yes` + /// for functions without bodies, as attributes on parameters are otherwise not validated. + /// Set this to `DiscardParams::No` for functions with bodies, as lowering of the parameters is done by `lower_body`. #[instrument(level = "debug", skip(self))] fn lower_fn_decl( &mut self, decl: &FnDecl, fn_node_id: NodeId, + fn_hir_id: HirId, kind: FnDeclKind, coro: Option, + discard_params: DiscardParams, ) -> &'hir hir::FnDecl<'hir> { let c_variadic = decl.c_variadic(); let mut splatted = decl.splatted(); @@ -1980,6 +1951,12 @@ impl<'hir> LoweringContext<'_, 'hir> { inputs = &inputs[..inputs.len() - 1]; } let inputs = self.arena.alloc_from_iter(inputs.iter().map(|param| { + if let DiscardParams::Yes = discard_params { + // FIXME This uses `fn_hir_id`, which is not correct, it should use the parameter hir id instead + // The parameter is currently not lowered for functions without bodies, so there is no place to store the lowered hir id + // This should be fixed by storing function parameters in the `hir::FnSig` instead of `hir::Body` + self.lower_attrs(fn_hir_id, ¶m.attrs, param.span, Target::Param); + } let itctx = match kind { FnDeclKind::Fn | FnDeclKind::Inherent | FnDeclKind::Impl | FnDeclKind::Trait => { ImplTraitContext::Universal @@ -2052,10 +2029,10 @@ impl<'hir> LoweringContext<'_, 'hir> { // Given we are only considering `ImplicitSelf` types, we needn't consider // the case where we have a mutable pattern to a reference as that would // no longer be an `ImplicitSelf`. - TyKind::Ref(_, mt) | TyKind::PinnedRef(_, mt) - if mt.ty.kind.is_implicit_self() => + TyKind::Ref(_, ty, mutbl) | TyKind::PinnedRef(_, ty, mutbl) + if ty.kind.is_implicit_self() => { - match mt.mutbl { + match mutbl { hir::Mutability::Not => hir::ImplicitSelfKind::RefImm, hir::Mutability::Mut => hir::ImplicitSelfKind::RefMut, } @@ -2527,10 +2504,6 @@ impl<'hir> LoweringContext<'_, 'hir> { .emit(); } - fn lower_mt(&mut self, mt: &MutTy, itctx: ImplTraitContext) -> hir::MutTy<'hir> { - hir::MutTy { ty: self.lower_ty_alloc(&mt.ty, itctx), mutbl: mt.mutbl } - } - #[instrument(level = "debug", skip(self), ret)] fn lower_param_bounds( &mut self, @@ -2612,7 +2585,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // We cannot just match on `ExprKind::Underscore` as `(_)` is represented as // `ExprKind::Paren(ExprKind::Underscore)` and should also be lowered to `GenericArg::Infer` // - // FIXME(macroless_generic_const_args): Handling of underscores should be moved into + // FIXME(gca_macroless_args): Handling of underscores should be moved into // lower_expr_to_const_arg_direct. It is left here as retaining compatibility of what is // currently allowed on stable gets hairy and annoying otherwise. match c.value.peel_parens().kind { @@ -2686,20 +2659,14 @@ impl<'hir> LoweringContext<'_, 'hir> { span: Span, ) -> hir::ConstItemRhs<'hir> { let is_direct = |body| { - if self.tcx.features().macroless_const_item_generic_const_args() { - self.can_lower_expr_to_const_arg_direct( - body, - DirectConstArgContext::MacrolessMinGenericConstArgs, - ) - .is_ok() + let context = if self.tcx.features().gca_macroless_items() { + self.direct_const_arg_context_enabled_by_gca_macro() } else { - // do not check can_lower_expr_to_const_arg_direct, but rather just - // ExprKind::DirectConstArg, because we don't want e.g. - // `impl { const C: u8 = N; }` to be a direct-rhs const - matches!(body, Expr { kind: ExprKind::DirectConstArg(_), .. }) - } + DirectConstArgContext::GCA_MACRO + }; + self.can_lower_expr_to_const_arg_direct(body, context).is_ok() }; - if self.tcx.features().min_generic_const_args() + if self.tcx.features().gca_min_const_items() && let Some(body) = body && is_direct(body) { @@ -2711,13 +2678,25 @@ impl<'hir> LoweringContext<'_, 'hir> { } } + fn direct_const_arg_context_enabled_by_gca_macro(&self) -> DirectConstArgContext { + let mut result = DirectConstArgContext::GCA_BASE_FEATURES; + if self.tcx.features().gca_min_const_items() { + result |= DirectConstArgContext::GCA_MIN_CONST_ITEMS; + } + if self.tcx.features().gca_adts() { + result |= DirectConstArgContext::GCA_ADTS; + } + result + } + + /// Only valid for const *arg* contexts, not const *item* contexts. fn ambient_direct_const_arg_context(&self) -> DirectConstArgContext { - if self.tcx.features().macroless_generic_const_args() { - DirectConstArgContext::MacrolessMinGenericConstArgs - } else if self.tcx.features().min_generic_const_args() { - DirectConstArgContext::MinGenericConstArgs + if self.tcx.features().gca_macroless_args() { + self.direct_const_arg_context_enabled_by_gca_macro() + } else if self.tcx.features().gca() { + DirectConstArgContext::GCA } else { - DirectConstArgContext::Stable + DirectConstArgContext::STABLE } } @@ -2729,9 +2708,16 @@ impl<'hir> LoweringContext<'_, 'hir> { res: Option>, context: DirectConstArgContext, ) -> Result<(), UnrepresentableConstArgError> { - if let DirectConstArgContext::MacrolessMinGenericConstArgs = context { + if context.contains(DirectConstArgContext::PATH_ANY) { Ok(()) - } else if qself.is_none() + } else if context.contains(DirectConstArgContext::PATH_CONST_CTOR) + && matches!(res, Some(Res::Def(DefKind::Ctor(_, CtorKind::Const), _))) + { + // FIXME(gca_adts): This check is incomplete. Type-relative paths and other complicating + // factors make things very difficult - e.g. `>::None` + Ok(()) + } else if context.contains(DirectConstArgContext::PATH_PLAIN_PARAM) + && qself.is_none() && path.is_single_argless_ident() && matches!(res, Some(Res::Def(DefKind::ConstParam, _))) { @@ -2747,59 +2733,61 @@ impl<'hir> LoweringContext<'_, 'hir> { expr: &Expr, context: DirectConstArgContext, ) -> Result<(), UnrepresentableConstArgError> { - use DirectConstArgContext::*; // Note the only stable case is currently ExprKind::Path - match (&expr.kind, context) { - ( - ExprKind::Call(Expr { kind: ExprKind::Path(_, _), .. }, args), - MacrolessMinGenericConstArgs, - ) => { + match &expr.kind { + ExprKind::Call(Expr { kind: ExprKind::Path(_, _), .. }, args) + if context.contains(DirectConstArgContext::TUPLE_CALL) => + { for arg in args { self.can_lower_expr_to_const_arg_direct(arg, context)?; } Ok(()) } - (ExprKind::Tup(exprs), MacrolessMinGenericConstArgs) => { + ExprKind::Tup(exprs) if context.contains(DirectConstArgContext::TUPLE) => { for expr in exprs { self.can_lower_expr_to_const_arg_direct(expr, context)?; } Ok(()) } - (ExprKind::Path(qself, path), _) => { + ExprKind::Path(qself, path) => { let res = self.get_partial_res(expr.id).and_then(|partial_res| partial_res.full_res()); self.can_lower_path_to_const_arg_direct(qself, path, expr.span, res, context) } - (ExprKind::Struct(se), MacrolessMinGenericConstArgs) => { + ExprKind::Struct(se) if context.contains(DirectConstArgContext::STRUCT) => { for f in &se.fields { self.can_lower_expr_to_const_arg_direct(&f.expr, context)?; } Ok(()) } - (ExprKind::Array(elements), MacrolessMinGenericConstArgs) => { + ExprKind::Array(elements) if context.contains(DirectConstArgContext::ARRAY) => { for element in elements { self.can_lower_expr_to_const_arg_direct(element, context)?; } Ok(()) } - (ExprKind::Underscore, MacrolessMinGenericConstArgs) => Ok(()), - (ExprKind::Paren(expr), MacrolessMinGenericConstArgs) => { + ExprKind::Underscore if context.contains(DirectConstArgContext::UNDERSCORE) => Ok(()), + ExprKind::Paren(expr) if context.contains(DirectConstArgContext::PAREN) => { self.can_lower_expr_to_const_arg_direct(expr, context) } - (ExprKind::Block(block, _), MacrolessMinGenericConstArgs) - if let [stmt] = block.stmts.as_slice() + ExprKind::Block(block, _) + if context.contains(DirectConstArgContext::BLOCK) + && let [stmt] = block.stmts.as_slice() && let StmtKind::Expr(expr) = &stmt.kind => { self.can_lower_expr_to_const_arg_direct(expr, context) } - (ExprKind::Lit(_), MacrolessMinGenericConstArgs) => Ok(()), - (ExprKind::Unary(UnOp::Neg, inner_expr), MacrolessMinGenericConstArgs) - if let ExprKind::Lit(_) = &inner_expr.kind => + ExprKind::Lit(_) if context.contains(DirectConstArgContext::LIT) => Ok(()), + ExprKind::Unary(UnOp::Neg, inner_expr) + if context.contains(DirectConstArgContext::LIT) + && let ExprKind::Lit(_) = &inner_expr.kind => { Ok(()) } - (ExprKind::ConstBlock(_), MacrolessMinGenericConstArgs) => Ok(()), - (ExprKind::DirectConstArg(_), MacrolessMinGenericConstArgs | MinGenericConstArgs) => { + ExprKind::ConstBlock(_) if context.contains(DirectConstArgContext::CONST_BLOCK) => { + Ok(()) + } + ExprKind::GcaMacro(_) if context.contains(DirectConstArgContext::GCA_MACRO) => { // Always report this as able to be represented directly. If it turns out not to be, // `lower_expr_to_const_arg_direct` will report an error. Ok(()) @@ -2981,16 +2969,13 @@ impl<'hir> LoweringContext<'_, 'hir> { span, } } - ExprKind::DirectConstArg(expr) => { + ExprKind::GcaMacro(expr) => { // `can_lower_expr_to_const_arg_direct` always returns success upon encountering a - // ExprKind::DirectConstArg, which effectively forces the expression to be lowered - // as a direct arg. If it actually turns out to not be possible, emit an error - // instead. - // Always use MacrolessMinGenericConstArgs, even if we're under regular GCA, because - // that's what the macro means: to enter a context that is like macroless GCA. + // ExprKind::GcaMacro, which effectively forces the expression to be lowered as a + // direct arg. If it actually turns out to not be possible, emit an error instead. match self.can_lower_expr_to_const_arg_direct( expr, - DirectConstArgContext::MacrolessMinGenericConstArgs, + self.direct_const_arg_context_enabled_by_gca_macro(), ) { Ok(()) => self.lower_expr_to_const_arg_direct(expr, id_override), Err(err) => err.emit(self), @@ -3020,7 +3005,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_anon_const_to_const_arg(&mut self, anon: &AnonConst) -> hir::ConstArg<'hir> { // Stable only allows one nesting of blocks for directly represented paths. mGCA allows // arbitrarily many, and are handled inside lower_expr_to_const_arg_direct for consistency. - let expr = if self.tcx.features().macroless_generic_const_args() { + let expr = if self.tcx.features().gca_macroless_args() { &anon.value } else { anon.value.maybe_unwrap_block() @@ -3058,7 +3043,7 @@ impl<'hir> LoweringContext<'_, 'hir> { })) } - fn lower_unsafe_source(&mut self, u: UnsafeSource) -> hir::UnsafeSource { + fn lower_unsafe_source(&self, u: UnsafeSource) -> hir::UnsafeSource { match u { CompilerGenerated => hir::UnsafeSource::CompilerGenerated, UserProvided => hir::UnsafeSource::UserProvided, @@ -3066,7 +3051,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } fn lower_trait_bound_modifiers( - &mut self, + &self, modifiers: TraitBoundModifiers, ) -> hir::TraitBoundModifiers { let constness = match modifiers.constness { @@ -3094,7 +3079,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn stmt_let_pat( &mut self, - attrs: Option<&'hir [hir::Attribute]>, + attrs: Option<&'hir [rustc_attr_ir::Attribute]>, span: Span, init: Option<&'hir hir::Expr<'hir>>, pat: &'hir hir::Pat<'hir>, @@ -3332,19 +3317,53 @@ impl<'hir> GenericArgsCtor<'hir> { } } -#[derive(Copy, Clone, Debug)] -enum DirectConstArgContext { +bitflags::bitflags! { + #[derive(Copy, Clone, Debug)] + struct DirectConstArgContext: u16 { + const TUPLE_CALL = 1 << 0; + const TUPLE = 1 << 1; + const PATH_PLAIN_PARAM = 1 << 2; + const PATH_CONST_CTOR = 1 << 3; + const PATH_ANY = 1 << 4; + const STRUCT = 1 << 5; + const ARRAY = 1 << 6; + const UNDERSCORE = 1 << 7; + const PAREN = 1 << 8; + const BLOCK = 1 << 9; + const LIT = 1 << 10; + const CONST_BLOCK = 1 << 11; + const GCA_MACRO = 1 << 12; + } +} + +impl DirectConstArgContext { /// The only allowed direct const arg representation is simple paths that nameres to generic /// const parameters. - Stable, - /// The allowed representations are what is allowed on stable, plus the `direct_const_arg!` macro. - MinGenericConstArgs, - /// Expressions attempt to be lowered directly, and if that fails, the expression falls back to - /// being represented as an anon const. + const STABLE: DirectConstArgContext = Self::PATH_PLAIN_PARAM; + + /// The allowed representations are what is allowed on stable, plus the `gca!` macro. + const GCA: DirectConstArgContext = Self::STABLE.union(Self::GCA_MACRO); + + /// These expressions are allowed inside the `gca!` macro, regardless of what feature is + /// enabling the base `gca!` functionality. /// - /// This context is also used under MinGenericConstArgs inside a `direct_const_arg!` macro, for - /// simplicity, as they allow the same code. - MacrolessMinGenericConstArgs, + /// These are also allowed under macroless features without a `gca!` macro. + const GCA_BASE_FEATURES: DirectConstArgContext = Self::GCA + .union(DirectConstArgContext::UNDERSCORE) + .union(DirectConstArgContext::PAREN) + .union(DirectConstArgContext::BLOCK) + .union(DirectConstArgContext::LIT) + .union(DirectConstArgContext::CONST_BLOCK); + + /// These are allowed under `#![feature(gca_min_const_items)]` + const GCA_MIN_CONST_ITEMS: DirectConstArgContext = DirectConstArgContext::PATH_ANY; + + /// These are allowed under `#![feature(gca_adts)]` + const GCA_ADTS: DirectConstArgContext = DirectConstArgContext::PATH_CONST_CTOR + .union(DirectConstArgContext::TUPLE_CALL) + .union(DirectConstArgContext::TUPLE) + .union(DirectConstArgContext::STRUCT) + .union(DirectConstArgContext::ARRAY); } #[derive(Debug)] @@ -3375,7 +3394,7 @@ impl UnrepresentableConstArgError { ConstArg { hir_id: lowering_context.next_id(), kind: hir::ConstArgKind::Error(e), - span: self.span, + span: lowering_context.lower_span(self.span), } } } diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index 0edba3fe0cd14..df3d7b1a3c0a6 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -1,9 +1,10 @@ use std::sync::Arc; use rustc_ast::*; -use rustc_hir::attrs::lang_items::LangItem; +use rustc_attr_ir::lang_items::LangItem; +use rustc_attr_ir::target::Target; +use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{self as hir, Target}; use rustc_span::{DesugaringKind, Ident, Span, Spanned, respan, span_bug}; use crate::diagnostics::{ diff --git a/compiler/rustc_ast_passes/src/diagnostics.rs b/compiler/rustc_ast_passes/src/diagnostics.rs index 4771a87335954..a3cffab3fc4dc 100644 --- a/compiler/rustc_ast_passes/src/diagnostics.rs +++ b/compiler/rustc_ast_passes/src/diagnostics.rs @@ -663,7 +663,7 @@ pub(crate) struct EmptyLabelManySpans(pub Vec); // The derive for `Vec` does multiple calls to `span_label`, adding commas between each impl Subdiagnostic for EmptyLabelManySpans { - fn add_to_diag(self, diag: &mut Diag<'_, G>) { + fn add_to_diag(self, diag: &mut Diag<'_>) { diag.span_labels(self.0, ""); } } diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index c1134e1dd7300..1176d6e1398f0 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -3,8 +3,9 @@ use rustc_ast::{self as ast, AttrVec, GenericBound, NodeId, PatKind, attr, token use rustc_attr_ir::{Attribute, AttributeKind}; use rustc_attr_parsing::AttributeParser; use rustc_errors::msg; -use rustc_feature::Features; +use rustc_feature::{DependentFeature, Features}; use rustc_session::Session; +use rustc_session::config::NextSolverConfig; use rustc_session::diagnostics::{feature_err, feature_warn}; use rustc_span::{Span, Spanned, sym}; @@ -441,13 +442,14 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(explicit_tail_calls, "`become` expression is experimental"); gate_all!(final_associated_functions, "`final` on trait functions is experimental"); gate_all!(fn_delegation, "functions delegation is not yet fully implemented"); + gate_all!(forced_keywords, "forced keywords are experimental"); gate_all!(frontmatter, "frontmatters are experimental"); + gate_all!(gca_min_const_items, "unbraced const blocks as const args are experimental"); gate_all!(gen_blocks, "gen blocks are experimental"); gate_all!(generic_const_items, "generic const items are experimental"); gate_all!(global_registration, "global registration is experimental"); gate_all!(guard_patterns, "guard patterns are experimental", "consider using match arm guards"); gate_all!(impl_restriction, "`impl` restrictions are experimental"); - gate_all!(min_generic_const_args, "unbraced const blocks as const args are experimental"); gate_all!(more_qualified_paths, "usage of qualified paths in this context is experimental"); gate_all!(move_expr, "`move(expr)` syntax is experimental"); gate_all!(mut_ref, "mutable by-reference bindings are experimental"); @@ -490,9 +492,9 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { "named parameters in parenthesized generic argument lists are experimental" ); - // `associated_const_equality` will be stabilized as part of `min_generic_const_args`. + // `associated_const_equality` will be stabilized as part of `gca_min_const_items`. for &span in spans.get(&sym::associated_const_equality).into_flat_iter() { - gate!(visitor, min_generic_const_args, span, "associated const equality is incomplete"); + gate!(visitor, gca_min_const_items, span, "associated const equality is incomplete"); } // Negative bounds are *super* internal. We require `-Zinternal-testing-features` *and* @@ -651,32 +653,53 @@ fn check_incompatible_features(sess: &Session, features: &Features) { } fn check_dependent_features(sess: &Session, features: &Features) { - for &(parent, children) in - rustc_feature::DEPENDENT_FEATURES.iter().filter(|(parent, _)| features.enabled(*parent)) + for &(parent, ref children) in + rustc_feature::DEPENDENT_FEATURES.iter().filter(|(parent, children)| { + features.enabled(*parent) && !check_enabled(features, children) + }) { - if children.iter().any(|f| !features.enabled(*f)) { - let parent_span = features - .enabled_features_iter_stable_order() - .find_map(|(name, span)| (name == parent).then_some(span)) - .unwrap(); - // FIXME: should probably format this in fluent instead of here - let missing = children + let parent_span = features + .enabled_features_iter_stable_order() + .find_map(|(name, span)| (name == parent).then_some(span)) + .unwrap(); + // FIXME: should probably format this in fluent instead of here + let missing = format(features, &children); + sess.dcx().emit_err(diagnostics::MissingDependentFeatures { parent_span, parent, missing }); + } + + fn check_enabled(features: &Features, feature: &DependentFeature) -> bool { + match feature { + DependentFeature::And(children) => { + children.iter().all(|child| check_enabled(features, child)) + } + DependentFeature::Or(children) => { + children.iter().any(|child| check_enabled(features, child)) + } + DependentFeature::Leaf(symbol) => features.enabled(*symbol), + } + } + + fn format(features: &Features, feature: &DependentFeature) -> String { + // FIXME: parentheses/precedence + match feature { + DependentFeature::And(children) => children .iter() - .filter(|f| !features.enabled(**f)) - .map(|s| format!("`{}`", s.as_str())) - .intersperse(String::from(", ")) - .collect(); - sess.dcx().emit_err(diagnostics::MissingDependentFeatures { - parent_span, - parent, - missing, - }); + .filter(|child| !check_enabled(features, child)) + .map(|child| format(features, child)) + .intersperse(String::from(" and ")) + .collect(), + DependentFeature::Or(children) => children + .iter() + .map(|child| format(features, child)) + .intersperse(String::from(" or ")) + .collect(), + DependentFeature::Leaf(symbol) => format!("`{}`", symbol.as_str()), } } } fn warn_next_solver_and_gce(sess: &Session, features: &Features) { - if !sess.opts.unstable_opts.next_solver.globally { + if sess.opts.unstable_opts.next_solver != NextSolverConfig::Globally { return; } @@ -694,7 +717,7 @@ fn warn_next_solver_and_gce(sess: &Session, features: &Features) { } fn check_features_requiring_new_solver(sess: &Session, features: &Features) { - if sess.opts.unstable_opts.next_solver.globally { + if sess.opts.unstable_opts.next_solver == NextSolverConfig::Globally { return; } @@ -703,13 +726,13 @@ fn check_features_requiring_new_solver(sess: &Session, features: &Features) { if let Some(gca_span) = features .enabled_lang_features() .iter() - .find(|feat| feat.gate_name == sym::generic_const_args) + .find(|feat| feat.gate_name == sym::gca_const_items) .map(|feat| feat.attr_sp) { #[allow(rustc::symbol_intern_string_literal)] sess.dcx().emit_err(diagnostics::MissingDependentFeatures { parent_span: gca_span, - parent: sym::generic_const_args, + parent: sym::gca_const_items, missing: String::from("-Znext-solver=globally"), }); } diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 977eb0ee4592d..b76502814dbb7 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -364,20 +364,16 @@ fn space_between(tt1: &TokenTree, tt2: &TokenTree) -> bool { // IDENT + `!`: `println!()`, but `if !x { ... }` needs a space after the `if` ( - Tok(tk::Token { kind: tk::Ident(sym, is_raw), span }, _), + Tok(tk::Token { kind: tk::Ident(sym, kind), span }, _), Tok(tk::Token { kind: tk::Bang, .. }, _), - ) if !Ident::new(*sym, *span).is_reserved() || matches!(is_raw, tk::IdentIsRaw::Yes) => { - false - } + ) if !Ident::new(*sym, *span).is_reserved() || matches!(kind, tk::IdentKind::Raw) => false, // IDENT|`fn`|`Self`|`pub` + `(`: `f(3)`, `fn(x: u8)`, `Self()`, `pub(crate)`, // but `let (a, b) = (1, 2)` needs a space after the `let` - (Tok(tk::Token { kind: tk::Ident(sym, is_raw), span }, _), Del(_, _, Parenthesis, _)) - if !Ident::new(*sym, *span).is_reserved() - || *sym == kw::Fn - || *sym == kw::SelfUpper - || *sym == kw::Pub - || matches!(is_raw, tk::IdentIsRaw::Yes) => + (&Tok(tk::Token { kind: tk::Ident(sym, kind), span }, _), Del(_, _, Parenthesis, _)) + if kind == tk::IdentKind::Raw + || matches!(sym, kw::Fn | kw::SelfUpper | kw::Pub) + || !Ident::new(sym, span).is_reserved() => { false } @@ -623,7 +619,7 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere // Isolated, starts with #! and doesn't continue with `[` // See [rustc_lexer::strip_shebang] and [gather_comments] from pprust/state.rs for details if cmnt.style == CommentStyle::Isolated - && cmnt.lines.first().map_or(false, |l| l.starts_with("#!")) + && cmnt.lines.first().is_some_and(|l| l.starts_with("#!")) { let cmnt = self.next_comment().unwrap(); self.print_comment(cmnt); @@ -1076,17 +1072,17 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere tk::Literal(lit) => literal_to_string(lit).into(), /* Name components */ - tk::Ident(name, is_raw) => { - IdentPrinter::new(name, is_raw.to_print_mode_ident(), convert_dollar_crate) + tk::Ident(name, kind) => { + IdentPrinter::new(name, kind.to_print_mode_ident(), convert_dollar_crate) .to_string() .into() } - tk::NtIdent(ident, is_raw) => { - IdentPrinter::for_ast_ident(ident, is_raw.to_print_mode_ident()).to_string().into() + tk::NtIdent(ident, kind) => { + IdentPrinter::for_ast_ident(ident, kind.to_print_mode_ident()).to_string().into() } - tk::Lifetime(name, is_raw) | tk::NtLifetime(Ident { name, .. }, is_raw) => { - IdentPrinter::new(name, is_raw.to_print_mode_lifetime(), None).to_string().into() + tk::Lifetime(name, kind) | tk::NtLifetime(Ident { name, .. }, kind) => { + IdentPrinter::new(name, kind.to_print_mode_lifetime(), None).to_string().into() } /* Other */ @@ -1350,20 +1346,23 @@ impl<'a> State<'a> { self.print_type(ty); self.word("]"); } - ast::TyKind::Ptr(mt) => { + ast::TyKind::Ptr(ty, mutbl) => { self.word("*"); - self.print_mt(mt, true); + self.print_mutability(*mutbl, true); + self.print_type(ty); } - ast::TyKind::Ref(lifetime, mt) => { + ast::TyKind::Ref(lifetime, ty, mutbl) => { self.word("&"); self.print_opt_lifetime(lifetime); - self.print_mt(mt, false); + self.print_mutability(*mutbl, false); + self.print_type(ty); } - ast::TyKind::PinnedRef(lifetime, mt) => { + ast::TyKind::PinnedRef(lifetime, ty, mutbl) => { self.word("&"); self.print_opt_lifetime(lifetime); self.word("pin "); - self.print_mt(mt, true); + self.print_mutability(*mutbl, true); + self.print_type(ty); } ast::TyKind::Never => { self.word("!"); @@ -1462,8 +1461,8 @@ impl<'a> State<'a> { self.print_type(ty); self.print_view(fields); } - ast::TyKind::DirectConstArg(expr) => { - self.word_nbsp("core::direct_const_arg!"); + ast::TyKind::GcaMacro(expr) => { + self.word_nbsp("core::gca!"); self.popen(); self.print_expr(expr, FixupContext::default()); self.pclose(); @@ -2233,11 +2232,6 @@ impl<'a> State<'a> { } } - fn print_mt(&mut self, mt: &ast::MutTy, print_const: bool) { - self.print_mutability(mt.mutbl, print_const); - self.print_type(&mt.ty) - } - fn print_param(&mut self, input: &ast::Param, is_closure: bool) { let ib = self.ibox(INDENT_UNIT); diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index b6c22e7da9cb1..d6e8f69dbfa03 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -883,8 +883,8 @@ impl<'a> State<'a> { self.word("/*DUMMY*/"); self.pclose(); } - ast::ExprKind::DirectConstArg(expr) => { - self.word_nbsp("core::direct_const_arg!"); + ast::ExprKind::GcaMacro(expr) => { + self.word_nbsp("core::gca!"); self.popen(); self.print_expr(expr, FixupContext::default()); self.pclose() diff --git a/compiler/rustc_attr_ir/src/attribute_docs.rs b/compiler/rustc_attr_ir/src/attribute_docs.rs index 4d40cd6d52f16..b354e0c075c77 100644 --- a/compiler/rustc_attr_ir/src/attribute_docs.rs +++ b/compiler/rustc_attr_ir/src/attribute_docs.rs @@ -12,7 +12,7 @@ macro_rules! include_example { }; } -#[cfg_attr(not(bootstrap), doc(attribute = "rustc_dump_clauses"))] +#[doc(attribute = "rustc_dump_clauses")] /// Dumps the list of [`ty::Clause`]s as computed by the [`clauses_of`] query. /// /// See [`AttributeKind::RustcDumpClauses`] for the internal representation of this attribute. @@ -223,3 +223,105 @@ const _: () = (); /// /// [`vtable_entries`]: ../rustc_middle/ty/struct.TyCtxt.html#method.vtable_entries const _: () = (); + +#[doc(attribute = "rustc_dyn_incompatible_trait", alias = "rustc_do_not_implement_via_object")] +/// Opts a trait out of [dyn compatibility]. +/// +/// This is useful to reserve the ability to add dyn incompatible supertraits or methods to a trait +/// in the future and to ensure the soundness of various constructs - see below for more about that. +/// +/// For example [`Field`], [`FnPtr`], [`Tuple`], [`TransmuteFrom`], [`Sized`] and [`Unsize`] must +/// be dyn incompatible because these traits describe properties and layouts of types that would +/// be invalid for trait objects. +/// +/// While making a trait dyn incompatible can also be done by including a (hidden and/or unstable) +/// dyn incompatible method in the trait, using `#[rustc_dyn_incompatible_trait]` should be +/// preferred because it is self-documenting and generates better error messages. +/// +/// # Example +/// +#[doc = include_example!("rustc_dyn_incompatible_trait")] +/// +/// # Unsafe traits and dyn (in)compatibility +/// +/// [Recall] that a trait object (`dyn Trait`) implements the base trait, its auto traits, and any supertraits of +/// the base trait. This means that it's possible to run into subtle soundness problems when relying +/// on the safety contract of a dyn compatible unsafe trait. See the following example: +/// +// ignore-tidy-odd-backticks +/// ```should_panic +#[doc = include_str!("../../../tests/ui/attributes/doc_examples/rustc_dyn_incompatible_trait2.rs")] +// ignore-tidy-odd-backticks +/// ``` +/// +/// A solution for this is to make `UnsafeTrait` dyn incompatible, forcing `LocalTrait` to also be +/// dyn incompatible so that a `dyn LocalTrait` cannot be formed. This is what we ended up doing for +/// [#154619]. +/// +/// [`Allocator`] has had similar problems with [`Clone`] ([#156920]) +/// but as we really wanted `dyn Allocator` to be a thing we ended up not making it dyn +/// incompatible -- we ended up moving the safety contract to [`AllocatorClone`] instead. +/// +/// See also [#156917] and [#160045] for more examples of this problem. +/// +/// See [`AttributeKind::RustcDynIncompatibleTrait`] for the internal representation of this attribute. +/// +/// [`Allocator`]: core::alloc::Allocator +/// [`AllocatorClone`]: core::alloc::AllocatorClone +/// [`Clone`]: core::clone::Clone +/// [`Field`]: core::field::Field +// FIXME: use core::ops::FnPtr once trickled down to beta +/// [`FnPtr`]: https://doc.rust-lang.org/nightly/core/ops/trait.FnPtr.html +/// [`Tuple`]: core::marker::Tuple +/// [`TransmuteFrom`]: core::mem::TransmuteFrom +/// [`Sized`]: core::marker::Sized +/// [`Unsize`]: core::marker::Unsize +/// [dyn compatibility]: https://doc.rust-lang.org/nightly/reference/items/traits.html#dyn-compatibility +/// [recall]: https://doc.rust-lang.org/nightly/reference/types/trait-object.html#r-type.trait-object.impls +/// [#154619]: https://github.com/rust-lang/rust/issues/154619 "`deref_patterns` is unsound due to `dyn` of subtrait of `DerefPure`" +/// [#156917]: https://github.com/rust-lang/rust/issues/156917 "`dyn Allocator` together with `Allocator + PartialEq` safety requirements leads to unsoundness" +/// [#156920]:https://github.com/rust-lang/rust/issues/156920 "`dyn Allocator` together with `Allocator + Clone` requirements is unsound, leading to UB with `Arc`" +/// [#160045]:https://github.com/rust-lang/rust/issues/160045 "`iter::Rev`'s `TrustedLen` impl is unsound with trait objects" +const _: () = (); + +#[doc(attribute = "rustc_comptime")] +/// Marks a function as only callable at compile time. +/// +/// Unlike `const fn`, which *can* be called from a [const context], comptime functions *must* be +/// called from a const context. It is an error to call a comptime function outside a const context. +/// +/// # Example +/// +#[doc = include_example!("rustc_comptime")] +/// +/// There are many things which only have meaning at compile time and can make use of such +/// functions: +/// - the [`type_info`] feature, for compile time type reflection. +/// - the const heap intrinsics [`const_allocate`] and [`const_deallocate`], at runtime, return a null +/// pointer and do nothing respectively. +/// - intrinsics such as [`size_of`] and friends cause an ICE when they are called at runtime. The +/// stabilized versions of these functions forward to these intrinsics via associated constants on +/// the `SizedTypeProperties` trait. +/// +/// At this time, +// < October of 2026 > +/// the primary motivation for comptime functions is the [`type_info`] feature. The constructs +/// in [`mem::type_info`] only makes sense at compile time as during compile time we can simply +/// ask the compiler for whatever information we need. At runtime, though, that cannot be done - to +/// support runtime reflection we would have to create some global table with all the information +/// we might possibly need. This is unfeasible and any (third party wrappers for) "runtime +/// reflection" based on `type_info` would be limited to retrieving information queried in advance +/// at compile time. +/// +/// See the [reflection and comptime project goal] for more information about reflection and +/// comptime. +/// +/// See [`AttributeKind::RustcComptime`] for the internal representation of this attribute. +/// +/// [const context]: https://doc.rust-lang.org/nightly/reference/const_eval.html#const-context +/// [`type_info`]: https://github.com/rust-lang/rust/issues/146922 +/// [`const_allocate`]: std::intrinsics::const_allocate +/// [`const_deallocate`]: std::intrinsics::const_deallocate +/// [`mem::type_info`]: std::mem::type_info +/// [reflection and comptime project goal]: https://goals.rust-lang.org/2026/reflection-and-comptime.html +const _: () = (); diff --git a/compiler/rustc_attr_ir/src/data_structures.rs b/compiler/rustc_attr_ir/src/data_structures.rs index d722d515582dc..bca4d8fa7b5ff 100644 --- a/compiler/rustc_attr_ir/src/data_structures.rs +++ b/compiler/rustc_attr_ir/src/data_structures.rs @@ -283,7 +283,6 @@ impl StrippedCfgItem { #[derive(StableHash, PrintAttribute)] pub enum Linkage { AvailableExternally, - Common, ExternalWeak, External, Internal, @@ -490,9 +489,7 @@ pub struct DocAttribute { pub aliases: FxIndexMap, pub hidden: Option, - // Because we need to emit the error if there is more than one `inline` attribute on an item - // at the same time as the other doc attributes, we store a list instead of using `Option`. - pub inline: ThinVec<(DocInline, Span)>, + pub inline: Option<(DocInline, Span)>, // unstable pub cfg: ThinVec, @@ -555,8 +552,7 @@ impl rustc_serialize::Encodable for DocAttribute // FIXME: The `doc(inline)` attribute is never encoded, but is it actually the right thing // to do? I suspect the condition was broken, should maybe instead not encode anything if we // have `doc(no_inline)`. - let inline: ThinVec<_> = - inline.iter().filter(|(i, _)| *i != DocInline::Inline).cloned().collect(); + let inline = inline.filter(|(i, _)| *i != DocInline::Inline); rustc_serialize::Encodable::::encode(&inline, encoder); rustc_serialize::Encodable::::encode(cfg, encoder); @@ -592,6 +588,8 @@ pub enum RustcDumpLayoutKind { #[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute, PartialEq, Eq)] pub enum RustcMirKind { + PrettyLiveLocals, + PrettyTransitiveLiveLocals, PeekMaybeInit, PeekMaybeUninit, PeekLiveness, @@ -1179,7 +1177,7 @@ pub enum AttributeKind { /// Represents `#[rustc_coinductive]`. RustcCoinductive, - /// Represents `#[rustc_comptime]` + /// Represents the [`rustc_comptime`](./attribute.rustc_comptime.html) attribute. RustcComptime(Span), /// Represents `#[rustc_confusables]`. @@ -1267,7 +1265,7 @@ pub enum AttributeKind { /// Represents the [`rustc_dump_vtable`](./attribute.rustc_dump_vtable.html) attribute. RustcDumpVtable(Span), - /// Represents `#[rustc_dyn_incompatible_trait]`. + /// Represents the [`rustc_dyn_incompatible_trait`](./attribute.rustc_dyn_incompatible_trait.html) attribute. RustcDynIncompatibleTrait(Span), /// Represents `#[rustc_effective_visibility]`. diff --git a/compiler/rustc_attr_ir/src/encode_cross_crate.rs b/compiler/rustc_attr_ir/src/encode_cross_crate.rs index 6f05f763f2ada..b6733e8c1db54 100644 --- a/compiler/rustc_attr_ir/src/encode_cross_crate.rs +++ b/compiler/rustc_attr_ir/src/encode_cross_crate.rs @@ -212,4 +212,218 @@ impl AttributeKind { // tidy-alphabetical-end } } + + /// Whether this attribute is an extern indicator. + /// + /// Must stay in sync with [`CodegenFnAttrs::contains_extern_indicator`]. + /// + /// [`CodegenFnAttrs::contains_extern_indicator`]: ../rustc_middle/middle/codegen_fn_attrs/struct.CodegenFnAttrs.html#method.contains_extern_indicator + pub fn is_extern_indicator(&self) -> bool { + use AttributeKind::*; + + match self { + // tidy-alphabetical-start + AllowInternalUnsafe(..) => false, + AllowInternalUnstable(..) => false, + AlwaysGca => false, + AutomaticallyDerived => false, + CfgAttrTrace(..) => false, + CfgTrace(..) => false, + CfiEncoding { .. } => false, + Cold => false, + CollapseDebugInfo(..) => false, + CompilerBuiltins => false, + ConstContinue(..) => false, + Coroutine => false, + Coverage(..) => false, + CrateName { .. } => false, + CrateType(_) => false, + CustomMir(_, _) => false, + DebuggerVisualizer(..) => false, + DefaultLibAllocator => false, + Deprecated { .. } => false, + DoNotRecommend => false, + Doc(_) => false, + DocComment { .. } => false, + EiiDeclaration(_) => false, + EiiImpl(..) => true, + ExportName { .. } => true, + ExportStable => false, + Feature(..) => false, + FfiConst => false, + FfiPure(..) => false, + Fundamental { .. } => false, + Ignore { .. } => false, + Inline(..) => false, + InstructionSet(..) => false, + InstrumentFn(..) => false, + Lang(item) => item.link_name().is_some(), + Link(..) => false, + LinkName { .. } => false, + LinkOrdinal { .. } => false, + LinkSection { .. } => false, + Linkage(linkage, _) => match *linkage { + crate::Linkage::Internal => false, + crate::Linkage::AvailableExternally + | crate::Linkage::External + | crate::Linkage::ExternalWeak + | crate::Linkage::LinkOnceAny + | crate::Linkage::LinkOnceODR + | crate::Linkage::WeakAny + | crate::Linkage::WeakODR => true, + }, + LoopMatch(..) => false, + MacroEscape => false, + MacroExport { .. } => false, + MacroUse { .. } => false, + Marker => false, + MayDangle(..) => false, + MoveSizeLimit { .. } => false, + MustNotSupend { .. } => false, + MustUse { .. } => false, + Naked(..) => false, + NeedsAllocator => false, + NeedsPanicRuntime => false, + NoBuiltins => false, + NoCore => false, + NoImplicitPrelude => false, + NoLink => false, + NoMain => false, + NoMangle(..) => true, + NoStd => false, + NonExhaustive(..) => false, + OnConst { .. } => false, + OnMove { .. } => false, + OnTypeError { .. } => false, + OnUnimplemented { .. } => false, + OnUnknown { .. } => false, + OnUnmatchedArgs { .. } => false, + Opaque => false, + Optimize(..) => false, + PanicRuntime => false, + PatchableFunctionEntry { .. } => false, + Path(..) => false, + PatternComplexityLimit { .. } => false, + PinV2(..) => false, + PreludeImport => false, + ProcMacro => false, + ProcMacroAttribute => false, + ProcMacroDerive { .. } => false, + ProfilerRuntime => false, + RecursionLimit { .. } => false, + ReexportTestHarnessMain(..) => false, + RegisterTool { .. } => false, + Repr { .. } => false, + RustcAbi { .. } => false, + RustcAlign { .. } => false, + RustcAllocator => false, + RustcAllocatorZeroed => false, + RustcAllocatorZeroedVariant { .. } => false, + RustcAllowConstFnUnstable(..) => false, + RustcAllowIncoherentImpl(..) => false, + RustcAllowLifetimeDependentSpecialization => false, + RustcAsPtr => false, + RustcAutodiff(..) => false, + RustcBodyStability { .. } => false, + RustcBuiltinMacro { .. } => false, + RustcCanonicalSymbol => false, + RustcCaptureAnalysis => false, + RustcCguTestAttr { .. } => false, + RustcClean { .. } => false, + RustcCoherenceIsCore => false, + RustcCoinductive => false, + RustcComptime(..) => false, + RustcConfusables { .. } => false, + RustcConstStability { .. } => false, + RustcConstStableIndirect => false, + RustcConversionSuggestion => false, + RustcDeallocator => false, + RustcDelayedBugFromInsideQuery => false, + RustcDenyExplicitImpl => false, + RustcDeprecatedSafe2024 { .. } => false, + RustcDiagnosticItem(..) => false, + RustcDoNotConstCheck => false, + RustcDocPrimitive(..) => false, + RustcDummy => false, + RustcDumpClauses => false, + RustcDumpDefParents => false, + RustcDumpDefPath(..) => false, + RustcDumpGenerics => false, + RustcDumpHiddenTypeOfOpaques => false, + RustcDumpInferredOutlives => false, + RustcDumpItemBounds => false, + RustcDumpLayout(..) => false, + RustcDumpObjectLifetimeDefaults => false, + RustcDumpSymbolName(..) => false, + RustcDumpUserArgs => false, + RustcDumpVariances => false, + RustcDumpVariancesOfOpaques => false, + RustcDumpVtable(..) => false, + RustcDynIncompatibleTrait(..) => false, + RustcEffectiveVisibility => false, + RustcEiiForeignItem => true, + RustcEvaluateWhereClauses => false, + RustcHasIncoherentInherentImpls => false, + RustcIfThisChanged(..) => false, + RustcInheritOverflowChecks => false, + RustcInsignificantDtor => false, + RustcIntrinsic => false, + RustcIntrinsicConstStableIndirect => false, + RustcLegacyConstGenerics { .. } => false, + RustcLintOptDenyFieldAccess { .. } => false, + RustcLintOptTy => false, + RustcLintQueryInstability => false, + RustcLintUntrackedQueryInformation => false, + RustcMacroTransparency(..) => false, + RustcMain => false, + RustcMir(..) => false, + RustcMustImplementOneOf { .. } => false, + RustcMustMatchExhaustively(..) => false, + RustcNeverReturnsNullPtr => false, + RustcNoImplicitAutorefs => false, + RustcNoImplicitBounds => false, + RustcNoMirInline => false, + RustcNoWritable => false, + RustcNonConstTraitMethod => false, + RustcNonnullOptimizationGuaranteed => false, + RustcNounwind => false, + RustcObjcClass { .. } => false, + RustcObjcSelector { .. } => false, + RustcOffloadKernel => true, + RustcPanicsWhenZero => false, + RustcParenSugar => false, + RustcPassByValue => false, + RustcPassIndirectlyInNonRusticAbis(..) => false, + RustcPreserveUbChecks => false, + RustcProcMacroDecls => false, + RustcPubTransparent(..) => false, + RustcReallocator => false, + RustcRegions => false, + RustcScalableVector { .. } => false, + RustcShouldNotBeCalledOnConstItems => false, + RustcSimdMonomorphizeLaneLimit(..) => false, + RustcSkipDuringMethodDispatch { .. } => false, + RustcSpecializationTrait => false, + RustcStdInternalSymbol => true, + RustcStrictCoherence(..) => false, + RustcTestMarker(..) => false, + RustcThenThisWouldNeed(..) => false, + RustcTrivialFieldReads => false, + Sanitize { .. } => false, + ShouldPanic { .. } => false, + Splat(..) => false, + Stability { .. } => false, + TargetFeature { .. } => false, + TestRunner(..) => false, + ThreadLocal => false, + TrackCaller(..) => false, + TypeLengthLimit { .. } => false, + Unroll(..) => false, + UnstableFeatureBound(..) => false, + UnstableRemoved(..) => false, + Used { .. } => false, + WindowsSubsystem(..) => false, + // tidy-alphabetical-end + } + } } diff --git a/compiler/rustc_attr_ir/src/lang_items.rs b/compiler/rustc_attr_ir/src/lang_items.rs index c1ad05dc8e4a8..60890f7799290 100644 --- a/compiler/rustc_attr_ir/src/lang_items.rs +++ b/compiler/rustc_attr_ir/src/lang_items.rs @@ -281,6 +281,7 @@ language_item_table! { PartialEq, sym::eq, eq_trait, Target::Trait, GenericRequirement::Exact(1); PartialOrd, sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1); CVoid, sym::c_void, c_void, Target::Enum, GenericRequirement::None; + F16B, sym::f16b, f16b, Target::Struct, GenericRequirement::Exact(0); Type, sym::type_info, type_struct, Target::Struct, GenericRequirement::None; TypeGeneric, sym::type_info_generic, type_generic, Target::Enum, GenericRequirement::None; diff --git a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs index 743d3c9b5e76e..4a6e57b12233a 100644 --- a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs +++ b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs @@ -12,12 +12,8 @@ impl CombineAttributeParser for AllowInternalUnstableParser { type Item = (Symbol, Span); const CONVERT: ConvertFn = |items, span| AttributeKind::AllowInternalUnstable(items, span); - const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ - Allow(Target::MacroDef), - Allow(Target::Fn), - Warn(Target::Field), - Warn(Target::Arm), - ]); + const ALLOWED_TARGETS: AllowedTargets<'_> = + AllowedTargets::AllowList(&[Allow(Target::MacroDef), Allow(Target::Fn)]); const TEMPLATE: AttributeTemplate = template!(Word, List: &["feat1, feat2, ..."]); const STABILITY: AttributeStability = unstable!(allow_internal_unstable); @@ -30,7 +26,7 @@ impl CombineAttributeParser for AllowInternalUnstableParser { .zip(iter::repeat(cx.attr_span)) } - fn finalize_check(cx: &FinalizeCheckContext<'_, '_>, attr_span: Span) { + fn finalize_check(cx: &mut FinalizeCheckContext<'_, '_>, attr_span: Span) { check_macro_only(cx, attr_span); } } diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index 8102208ec519d..d244e6c59e5f9 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -306,7 +306,8 @@ pub fn parse_cfg_attr( features: Option<&Features>, lint_node_id: ast::NodeId, ) -> Option<(CfgEntry, Vec<(WithTokens, Span)>)> { - match &cfg_attr.get_normal_item().args { + let item = cfg_attr.get_normal_item(); + match &item.args { ast::AttrArgs::Delimited(ast::DelimArgs { dspan, delim, tokens }) if !tokens.is_empty() => { check_cfg_attr_bad_delim(&sess.psess, *dspan, *delim); match parse_in(&sess.psess, tokens.clone(), "`cfg_attr` input", |p| { @@ -316,11 +317,11 @@ pub fn parse_cfg_attr( Err(e) => { let suggestions = CFG_ATTR_TEMPLATE.suggestions( ParsedDescription::Attribute, - cfg_attr.get_normal_item().unsafety, + item.unsafety, sym::cfg_attr, ); e.with_span_suggestions( - cfg_attr.get_normal_item().span, + item.span, "must be of the form", suggestions, Applicability::HasPlaceholders, @@ -334,25 +335,24 @@ pub fn parse_cfg_attr( } } _ => { - let (span, reason) = if let ast::AttrArgs::Delimited(ast::DelimArgs { dspan, .. }) = - cfg_attr.get_normal_item().args - { - (dspan.entire(), AttributeParseErrorReason::ExpectedAtLeastOneArgument) - } else { - (cfg_attr.get_normal_item().span, AttributeParseErrorReason::ExpectedList) - }; + let (span, reason) = + if let ast::AttrArgs::Delimited(ast::DelimArgs { dspan, .. }) = item.args { + (dspan.entire(), AttributeParseErrorReason::ExpectedAtLeastOneArgument) + } else { + (item.span, AttributeParseErrorReason::ExpectedList) + }; sess.dcx().emit_err(AttributeParseError { span, - inner_span: cfg_attr.get_normal_item().span, + inner_span: item.span, template: CFG_ATTR_TEMPLATE, - path: AttrPath::from_ast(&cfg_attr.get_normal_item().path, identity), + path: AttrPath::from_ast(&item.path, identity), description: ParsedDescription::Attribute, reason, suggestions: diagnostics::AttributeParseErrorSuggestions::CreatedByTemplate( CFG_ATTR_TEMPLATE.suggestions( ParsedDescription::Attribute, - cfg_attr.get_normal_item().unsafety, + item.unsafety, sym::cfg_attr, ), ), @@ -389,13 +389,14 @@ fn parse_cfg_attr_internal<'a>( )?; let pred_span = pred_start.with_hi(parser.token.span.hi()); + let item = attribute.get_normal_item(); let cfg_predicate = AttributeParser::parse_single_args( sess, attribute.span, - attribute.get_normal_item().span, + item.span, attribute.style, AttrPath { segments: attribute.path().into_boxed_slice(), span: attribute.span }, - Some(attribute.get_normal_item().unsafety), + Some(item.unsafety), AttributeSafety::Normal, ParsedDescription::Attribute, pred_span, diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs b/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs index 53d7044521e5a..6c6793b29f586 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs @@ -89,13 +89,13 @@ pub fn parse_cfg_select( let mut branches = CfgSelectBranches::default(); while p.token != token::Eof { - p.recover_from_outer_attributes("`cfg_select` branches").map_err(|e| e.emit())?; + p.recover_from_outer_attributes("`cfg_select` branches").map_err(|e| e.emit_err())?; if p.eat_keyword(exp!(Underscore)) { let underscore = p.prev_token; - p.expect(exp!(FatArrow)).map_err(|e| e.emit())?; + p.expect(exp!(FatArrow)).map_err(|e| e.emit_err())?; - let tts = p.parse_cfg_select_branch_rhs().map_err(|e| e.emit())?; + let tts = p.parse_cfg_select_branch_rhs().map_err(|e| e.emit_err())?; let span = underscore.span.to(p.token.span); match branches.wildcard { @@ -110,7 +110,7 @@ pub fn parse_cfg_select( ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, AllowExprMetavar::Yes, ) - .map_err(|diag| diag.emit())?; + .map_err(|diag| diag.emit_err())?; let cfg_span = meta.span(); let cfg = AttributeParser::parse_single_args( sess, @@ -132,9 +132,9 @@ pub fn parse_cfg_select( &AttributeTemplate::default(), )?; - p.expect(exp!(FatArrow)).map_err(|e| e.emit())?; + p.expect(exp!(FatArrow)).map_err(|e| e.emit_err())?; - let tts = p.parse_cfg_select_branch_rhs().map_err(|e| e.emit())?; + let tts = p.parse_cfg_select_branch_rhs().map_err(|e| e.emit_err())?; let span = cfg_span.to(p.token.span); match branches.wildcard { diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index bff7d7ad81cb9..2553829f8c5ce 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -349,7 +349,7 @@ impl NoArgsAttributeParser for TrackCallerParser { const STABILITY: AttributeStability = AttributeStability::Stable; const CREATE: fn(Span) -> AttributeKind = AttributeKind::TrackCaller; - fn finalize_check(cx: &FinalizeCheckContext<'_, '_>, attr_span: Span) { + fn finalize_check(cx: &mut FinalizeCheckContext<'_, '_>, attr_span: Span) { match cx.target { Target::Fn => { // `#[track_caller]` is not valid on weak lang items because they are called via @@ -571,7 +571,7 @@ impl CombineAttributeParser for TargetFeatureParser { parse_tf_attribute(cx, args) } - fn finalize_check(cx: &FinalizeCheckContext<'_, '_>, attr_span: Span) { + fn finalize_check(cx: &mut FinalizeCheckContext<'_, '_>, attr_span: Span) { // `#[target_feature]` is incompatible with lang item functions, // except on WASM where calling target-feature functions is safe (see #84988). if !cx.sess().target.is_like_wasm && !cx.sess().opts.actually_rustdoc { diff --git a/compiler/rustc_attr_parsing/src/attributes/doc.rs b/compiler/rustc_attr_parsing/src/attributes/doc.rs index 7aeedc1493027..7dc5c6ce9f2b8 100644 --- a/compiler/rustc_attr_parsing/src/attributes/doc.rs +++ b/compiler/rustc_attr_parsing/src/attributes/doc.rs @@ -5,7 +5,7 @@ use rustc_attr_ir::{ DocInline, HideOrShow, }; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, IndexEntry}; -use rustc_errors::Applicability; +use rustc_errors::{Applicability, MultiSpan, msg}; use rustc_feature::AttributeStability; use rustc_lint_defs::builtin::{INVALID_DOC_ATTRIBUTES, UNUSED_ATTRIBUTES}; use rustc_span::{Span, Symbol, edition, sym}; @@ -18,10 +18,10 @@ use crate::diagnostics::{ DocAliasStartEnd, DocAttrNotCrateLevel, DocAttributeNotAttribute, DocAutoCfgExpectsHideOrShow, DocAutoCfgHideShowExpectsList, DocAutoCfgHideShowNoIdentBeforeValues, DocAutoCfgHideShowUnexpectedItem, DocAutoCfgHideShowUnexpectedItemAfterValues, - DocAutoCfgHideShowValuesMix, DocAutoCfgWrongLiteral, DocKeywordNotKeyword, DocTestLiteral, - DocTestTakesList, DocTestUnknown, DocUnknownAny, DocUnknownInclude, DocUnknownPasses, - DocUnknownPlugins, DocUnknownSpotlight, ExpectedNameValue, ExpectedNoArgs, - IllFormedAttributeInput, MalformedDoc, UnusedDuplicate, + DocAutoCfgHideShowValuesMix, DocAutoCfgWrongLiteral, DocInlineConflict, DocInlineOnlyUse, + DocKeywordNotKeyword, DocTestLiteral, DocTestTakesList, DocTestUnknown, DocUnknownAny, + DocUnknownInclude, DocUnknownPasses, DocUnknownPlugins, DocUnknownSpotlight, ExpectedNameValue, + ExpectedNoArgs, IllFormedAttributeInput, MalformedDoc, UnusedDuplicate, }; use crate::parser::{ ArgParser, MetaItemListParser, MetaItemOrLitParser, MetaItemParser, OwnedPathParser, @@ -263,7 +263,29 @@ impl DocParser { return; } - self.attribute.inline.push((inline, path.span())); + let span = path.span(); + if let Some((previous_inline, previous_span)) = self.attribute.inline { + if previous_inline != inline { + let mut spans = MultiSpan::from_spans(vec![previous_span, span]); + spans.push_span_label(previous_span, msg!("this attribute...")); + spans.push_span_label(span, msg!("{\".\"}..conflicts with this attribute")); + cx.emit_err(DocInlineConflict { spans }); + } + } else { + self.attribute.inline = Some((inline, span)); + } + + match cx.target { + Target::Use | Target::ExternCrate => {} + _ => { + let item_span = cx.target_span; + cx.emit_lint( + INVALID_DOC_ATTRIBUTES, + DocInlineOnlyUse { attr_span: span, item_span }, + span, + ); + } + } } fn parse_cfg(&mut self, cx: &mut AcceptContext<'_, '_>, args: &ArgParser) { diff --git a/compiler/rustc_attr_parsing/src/attributes/inline.rs b/compiler/rustc_attr_parsing/src/attributes/inline.rs index f3e305ea266a1..b4a5c9da155c9 100644 --- a/compiler/rustc_attr_parsing/src/attributes/inline.rs +++ b/compiler/rustc_attr_parsing/src/attributes/inline.rs @@ -1,9 +1,9 @@ -use rustc_attr_ir::{AttributeKind, InlineAttr, find_attr}; +use rustc_attr_ir::{Attribute, AttributeKind, InlineAttr, find_attr}; use rustc_feature::AttributeStability; -use rustc_lint_defs::builtin::ILL_FORMED_ATTRIBUTE_INPUT; +use rustc_lint_defs::builtin::{ILL_FORMED_ATTRIBUTE_INPUT, UNUSED_ATTRIBUTES}; use super::prelude::*; -use crate::diagnostics::InlineForceInlineConflict; +use crate::diagnostics::{InlineForceInlineConflict, InlineIgnoredForExported}; pub(crate) struct InlineParser; @@ -59,6 +59,25 @@ impl SingleAttributeParser for InlineParser { } } } + + fn finalize_check(cx: &mut FinalizeCheckContext<'_, '_>, attr_span: Span) { + let exported = cx.parsed_attrs.iter().any(|attr| { + let Attribute::Parsed(kind) = attr else { return false }; + kind.is_extern_indicator() + }); + if matches!( + cx.target, + Target::Fn + | Target::Closure + | Target::Method( + MethodKind::Trait { body: true } | MethodKind::TraitImpl | MethodKind::Inherent, + ) + ) && !find_attr!(cx.parsed_attrs, Inline(InlineAttr::Never, _)) + && exported + { + cx.emit_lint(UNUSED_ATTRIBUTES, InlineIgnoredForExported, attr_span); + } + } } pub(crate) struct RustcForceInlineParser; @@ -94,7 +113,7 @@ impl SingleAttributeParser for RustcForceInlineParser { )) } - fn finalize_check(cx: &FinalizeCheckContext<'_, '_>, attr_span: Span) { + fn finalize_check(cx: &mut FinalizeCheckContext<'_, '_>, attr_span: Span) { let Some(inline_span) = find_attr!(cx.parsed_attrs, Inline(attr, span) if !matches!(attr, InlineAttr::Force { .. }) => span) else { return; diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index a00e5af00c2eb..ed911a63eb7fe 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -1,8 +1,10 @@ +use rustc_abi::ExternAbi; +use rustc_ast::ItemKind; use rustc_attr_ir::AttributeKind::{LinkName, LinkOrdinal, LinkSection}; use rustc_attr_ir::*; use rustc_errors::msg; use rustc_feature::{AttributeStability, Features}; -use rustc_lint_defs::builtin::ILL_FORMED_ATTRIBUTE_INPUT; +use rustc_lint_defs::builtin::{ILL_FORMED_ATTRIBUTE_INPUT, UNUSED_ATTRIBUTES}; use rustc_session::Session; use rustc_session::diagnostics::feature_err; use rustc_span::edition::Edition::Edition2024; @@ -17,7 +19,7 @@ use crate::attributes::cfg::parse_cfg_entry; use crate::diagnostics::{ AsNeededCompatibility, BothFfiConstAndPure, BundleNeedsStatic, EmptyLinkName, ExportSymbolsNeedsStatic, ImportNameTypeRaw, ImportNameTypeX86, IncompatibleWasmLink, - InvalidLinkModifier, InvalidMachoSection, InvalidMachoSectionReason, LinkFrameworkApple, + InvalidLinkModifier, InvalidMachoSection, InvalidMachoSectionReason, Link, LinkFrameworkApple, LinkOrdinalOutOfRange, LinkRequiresName, MultipleModifiers, NullOnLinkName, NullOnLinkSection, RawDylibOnlyWindows, WholeArchiveNeedsStatic, }; @@ -258,6 +260,29 @@ impl CombineAttributeParser for LinkParser { import_name_type, }) } + + fn finalize_check(cx: &mut FinalizeCheckContext<'_, '_>, attr_span: Span) { + if cx.target != Target::ForeignMod { + return; + } + + let item = cx.target_item.expect("missing AST target item for Target::ForeignMod"); + let ItemKind::ForeignMod(fm) = &item.kind else { + panic!("expected foreign module AST target item for Target::ForeignMod"); + }; + let abi = fm.abi.map_or(ExternAbi::FALLBACK, |abi| { + abi.symbol_unescaped.as_str().parse().unwrap_or_else(|_| { + cx.dcx().span_delayed_bug( + abi.span, + "LinkParser::finalize_check was unable to pre-detect the ABI, so it continues to use the recovery value solely to check for unused_attributes in the lint; a user error E0703 will be reported later in lower_abi", + ); + ExternAbi::Rust + }) + }); + if matches!(abi, ExternAbi::Rust) { + cx.emit_lint(UNUSED_ATTRIBUTES, Link, attr_span); + } + } } impl LinkParser { @@ -584,7 +609,7 @@ impl NoArgsAttributeParser for FfiPureParser { const STABILITY: AttributeStability = unstable!(ffi_pure); const CREATE: fn(Span) -> AttributeKind = AttributeKind::FfiPure; - fn finalize_check(cx: &FinalizeCheckContext<'_, '_>, attr_span: Span) { + fn finalize_check(cx: &mut FinalizeCheckContext<'_, '_>, attr_span: Span) { // `#[ffi_const]` functions cannot be `#[ffi_pure]`. if cx.all_attrs.iter().any(|a| a.word_is(sym::ffi_const)) { cx.emit_err(BothFfiConstAndPure { attr_span }); @@ -660,10 +685,7 @@ impl SingleAttributeParser for LinkageParser { ]); const TEMPLATE: AttributeTemplate = template!(NameValueStr: [ "available_externally", - "common", "extern_weak", - "external", - "internal", "linkonce", "linkonce_odr", "weak", @@ -686,10 +708,7 @@ impl SingleAttributeParser for LinkageParser { // and don't have to be, LLVM treats them as no-ops. let linkage = match value { sym::available_externally => Linkage::AvailableExternally, - sym::common => Linkage::Common, sym::extern_weak => Linkage::ExternalWeak, - sym::external => Linkage::External, - sym::internal => Linkage::Internal, sym::linkonce => Linkage::LinkOnceAny, sym::linkonce_odr => Linkage::LinkOnceODR, sym::weak => Linkage::WeakAny, @@ -700,10 +719,7 @@ impl SingleAttributeParser for LinkageParser { name_value.value_span, &[ sym::available_externally, - sym::common, sym::extern_weak, - sym::external, - sym::internal, sym::linkonce, sym::linkonce_odr, sym::weak, diff --git a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs index 4ead90af8d630..10e42ced30705 100644 --- a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs +++ b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs @@ -29,7 +29,7 @@ impl NoArgsAttributeParser for RustcPubTransparentParser { const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPubTransparent; - fn finalize_check(cx: &FinalizeCheckContext<'_, '_>, attr_span: Span) { + fn finalize_check(cx: &mut FinalizeCheckContext<'_, '_>, attr_span: Span) { // `#[rustc_pub_transparent]` may only be applied to `#[repr(transparent)]` types. let is_transparent = find_attr!( cx.parsed_attrs, diff --git a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs index c9625521aec44..8bb5e6b7fc270 100644 --- a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs @@ -1,10 +1,11 @@ +use rustc_ast::ItemKind; use rustc_attr_ir::{MacroUseArgs, find_attr}; use rustc_feature::AttributeStability; -use rustc_lint_defs::builtin::INVALID_MACRO_EXPORT_ARGUMENTS; +use rustc_lint_defs::builtin::{INVALID_MACRO_EXPORT_ARGUMENTS, UNUSED_ATTRIBUTES}; use rustc_structures::CollapseMacroDebuginfo; use super::prelude::*; -use crate::diagnostics::MacroOnlyAttribute; +use crate::diagnostics::{MacroExport, MacroOnlyAttribute}; pub(crate) struct MacroEscapeParser; impl NoArgsAttributeParser for MacroEscapeParser { @@ -131,16 +132,12 @@ pub(crate) struct AllowInternalUnsafeParser; impl NoArgsAttributeParser for AllowInternalUnsafeParser { const PATH: &[Symbol] = &[sym::allow_internal_unsafe]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Ignore; - const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ - Allow(Target::Fn), - Allow(Target::MacroDef), - Warn(Target::Field), - Warn(Target::Arm), - ]); + const ALLOWED_TARGETS: AllowedTargets<'_> = + AllowedTargets::AllowList(&[Allow(Target::Fn), Allow(Target::MacroDef)]); const STABILITY: AttributeStability = unstable!(allow_internal_unsafe); const CREATE: fn(Span) -> AttributeKind = |span| AttributeKind::AllowInternalUnsafe(span); - fn finalize_check(cx: &FinalizeCheckContext<'_, '_>, attr_span: Span) { + fn finalize_check(cx: &mut FinalizeCheckContext<'_, '_>, attr_span: Span) { check_macro_only(cx, attr_span); } } @@ -179,8 +176,22 @@ impl SingleAttributeParser for MacroExportParser { return None; } }; + Some(AttributeKind::MacroExport { span: cx.attr_span, local_inner_macros }) } + + fn finalize_check(cx: &mut FinalizeCheckContext<'_, '_>, attr_span: Span) { + if cx.target != Target::MacroDef { + return; + } + + let item = cx.target_item.unwrap(); + if let ItemKind::MacroDef(_, macro_def) = &item.kind + && !macro_def.macro_rules + { + cx.emit_lint(UNUSED_ATTRIBUTES, MacroExport::OnDeclMacro, attr_span); + } + } } pub(crate) struct CollapseDebugInfoParser; diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 242b4a73b06a6..8e47414f6c72a 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -169,7 +169,7 @@ pub(crate) trait SingleAttributeParser: 'static { /// combinations. `attr_span` is the span of this attribute. /// /// Defaults to a no-op. - fn finalize_check(_cx: &FinalizeCheckContext<'_, '_>, _attr_span: Span) {} + fn finalize_check(_cx: &mut FinalizeCheckContext<'_, '_>, _attr_span: Span) {} } /// Use in combination with [`SingleAttributeParser`]. @@ -287,7 +287,7 @@ pub(crate) trait NoArgsAttributeParser: 'static { /// `attr_span` is the span of this attribute. /// /// Defaults to a no-op. - fn finalize_check(_cx: &FinalizeCheckContext<'_, '_>, _attr_span: Span) {} + fn finalize_check(_cx: &mut FinalizeCheckContext<'_, '_>, _attr_span: Span) {} } pub(crate) struct WithoutArgs(PhantomData); @@ -311,7 +311,7 @@ impl SingleAttributeParser for WithoutArgs { Some(T::CREATE(cx.attr_span)) } - fn finalize_check(cx: &FinalizeCheckContext<'_, '_>, attr_span: Span) { + fn finalize_check(cx: &mut FinalizeCheckContext<'_, '_>, attr_span: Span) { T::finalize_check(cx, attr_span) } } @@ -355,7 +355,7 @@ pub(crate) trait CombineAttributeParser: 'static { /// `attr_span` is the span of the first attribute that was encountered. /// /// Defaults to a no-op. - fn finalize_check(_cx: &FinalizeCheckContext<'_, '_>, _attr_span: Span) {} + fn finalize_check(_cx: &mut FinalizeCheckContext<'_, '_>, _attr_span: Span) {} } /// Use in combination with [`CombineAttributeParser`]. diff --git a/compiler/rustc_attr_parsing/src/attributes/non_exhaustive.rs b/compiler/rustc_attr_parsing/src/attributes/non_exhaustive.rs index 30fdadf95e4ce..2acba84956e67 100644 --- a/compiler/rustc_attr_parsing/src/attributes/non_exhaustive.rs +++ b/compiler/rustc_attr_parsing/src/attributes/non_exhaustive.rs @@ -21,7 +21,7 @@ impl NoArgsAttributeParser for NonExhaustiveParser { const STABILITY: AttributeStability = AttributeStability::Stable; const CREATE: fn(Span) -> AttributeKind = AttributeKind::NonExhaustive; - fn finalize_check(cx: &FinalizeCheckContext<'_, '_>, attr_span: Span) { + fn finalize_check(cx: &mut FinalizeCheckContext<'_, '_>, attr_span: Span) { if cx.target != Target::Struct { return; } diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs index 6115ed207d98c..bc4f9d20545fc 100644 --- a/compiler/rustc_attr_parsing/src/attributes/repr.rs +++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs @@ -4,6 +4,7 @@ use rustc_attr_ir::IntType::{SignedInt, UnsignedInt}; use rustc_attr_ir::ReprAttr; use rustc_feature::AttributeStability; use rustc_session::diagnostics::feature_err; +use rustc_span::OrdSpan; use super::prelude::*; use crate::diagnostics; @@ -272,7 +273,7 @@ fn parse_alignment(node: &LitKind, cx: &AcceptContext<'_, '_>) -> Result); +pub(crate) struct RustcAlignParser(Option<(Align, OrdSpan)>); impl RustcAlignParser { const PATH: &[Symbol] = &[sym::rustc_align]; @@ -293,7 +294,7 @@ impl RustcAlignParser { }; match parse_alignment(&lit.kind, cx) { - Ok(literal) => self.0 = Ord::max(self.0, Some((literal, cx.attr_span))), + Ok(literal) => self.0 = Ord::max(self.0, Some((literal, OrdSpan(cx.attr_span)))), Err(message) => { cx.emit_err(diagnostics::InvalidAlignmentValue { span: lit.span, @@ -318,7 +319,7 @@ impl AttributeParser for RustcAlignParser { fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option { let (align, span) = self.0?; - Some(AttributeKind::RustcAlign { align, span }) + Some(AttributeKind::RustcAlign { align, span: span.0 }) } } @@ -342,6 +343,6 @@ impl AttributeParser for RustcAlignStaticParser { fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option { let (align, span) = self.0.0?; - Some(AttributeKind::RustcAlign { align, span }) + Some(AttributeKind::RustcAlign { align, span: span.0 }) } } diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index bb982184772e7..b9c03883d7c0b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -1,6 +1,6 @@ use std::path::PathBuf; -use rustc_ast::{LitIntType, LitKind, MetaItemLit}; +use rustc_ast::{GenericParamKind, ItemKind, LitIntType, LitKind, MetaItemLit}; use rustc_attr_ir::lang_items::LangItem; use rustc_attr_ir::{ BorrowckGraphvizFormatKind, CguFields, CguKind, RustcCleanAttribute, RustcCleanQueries, @@ -169,6 +169,48 @@ impl SingleAttributeParser for RustcLegacyConstGenericsParser { attr_span: cx.attr_span, }) } + + fn finalize_check(cx: &mut FinalizeCheckContext<'_, '_>, attr_span: Span) { + if cx.target != Target::Fn { + // Invalid targets are already diagnosed by target checking. + return; + } + let item = cx.target_item.expect("missing AST target item for Target::Fn"); + let ItemKind::Fn(function) = &item.kind else { + panic!("expected fn AST target item for Target::Fn"); + }; + let index_list = rustc_attr_ir::find_attr!(cx.parsed_attrs, RustcLegacyConstGenerics { fn_indexes, .. } => fn_indexes) + .expect("missing parsed RustcLegacyConstGenerics attribute in finalize_check"); + let generics = &function.generics; + + for param in &generics.params { + if !matches!(param.kind, GenericParamKind::Const { .. }) { + cx.emit_err(diagnostics::RustcLegacyConstGenericsOnly { + attr_span, + param_span: param.span(), + }); + return; + } + } + + if index_list.len() != generics.params.len() { + cx.emit_err(diagnostics::RustcLegacyConstGenericsIndex { + attr_span, + generics_span: generics.span, + }); + return; + } + + let arg_count = function.sig.decl.inputs.len() + generics.params.len(); + for (index, span) in index_list { + if *index >= arg_count { + cx.emit_err(diagnostics::RustcLegacyConstGenericsIndexExceed { + span: *span, + arg_count, + }); + } + } + } } pub(crate) struct RustcInheritOverflowChecksParser; @@ -626,6 +668,14 @@ impl CombineAttributeParser for RustcMirParser { .filter_map(|mi| { if let Some(ident) = mi.ident() { match ident.name { + sym::rustc_pretty_live_locals => { + cx.expect_no_args(mi.args())?; + Some(RustcMirKind::PrettyLiveLocals) + } + sym::rustc_pretty_transitive_live_locals => { + cx.expect_no_args(mi.args())?; + Some(RustcMirKind::PrettyTransitiveLiveLocals) + } sym::rustc_peek_maybe_init => Some(RustcMirKind::PeekMaybeInit), sym::rustc_peek_maybe_uninit => Some(RustcMirKind::PeekMaybeUninit), sym::rustc_peek_liveness => Some(RustcMirKind::PeekLiveness), diff --git a/compiler/rustc_attr_parsing/src/attributes/semantics.rs b/compiler/rustc_attr_parsing/src/attributes/semantics.rs index bc3fee8bc0ba3..6c4cd5ef667c8 100644 --- a/compiler/rustc_attr_parsing/src/attributes/semantics.rs +++ b/compiler/rustc_attr_parsing/src/attributes/semantics.rs @@ -28,6 +28,6 @@ impl NoArgsAttributeParser for AlwaysGcaParser { const PATH: &[Symbol] = &[sym::rustc_always_gca]; const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::AssocConst(AssocCtxt::Trait))]); - const STABILITY: AttributeStability = unstable!(min_generic_const_args); + const STABILITY: AttributeStability = unstable!(gca_min_const_items); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::AlwaysGca; } diff --git a/compiler/rustc_attr_parsing/src/attributes/traits.rs b/compiler/rustc_attr_parsing/src/attributes/traits.rs index 1d0d26ea62cb3..e55a033eb3188 100644 --- a/compiler/rustc_attr_parsing/src/attributes/traits.rs +++ b/compiler/rustc_attr_parsing/src/attributes/traits.rs @@ -8,7 +8,7 @@ use crate::attributes::{NoArgsAttributeParser, SingleAttributeParser}; use crate::context::AcceptContext; use crate::parser::ArgParser; use crate::target_checking::AllowedTargets; -use crate::target_checking::Policy::{Allow, Warn}; +use crate::target_checking::Policy::Allow; pub(crate) struct RustcSkipDuringMethodDispatchParser; impl SingleAttributeParser for RustcSkipDuringMethodDispatchParser { @@ -63,12 +63,7 @@ impl NoArgsAttributeParser for RustcParenSugarParser { pub(crate) struct MarkerParser; impl NoArgsAttributeParser for MarkerParser { const PATH: &[Symbol] = &[sym::marker]; - const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ - Allow(Target::Trait), - Warn(Target::Field), - Warn(Target::Arm), - Warn(Target::MacroDef), - ]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Trait)]); const STABILITY: AttributeStability = unstable!(marker_trait_attr); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::Marker; } diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index f936f5aab8265..060fd74c1db21 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -12,7 +12,7 @@ use rustc_ast::{AttrStyle, MetaItemLit, Safety}; use rustc_attr_ir::target::Target; use rustc_attr_ir::{AttrPath, Attribute, AttributeKind}; use rustc_data_structures::sync::{DynSend, DynSync}; -use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level, MultiSpan}; +use rustc_errors::{Diag, DiagCtxtHandle, DiagLocation, Diagnostic, Level, MultiSpan}; use rustc_feature::AttributeStability; use rustc_lint_defs::builtin::UNUSED_ATTRIBUTES; use rustc_lint_defs::{Lint, LintId}; @@ -102,7 +102,7 @@ pub(crate) type FinalizeFn = fn(&mut FinalizeContext<'_, '_>) -> FinalizeOutput; /// finalized, so it can inspect the fully parsed attributes via /// [`FinalizeCheckContext::parsed_attrs`]. The [`Span`] is the span of the attribute the /// check is associated with, used for diagnostics. -pub(crate) type FinalizeCheckFn = fn(&FinalizeCheckContext<'_, '_>, Span); +pub(crate) type FinalizeCheckFn = fn(&mut FinalizeCheckContext<'_, '_>, Span); /// The result of finalizing a single attribute parser. pub(crate) struct FinalizeOutput { @@ -408,6 +408,7 @@ pub struct AcceptContext<'f, 'sess> { } impl<'f, 'sess: 'f> SharedContext<'f, 'sess> { + #[track_caller] pub(crate) fn emit_err(&self, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed { self.cx.emit_err(diag) } @@ -415,21 +416,27 @@ impl<'f, 'sess: 'f> SharedContext<'f, 'sess> { /// 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. + #[track_caller] pub(crate) fn emit_lint( &mut self, lint: &'static Lint, - diagnostic: impl for<'x> Diagnostic<'x, ()> + DynSend + DynSync + 'static, + diagnostic: impl for<'x> Diagnostic<'x> + DynSend + DynSync + 'static, span: impl Into, ) { + let emitted_at = DiagLocation::caller(); self.emit_lint_inner( lint, - EmitAttribute(Box::new(move |dcx, level, _| diagnostic.into_diag(dcx, level))), + EmitAttribute(Box::new(move |dcx, level, _| { + let mut diag = diagnostic.into_diag(dcx, level); + diag.emitted_at = emitted_at; + diag + })), span, ); } pub(crate) fn emit_lint_with_sess< - F: for<'a> FnOnce(DiagCtxtHandle<'a>, Level, &Session) -> Diag<'a, ()> + F: for<'a> FnOnce(DiagCtxtHandle<'a>, Level, &Session) -> Diag<'a> + DynSend + DynSync + 'static, @@ -497,6 +504,7 @@ impl<'f, 'sess: 'f> AcceptContext<'f, 'sess> { /// /// The provided span is used as a fallback for diagnostic generation in case `arg` does not /// contain any. It should be the span of the node that contains `arg`. + #[track_caller] pub(crate) fn expect_single_element_list<'arg>( &mut self, arg: &'arg ArgParser, @@ -529,6 +537,7 @@ impl<'f, 'sess: 'f> AcceptContext<'f, 'sess> { /// /// - You want to emit your own diagnostics (for instance, with [`SharedContext::emit_err`]). /// - The attribute can be parsed in multiple ways and it does not make sense to emit an error. + #[track_caller] pub(crate) fn expect_list<'arg>( &mut self, args: &'arg ArgParser, @@ -550,6 +559,7 @@ impl<'f, 'sess: 'f> AcceptContext<'f, 'sess> { /// /// - You want to emit your own diagnostics (for instance, with [`SharedContext::emit_err`]). /// - The attribute can be parsed in multiple ways and it does not make sense to emit an error. + #[track_caller] pub(crate) fn expect_single<'arg>( &mut self, list: &'arg MetaItemListParser, @@ -589,6 +599,7 @@ impl<'f, 'sess: 'f> AcceptContext<'f, 'sess> { /// /// - You want to emit your own diagnostics (for instance, with [`SharedContext::emit_err`]). /// - The attribute can be parsed in multiple ways and it does not make sense to emit an error. + #[track_caller] pub(crate) fn expect_name_value<'arg, Arg>( &mut self, arg: &'arg Arg, @@ -609,6 +620,7 @@ impl<'f, 'sess: 'f> AcceptContext<'f, 'sess> { /// /// - You want to emit your own diagnostics (for instance, with [`SharedContext::emit_err`]). /// - The attribute can be parsed in multiple ways and it does not make sense to emit an error. + #[track_caller] pub(crate) fn expect_no_args<'arg>(&mut self, arg: &'arg ArgParser) -> Option<()> { if let Err(span) = arg.as_no_args() { self.adcx().expected_no_args(span); @@ -631,6 +643,7 @@ impl<'f, 'sess: 'f> AcceptContext<'f, 'sess> { /// /// - You want to emit your own diagnostics (for instance, with [`SharedContext::emit_err`]). /// - The attribute can be parsed in multiple ways and it does not make sense to emit an error. + #[track_caller] pub(crate) fn expect_string_literal<'arg, Arg>(&mut self, arg: &'arg Arg) -> Option where Arg: ExpectStringLiteral, @@ -644,6 +657,7 @@ pub(crate) trait ExpectNameValue { where Self: 'a; + #[track_caller] fn expect_name_value<'a, 'f, 'sess>( &'a self, cx: &mut AcceptContext<'f, 'sess>, @@ -655,6 +669,7 @@ pub(crate) trait ExpectNameValue { impl ExpectNameValue for MetaItemOrLitParser { type Output<'a> = (Ident, &'a NameValueParser); + #[track_caller] fn expect_name_value<'a, 'f, 'sess>( &'a self, cx: &mut AcceptContext<'f, 'sess>, @@ -673,6 +688,7 @@ impl ExpectNameValue for MetaItemOrLitParser { impl ExpectNameValue for MetaItemParser { type Output<'a> = (Ident, &'a NameValueParser); + #[track_caller] fn expect_name_value<'a, 'f, 'sess>( &'a self, cx: &mut AcceptContext<'f, 'sess>, @@ -697,6 +713,7 @@ impl ExpectNameValue for MetaItemParser { impl ExpectNameValue for ArgParser { type Output<'a> = &'a NameValueParser; + #[track_caller] fn expect_name_value<'a, 'f, 'sess>( &'a self, cx: &mut AcceptContext<'f, 'sess>, @@ -713,11 +730,13 @@ impl ExpectNameValue for ArgParser { } pub(crate) trait ExpectStringLiteral { + #[track_caller] fn expect_string_literal<'f, 'sess>(&self, cx: &mut AcceptContext<'f, 'sess>) -> Option; } impl ExpectStringLiteral for NameValueParser { + #[track_caller] fn expect_string_literal<'f, 'sess>( &self, cx: &mut AcceptContext<'f, 'sess>, @@ -731,6 +750,7 @@ impl ExpectStringLiteral for NameValueParser { } impl ExpectStringLiteral for MetaItemOrLitParser { + #[track_caller] fn expect_string_literal<'f, 'sess>( &self, cx: &mut AcceptContext<'f, 'sess>, @@ -893,11 +913,12 @@ pub enum ShouldEmit { } impl ShouldEmit { + #[track_caller] pub(crate) fn emit_err(self, diag: Diag<'_>) -> ErrorGuaranteed { match self { - ShouldEmit::EarlyFatal { .. } if diag.level() == Level::DelayedBug => diag.emit(), - ShouldEmit::EarlyFatal { .. } => diag.upgrade_to_fatal().emit(), - ShouldEmit::ErrorsAndLints { .. } => diag.emit(), + ShouldEmit::EarlyFatal { .. } if diag.level() == Level::DelayedBug => diag.emit_err(), + ShouldEmit::EarlyFatal { .. } => diag.upgrade_to_fatal().emit_fatal(), + ShouldEmit::ErrorsAndLints { .. } => diag.emit_err(), ShouldEmit::Nothing => diag.delay_as_bug(), } } @@ -912,6 +933,7 @@ pub(crate) struct AttributeDiagnosticContext<'a, 'f, 'sess> { } impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { + #[track_caller] fn emit_parse_error( &mut self, mut span: Span, @@ -950,6 +972,7 @@ impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { /// Helpers that can be used to generate errors during attribute parsing. impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { + #[track_caller] pub(crate) fn expected_integer_literal_in_range( &mut self, span: Span, @@ -964,6 +987,7 @@ impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { /// The provided span is used as a fallback in case `args` does not contain any. It should be /// the span of the node that contains `args`. + #[track_caller] pub(crate) fn expected_list(&mut self, span: Span, args: &ArgParser) -> ErrorGuaranteed { let span = match args { ArgParser::NoArgs => span, @@ -973,6 +997,7 @@ impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedList) } + #[track_caller] pub(crate) fn expected_list_with_num_args_or_more( &mut self, args: usize, @@ -984,45 +1009,54 @@ impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { ) } + #[track_caller] pub(crate) fn expected_list_or_no_args(&mut self, span: Span) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedListOrNoArgs) } + #[track_caller] pub(crate) fn expected_nv_or_no_args(&mut self, span: Span) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNameValueOrNoArgs) } + #[track_caller] pub(crate) fn expected_non_empty_string_literal(&mut self, span: Span) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNonEmptyStringLiteral) } + #[track_caller] pub(crate) fn expected_no_args(&mut self, span: Span) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNoArgs) } /// Emit an error that a `name` was expected here + #[track_caller] pub(crate) fn expected_identifier(&mut self, span: Span) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedIdentifier) } /// Emit an error that a `name = value` pair was expected at this span. The symbol can be given for /// a nicer error message talking about the specific name that was found lacking a value. + #[track_caller] fn expected_name_value(&mut self, span: Span, name: Option) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNameValue(name)) } /// Emit an error that a `name = value` argument is missing in a list of name-value pairs. + #[track_caller] pub(crate) fn missing_name_value(&mut self, span: Span, name: Symbol) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::MissingNameValue(name)) } /// Emit an error that a `name = value` pair was found where that name was already seen. + #[track_caller] pub(crate) fn duplicate_key(&mut self, span: Span, key: Symbol) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::DuplicateKey(key)) } /// An error that should be emitted when a [`MetaItemOrLitParser`] /// was expected *not* to be a literal, but instead a meta item. + #[track_caller] pub(crate) fn expected_not_literal(&mut self, span: Span) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNotLiteral) } @@ -1031,6 +1065,7 @@ impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { /// The `provided_arguments` argument allows distinguishing between "expected an argument here" /// (when zero arguments are provided) and "expect a single argument here" (when two or more /// arguments are provided). + #[track_caller] pub(crate) fn expected_single_argument( &mut self, span: Span, @@ -1045,11 +1080,13 @@ impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { self.emit_parse_error(span, reason) } + #[track_caller] pub(crate) fn expected_at_least_one_argument(&mut self, span: Span) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedAtLeastOneArgument) } /// Produces an error along the lines of `expected one of [foo, meow]` + #[track_caller] pub(crate) fn expected_specific_argument( &mut self, span: Span, @@ -1067,6 +1104,7 @@ impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { /// Produces an error along the lines of `expected one of [foo, meow] as an argument`. /// i.e. slightly different wording to [`expected_specific_argument`](Self::expected_specific_argument). + #[track_caller] pub(crate) fn expected_specific_argument_and_list( &mut self, span: Span, @@ -1083,6 +1121,7 @@ impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { } /// produces an error along the lines of `expected one of ["foo", "meow"]` + #[track_caller] pub(crate) fn expected_specific_argument_strings( &mut self, span: Span, diff --git a/compiler/rustc_attr_parsing/src/diagnostics.rs b/compiler/rustc_attr_parsing/src/diagnostics.rs index e5f49690f71dc..7d0a7832f52cf 100644 --- a/compiler/rustc_attr_parsing/src/diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/diagnostics.rs @@ -12,6 +12,41 @@ use rustc_target::spec::TargetTuple; use crate::AttributeTemplate; use crate::context::Suggestion; +#[derive(Diagnostic)] +#[diag("#[rustc_legacy_const_generics] functions must only have const generics")] +pub(crate) struct RustcLegacyConstGenericsOnly { + #[primary_span] + pub attr_span: Span, + #[label("non-const generic parameter")] + pub param_span: Span, +} + +#[derive(Diagnostic)] +#[diag("#[rustc_legacy_const_generics] must have one index for each generic parameter")] +pub(crate) struct RustcLegacyConstGenericsIndex { + #[primary_span] + pub attr_span: Span, + #[label("generic parameters")] + pub generics_span: Span, +} + +#[derive(Diagnostic)] +#[diag("index exceeds number of arguments")] +pub(crate) struct RustcLegacyConstGenericsIndexExceed { + #[primary_span] + #[label( + "there {$arg_count -> + [one] is + *[other] are + } only {$arg_count} {$arg_count -> + [one] argument + *[other] arguments + }" + )] + pub span: Span, + pub arg_count: usize, +} + #[derive(Diagnostic)] #[diag("`{$name}` attribute cannot be used at crate level")] pub(crate) struct InvalidAttrAtCrateLevel { @@ -292,6 +327,26 @@ pub(crate) struct DocTestLiteral; )] pub(crate) struct AttrCrateLevelOnly; +#[derive(Diagnostic)] +#[diag("conflicting doc inlining attributes")] +#[help("remove one of the conflicting attributes")] +pub(crate) struct DocInlineConflict { + #[primary_span] + pub spans: MultiSpan, +} + +#[derive(Diagnostic)] +#[diag("this attribute can only be applied to a `use` item")] +#[note( + "read for more information" +)] +pub(crate) struct DocInlineOnlyUse { + #[label("only applicable on `use` items")] + pub attr_span: Span, + #[label("not a `use` item")] + pub item_span: Span, +} + #[derive(Diagnostic)] #[diag("`#[diagnostic::do_not_recommend]` does not expect any arguments")] pub(crate) struct DoNotRecommendDoesNotExpectArgs; @@ -896,6 +951,13 @@ pub(crate) struct InlineForceInlineConflict { pub inline_span: Span, } +#[derive(Diagnostic)] +#[diag("`#[inline]` is ignored on externally exported functions")] +#[help( + "externally exported functions are functions with `#[no_mangle]`, `#[export_name]`, or `#[linkage]`" +)] +pub(crate) struct InlineIgnoredForExported; + #[derive(Diagnostic)] #[diag("`#[ffi_const]` function cannot be `#[ffi_pure]`", code = E0757)] pub(crate) struct BothFfiConstAndPure { @@ -1228,7 +1290,7 @@ pub(crate) struct UnknownVersionLiteral { #[diag("multiple `{$name}` attributes")] pub(crate) struct UnusedMultiple { #[primary_span] - #[suggestion("remove this attribute", code = "", applicability = "machine-applicable")] + #[suggestion("remove this attribute", code = "", applicability = "unspecified")] pub this: Span, #[note("attribute also specified here")] pub other: Span, @@ -1476,9 +1538,9 @@ pub(crate) enum AttributeParseErrorSuggestions { } impl<'a> AttributeParseError<'a> { - fn render_expected_specific_argument( + fn render_expected_specific_argument( &self, - diag: &mut Diag<'_, G>, + diag: &mut Diag<'_>, possibilities: &[Symbol], strings: bool, ) { @@ -1509,9 +1571,9 @@ impl<'a> AttributeParseError<'a> { } } - fn render_expected_specific_argument_list( + fn render_expected_specific_argument_list( &self, - diag: &mut Diag<'_, G>, + diag: &mut Diag<'_>, possibilities: &[Symbol], strings: bool, ) { @@ -1543,7 +1605,7 @@ impl<'a> AttributeParseError<'a> { } } - fn render_suggestions(&self, diag: &mut Diag<'_, G>) { + fn render_suggestions(&self, diag: &mut Diag<'_>) { let description = self.description(); match &self.suggestions { @@ -1592,8 +1654,8 @@ impl AttributeParseErrorSuggestions { } } -impl<'a, G> Diagnostic<'a, G> for AttributeParseError<'_> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { +impl<'a> Diagnostic<'a> for AttributeParseError<'_> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let name = self.path.to_string(); let description = self.description(); @@ -1604,7 +1666,7 @@ impl<'a, G> Diagnostic<'a, G> for AttributeParseError<'_> { match &self.reason { AttributeParseErrorReason::ExpectedStringLiteral { byte_string } => { if let Some(start_point_span) = byte_string { - diag.span_suggestion( + diag.span_suggestion_short( *start_point_span, "consider removing the prefix", "", @@ -1825,6 +1887,13 @@ pub(crate) struct EmptyLinkName { pub span: Span, } +#[derive(Diagnostic)] +#[diag("attribute should be applied to an `extern` block with non-Rust ABI")] +#[warning( + "this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!" +)] +pub(crate) struct Link; + #[derive(Diagnostic)] #[diag("link kind `framework` is only supported on Apple targets", code = E0455)] pub(crate) struct LinkFrameworkApple { @@ -2047,7 +2116,7 @@ pub(crate) struct AdditionalCommaSuggestion { #[derive(Diagnostic)] #[diag("unused attribute")] pub(crate) struct UnusedDuplicate { - #[suggestion("remove this attribute", code = "", applicability = "machine-applicable")] + #[suggestion("remove this attribute", code = "", applicability = "unspecified")] pub this: Span, #[note("attribute also specified here")] pub other: Span, @@ -2056,3 +2125,10 @@ pub(crate) struct UnusedDuplicate { )] pub warning: bool, } + +#[derive(Diagnostic)] +pub(crate) enum MacroExport { + #[diag("`#[macro_export]` has no effect on declarative macro definitions")] + #[note("declarative macros follow the same exporting rules as regular items")] + OnDeclMacro, +} diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index 1cecce8fd43ef..4ce284a0a9d5a 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -27,7 +27,7 @@ use crate::{AttributeTemplate, ShouldEmit}; pub struct EmitAttribute( pub Box< - dyn for<'a> FnOnce(DiagCtxtHandle<'a>, Level, &Session) -> Diag<'a, ()> + dyn for<'a> FnOnce(DiagCtxtHandle<'a>, Level, &Session) -> Diag<'a> + DynSend + DynSync + 'static, @@ -304,6 +304,7 @@ impl<'sess> AttributeParser<'sess> { self.sess().dcx() } + #[track_caller] pub(crate) fn emit_err(&self, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed { self.should_emit.emit_err(self.sess.dcx().create_err(diag)) } @@ -510,23 +511,25 @@ impl<'sess> AttributeParser<'sess> { // Now that all attributes have been parsed, run the deferred checks. These can // inspect the fully parsed attributes via `FinalizeCheckContext::parsed_attrs`. - for (check, attr_span) in deferred_checks { - check( - &FinalizeCheckContext { - shared: SharedContext { - cx: self, - target_span, - target, - emit_lint: &mut emit_lint, - #[cfg(debug_assertions)] - has_lint_been_emitted: AtomicBool::new(false), + if !matches!(self.should_emit, ShouldEmit::Nothing) { + for (check, attr_span) in deferred_checks { + check( + &mut FinalizeCheckContext { + shared: SharedContext { + cx: self, + target_span, + target, + emit_lint: &mut emit_lint, + #[cfg(debug_assertions)] + has_lint_been_emitted: AtomicBool::new(false), + }, + all_attrs: &attr_paths, + parsed_attrs: &attributes, + target_item, }, - all_attrs: &attr_paths, - parsed_attrs: &attributes, - target_item, - }, - attr_span, - ); + attr_span, + ); + } } if !matches!(self.should_emit, ShouldEmit::Nothing) && target == Target::WherePredicate { diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index 8efe5bf4f1f90..c85b1f8fee967 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -434,7 +434,7 @@ fn expr_to_lit<'sess>( let lit = MetaItemLit { symbol: token_lit.symbol, suffix: token_lit.suffix, - kind: LitKind::Err(err.emit()), + kind: LitKind::Err(err.emit_err()), span: expr.span, }; Ok(Some(lit)) diff --git a/compiler/rustc_borrowck/Cargo.toml b/compiler/rustc_borrowck/Cargo.toml index 647a47def1f52..e961cf61b6478 100644 --- a/compiler/rustc_borrowck/Cargo.toml +++ b/compiler/rustc_borrowck/Cargo.toml @@ -9,6 +9,7 @@ either = "1.5.0" itertools = "0.15" polonius-engine = "0.13.0" rustc_abi = { path = "../rustc_abi" } +rustc_attr_ir = { path = "../rustc_attr_ir" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_graphviz = { path = "../rustc_graphviz" } diff --git a/compiler/rustc_borrowck/src/consumers.rs b/compiler/rustc_borrowck/src/consumers.rs index 4039c2d88c6be..f6d0599773e94 100644 --- a/compiler/rustc_borrowck/src/consumers.rs +++ b/compiler/rustc_borrowck/src/consumers.rs @@ -16,7 +16,7 @@ pub use super::polonius::legacy::{ PoloniusFacts as PoloniusInput, PoloniusLocationTable, PoloniusOutput, PoloniusRegionVid, RichLocation, RustcFacts, }; -pub use super::region_infer::RegionInferenceContext; +use super::region_infer::RegionInferenceContext; use crate::BorrowCheckRootCtxt; /// Struct used during mir borrowck to collect bodies with facts for a typeck root and all diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index f4b2ceb850666..2f8ff159ceb0a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -5,15 +5,16 @@ use std::ops::ControlFlow; use either::Either; use hir::{ClosureKind, Path}; +use rustc_attr_ir::diagnostic::{CustomDiagnostic, FormatArgs}; +use rustc_attr_ir::find_attr; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, MultiSpan, struct_span_code_err}; use rustc_hir as hir; -use rustc_hir::attrs::diagnostic::{CustomDiagnostic, FormatArgs}; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr}; -use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, PatField, find_attr}; +use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, PatField}; use rustc_index::bit_set::DenseBitSet; use rustc_infer::traits::TraitErrors; use rustc_middle::hir::nested_filter::OnlyBodies; @@ -893,7 +894,7 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { .errors .iter() .map(|error| error.span) - .any(|sp| span < sp && !sp.contains(span)) + .any(|sp| span.lo_hi() < sp.lo_hi() && !sp.contains(span)) }) { show_assign_sugg = true; if all_init_spans.iter().any(|init_span| !init_span.contains(span)) @@ -929,7 +930,7 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { let mut shown = false; let mut shown_condition_value = false; for error in visitor.errors { - if error.span < span && !error.span.overlaps(span) { + if error.span.lo_hi() < span.lo_hi() && !error.span.overlaps(span) { // When we have a case like `match-cfg-fake-edges.rs`, we don't want to mention // match arms coming after the primary span because they aren't relevant: // ``` @@ -950,7 +951,7 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { } if !shown { for sp in &reachable_spans { - if *sp < span && !sp.overlaps(span) { + if sp.lo_hi() < span.lo_hi() && !sp.overlaps(span) { err.span_label(*sp, "binding initialized here in some conditions"); } } @@ -2544,7 +2545,7 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { if let hir::ExprKind::MethodCall(body_call, recv, ..) = ex.kind && body_call.ident.name == sym::next - && recv.span.source_equal(self.expr_span) + && recv.span.lo_hi() == self.expr_span.lo_hi() { self.body_expr = Some(ex); } @@ -4599,7 +4600,7 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { // `return_region`. Then use the `rustc_hir` type to get only // the lifetime span. match &fn_decl.inputs[index].kind { - hir::TyKind::Ref(lifetime, _) => { + hir::TyKind::Ref(lifetime, ..) => { // With access to the lifetime, we can get // the span of it. arguments.push((*argument, lifetime.ident.span)); @@ -4614,7 +4615,7 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { .hir_node_by_def_id(alias_to) .expect_item() .expect_impl() - && let hir::TyKind::Ref(lifetime, _) = self_ty.kind + && let hir::TyKind::Ref(lifetime, ..) = self_ty.kind { arguments.push((*argument, lifetime.ident.span)); } @@ -4636,7 +4637,7 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { let return_ty = sig.output().skip_binder(); let mut return_span = fn_decl.output.span(); if let hir::FnRetTy::Return(ty) = &fn_decl.output - && let hir::TyKind::Ref(lifetime, _) = ty.kind + && let hir::TyKind::Ref(lifetime, ..) = ty.kind { return_span = lifetime.ident.span; } diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 429eb85ed9e11..2bc361968a761 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -55,10 +55,10 @@ impl<'tcx> BorrowExplanation<'tcx> { pub(crate) fn is_explained(&self) -> bool { !matches!(self, BorrowExplanation::Unexplained) } - pub(crate) fn add_explanation_to_diagnostic( + pub(crate) fn add_explanation_to_diagnostic( &self, cx: &MirBorrowckCtxt<'_, '_, 'tcx>, - err: &mut Diag<'_, G>, + err: &mut Diag<'_>, borrow_desc: &str, borrow_span: Option, multiple_borrow_span: Option<(Span, Span)>, @@ -415,12 +415,12 @@ impl<'tcx> BorrowExplanation<'tcx> { let mut preds = path .iter() .filter_map(|constraint| match constraint.category { - ConstraintCategory::Predicate(pred) if !pred.is_dummy() => Some(pred), + ConstraintCategory::Predicate(pred, _) if !pred.is_dummy() => Some(pred), _ => None, }) .collect::>(); - preds.sort(); - preds.dedup(); + preds.sort_by_key(|span| span.lo_hi()); + preds.dedup_by_key(|span| span.lo_hi()); if !preds.is_empty() { let s = if preds.len() == 1 { "" } else { "s" }; err.span_note( @@ -437,10 +437,10 @@ impl<'tcx> BorrowExplanation<'tcx> { } } - fn add_object_lifetime_default_note( + fn add_object_lifetime_default_note( &self, tcx: TyCtxt<'tcx>, - err: &mut Diag<'_, G>, + err: &mut Diag<'_>, unsize_ty: Ty<'tcx>, ) { if let ty::Adt(def, args) = unsize_ty.kind() { @@ -494,9 +494,9 @@ impl<'tcx> BorrowExplanation<'tcx> { } } - fn add_lifetime_bound_suggestion_to_diagnostic( + fn add_lifetime_bound_suggestion_to_diagnostic( &self, - err: &mut Diag<'_, G>, + err: &mut Diag<'_>, category: &ConstraintCategory<'tcx>, span: Span, region_name: &RegionName, @@ -523,14 +523,14 @@ impl<'tcx> BorrowExplanation<'tcx> { } } -fn suggest_rewrite_if_let( +fn suggest_rewrite_if_let( tcx: TyCtxt<'_>, expr: &hir::Expr<'_>, pat: &str, init: &hir::Expr<'_>, conseq: &hir::Expr<'_>, alt: Option<&hir::Expr<'_>>, - err: &mut Diag<'_, G>, + err: &mut Diag<'_>, ) { let source_map = tcx.sess.source_map(); err.span_note( diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 5ebf950358c4f..cb60a97eea053 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -3,10 +3,10 @@ use std::collections::BTreeMap; use rustc_abi::{FieldIdx, VariantIdx}; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::formatting::DiagMessageAddArg; use rustc_errors::{Applicability, Diag, DiagMessage, MultiSpan, listify, msg}; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def::{CtorKind, Namespace}; use rustc_hir::{ self as hir, CoroutineKind, GenericBound, WhereBoundPredicate, WherePredicateKind, @@ -94,13 +94,19 @@ pub(crate) struct BorrowckDiagnosticsBuffer<'diag, 'tcx> { buffered_mut_errors: FxIndexMap, usize)>, - /// Buffer of diagnostics to be reported. - buffered_diags: Vec>, + /// Buffer of diagnostics to be reported. Each one is paired with a span for sorting purposes; + /// by default it's the primary span. + buffered_diags: Vec<(Span, Diag<'diag>)>, } impl<'diag, 'tcx> BorrowckDiagnosticsBuffer<'diag, 'tcx> { pub(crate) fn buffer_error(&mut self, diag: Diag<'diag>) { - self.buffered_diags.push(diag); + let sort_span = diag.span.primary_span().unwrap_or(DUMMY_SP); + self.buffered_diags.push((sort_span, diag)); + } + + pub(crate) fn buffer_error_with_sort_span(&mut self, diag: Diag<'diag>, sort_span: Span) { + self.buffered_diags.push((sort_span, diag)); } pub(crate) fn emit_errors(&mut self) { @@ -117,8 +123,8 @@ impl<'diag, 'tcx> BorrowckDiagnosticsBuffer<'diag, 'tcx> { } if !self.buffered_diags.is_empty() { - self.buffered_diags.sort_by_key(|diag| diag.sort_span); - for diag in self.buffered_diags.drain(..) { + self.buffered_diags.sort_by_key(|(sort_span, _)| sort_span.lo_hi()); + for (_, diag) in self.buffered_diags.drain(..) { diag.emit(); } } @@ -130,6 +136,10 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { self.diags_buffer.buffer_error(diag.with_dcx(self.dcx())); } + pub(crate) fn buffer_error_with_sort_span(&mut self, diag: Diag<'_>, sort_span: Span) { + self.diags_buffer.buffer_error_with_sort_span(diag.with_dcx(self.dcx()), sort_span); + } + pub(crate) fn buffer_move_error( &mut self, move_out_indices: Vec, @@ -642,9 +652,9 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { /// /// This is very similar to `fn suggest_static_lifetime_for_gat_from_hrtb` which handles this /// note for failed type tests instead of outlives errors. - fn add_placeholder_from_predicate_note( + fn add_placeholder_from_predicate_note( &self, - diag: &mut Diag<'_, G>, + diag: &mut Diag<'_>, path: &[OutlivesConstraint<'tcx>], ) { let tcx = self.infcx.tcx; @@ -704,9 +714,9 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { /// Add a label to region errors and borrow explanations when outlives constraints arise from /// proving a type implements `Sized` or `Copy`. - fn add_sized_or_copy_bound_info( + fn add_sized_or_copy_bound_info( &self, - err: &mut Diag<'_, G>, + err: &mut Diag<'_>, blamed_category: ConstraintCategory<'tcx>, path: &[OutlivesConstraint<'tcx>], ) { diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index aa62d6aaa0751..4d65410455118 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -1206,8 +1206,8 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { suggestions.push((binding_span.shrink_to_lo(), msg, "ref ".to_string())); } } - suggestions.sort_unstable_by_key(|&(span, _, _)| span); - suggestions.dedup_by_key(|&mut (span, _, _)| span); + suggestions.sort_unstable_by_key(|&(span, _, _)| span.lo_hi()); + suggestions.dedup_by_key(|&mut (span, _, _)| span.lo_hi()); for (span, msg, suggestion) in suggestions { err.span_suggestion_verbose(span, msg, suggestion, Applicability::MachineApplicable); } diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 7a614db25d668..1182cdb5c2c6e 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -3,8 +3,8 @@ use core::ops::ControlFlow; use either::Either; use hir::{ExprKind, Param}; use rustc_abi::FieldIdx; +use rustc_attr_ir::lang_items::LangItem; use rustc_errors::{Applicability, Diag}; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::{self as hir, BindingMode, ByRef, Expr, Node}; @@ -927,8 +927,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { if let Node::TraitItem(ti) = self.infcx.tcx.hir_node_by_def_id(f_in_trait) && let hir::TraitItemKind::Fn(sig, _) = ti.kind && let Some(ty) = sig.decl.inputs.get(local.index() - 1) - && let hir::TyKind::Ref(_, mut_ty) = ty.kind - && let hir::Mutability::Not = mut_ty.mutbl + && let hir::TyKind::Ref(_, _, hir::Mutability::Not) = ty.kind && sig.decl.implicit_self().has_implicit_self() { Some(ty.span) @@ -2020,7 +2019,7 @@ fn get_mut_span_in_struct_field<'tcx>( // Now we're dealing with the actual struct that we're going to suggest a change to, // we can expect a field that is an immutable reference to a type. && let hir::Node::Field(field) = tcx.hir_node_by_def_id(field.did.as_local()?) - && let hir::TyKind::Ref(lt, hir::MutTy { mutbl: hir::Mutability::Not, ty }) = field.ty.kind + && let hir::TyKind::Ref(lt, ty, hir::Mutability::Not) = field.ty.kind { return Some(lt.ident.span.between(ty.span)); } diff --git a/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs b/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs index 009dff77efd0f..9038b71ade222 100644 --- a/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs +++ b/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs @@ -17,7 +17,7 @@ use rustc_trait_selection::error_reporting::infer::region::unexpected_hidden_reg use crate::MirBorrowckCtxt; use crate::borrow_set::BorrowData; -use crate::consumers::RegionInferenceContext; +use crate::region_infer::RegionInferenceContext; use crate::region_infer::opaque_types::DeferredOpaqueTypeError; use crate::type_check::Locations; @@ -61,7 +61,7 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { { last_unexpected_hidden_region = Some((hidden_type.span, named_ty, named_key)); - diag.emit() + diag.emit_err() } else { diag.delay_as_bug() } diff --git a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs index 75c666f49a2f7..d7ad34048e5bf 100644 --- a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs +++ b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs @@ -220,9 +220,8 @@ impl OutlivesSuggestionBuilder { // We want this message to appear after other messages on the mir def. let mir_span = mbcx.body.span; - diag.sort_span = mir_span.shrink_to_hi(); // Buffer the diagnostic - mbcx.buffer_error(diag); + mbcx.buffer_error_with_sort_span(diag, mir_span.shrink_to_hi()); } } diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 93d888d17dc51..c101191102979 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -1,11 +1,12 @@ //! Error reporting machinery for lifetime errors. -use rustc_data_structures::fx::FxIndexSet; +use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, msg}; use rustc_hir as hir; use rustc_hir::GenericBound::Trait; use rustc_hir::QPath::Resolved; use rustc_hir::WherePredicateKind::BoundPredicate; +use rustc_hir::def::DefKind; use rustc_hir::def::Res::Def; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; @@ -27,9 +28,9 @@ use rustc_trait_selection::traits::{Obligation, ObligationCtxt}; use tracing::{debug, instrument, trace}; use super::{LIMITATION_NOTE, OutlivesSuggestionBuilder, RegionName, RegionNameSource}; -use crate::consumers::{OutlivesConstraint, RegionInferenceContext}; +use crate::consumers::OutlivesConstraint; use crate::nll::ConstraintDescription; -use crate::region_infer::TypeTest; +use crate::region_infer::{RegionInferenceContext, TypeTest}; use crate::session_diagnostics::{ FnMutError, FnMutReturnTypeErr, GenericDoesNotLiveLongEnough, LifetimeOutliveErr, LifetimeReturnCategoryErr, RequireStaticErr, VarHereDenote, @@ -57,7 +58,7 @@ impl<'tcx> ConstraintDescription for ConstraintCategory<'tcx> { ConstraintCategory::ClosureUpvar(_) => "closure capture ", ConstraintCategory::Usage => "this usage ", ConstraintCategory::SolverRegionConstraint(_) - | ConstraintCategory::Predicate(_) + | ConstraintCategory::Predicate(_, _) | ConstraintCategory::Boring | ConstraintCategory::BoringNoLocation | ConstraintCategory::Internal @@ -391,6 +392,165 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { outlives_suggestion.add_suggestion(self); } + /// Point at `'static` obligations from the item being called. + /// + /// ```text + /// error[E0521]: borrowed data escapes outside of function + /// --> $DIR/static-impl-obligation.rs:163:9 + /// | + /// LL | fn bar<'a>(x: &'a &'a u32) { + /// | -- - `x` is only valid in the function body + /// | | + /// | lifetime `'a` defined here + /// LL | let y: &dyn Foo = x; + /// LL | y.hello(); + /// | ^^^^^^^^^ + /// | | + /// | `x` escapes the function body here + /// | argument requires that `'a` must outlive `'static` + /// | + /// note: `'static` lifetime requirement from `<(dyn o::Foo + 'static)>::hello` introduced here + /// --> $DIR/static-impl-obligation.rs:158:20 + /// | + /// LL | impl dyn Foo + 'static where Self: 'static { + /// | ^^^^^^^ ^^^^^^^ lifetime requirement introduced here + /// | | + /// | lifetime requirement introduced here + /// LL | fn hello(&'static self) where Self: 'static {} + /// | ^^^^^^^^^^^^^ ^^^^^^^ lifetime requirement introduced here + /// | | + /// | lifetime requirement introduced here + /// ``` + fn explain_impl_static_obligation( + &self, + diag: &mut Diag<'_>, + ty: Ty<'tcx>, + outlived_fr: RegionVid, + ) { + let tcx = self.infcx.tcx; + if self.regioncx.to_error_region(outlived_fr) != Some(tcx.lifetimes.re_static) { + return; + } + let ty::FnDef(def_id, args) = ty.kind() else { + return; + }; + let typing_env = self.infcx.typing_env(self.infcx.param_env); + let Ok(Some(instance)) = ty::Instance::try_resolve( + tcx, + typing_env, + *def_id, + self.infcx.deeply_resolve_ignoring_regions(args.no_bound_vars().unwrap()), + ) else { + return; + }; + let def_id = instance.def_id(); + let mut bounds = + tcx.clauses_of(def_id) + .instantiate(tcx, instance.args) + .into_iter() + .map(|(c, sp)| (c.skip_norm_wip().as_predicate(), sp)) + .filter(|(pred, _)| match pred.kind().skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives( + ty::OutlivesClause(_, lt), + )) + | ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives( + ty::OutlivesClause(_, lt), + )) if lt.is_static() => true, + _ => false, + }) + .map(|(_, sp)| sp) + .collect::>(); + + let mut labels = FxHashMap::default(); + + let parent = tcx.parent(def_id); + if let Some(rcvr) = + tcx.fn_sig(def_id).instantiate_identity().skip_norm_wip().inputs().skip_binder().get(0) + { + // Look at the receiver for `&'static self`, which introduces a `'static` obligation. + // ``` + // impl Foo { + // fn foo(&'static self) {} + // ^^^^^^^^^^^^^ + // ``` + if let ty::Ref(region, _, _) = rcvr.kind() + && *region == tcx.lifetimes.re_static + && let Some(assoc) = tcx.opt_associated_item(def_id) + && assoc.is_method() + { + let def_span = tcx.def_span(def_id); + // We have a `&'static self` receiver. + if let Some(def_id) = def_id.as_local() + && let owner = tcx.expect_hir_owner_node(def_id) + && let Some(decl) = owner.fn_decl() + && let Some(ty) = decl.inputs.get(0) + { + // Point at the `&'static self` receiver. + bounds.push(ty.span); + } else if !bounds.iter().any(|sp| sp.overlaps(def_span)) { + // The method is not defined on the local crate, point at the signature instead of + // just the receiver as an approximation. We don't add it if there are already + // other spans with overlap with the def `Span`, as the other will be more specific. + bounds.push(def_span) + } + } + + if let DefKind::Impl { .. } = tcx.def_kind(parent) + && let ty = tcx.type_of(parent).instantiate_identity().skip_norm_wip() + && let ty::Dynamic(_, region) = ty.kind() + && *region == tcx.lifetimes.re_static + { + // We have a call into a method of either `impl dyn Trait {}` or + // `impl dyn Trait + 'static {}`. + if let Some(def_id) = parent.as_local() + && let hir::OwnerNode::Item(item) = tcx.expect_hir_owner_node(def_id) + && let hir::ItemKind::Impl(impl_) = item.kind + && let hir::TyKind::TraitObject(_, tagged_ref) = impl_.self_ty.kind + { + if tagged_ref.is_static() { + // impl dyn Trait + 'static { + // ^^^^^^^ lifetime requirement introduced here + bounds.push(tagged_ref.pointer().ident.span); + } else if tagged_ref.is_implicit() { + // impl dyn Trait { + // ^^^^^^^^^ `dyn Trait` introduces an... + bounds.push(impl_.self_ty.span); + labels.insert( + impl_.self_ty.span, + "`dyn Trait` introduces an implicit `'static` lifetime requirement", + ); + } + } else { + // Non-local `impl`, we don't have a way to differentiate between `+ 'static` + // and bare `dyn Trait`. We point at the whole def `Span` for now. + let def_span = tcx.def_span(parent); + if !bounds.iter().any(|sp| sp.overlaps(def_span)) { + bounds.push(def_span); + } + } + } + } + + if !bounds.is_empty() { + let mut multispan: MultiSpan = bounds.clone().into(); + for span in bounds { + let label = labels.get(&span).unwrap_or(&"lifetime requirement introduced here"); + multispan.push_span_label(span, *label); + } + multispan.push_span_context(tcx.def_span(def_id).shrink_to_lo()); + if let DefKind::Impl { .. } | DefKind::Trait = tcx.def_kind(parent) { + multispan.push_span_context(tcx.def_span(parent).shrink_to_lo()); + } + diag.span_note( + multispan, + format!( + "`'static` lifetime requirement from `{}` introduced here", + tcx.def_path_str(def_id) + ), + ); + } + } + /// Report that `longer_fr: error_vid`, which doesn't hold, /// where `longer_fr` is a placeholder. fn report_erroneous_rvid_reaches_placeholder( @@ -503,6 +663,10 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { } }; + if let ConstraintCategory::CallArgument(Some(ty)) = category { + self.explain_impl_static_obligation(&mut diag, ty, outlived_fr); + } + match variance_info { ty::VarianceDiagInfo::None => {} ty::VarianceDiagInfo::Invariant { ty, param_index } => { @@ -662,7 +826,7 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { /// --> $DIR/lifetime-bound-will-change-warning.rs:44:5 /// | /// LL | fn test2<'a>(x: &'a Box) { - /// | - `x` is a reference that is only valid in the function body + /// | - `x` is only valid in the function body /// LL | // but ref_obj will not, so warn. /// LL | ref_obj(x) /// | ^^^^^^^^^^ `x` escapes the function body here @@ -714,9 +878,7 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { if let Some((Some(fr_name), fr_span)) = fr_name_and_span { diag.span_label( fr_span, - format!( - "`{fr_name}` is a reference that is only valid in the {escapes_from} body", - ), + format!("`{fr_name}` is only valid in the {escapes_from} body"), ); diag.span_label(*span, format!("`{fr_name}` escapes the {escapes_from} body here")); diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 0e98e0c0bb604..871b974d28eea 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -104,7 +104,7 @@ impl RegionName { } } - pub(crate) fn highlight_region_name(&self, diag: &mut Diag<'_, G>) { + pub(crate) fn highlight_region_name(&self, diag: &mut Diag<'_>) { match &self.source { RegionNameSource::NamedLateParamRegion(span) | RegionNameSource::NamedEarlyParamRegion(span) => { @@ -621,7 +621,10 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { // // & // - let's call the lifetime of this reference `'1` - (ty::Ref(region, referent_ty, _), hir::TyKind::Ref(_lifetime, referent_hir_ty)) => { + ( + ty::Ref(region, referent_ty, _), + hir::TyKind::Ref(_lifetime, referent_hir_ty, _), + ) => { if region.as_var() == needle_fr { // Just grab the first character, the `&`. let source_map = self.infcx.tcx.sess.source_map(); @@ -631,7 +634,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { } // Otherwise, let's descend into the referent types. - search_stack.push((*referent_ty, referent_hir_ty.ty)); + search_stack.push((*referent_ty, referent_hir_ty)); } // Match up something like `Foo<'1>` @@ -669,8 +672,8 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { search_stack.push((*elem_ty, elem_hir_ty)); } - (ty::RawPtr(mut_ty, _), hir::TyKind::Ptr(mut_hir_ty)) => { - search_stack.push((*mut_ty, mut_hir_ty.ty)); + (ty::RawPtr(inner_ty, _), hir::TyKind::Ptr(hir_ty, _)) => { + search_stack.push((*inner_ty, hir_ty)); } _ => { diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 546d91187e05e..c2233fa495a47 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -2,7 +2,6 @@ // tidy-alphabetical-start #![allow(internal_features)] -#![cfg_attr(bootstrap, feature(never_type))] #![feature(default_field_values)] #![feature(deref_patterns)] #![feature(file_buffered)] @@ -232,7 +231,7 @@ pub struct ClosureOutlivesRequirement<'tcx> { // Make sure this enum doesn't unintentionally grow #[cfg(target_pointer_width = "64")] -rustc_data_structures::static_assert_size!(ConstraintCategory<'_>, 16); +rustc_data_structures::static_assert_size!(ConstraintCategory<'_>, 24); /// The subject of a `ClosureOutlivesRequirement` -- that is, the thing /// that must outlive some region. @@ -310,7 +309,7 @@ struct CollectRegionConstraintsResult<'tcx> { deferred_closure_requirements: DeferredClosureRequirements<'tcx>, deferred_opaque_type_errors: Vec>, polonius_facts: Option>, - polonius_context: Option, + polonius_context: Option>, } /// Start borrow checking by collecting the region constraints for @@ -347,7 +346,7 @@ fn borrowck_collect_region_constraints<'tcx>( let location_map = Rc::new(DenseLocationMap::new(body)); - let polonius_input = root_cx.consumer.as_ref().map_or(false, |c| c.polonius_input()) + let polonius_input = root_cx.consumer.as_ref().is_some_and(|c| c.polonius_input()) || infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled(); let mut polonius_facts = (polonius_input || PoloniusFacts::enabled(infcx.tcx)).then_some(PoloniusFacts::default()); @@ -361,7 +360,6 @@ fn borrowck_collect_region_constraints<'tcx>( deferred_closure_requirements, polonius_context, } = type_check::type_check( - root_cx, &infcx, body, &promoted, @@ -798,7 +796,7 @@ pub(crate) struct MirBorrowckCtxt<'a, 'diag, 'tcx> { /// Results of Polonius analysis. polonius_output: Option<&'a PoloniusOutput>, /// When using `-Zpolonius=next`: the data used to compute errors and diagnostics. - polonius_context: Option<&'a PoloniusContext>, + polonius_context: Option<&'a PoloniusContext<'tcx>>, } // Check that: diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index 1a328f62fc73e..62319ce5ebe3b 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -6,8 +6,8 @@ use std::rc::Rc; use std::str::FromStr; use polonius_engine::{Algorithm, AllFacts, Output}; +use rustc_attr_ir::find_attr; use rustc_data_structures::frozen::Frozen; -use rustc_hir::find_attr; use rustc_index::IndexSlice; use rustc_middle::mir::pretty::PrettyPrintMirOptions; use rustc_middle::mir::{Body, MirDumper, PassWhere, Promoted}; @@ -46,7 +46,7 @@ pub(crate) struct NllOutput<'tcx> { /// When using `-Zpolonius=next`: the data used to compute errors and diagnostics, e.g. /// localized typeck and liveness constraints. - pub polonius_context: Option, + pub polonius_context: Option>, } /// Rewrites the regions in the MIR to use NLL variables, also scraping out the set of universal @@ -96,14 +96,15 @@ pub(crate) fn compute_closure_requirements_modulo_opaques<'tcx>( &universal_region_relations, infcx, ); - let mut regioncx = RegionInferenceContext::new( + let (_, closure_region_requirements, _nll_errors) = RegionInferenceContext::solve( &infcx, lowered_constraints, universal_region_relations.clone(), location_map, + body, + None, ); - let (closure_region_requirements, _nll_errors) = regioncx.solve(infcx, body, None); closure_region_requirements } @@ -121,7 +122,7 @@ pub(crate) fn compute_regions<'tcx>( universal_region_relations: Frozen>, constraints: MirTypeckRegionConstraints<'tcx>, mut polonius_facts: Option>, - mut polonius_context: Option, + mut polonius_context: Option>, ) -> NllOutput<'tcx> { let polonius_output = root_cx.consumer.as_ref().map_or(false, |c| c.polonius_output()) || infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled(); @@ -144,30 +145,23 @@ pub(crate) fn compute_regions<'tcx>( &lowered_constraints, ); - let num_points = location_map.num_points(); - // If requested for `-Zpolonius=next`, compute loan liveness information. // This is done prior to `RegionInferenceContext::new`, because we may add // additional liveness constraints. if let Some(polonius_context) = polonius_context.as_mut() { let _timer = infcx.tcx.prof.generic_activity("borrowck_polonius_loan_liveness"); polonius_context.compute_loan_liveness( + infcx, &mut lowered_constraints.liveness_constraints, lowered_constraints.outlives_constraints.outlives().iter().copied(), &universal_region_relations.universal_regions, body, + move_data, + &location_map, borrow_set, - num_points, ); } - let mut regioncx = RegionInferenceContext::new( - infcx, - lowered_constraints, - universal_region_relations, - location_map, - ); - // If requested: dump NLL facts, and run legacy polonius analysis. let polonius_output = polonius_facts.as_ref().and_then(|polonius_facts| { if infcx.tcx.sess.opts.unstable_opts.nll_facts { @@ -190,8 +184,14 @@ pub(crate) fn compute_regions<'tcx>( }); // Solve the region constraints. - let (closure_region_requirements, nll_errors) = - regioncx.solve(infcx, body, polonius_output.clone()); + let (regioncx, closure_region_requirements, nll_errors) = RegionInferenceContext::solve( + infcx, + lowered_constraints, + universal_region_relations, + location_map, + body, + polonius_output.clone(), + ); NllOutput { regioncx, diff --git a/compiler/rustc_borrowck/src/polonius/constraints.rs b/compiler/rustc_borrowck/src/polonius/constraints.rs index 637068a9799d7..d445d2e8ef96d 100644 --- a/compiler/rustc_borrowck/src/polonius/constraints.rs +++ b/compiler/rustc_borrowck/src/polonius/constraints.rs @@ -1,17 +1,16 @@ -use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; -use rustc_index::interval::SparseIntervalMatrix; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_index::IndexVec; use rustc_middle::mir::{Body, Location}; use rustc_middle::ty::RegionVid; -use rustc_mir_dataflow::points::PointIndex; -use tracing::debug; +use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex}; +use smallvec::SmallVec; use crate::BorrowSet; use crate::constraints::OutlivesConstraint; use crate::dataflow::BorrowIndex; -use crate::polonius::{ConstraintDirection, LiveRegionVariances}; -use crate::region_infer::values::LivenessValues; +use crate::polonius::ConstraintDirection; +use crate::polonius::liveness::LivenessSource; use crate::type_check::Locations; -use crate::universal_regions::UniversalRegions; /// A localized outlives constraint reifies the CFG location where the outlives constraint holds, /// within the origins themselves as if they were different from point to point: from `a: b` @@ -42,19 +41,19 @@ pub(super) struct LocalizedNode { pub(super) struct LocalizedConstraintGraph { /// The actual, physical, edges we have recorded for a given node. We localize them on-demand /// when traversing from the node to the successor region. - edges: FxHashMap>, + edges: FxHashMap>, /// The logical edges representing the outlives constraints that hold at all points in the CFG, /// which we don't localize to avoid creating a lot of unnecessary edges in the graph. Some CFGs /// can be big, and we don't need to create such a physical edge for every point in the CFG. - logical_edges: FxHashMap>, + logical_edges: IndexVec>, } /// The visitor interface when traversing a `LocalizedConstraintGraph`. pub(super) trait LocalizedConstraintGraphVisitor { /// Callback called when traversing a given `loan` encounters a localized `node` it hasn't - /// visited before. - fn on_node_traversed(&mut self, _loan: BorrowIndex, _node: LocalizedNode) {} + /// visited before, and whether its region is live at that point. + fn on_node_traversed(&mut self, _loan: BorrowIndex, _node: LocalizedNode, _is_live: bool) {} /// Callback called when discovering a new `successor` node for the `current_node`. fn on_successor_discovered(&mut self, _current_node: LocalizedNode, _successor: LocalizedNode) { @@ -64,27 +63,31 @@ pub(super) trait LocalizedConstraintGraphVisitor { impl LocalizedConstraintGraph { /// Traverses the constraints and returns the indexed graph of edges per node. pub(super) fn new<'tcx>( - liveness: &LivenessValues, + location_map: &DenseLocationMap, outlives_constraints: impl Iterator>, ) -> Self { - let mut edges: FxHashMap<_, FxIndexSet<_>> = FxHashMap::default(); - let mut logical_edges: FxHashMap<_, FxIndexSet<_>> = FxHashMap::default(); + let mut edges: FxHashMap<_, SmallVec<[RegionVid; 4]>> = FxHashMap::default(); + let mut logical_edges: IndexVec<_, SmallVec<[RegionVid; 4]>> = IndexVec::new(); for outlives_constraint in outlives_constraints { match outlives_constraint.locations { Locations::All(_) => { - logical_edges - .entry(outlives_constraint.sup) - .or_default() - .insert(outlives_constraint.sub); + let succs = + logical_edges.ensure_contains_elem(outlives_constraint.sup, SmallVec::new); + if !succs.contains(&outlives_constraint.sub) { + succs.push(outlives_constraint.sub); + } } Locations::Single(location) => { let node = LocalizedNode { region: outlives_constraint.sup, - point: liveness.point_from_location(location), + point: location_map.point_from_location(location), }; - edges.entry(node).or_default().insert(outlives_constraint.sub); + let succs = edges.entry(node).or_default(); + if !succs.contains(&outlives_constraint.sub) { + succs.push(outlives_constraint.sub); + } } } } @@ -97,14 +100,11 @@ impl LocalizedConstraintGraph { pub(super) fn traverse<'tcx>( &self, body: &Body<'tcx>, - liveness: &LivenessValues, - live_region_variances: &LiveRegionVariances, - universal_regions: &UniversalRegions<'tcx>, borrow_set: &BorrowSet<'tcx>, + location_map: &DenseLocationMap, + liveness_source: &mut impl LivenessSource, visitor: &mut impl LocalizedConstraintGraphVisitor, ) { - let live_regions = liveness.points(); - let mut visited = FxHashSet::default(); let mut stack = Vec::new(); @@ -116,24 +116,22 @@ impl LocalizedConstraintGraph { let start_node = LocalizedNode { region: loan.region, - point: liveness.point_from_location(loan.reserve_location), + point: location_map.point_from_location(loan.reserve_location), }; + visited.insert(start_node); stack.push(start_node); while let Some(node) = stack.pop() { - if !visited.insert(node) { - continue; - } - + let liveness = liveness_source.liveness_for_region(node.region); // We've reached a node we haven't visited before. - let location = liveness.location_from_point(node.point); - visitor.on_node_traversed(loan_idx, node); + let location = location_map.to_location(node.point); + visitor.on_node_traversed(loan_idx, node, liveness.is_live_at(node.point)); // When we find a _new_ successor, we'd like to // - visit it eventually, // - and let the generic visitor know about it. let mut successor_found = |succ| { - if !visited.contains(&succ) { + if visited.insert(succ) { stack.push(succ); visitor.on_successor_discovered(node, succ); } @@ -145,9 +143,6 @@ impl LocalizedConstraintGraph { // - the materialized logical edges that exist virtually at all points for this // node's region, localized at this point. - // Universal regions propagate loans along the CFG, i.e. forwards only. - let is_universal_region = universal_regions.is_universal_region(node.region); - // The physical edges present at this node are: // // 1. the typeck edges that flow from region to region *at this point*. @@ -158,52 +153,66 @@ impl LocalizedConstraintGraph { // 2a. the liveness edges that flow *forward*, from this node's point to its // successors in the CFG. - if body[location.block].statements.get(location.statement_index).is_some() { - // Intra-block edges, straight line constraints from each point to its successor - // within the same block. - let next_point = node.point + 1; - if let Some(succ) = compute_forward_successor( - node.region, - next_point, - live_regions, - live_region_variances, - is_universal_region, - ) { - successor_found(succ); - } - } else { - // Inter-block edges, from the block's terminator to each successor block's - // entry point. - for successor_block in body[location.block].terminator().successors() { - let next_location = Location { block: successor_block, statement_index: 0 }; - let next_point = liveness.point_from_location(next_location); - if let Some(succ) = compute_forward_successor( - node.region, - next_point, - live_regions, - live_region_variances, - is_universal_region, - ) { - successor_found(succ); + // + // - for covariant cases: loans flow in the regular direction, from the current point + // to the next point. + // - for invariant cases, loans can flow in both directions, but here we're only + // interested in the forward path of the bidirectional edge. + // + // We still need to check liveness for each next point though. + if matches!( + liveness.direction, + ConstraintDirection::Forward | ConstraintDirection::Bidirectional + ) { + if body[location.block].statements.get(location.statement_index).is_some() { + // Intra-block edges, straight line constraints from each point to its successor + // within the same block. + let next_point = node.point + 1; + if liveness.is_live_at(next_point) { + successor_found(LocalizedNode { + region: node.region, + point: next_point, + }); + } + } else { + // Inter-block edges, from the block's terminator to each successor block's + // entry point. + for successor_block in body[location.block].terminator().successors() { + let next_location = + Location { block: successor_block, statement_index: 0 }; + let next_point = location_map.point_from_location(next_location); + if liveness.is_live_at(next_point) { + successor_found(LocalizedNode { + region: node.region, + point: next_point, + }); + } } } } // 2b. the liveness edges that flow *backward*, from this node's point to its // predecessors in the CFG. - if !is_universal_region { + // + // - for contravariant cases: loans flow in the inverse direction, from the current + // point to the previous point. + // - for invariant cases, loans can flow in both directions, but here we only + // want the backward path of the bidirectional edge. + // + // Liveness flows into the regions live at the next point. So, in a backwards view, we'll link + // the region from the current point, if it's live there, to the previous point. + if matches!( + liveness.direction, + ConstraintDirection::Backward | ConstraintDirection::Bidirectional + ) && liveness.is_live_at(node.point) + { if location.statement_index > 0 { // Backward edges to the predecessor point in the same block. let previous_point = PointIndex::from(node.point.as_usize() - 1); - if let Some(succ) = compute_backward_successor( - node.region, - node.point, - previous_point, - live_regions, - live_region_variances, - ) { - successor_found(succ); - } + successor_found(LocalizedNode { + region: node.region, + point: previous_point, + }); } else { // Backward edges from the block entry point to the terminator of the // predecessor blocks. @@ -213,22 +222,19 @@ impl LocalizedConstraintGraph { block: pred_block, statement_index: body[pred_block].statements.len(), }; - let previous_point = liveness.point_from_location(previous_location); - if let Some(succ) = compute_backward_successor( - node.region, - node.point, - previous_point, - live_regions, - live_region_variances, - ) { - successor_found(succ); - } + let previous_point = + location_map.point_from_location(previous_location); + successor_found(LocalizedNode { + region: node.region, + point: previous_point, + }); } } } // And finally, we have the logical edges, materialized at this point. - for &logical_succ in self.logical_edges.get(&node.region).into_flat_iter() { + let logical_succs = self.logical_edges.get(node.region); + for &logical_succ in logical_succs.into_flat_iter() { let succ = LocalizedNode { region: logical_succ, point: node.point }; successor_found(succ); } @@ -236,98 +242,3 @@ impl LocalizedConstraintGraph { } } } - -/// Returns the successor for the current region/point node when propagating a loan through forward -/// edges, if applicable, according to liveness and variance. -fn compute_forward_successor( - region: RegionVid, - next_point: PointIndex, - live_regions: &SparseIntervalMatrix, - live_region_variances: &LiveRegionVariances, - is_universal_region: bool, -) -> Option { - // 1. Universal regions are semantically live at all points. - if is_universal_region { - let succ = LocalizedNode { region, point: next_point }; - return Some(succ); - } - - // 2. Otherwise, gather the edges due to explicit region liveness, when applicable. - if !live_regions.contains(region, next_point) { - debug!(?region, ?next_point, "region isn't live at successor"); - return None; - } - - // Here, `region` could be live at the current point, and is live at the next point: add a - // constraint between them, according to variance. - - // Note: there currently are cases related to promoted and const generics, where we don't yet - // have variance information (possibly about temporary regions created when typeck sanitizes the - // promoteds). Until that is done, we conservatively fallback to maximizing reachability by - // adding a bidirectional edge here. This will not limit traversal whatsoever, and thus - // propagate liveness when needed. - // - // FIXME: add the missing variance information and remove this fallback bidirectional edge. - let direction = live_region_variances - .get(region) - .copied() - .flatten() - .unwrap_or(ConstraintDirection::Bidirectional); - - debug!(?direction); - - match direction { - ConstraintDirection::Backward => { - // Contravariant cases: loans flow in the inverse direction, but we're only interested - // in forward successors and there are none here. - None - } - ConstraintDirection::Forward | ConstraintDirection::Bidirectional => { - // 1. For covariant cases: loans flow in the regular direction, from the current point - // to the next point. - // 2. For invariant cases, loans can flow in both directions, but here as well, we only - // want the forward path of the bidirectional edge. - Some(LocalizedNode { region, point: next_point }) - } - } -} - -/// Returns the successor for the current region/point node when propagating a loan through backward -/// edges, if applicable, according to liveness and variance. -fn compute_backward_successor( - region: RegionVid, - current_point: PointIndex, - previous_point: PointIndex, - live_regions: &SparseIntervalMatrix, - live_region_variances: &LiveRegionVariances, -) -> Option { - // Liveness flows into the regions live at the next point. So, in a backwards view, we'll link - // the region from the current point, if it's live there, to the previous point. - if !live_regions.contains(region, current_point) { - debug!(?region, ?current_point, "region isn't live at current point"); - return None; - } - - // FIXME: add the missing variance information and remove this fallback bidirectional edge. See - // the same comment in `compute_forward_successor`. - let direction = live_region_variances - .get(region) - .copied() - .flatten() - .unwrap_or(ConstraintDirection::Bidirectional); - - match direction { - ConstraintDirection::Forward => { - // Covariant cases: loans flow in the regular direction, but we're only interested in - // backward successors and there are none here. - None - } - ConstraintDirection::Backward | ConstraintDirection::Bidirectional => { - // 1. For contravariant cases: loans flow in the inverse direction, from the current - // point to the previous point. - // 2. For invariant cases, loans can flow in both directions, but here as well, we only - // want the backward path of the bidirectional edge. - Some(LocalizedNode { region, point: previous_point }) - } - } -} diff --git a/compiler/rustc_borrowck/src/polonius/dump.rs b/compiler/rustc_borrowck/src/polonius/dump.rs index 0c63b00ce5461..c32b118a9a1cc 100644 --- a/compiler/rustc_borrowck/src/polonius/dump.rs +++ b/compiler/rustc_borrowck/src/polonius/dump.rs @@ -5,21 +5,44 @@ use rustc_index::IndexVec; use rustc_middle::mir::pretty::{MirDumper, PassWhere, PrettyPrintMirOptions}; use rustc_middle::mir::{Body, Location}; use rustc_middle::ty::{RegionVid, TyCtxt}; -use rustc_mir_dataflow::points::PointIndex; +use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex}; use rustc_session::config::MirIncludeSpans; use crate::borrow_set::BorrowSet; use crate::constraints::OutlivesConstraint; use crate::dataflow::BorrowIndex; -use crate::polonius::{LocalizedConstraintGraphVisitor, LocalizedNode, PoloniusContext}; +use crate::polonius::liveness::RegionLiveness; +use crate::polonius::{ + LiveRegionVariances, LivenessSource, LocalizedConstraintGraphVisitor, LocalizedNode, + PoloniusContext, +}; use crate::region_infer::values::LivenessValues; use crate::type_check::Locations; +use crate::universal_regions::UniversalRegions; use crate::{BorrowckInferCtxt, ClosureRegionRequirements, RegionInferenceContext}; /// The polonius MIR dump template: a regular HTML file for easy editing, with special dummy /// sections to be replaced by real contents. const TEMPLATE: &str = include_str!("./dump/polonius-mir-dump.template.html"); +/// A `LivenessSource` for already-existing liveness and variance data. +struct CachedLivenessSource<'a, 'tcx> { + live_region_variances: &'a LiveRegionVariances, + universal_regions: &'a UniversalRegions<'tcx>, + liveness: &'a LivenessValues, +} + +impl<'a, 'tcx> LivenessSource for CachedLivenessSource<'a, 'tcx> { + fn liveness_for_region(&mut self, region: RegionVid) -> RegionLiveness<'_> { + RegionLiveness::new( + region, + self.live_region_variances, + self.universal_regions, + self.liveness.points(), + ) + } +} + /// `-Zdump-mir=polonius` dumps MIR annotated with NLL and polonius specific information. pub(crate) fn dump_polonius_mir<'tcx>( infcx: &BorrowckInferCtxt<'tcx>, @@ -27,7 +50,7 @@ pub(crate) fn dump_polonius_mir<'tcx>( regioncx: &RegionInferenceContext<'tcx>, closure_region_requirements: &Option>, borrow_set: &BorrowSet<'tcx>, - polonius_context: Option<&PoloniusContext>, + polonius_context: Option<&PoloniusContext<'tcx>>, ) { let tcx = infcx.tcx; if !tcx.sess.opts.unstable_opts.polonius.is_next_enabled() { @@ -41,16 +64,15 @@ pub(crate) fn dump_polonius_mir<'tcx>( // If we have a polonius graph to dump along the rest of the MIR and NLL info, we extract its // constraints here. + let mut liveness_source = CachedLivenessSource { + live_region_variances: &polonius_context.live_region_variances, + universal_regions: regioncx.universal_regions(), + liveness: regioncx.liveness_constraints(), + }; let mut collector = MirDumpCollector::default(); if let Some(graph) = &polonius_context.graph { - graph.traverse( - body, - regioncx.liveness_constraints(), - &polonius_context.live_region_variances, - regioncx.universal_regions(), - borrow_set, - &mut collector, - ); + let location_map = regioncx.liveness_constraints().location_map(); + graph.traverse(body, borrow_set, location_map, &mut liveness_source, &mut collector); } let extra_data = &|pass_where, out: &mut dyn io::Write| { @@ -98,7 +120,7 @@ struct MirDumpCollector { } impl LocalizedConstraintGraphVisitor for MirDumpCollector { - fn on_node_traversed(&mut self, loan: BorrowIndex, node: LocalizedNode) { + fn on_node_traversed(&mut self, loan: BorrowIndex, node: LocalizedNode, _is_live: bool) { self.reachability.entry(loan).or_default().push(node); } @@ -151,7 +173,7 @@ fn emit_polonius_dump<'tcx>( "POLONIUS_CONSTRAINTS" => { edge_count = emit_mermaid_constraint_graph( borrow_set, - regioncx.liveness_constraints(), + regioncx.liveness_constraints().location_map(), &collector.constraints, out, )?; @@ -250,7 +272,7 @@ fn emit_polonius_mir<'tcx>( out, )?; - let liveness = regioncx.liveness_constraints(); + let location_map = regioncx.liveness_constraints().location_map(); // Add localized outlives constraints match pass_where { @@ -260,8 +282,8 @@ fn emit_polonius_mir<'tcx>( for constraint in localized_outlives_constraints { let LocalizedOutlivesConstraint { source, from, target, to } = constraint; - let from = liveness.location_from_point(*from); - let to = liveness.location_from_point(*to); + let from = location_map.to_location(*from); + let to = location_map.to_location(*to); writeln!(out, "| {source:?} at {from:?} -> {target:?} at {to:?}")?; } writeln!(out, "|")?; @@ -441,12 +463,12 @@ fn emit_mermaid_nll_sccs<'tcx>( /// region, and loan introductions. fn emit_mermaid_constraint_graph<'tcx>( borrow_set: &BorrowSet<'tcx>, - liveness: &LivenessValues, + location_map: &DenseLocationMap, localized_outlives_constraints: &[LocalizedOutlivesConstraint], out: &mut dyn io::Write, ) -> io::Result { let node_label = |region: RegionVid, point: PointIndex| { - let location = liveness.location_from_point(point); + let location = location_map.to_location(point); node_name(region, location) }; @@ -512,6 +534,7 @@ fn emit_loan_reachability( reachability: &FxIndexMap>, out: &mut dyn io::Write, ) -> io::Result<()> { + let location_map = liveness.location_map(); for (loan, _) in borrow_set.iter_enumerated() { let Some(reachability) = reachability.get(&loan) else { continue; @@ -532,7 +555,7 @@ fn emit_loan_reachability( for (idx, node) in reachability.iter().enumerate() { writeln!(out, "
  • ")?; - let location = liveness.location_from_point(node.point); + let location = location_map.to_location(node.point); let kind = if idx == 0 { "starts in" } else { "reaches" }; writeln!( out, diff --git a/compiler/rustc_borrowck/src/polonius/liveness.rs b/compiler/rustc_borrowck/src/polonius/liveness.rs new file mode 100644 index 0000000000000..83f56082f27da --- /dev/null +++ b/compiler/rustc_borrowck/src/polonius/liveness.rs @@ -0,0 +1,131 @@ +use rustc_index::IndexVec; +use rustc_index::interval::{IntervalSet, SparseIntervalMatrix}; +use rustc_middle::mir::Local; +use rustc_middle::ty::{GenericArg, RegionVid, Ty, TyCtxt}; +use rustc_mir_dataflow::points::PointIndex; + +use crate::polonius::{ConstraintDirection, LiveRegionVariances}; +use crate::region_infer::values::LivenessValues; +use crate::type_check::liveness::LivenessComputation; +use crate::universal_regions::UniversalRegions; + +/// The source of liveness information for a given region. +pub(super) trait LivenessSource { + fn liveness_for_region(&mut self, region: RegionVid) -> RegionLiveness<'_>; +} + +/// For a given region, the relevant liveness and variance information. +pub(super) struct RegionLiveness<'a> { + pub(super) direction: ConstraintDirection, + live_points: Option<&'a IntervalSet>, +} + +impl<'a> RegionLiveness<'a> { + #[inline] + pub(super) fn new<'tcx>( + region: RegionVid, + live_region_variances: &LiveRegionVariances, + universal_regions: &UniversalRegions<'tcx>, + live_points: &'a SparseIntervalMatrix, + ) -> Self { + // Universal regions propagate loans along the CFG, i.e. forwards only. + let is_universal_region = universal_regions.is_universal_region(region); + + // Note: there currently are cases related to promoted and const generics, where we don't yet + // have variance information (possibly about temporary regions created when typeck sanitizes the + // promoteds). Until that is done, we conservatively fallback to maximizing reachability by + // adding a bidirectional edge here. This will not limit traversal whatsoever, and thus + // propagate liveness when needed. + // + // FIXME: add the missing variance information and remove this fallback bidirectional edge. + let direction = if is_universal_region { + ConstraintDirection::Forward + } else { + live_region_variances + .get(region) + .copied() + .flatten() + .unwrap_or(ConstraintDirection::Bidirectional) + }; + let live_points = live_points.row(region); + Self { direction, live_points } + } + + pub(super) fn is_live_at(&self, point: PointIndex) -> bool { + self.live_points.is_some_and(|points| points.contains(point)) + } +} + +/// The data needed to compute region liveness on-demand while traversing the localized outlives +/// constraint graph to compute loan liveness. Each region comes from a local's type, so this holds +/// mostly the locals and drop-liveness specifics about their type, to be used as inputs to the +/// `LivenessComputation` giving us the region liveness. +#[derive(Default)] +pub(crate) struct DeferredRegionLiveness<'tcx> { + /// For each region, the local whose liveness is deferred. + /// + /// Importantly, because of MIR renumbering, this will always be a 1:1 relationship. + by_region: IndexVec>, + + /// For each deferred local, gets the regions contained within that local at use and drop. + drop_args_by_local: IndexVec>>>, +} + +impl<'tcx> DeferredRegionLiveness<'tcx> { + /// We want to defer computing the liveness of the region in the local's type, so we store the + /// necessary inputs to do that later. + pub(crate) fn defer_local( + &mut self, + tcx: TyCtxt<'tcx>, + universal_regions: &UniversalRegions<'tcx>, + local: Local, + local_ty: Ty<'tcx>, + dropck_kinds: &[GenericArg<'tcx>], + ) { + // We already have drop data for this local, because we need to register + // region constraints eagerly. So, we'll store this so we don't need to + // recompute. + self.drop_args_by_local.insert(local, dropck_kinds.to_vec()); + + // Then, we want to map all the regions contained within this local to + // the local itself. Later, when asked for liveness of a given region, + // we can trace liveness for the local containing it. + tcx.for_each_free_region(&local_ty, |region| { + // See note in `VarianceExtractor::record_variance`. + if region.is_bound() || region.is_erased() { + return; + } + let vid = universal_regions.to_region_vid(region); + // Because of MIR renumbering, we should always have a 1:1 mapping + // between a region and a local. + let previous = self.by_region.insert(vid, local); + debug_assert!( + previous.is_none(), + "{vid:?} is in the type of both {previous:?} and {local:?}, but \ + MIR renumbering should ensure that this is impossible.", + ); + }); + } + + /// For a given region, compute the liveness for the local containing it, if it is deferred. + #[inline] + pub(crate) fn ensure_deferred_liveness( + &mut self, + region: RegionVid, + universal_regions: &UniversalRegions<'tcx>, + liveness: &mut LivenessValues, + live_region_variances: &mut LiveRegionVariances, + comp: &mut LivenessComputation<'_, 'tcx>, + ) { + let Some(local) = self.by_region.remove(region) else { + return; + }; + let Some(drop_args) = self.drop_args_by_local.remove(local) else { + return; + }; + + comp.compute(local, universal_regions, Some(live_region_variances), liveness, || { + &drop_args + }); + } +} diff --git a/compiler/rustc_borrowck/src/polonius/mod.rs b/compiler/rustc_borrowck/src/polonius/mod.rs index 0735aa6120c37..664bcedfc5ef2 100644 --- a/compiler/rustc_borrowck/src/polonius/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/mod.rs @@ -36,6 +36,7 @@ mod constraints; mod dump; pub(crate) mod legacy; +mod liveness; mod liveness_constraints; use rustc_data_structures::fx::FxHashSet; @@ -43,16 +44,20 @@ use rustc_index::IndexVec; use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::{Body, Local}; use rustc_middle::ty::RegionVid; -use rustc_mir_dataflow::points::PointIndex; +use rustc_mir_dataflow::move_paths::MoveData; +use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex}; pub(self) use self::constraints::*; pub(crate) use self::dump::dump_polonius_mir; pub(crate) use self::liveness_constraints::record_live_region_variance; -use crate::BorrowSet; use crate::constraints::OutlivesConstraint; use crate::dataflow::BorrowIndex; +pub(crate) use crate::polonius::liveness::DeferredRegionLiveness; +use crate::polonius::liveness::{LivenessSource, RegionLiveness}; use crate::region_infer::values::LivenessValues; +use crate::type_check::liveness::{LivenessComputation, LocalUseMap}; use crate::universal_regions::UniversalRegions; +use crate::{BorrowSet, BorrowckInferCtxt}; pub(crate) type LiveRegionVariances = IndexVec>; @@ -84,7 +89,7 @@ impl LiveLoans { /// polonius localized constraints, during NLL region inference as well as MIR dumping, /// - data needed by the borrowck error computation and diagnostics. #[derive(Default)] -pub(crate) struct PoloniusContext { +pub(crate) struct PoloniusContext<'tcx> { /// The graph from which we extract the localized outlives constraints. graph: Option, @@ -97,6 +102,10 @@ pub(crate) struct PoloniusContext { /// currently has more boring locals than NLLs so we record the latter to use in errors and /// diagnostics, to focus on the locals we consider relevant and match NLL diagnostics. pub(crate) boring_nll_locals: FxHashSet, + + pub(crate) deferred_liveness: DeferredRegionLiveness<'tcx>, + + pub(crate) local_use_map: Option, } /// The direction a constraint can flow into. Used to create liveness constraints according to @@ -113,7 +122,7 @@ pub(crate) enum ConstraintDirection { Bidirectional, } -impl PoloniusContext { +impl<'tcx> PoloniusContext<'tcx> { /// Computes live loans using the set of loans model for `-Zpolonius=next`. /// /// First, creates a constraint graph combining regions and CFG points, by: @@ -124,14 +133,16 @@ impl PoloniusContext { /// loan scope and active loans computations. /// /// The constraint data will be used to compute errors and diagnostics. - pub(crate) fn compute_loan_liveness<'tcx>( + pub(crate) fn compute_loan_liveness( &mut self, + infcx: &BorrowckInferCtxt<'tcx>, liveness: &mut LivenessValues, outlives_constraints: impl Iterator>, universal_regions: &UniversalRegions<'tcx>, body: &Body<'tcx>, + move_data: &MoveData<'tcx>, + location_map: &DenseLocationMap, borrow_set: &BorrowSet<'tcx>, - num_points: usize, ) { // We don't need to prepare the graph (index NLL constraints, etc.) if we have no loans to // trace throughout localized constraints. @@ -139,18 +150,25 @@ impl PoloniusContext { // From the outlives constraints, liveness, and variances, we can compute reachability // on the lazy localized constraint graph to trace the liveness of loans, for the next // step in the chain (the NLL loan scope and active loans computations). - let graph = LocalizedConstraintGraph::new(liveness, outlives_constraints); + let graph = LocalizedConstraintGraph::new(location_map, outlives_constraints); - let mut live_loans = LiveLoans::new(num_points, borrow_set.len()); - let mut visitor = LoanLivenessVisitor { liveness, live_loans: &mut live_loans }; - graph.traverse( - body, + let local_use_map = self + .local_use_map + .as_ref() + .expect("local use map should be computed before loan liveness"); + let deferred_liveness = std::mem::take(&mut self.deferred_liveness); + let mut live_loans = LiveLoans::new(location_map.num_points(), borrow_set.len()); + let comp = + LivenessComputation::new(infcx, body, location_map, move_data, &local_use_map); + let mut liveness_source = DeferredLivenessSource { liveness, - &self.live_region_variances, + live_region_variances: &mut self.live_region_variances, universal_regions, - borrow_set, - &mut visitor, - ); + deferred_liveness, + comp, + }; + let mut visitor = LoanLivenessVisitor { live_loans: &mut live_loans }; + graph.traverse(body, borrow_set, location_map, &mut liveness_source, &mut visitor); liveness.record_live_loans(live_loans); // The graph can be traversed again during MIR dumping, so we store it here. @@ -159,14 +177,44 @@ impl PoloniusContext { } } +/// A `LivenessSource` that will dynamically compute liveness on-demand when traversing +/// the outlives graph. This allows avoiding computing liveness eagerly on many more locals +/// than NLLs, which can be expensive. +struct DeferredLivenessSource<'a, 'tcx> { + liveness: &'a mut LivenessValues, + live_region_variances: &'a mut LiveRegionVariances, + universal_regions: &'a UniversalRegions<'tcx>, + deferred_liveness: DeferredRegionLiveness<'tcx>, + comp: LivenessComputation<'a, 'tcx>, +} + +impl<'a> LivenessSource for DeferredLivenessSource<'a, '_> { + #[inline] + fn liveness_for_region(&mut self, region: RegionVid) -> RegionLiveness<'_> { + self.deferred_liveness.ensure_deferred_liveness( + region, + self.universal_regions, + &mut self.liveness, + &mut self.live_region_variances, + &mut self.comp, + ); + + RegionLiveness::new( + region, + self.live_region_variances, + self.universal_regions, + self.liveness.points(), + ) + } +} + /// Visitor to record loan liveness when traversing the localized constraint graph. struct LoanLivenessVisitor<'a> { - liveness: &'a LivenessValues, live_loans: &'a mut LiveLoans, } impl LocalizedConstraintGraphVisitor for LoanLivenessVisitor<'_> { - fn on_node_traversed(&mut self, loan: BorrowIndex, node: LocalizedNode) { + fn on_node_traversed(&mut self, loan: BorrowIndex, node: LocalizedNode, is_live: bool) { // Record the loan as being live on entry to this point if it reaches a live region // there. // @@ -208,7 +256,7 @@ impl LocalizedConstraintGraphVisitor for LoanLivenessVisitor<'_> { // // FIXME: analyze potential unsoundness, possibly in concert with a borrowck // implementation in a-mir-formality, fuzzing, or manually crafting counter-examples. - if self.liveness.is_live_at_point(node.region, node.point) { + if is_live { self.live_loans.insert(node.point, loan); } } diff --git a/compiler/rustc_borrowck/src/region_infer/dump_mir.rs b/compiler/rustc_borrowck/src/region_infer/dump_mir.rs index 68f822aac403a..72e16b4b86dfc 100644 --- a/compiler/rustc_borrowck/src/region_infer/dump_mir.rs +++ b/compiler/rustc_borrowck/src/region_infer/dump_mir.rs @@ -68,7 +68,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { with_msg: &mut dyn FnMut(&str) -> io::Result<()>, ) -> io::Result<()> { for region in self.definitions.indices() { - let value = self.liveness_constraints.pretty_print_live_points(region); + let value = self.liveness_constraints().pretty_print_live_points(region); if value != "{}" { with_msg(&format!("{region:?} live at {value}"))?; } diff --git a/compiler/rustc_borrowck/src/region_infer/graphviz.rs b/compiler/rustc_borrowck/src/region_infer/graphviz.rs index ceb33d82deba8..5b0491a526204 100644 --- a/compiler/rustc_borrowck/src/region_infer/graphviz.rs +++ b/compiler/rustc_borrowck/src/region_infer/graphviz.rs @@ -8,6 +8,7 @@ use std::io::{self, Write}; use itertools::Itertools; use rustc_graphviz as dot; use rustc_middle::ty::UniverseIndex; +use rustc_span::bug; use super::*; diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 76af4e2dd5530..60b258eca80df 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1,45 +1,27 @@ -use std::collections::VecDeque; use std::fmt; -use std::rc::Rc; -use rustc_data_structures::frozen::Frozen; +pub(crate) use region_context::*; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::graph::scc::Sccs; -use rustc_errors::Diag; use rustc_hir::def_id::CRATE_DEF_ID; use rustc_index::IndexVec; -use rustc_infer::infer::outlives::test_type_match; -use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound, VerifyIfEq}; -use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin}; -use rustc_middle::mir::{ - AnnotationSource, BasicBlock, Body, ConstraintCategory, Local, Location, ReturnConstraint, - TerminatorKind, -}; +use rustc_infer::infer::NllRegionVariableOrigin; +use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound}; +use rustc_middle::mir::{ConstraintCategory, Local, Location}; use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; -use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable, UniverseIndex, fold_regions}; -use rustc_mir_dataflow::points::DenseLocationMap; -use rustc_span::hygiene::DesugaringKind; -use rustc_span::{DUMMY_SP, Span, bug}; -use tracing::{Level, debug, enabled, instrument, trace}; +use rustc_middle::ty::{self, RegionVid, TyCtxt}; +use rustc_span::Span; +use tracing::{Level, debug, enabled, instrument}; -use crate::constraints::graph::NormalConstraintGraph; +use crate::BorrowckInferCtxt; use crate::constraints::{ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet}; -use crate::dataflow::BorrowIndex; -use crate::diagnostics::{RegionErrorKind, RegionErrors, UniverseInfo}; -use crate::handle_placeholders::{LoweredConstraints, RegionTracker}; -use crate::polonius::legacy::PoloniusOutput; -use crate::region_infer::values::{LivenessValues, RegionElement, RegionValues}; +use crate::handle_placeholders::RegionTracker; use crate::type_check::Locations; -use crate::type_check::free_region_relations::UniversalRegionRelations; -use crate::universal_regions::UniversalRegions; -use crate::{ - BorrowckInferCtxt, ClosureOutlivesRequirement, ClosureOutlivesSubject, - ClosureOutlivesSubjectTy, ClosureRegionRequirements, -}; mod dump_mir; mod graphviz; pub(crate) mod opaque_types; +mod region_context; mod reverse_sccs; pub(crate) mod values; @@ -75,50 +57,6 @@ impl Representative { pub(crate) type ConstraintSccs = Sccs; -pub struct RegionInferenceContext<'tcx> { - /// Contains the definition for every region variable. Region - /// variables are identified by their index (`RegionVid`). The - /// definition contains information about where the region came - /// from as well as its final inferred value. - pub(crate) definitions: Frozen>>, - - /// The liveness constraints added to each region. For most - /// regions, these start out empty and steadily grow, though for - /// each universally quantified region R they start out containing - /// the entire CFG and `end(R)`. - liveness_constraints: LivenessValues, - - /// The outlives constraints computed by the type-check. - constraints: Frozen>, - - /// The constraint-set, but in graph form, making it easy to traverse - /// the constraints adjacent to a particular region. Used to construct - /// the SCC (see `constraint_sccs`) and for error reporting. - constraint_graph: Frozen, - - /// The SCC computed from `constraints` and the constraint - /// graph. We have an edge from SCC A to SCC B if `A: B`. Used to - /// compute the values of each region. - constraint_sccs: ConstraintSccs, - - scc_annotations: IndexVec, - - /// Map universe indexes to information on why we created it. - universe_causes: FxIndexMap>, - - /// The final inferred values of the region variables; we compute - /// one value per SCC. To get the value for any given *region*, - /// you first find which scc it is a part of. - scc_values: RegionValues<'tcx, ConstraintSccIndex>, - - /// Type constraints that we check after solving. - type_tests: Vec>, - - /// Information about how the universally quantified regions in - /// scope on this function relate to one another. - universal_region_relations: Frozen>, -} - #[derive(Debug)] pub(crate) struct RegionDefinition<'tcx> { /// What kind of variable is this -- a free region? existential @@ -321,1600 +259,9 @@ fn sccs_info<'tcx>(infcx: &BorrowckInferCtxt<'tcx>, sccs: &ConstraintSccs) { debug!("SCC edges {:#?}", scc_node_to_edges); } -impl<'tcx> RegionInferenceContext<'tcx> { - /// Creates a new region inference context with a total of - /// `num_region_variables` valid inference variables; the first N - /// of those will be constant regions representing the free - /// regions defined in `universal_regions`. - /// - /// The `outlives_constraints` and `type_tests` are an initial set - /// of constraints produced by the MIR type check. - pub(crate) fn new( - infcx: &BorrowckInferCtxt<'tcx>, - lowered_constraints: LoweredConstraints<'tcx>, - universal_region_relations: Frozen>, - location_map: Rc, - ) -> Self { - let universal_regions = &universal_region_relations.universal_regions; - - let LoweredConstraints { - constraint_sccs, - definitions, - outlives_constraints, - scc_annotations, - type_tests, - liveness_constraints, - universe_causes, - placeholder_indices, - } = lowered_constraints; - - debug!("universal_regions: {:#?}", universal_region_relations.universal_regions); - debug!("outlives constraints: {:#?}", outlives_constraints); - debug!("placeholder_indices: {:#?}", placeholder_indices); - debug!("type tests: {:#?}", type_tests); - - let constraint_graph = Frozen::freeze(outlives_constraints.graph(definitions.len())); - - if cfg!(debug_assertions) { - sccs_info(infcx, &constraint_sccs); - } - - let mut scc_values = - RegionValues::new(location_map, universal_regions.len(), placeholder_indices); - - // Initializes the region variables with their initial live points. - for (region, definition) in definitions.iter_enumerated() { - let scc = constraint_sccs.scc(region); - - // For each universally quantified region (lifetime parameter). The - // first N variables always correspond to the regions appearing in the - // function signature (both named and anonymous) and in where-clauses. - match definition.origin { - // For each free, universally quantified region X: - NllRegionVariableOrigin::FreeRegion => { - // Add `end(X)` into the set for X. - scc_values.add_free_region(scc, region); - } - - NllRegionVariableOrigin::Placeholder(placeholder) => { - scc_values.add_placeholder(scc, placeholder); - } - - NllRegionVariableOrigin::Existential { .. } => { - // For existential, regions, nothing to do. - } - } - - // Initially copy the liveness constraints of any region that - // has them, setting `scc_values[scc(region)] |= liveness_constraints[region]`. - // - // These values will later be propagated during [`Self::propagate_constraints()`]. - // The values include any live-at-all-points constraints added previously in `liveness::generate`. - if let Some(liveness) = liveness_constraints.point_liveness(region) { - scc_values.merge_liveness(scc, liveness) - } - } - - Self { - definitions, - liveness_constraints, - constraints: outlives_constraints, - constraint_graph, - constraint_sccs, - scc_annotations, - universe_causes, - scc_values, - type_tests, - universal_region_relations, - } - } - - /// Returns an iterator over all the region indices. - pub(crate) fn regions(&self) -> impl Iterator + 'tcx { - self.definitions.indices() - } - - /// Given a universal region in scope on the MIR, returns the - /// corresponding index. - /// - /// Panics if `r` is not a registered universal region, most notably - /// if it is a placeholder. Handling placeholders requires access to the - /// `MirTypeckRegionConstraints`. - pub(crate) fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid { - self.universal_regions().to_region_vid(r) - } - - /// Returns an iterator over all the outlives constraints. - pub(crate) fn outlives_constraints(&self) -> impl Iterator> { - self.constraints.outlives().iter().copied() - } - - /// Adds annotations for `#[rustc_regions]`; see `UniversalRegions::annotate`. - pub(crate) fn annotate(&self, tcx: TyCtxt<'tcx>, err: &mut Diag<'_, ()>) { - self.universal_regions().annotate(tcx, err) - } - - /// Returns `true` if the region `r` contains the point `p`. - /// - /// Panics if called before `solve()` executes, - pub(crate) fn region_contains_point(&self, r: RegionVid, p: Location) -> bool { - let scc = self.constraint_sccs.scc(r); - self.scc_values.contains_point(scc, p) - } - - /// Returns the lowest statement index in `start..=end` which is not contained by `r`. - /// - /// Panics if called before `solve()` executes. - pub(crate) fn first_non_contained_inclusive( - &self, - r: RegionVid, - block: BasicBlock, - start: usize, - end: usize, - ) -> Option { - let scc = self.constraint_sccs.scc(r); - self.scc_values.first_non_contained_inclusive(scc, block, start, end) - } - - /// Returns access to the value of `r` for debugging purposes. - pub(crate) fn region_value_str(&self, r: RegionVid) -> String { - let scc = self.constraint_sccs.scc(r); - self.scc_values.region_value_str(scc) - } - - pub(crate) fn placeholders_contained_in( - &self, - r: RegionVid, - ) -> impl Iterator> { - let scc = self.constraint_sccs.scc(r); - self.scc_values.placeholders_contained_in(scc) - } - - /// Performs region inference and report errors if we see any - /// unsatisfiable constraints. If this is a closure, returns the - /// region requirements to propagate to our creator, if any. - #[instrument(skip(self, infcx, body, polonius_output), level = "debug")] - pub(super) fn solve( - &mut self, - infcx: &InferCtxt<'tcx>, - body: &Body<'tcx>, - polonius_output: Option>, - ) -> (Option>, RegionErrors<'tcx>) { - let mir_def_id = body.source.def_id(); - self.propagate_constraints(); - - let mut errors_buffer = RegionErrors::new(infcx.tcx); - - // If this is a nested body, we propagate unsatisfied - // outlives constraints to the parent body instead of - // eagerly erroing. - let mut propagated_outlives_requirements = - infcx.tcx.is_typeck_child(mir_def_id).then(Vec::new); - - self.check_type_tests(infcx, propagated_outlives_requirements.as_mut(), &mut errors_buffer); - - debug!(?errors_buffer); - debug!(?propagated_outlives_requirements); - - // In Polonius mode, the errors about missing universal region relations are in the output - // and need to be emitted or propagated. Otherwise, we need to check whether the - // constraints were too strong, and if so, emit or propagate those errors. - if infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled() { - self.check_polonius_subset_errors( - propagated_outlives_requirements.as_mut(), - &mut errors_buffer, - polonius_output - .as_ref() - .expect("Polonius output is unavailable despite `-Z polonius`"), - ); - } else { - self.check_universal_regions( - propagated_outlives_requirements.as_mut(), - &mut errors_buffer, - ); - } - - debug!(?errors_buffer); - - let propagated_outlives_requirements = propagated_outlives_requirements.unwrap_or_default(); - - if propagated_outlives_requirements.is_empty() { - (None, errors_buffer) - } else { - let num_external_vids = self.universal_regions().num_global_and_external_regions(); - ( - Some(ClosureRegionRequirements { - num_external_vids, - outlives_requirements: propagated_outlives_requirements, - }), - errors_buffer, - ) - } - } - - /// Propagate the region constraints: this will grow the values - /// for each region variable until all the constraints are - /// satisfied. Note that some values may grow **too** large to be - /// feasible, but we check this later. - #[instrument(skip(self), level = "debug")] - fn propagate_constraints(&mut self) { - debug!("constraints={:#?}", { - let mut constraints: Vec<_> = self.outlives_constraints().collect(); - constraints.sort_by_key(|c| (c.sup, c.sub)); - constraints - .into_iter() - .map(|c| (c, self.constraint_sccs.scc(c.sup), self.constraint_sccs.scc(c.sub))) - .collect::>() - }); - - // To propagate constraints, we walk the DAG induced by the - // SCC. For each SCC `A`, we visit its successors and compute - // their values, then we union all those values to get our - // own. This one-shot approach works because iteration is in - // dependency order. I.e. a chain A: B: C will visit C, B, A. - for scc_a in self.constraint_sccs.all_sccs() { - // Walk each SCC `B` such that `A: B`... - for &scc_b in self.constraint_sccs.successors(scc_a) { - debug!(?scc_b); - self.scc_values.add_region(scc_a, scc_b); - } - } - } - - /// Returns `true` if all the placeholders in the value of `scc_b` are nameable - /// in `scc_a`. Used during constraint propagation, and only once - /// the value of `scc_b` has been computed. - fn can_name_all_placeholders( - &self, - scc_a: ConstraintSccIndex, - scc_b: ConstraintSccIndex, - ) -> bool { - self.scc_annotations[scc_a].can_name_all_placeholders(self.scc_annotations[scc_b]) - } - - /// Once regions have been propagated, this method is used to see - /// whether the "type tests" produced by typeck were satisfied; - /// type tests encode type-outlives relationships like `T: - /// 'a`. See `TypeTest` for more details. - fn check_type_tests( - &self, - infcx: &InferCtxt<'tcx>, - mut propagated_outlives_requirements: Option<&mut Vec>>, - errors_buffer: &mut RegionErrors<'tcx>, - ) { - let tcx = infcx.tcx; - - // Sometimes we register equivalent type-tests that would - // result in basically the exact same error being reported to - // the user. Avoid that. - let mut deduplicate_errors = FxIndexSet::default(); - let mut failed_type_tests = Vec::new(); - - for type_test in &self.type_tests { - debug!("check_type_test: {:?}", type_test); - - let generic_ty = type_test.generic_kind.to_ty(tcx); - if self.eval_verify_bound( - infcx, - generic_ty, - type_test.lower_bound, - &type_test.verify_bound, - ) { - continue; - } - - if let Some(propagated_outlives_requirements) = &mut propagated_outlives_requirements - && self.try_promote_type_test(infcx, type_test, propagated_outlives_requirements) - { - continue; - } - - // Type-test failed. Collect it so we can suppress redundant errors below. - let erased_generic_kind = infcx.tcx.erase_and_anonymize_regions(type_test.generic_kind); - failed_type_tests.push((erased_generic_kind, type_test)); - } - - // An async body can produce both `G: 'static` and `G: 'a` type-test failures at - // the same span, as in `tests/ui/async-await/spurious-static-bound-issue-115376.rs`. - // Reporting the weaker bound adds a redundant diagnostic and suggests a lifetime - // bound that cannot fix the missing `G: 'static` requirement. Keep the `'static` - // error and suppress weaker failures for the same erased generic kind and span. - // This is a diagnostic heuristic, using the same erasure as deduplication below. - // - // Collect all failed `'static` bounds before reporting errors so suppression does - // not depend on the order of the type tests. Compare SCCs because a lower-bound - // region can be equivalent to `'static` without being `fr_static` itself. - let static_scc = self.constraint_sccs.scc(self.universal_regions().fr_static); - let static_bound_errors: FxIndexSet<_> = failed_type_tests - .iter() - .filter_map(|&(erased_generic_kind, type_test)| { - if self.constraint_sccs.scc(type_test.lower_bound) == static_scc { - Some((erased_generic_kind, type_test.span)) - } else { - None - } - }) - .collect(); - - // If `G: 'static` failed at this span, then same-span `G: 'a` failures are weaker. - for (erased_generic_kind, type_test) in failed_type_tests { - if self.constraint_sccs.scc(type_test.lower_bound) != static_scc - && static_bound_errors.contains(&(erased_generic_kind, type_test.span)) - { - continue; - } - - // Skip duplicate-ish errors. - if deduplicate_errors.insert(( - erased_generic_kind, - type_test.lower_bound, - type_test.span, - )) { - debug!( - "check_type_test: reporting error for erased_generic_kind={:?}, \ - lower_bound_region={:?}, \ - type_test.span={:?}", - erased_generic_kind, type_test.lower_bound, type_test.span, - ); - - errors_buffer.push(RegionErrorKind::TypeTestError { type_test: type_test.clone() }); - } - } - } - - /// Invoked when we have some type-test (e.g., `T: 'X`) that we cannot - /// prove to be satisfied. If this is a closure, we will attempt to - /// "promote" this type-test into our `ClosureRegionRequirements` and - /// hence pass it up the creator. To do this, we have to phrase the - /// type-test in terms of external free regions, as local free - /// regions are not nameable by the closure's creator. - /// - /// Promotion works as follows: we first check that the type `T` - /// contains only regions that the creator knows about. If this is - /// true, then -- as a consequence -- we know that all regions in - /// the type `T` are free regions that outlive the closure body. If - /// false, then promotion fails. - /// - /// Once we've promoted T, we have to "promote" `'X` to some region - /// that is "external" to the closure. Generally speaking, a region - /// may be the union of some points in the closure body as well as - /// various free lifetimes. We can ignore the points in the closure - /// body: if the type T can be expressed in terms of external regions, - /// we know it outlives the points in the closure body. That - /// just leaves the free regions. - /// - /// The idea then is to lower the `T: 'X` constraint into multiple - /// bounds -- e.g., if `'X` is the union of two free lifetimes, - /// `'1` and `'2`, then we would create `T: '1` and `T: '2`. - #[instrument(level = "debug", skip(self, infcx, propagated_outlives_requirements))] - fn try_promote_type_test( - &self, - infcx: &InferCtxt<'tcx>, - type_test: &TypeTest<'tcx>, - propagated_outlives_requirements: &mut Vec>, - ) -> bool { - let tcx = infcx.tcx; - let TypeTest { generic_kind, lower_bound, span: blame_span, verify_bound: _ } = *type_test; - - let generic_ty = generic_kind.to_ty(tcx); - let Some(subject) = self.try_promote_type_test_subject(infcx, generic_ty) else { - return false; - }; - - let r_scc = self.constraint_sccs.scc(lower_bound); - debug!( - "lower_bound = {:?} r_scc={:?} universe={:?}", - lower_bound, - r_scc, - self.max_nameable_universe(r_scc) - ); - // If the type test requires that `T: 'a` where `'a` is a - // placeholder from another universe, that effectively requires - // `T: 'static`, so we have to propagate that requirement. - // - // It doesn't matter *what* universe because the promoted `T` will - // always be in the root universe. - if let Some(p) = self.scc_values.placeholders_contained_in(r_scc).next() { - debug!("encountered placeholder in higher universe: {:?}, requiring 'static", p); - let static_r = self.universal_regions().fr_static; - propagated_outlives_requirements.push(ClosureOutlivesRequirement { - subject, - outlived_free_region: static_r, - blame_span, - category: ConstraintCategory::Boring, - }); - - // we can return here -- the code below might push add'l constraints - // but they would all be weaker than this one. - return true; - } - - // For each region outlived by lower_bound find a non-local, - // universal region (it may be the same region) and add it to - // `ClosureOutlivesRequirement`. - let mut found_outlived_universal_region = false; - for ur in self.scc_values.universal_regions_outlived_by(r_scc) { - found_outlived_universal_region = true; - debug!("universal_region_outlived_by ur={:?}", ur); - let non_local_ub = self.universal_region_relations.non_local_upper_bounds(ur); - debug!(?non_local_ub); - - // This is slightly too conservative. To show T: '1, given `'2: '1` - // and `'3: '1` we only need to prove that T: '2 *or* T: '3, but to - // avoid potential non-determinism we approximate this by requiring - // T: '1 and T: '2. - for upper_bound in non_local_ub { - debug_assert!(self.universal_regions().is_universal_region(upper_bound)); - debug_assert!(!self.universal_regions().is_local_free_region(upper_bound)); - - let requirement = ClosureOutlivesRequirement { - subject, - outlived_free_region: upper_bound, - blame_span, - category: ConstraintCategory::Boring, - }; - debug!(?requirement, "adding closure requirement"); - propagated_outlives_requirements.push(requirement); - } - } - // If we succeed to promote the subject, i.e. it only contains non-local regions, - // and fail to prove the type test inside of the closure, the `lower_bound` has to - // also be at least as large as some universal region, as the type test is otherwise - // trivial. - assert!(found_outlived_universal_region); - true - } - - /// When we promote a type test `T: 'r`, we have to replace all region - /// variables in the type `T` with an equal universal region from the - /// closure signature. - /// This is not always possible, so this is a fallible process. - #[instrument(level = "debug", skip(self, infcx), ret)] - fn try_promote_type_test_subject( - &self, - infcx: &InferCtxt<'tcx>, - ty: Ty<'tcx>, - ) -> Option> { - let tcx = infcx.tcx; - let mut failed = false; - let ty = fold_regions(tcx, ty, |r, _depth| { - let r_vid = self.to_region_vid(r); - let r_scc = self.constraint_sccs.scc(r_vid); - - // The challenge is this. We have some region variable `r` - // whose value is a set of CFG points and universal - // regions. We want to find if that set is *equivalent* to - // any of the named regions found in the closure. - // To do so, we simply check every candidate `u_r` for equality. - self.scc_values - .universal_regions_outlived_by(r_scc) - .filter(|&u_r| !self.universal_regions().is_local_free_region(u_r)) - .find(|&u_r| self.eval_equal(u_r, r_vid)) - .map(|u_r| ty::Region::new_var(tcx, u_r)) - // In case we could not find a named region to map to, - // we will return `None` below. - .unwrap_or_else(|| { - failed = true; - r - }) - }); - - debug!("try_promote_type_test_subject: folded ty = {:?}", ty); - - // This will be true if we failed to promote some region. - if failed { - return None; - } - - Some(ClosureOutlivesSubject::Ty(ClosureOutlivesSubjectTy::bind(tcx, ty))) - } - - /// Like `universal_upper_bound`, but returns an approximation more suitable - /// for diagnostics. If `r` contains multiple disjoint universal regions - /// (e.g. 'a and 'b in `fn foo<'a, 'b> { ... }`, we pick the lower-numbered region. - /// This corresponds to picking named regions over unnamed regions - /// (e.g. picking early-bound regions over a closure late-bound region). - /// - /// This means that the returned value may not be a true upper bound, since - /// only 'static is known to outlive disjoint universal regions. - /// Therefore, this method should only be used in diagnostic code, - /// where displaying *some* named universal region is better than - /// falling back to 'static. - #[instrument(level = "debug", skip(self))] - pub(crate) fn approx_universal_upper_bound(&self, r: RegionVid) -> RegionVid { - debug!("{}", self.region_value_str(r)); - - // Find the smallest universal region that contains all other - // universal regions within `region`. - let mut lub = self.universal_regions().fr_fn_body; - let r_scc = self.constraint_sccs.scc(r); - let static_r = self.universal_regions().fr_static; - for ur in self.scc_values.universal_regions_outlived_by(r_scc) { - let new_lub = self.universal_region_relations.postdom_upper_bound(lub, ur); - debug!(?ur, ?lub, ?new_lub); - // The upper bound of two non-static regions is static: this - // means we know nothing about the relationship between these - // two regions. Pick a 'better' one to use when constructing - // a diagnostic - if ur != static_r && lub != static_r && new_lub == static_r { - // Prefer the region with an `external_name` - this - // indicates that the region is early-bound, so working with - // it can produce a nicer error. - if self.region_definition(ur).external_name.is_some() { - lub = ur; - } else if self.region_definition(lub).external_name.is_some() { - // Leave lub unchanged - } else { - // If we get here, we don't have any reason to prefer - // one region over the other. Just pick the - // one with the lower index for now. - lub = std::cmp::min(ur, lub); - } - } else { - lub = new_lub; - } - } - - debug!(?r, ?lub); - - lub - } - - /// Tests if `test` is true when applied to `lower_bound` at - /// `point`. - fn eval_verify_bound( - &self, - infcx: &InferCtxt<'tcx>, - generic_ty: Ty<'tcx>, - lower_bound: RegionVid, - verify_bound: &VerifyBound<'tcx>, - ) -> bool { - debug!("eval_verify_bound(lower_bound={:?}, verify_bound={:?})", lower_bound, verify_bound); - - match verify_bound { - VerifyBound::IfEq(verify_if_eq_b) => { - self.eval_if_eq(infcx, generic_ty, lower_bound, *verify_if_eq_b) - } - - VerifyBound::IsEmpty => { - let lower_bound_scc = self.constraint_sccs.scc(lower_bound); - self.scc_values.elements_contained_in(lower_bound_scc).next().is_none() - } - - VerifyBound::OutlivedBy(r) => { - let r_vid = self.to_region_vid(*r); - self.eval_outlives(r_vid, lower_bound) - } - - VerifyBound::AnyBound(verify_bounds) => verify_bounds.iter().any(|verify_bound| { - self.eval_verify_bound(infcx, generic_ty, lower_bound, verify_bound) - }), - - VerifyBound::AllBounds(verify_bounds) => verify_bounds.iter().all(|verify_bound| { - self.eval_verify_bound(infcx, generic_ty, lower_bound, verify_bound) - }), - } - } - - fn eval_if_eq( - &self, - infcx: &InferCtxt<'tcx>, - generic_ty: Ty<'tcx>, - lower_bound: RegionVid, - verify_if_eq_b: ty::Binder<'tcx, VerifyIfEq<'tcx>>, - ) -> bool { - let generic_ty = self.normalize_to_scc_representatives(infcx.tcx, generic_ty); - let verify_if_eq_b = self.normalize_to_scc_representatives(infcx.tcx, verify_if_eq_b); - match test_type_match::extract_verify_if_eq(infcx.tcx, &verify_if_eq_b, generic_ty) { - Some(r) => { - let r_vid = self.to_region_vid(r); - self.eval_outlives(r_vid, lower_bound) - } - None => false, - } - } - - /// This is a conservative normalization procedure. It takes every - /// free region in `value` and replaces it with the - /// "representative" of its SCC (see `scc_representatives` field). - /// We are guaranteed that if two values normalize to the same - /// thing, then they are equal; this is a conservative check in - /// that they could still be equal even if they normalize to - /// different results. (For example, there might be two regions - /// with the same value that are not in the same SCC). - /// - /// N.B., this is not an ideal approach and I would like to revisit - /// it. However, it works pretty well in practice. In particular, - /// this is needed to deal with projection outlives bounds like - /// - /// ```text - /// >::Item: '1 - /// ``` - /// - /// In particular, this routine winds up being important when - /// there are bounds like `where >::Item: 'b` in the - /// environment. In this case, if we can show that `'0 == 'a`, - /// and that `'b: '1`, then we know that the clause is - /// satisfied. In such cases, particularly due to limitations of - /// the trait solver =), we usually wind up with a where-clause like - /// `T: Foo<'a>` in scope, which thus forces `'0 == 'a` to be added as - /// a constraint, and thus ensures that they are in the same SCC. - /// - /// So why can't we do a more correct routine? Well, we could - /// *almost* use the `relate_tys` code, but the way it is - /// currently setup it creates inference variables to deal with - /// higher-ranked things and so forth, and right now the inference - /// context is not permitted to make more inference variables. So - /// we use this kind of hacky solution. - fn normalize_to_scc_representatives(&self, tcx: TyCtxt<'tcx>, value: T) -> T - where - T: TypeFoldable>, - { - fold_regions(tcx, value, |r, _db| { - let vid = self.to_region_vid(r); - let scc = self.constraint_sccs.scc(vid); - let repr = self.scc_representative(scc); - ty::Region::new_var(tcx, repr) - }) - } - - /// Evaluate whether `sup_region == sub_region`. - /// - /// Panics if called before `solve()` executes, - // This is `pub` because it's used by unstable external borrowck data users, see `consumers.rs`. - pub fn eval_equal(&self, r1: RegionVid, r2: RegionVid) -> bool { - self.eval_outlives(r1, r2) && self.eval_outlives(r2, r1) - } - - /// Evaluate whether `sup_region: sub_region`. - /// - /// Panics if called before `solve()` executes, - // This is `pub` because it's used by unstable external borrowck data users, see `consumers.rs`. - #[instrument(skip(self), level = "debug", ret)] - pub fn eval_outlives(&self, sup_region: RegionVid, sub_region: RegionVid) -> bool { - debug!( - "sup_region's value = {:?} universal={:?}", - self.region_value_str(sup_region), - self.universal_regions().is_universal_region(sup_region), - ); - debug!( - "sub_region's value = {:?} universal={:?}", - self.region_value_str(sub_region), - self.universal_regions().is_universal_region(sub_region), - ); - - let sub_region_scc = self.constraint_sccs.scc(sub_region); - let sup_region_scc = self.constraint_sccs.scc(sup_region); - - if sub_region_scc == sup_region_scc { - debug!("{sup_region:?}: {sub_region:?} holds trivially; they are in the same SCC"); - return true; - } - - let fr_static = self.universal_regions().fr_static; - - // If we are checking that `'sup: 'sub`, and `'sub` contains - // some placeholder that `'sup` cannot name, then this is only - // true if `'sup` outlives static. - // - // Avoid infinite recursion if `sub_region` is already `'static` - if sub_region != fr_static - && !self.can_name_all_placeholders(sup_region_scc, sub_region_scc) - { - debug!( - "sub universe `{sub_region_scc:?}` is not nameable \ - by super `{sup_region_scc:?}`, promoting to static", - ); - - return self.eval_outlives(sup_region, fr_static); - } - - // Both the `sub_region` and `sup_region` consist of the union - // of some number of universal regions (along with the union - // of various points in the CFG; ignore those points for - // now). Therefore, the sup-region outlives the sub-region if, - // for each universal region R1 in the sub-region, there - // exists some region R2 in the sup-region that outlives R1. - let universal_outlives = - self.scc_values.universal_regions_outlived_by(sub_region_scc).all(|r1| { - self.scc_values - .universal_regions_outlived_by(sup_region_scc) - .any(|r2| self.universal_region_relations.outlives(r2, r1)) - }); - - if !universal_outlives { - debug!("sub region contains a universal region not present in super"); - return false; - } - - // Now we have to compare all the points in the sub region and make - // sure they exist in the sup region. - - if self.universal_regions().is_universal_region(sup_region) { - // Micro-opt: universal regions contain all points. - debug!("super is universal and hence contains all points"); - return true; - } - - debug!("comparison between points in sup/sub"); - - self.scc_values.contains_points(sup_region_scc, sub_region_scc) - } - - /// Once regions have been propagated, this method is used to see - /// whether any of the constraints were too strong. In particular, - /// we want to check for a case where a universally quantified - /// region exceeded its bounds. Consider: - /// ```compile_fail - /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x } - /// ``` - /// In this case, returning `x` requires `&'a u32 <: &'b u32` - /// and hence we establish (transitively) a constraint that - /// `'a: 'b`. The `propagate_constraints` code above will - /// therefore add `end('a)` into the region for `'b` -- but we - /// have no evidence that `'b` outlives `'a`, so we want to report - /// an error. - /// - /// If `propagated_outlives_requirements` is `Some`, then we will - /// push unsatisfied obligations into there. Otherwise, we'll - /// report them as errors. - fn check_universal_regions( - &self, - mut propagated_outlives_requirements: Option<&mut Vec>>, - errors_buffer: &mut RegionErrors<'tcx>, - ) { - for (fr, fr_definition) in self.definitions.iter_enumerated() { - debug!(?fr, ?fr_definition); - match fr_definition.origin { - NllRegionVariableOrigin::FreeRegion => { - // Go through each of the universal regions `fr` and check that - // they did not grow too large, accumulating any requirements - // for our caller into the `outlives_requirements` vector. - self.check_universal_region( - fr, - &mut propagated_outlives_requirements, - errors_buffer, - ); - } - - NllRegionVariableOrigin::Placeholder(placeholder) => { - self.check_bound_universal_region(fr, placeholder, errors_buffer); - } - - NllRegionVariableOrigin::Existential { .. } => { - // nothing to check here - } - } - } - } - - /// Checks if Polonius has found any unexpected free region relations. - /// - /// In Polonius terms, a "subset error" (or "illegal subset relation error") is the equivalent - /// of NLL's "checking if any region constraints were too strong": a placeholder origin `'a` - /// was unexpectedly found to be a subset of another placeholder origin `'b`, and means in NLL - /// terms that the "longer free region" `'a` outlived the "shorter free region" `'b`. - /// - /// More details can be found in this blog post by Niko: - /// - /// - /// In the canonical example - /// ```compile_fail - /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x } - /// ``` - /// returning `x` requires `&'a u32 <: &'b u32` and hence we establish (transitively) a - /// constraint that `'a: 'b`. It is an error that we have no evidence that this - /// constraint holds. - /// - /// If `propagated_outlives_requirements` is `Some`, then we will - /// push unsatisfied obligations into there. Otherwise, we'll - /// report them as errors. - fn check_polonius_subset_errors( - &self, - mut propagated_outlives_requirements: Option<&mut Vec>>, - errors_buffer: &mut RegionErrors<'tcx>, - polonius_output: &PoloniusOutput, - ) { - debug!( - "check_polonius_subset_errors: {} subset_errors", - polonius_output.subset_errors.len() - ); - - // Similarly to `check_universal_regions`: a free region relation, which was not explicitly - // declared ("known") was found by Polonius, so emit an error, or propagate the - // requirements for our caller into the `propagated_outlives_requirements` vector. - // - // Polonius doesn't model regions ("origins") as CFG-subsets or durations, but the - // `longer_fr` and `shorter_fr` terminology will still be used here, for consistency with - // the rest of the NLL infrastructure. The "subset origin" is the "longer free region", - // and the "superset origin" is the outlived "shorter free region". - // - // Note: Polonius will produce a subset error at every point where the unexpected - // `longer_fr`'s "placeholder loan" is contained in the `shorter_fr`. This can be helpful - // for diagnostics in the future, e.g. to point more precisely at the key locations - // requiring this constraint to hold. However, the error and diagnostics code downstream - // expects that these errors are not duplicated (and that they are in a certain order). - // Otherwise, diagnostics messages such as the ones giving names like `'1` to elided or - // anonymous lifetimes for example, could give these names differently, while others like - // the outlives suggestions or the debug output from `#[rustc_regions]` would be - // duplicated. The polonius subset errors are deduplicated here, while keeping the - // CFG-location ordering. - // We can iterate the HashMap here because the result is sorted afterwards. - #[allow(rustc::potential_query_instability)] - let mut subset_errors: Vec<_> = polonius_output - .subset_errors - .iter() - .flat_map(|(_location, subset_errors)| subset_errors.iter()) - .collect(); - subset_errors.sort(); - subset_errors.dedup(); - - for &(longer_fr, shorter_fr) in subset_errors.into_iter() { - debug!( - "check_polonius_subset_errors: subset_error longer_fr={:?},\ - shorter_fr={:?}", - longer_fr, shorter_fr - ); - - let propagated = self.try_propagate_universal_region_error( - longer_fr.into(), - shorter_fr.into(), - &mut propagated_outlives_requirements, - ); - if propagated == RegionRelationCheckResult::Error { - errors_buffer.push(RegionErrorKind::RegionError { - longer_fr: longer_fr.into(), - shorter_fr: shorter_fr.into(), - fr_origin: NllRegionVariableOrigin::FreeRegion, - is_reported: true, - }); - } - } - - // Handle the placeholder errors as usual, until the chalk-rustc-polonius triumvirate has - // a more complete picture on how to separate this responsibility. - for (fr, fr_definition) in self.definitions.iter_enumerated() { - match fr_definition.origin { - NllRegionVariableOrigin::FreeRegion => { - // handled by polonius above - } - - NllRegionVariableOrigin::Placeholder(placeholder) => { - self.check_bound_universal_region(fr, placeholder, errors_buffer); - } - - NllRegionVariableOrigin::Existential { .. } => { - // nothing to check here - } - } - } - } - - /// The largest universe of any region nameable from this SCC. - fn max_nameable_universe(&self, scc: ConstraintSccIndex) -> UniverseIndex { - self.scc_annotations[scc].max_nameable_universe() - } - - /// Checks the final value for the free region `fr` to see if it - /// grew too large. In particular, examine what `end(X)` points - /// wound up in `fr`'s final value; for each `end(X)` where `X != - /// fr`, we want to check that `fr: X`. If not, that's either an - /// error, or something we have to propagate to our creator. - /// - /// Things that are to be propagated are accumulated into the - /// `outlives_requirements` vector. - #[instrument(skip(self, propagated_outlives_requirements, errors_buffer), level = "debug")] - fn check_universal_region( - &self, - longer_fr: RegionVid, - propagated_outlives_requirements: &mut Option<&mut Vec>>, - errors_buffer: &mut RegionErrors<'tcx>, - ) { - let longer_fr_scc = self.constraint_sccs.scc(longer_fr); - - // Because this free region must be in the ROOT universe, we - // know it cannot contain any bound universes. - assert!(self.max_nameable_universe(longer_fr_scc).is_root()); - - // Only check all of the relations for the main representative of each - // SCC, otherwise just check that we outlive said representative. This - // reduces the number of redundant relations propagated out of - // closures. - // Note that the representative will be a universal region if there is - // one in this SCC, so we will always check the representative here. - let representative = self.scc_representative(longer_fr_scc); - if representative != longer_fr { - if let RegionRelationCheckResult::Error = self.check_universal_region_relation( - longer_fr, - representative, - propagated_outlives_requirements, - ) { - errors_buffer.push(RegionErrorKind::RegionError { - longer_fr, - shorter_fr: representative, - fr_origin: NllRegionVariableOrigin::FreeRegion, - is_reported: true, - }); - } - return; - } - - // Find every region `o` such that `fr: o` - // (because `fr` includes `end(o)`). - let mut error_reported = false; - for shorter_fr in self.scc_values.universal_regions_outlived_by(longer_fr_scc) { - if let RegionRelationCheckResult::Error = self.check_universal_region_relation( - longer_fr, - shorter_fr, - propagated_outlives_requirements, - ) { - // We only report the first region error. Subsequent errors are hidden so as - // not to overwhelm the user, but we do record them so as to potentially print - // better diagnostics elsewhere... - errors_buffer.push(RegionErrorKind::RegionError { - longer_fr, - shorter_fr, - fr_origin: NllRegionVariableOrigin::FreeRegion, - is_reported: !error_reported, - }); - - error_reported = true; - } - } - } - - /// Checks that we can prove that `longer_fr: shorter_fr`. If we can't we attempt to propagate - /// the constraint outward (e.g. to a closure environment), but if that fails, there is an - /// error. - fn check_universal_region_relation( - &self, - longer_fr: RegionVid, - shorter_fr: RegionVid, - propagated_outlives_requirements: &mut Option<&mut Vec>>, - ) -> RegionRelationCheckResult { - // If it is known that `fr: o`, carry on. - if self.universal_region_relations.outlives(longer_fr, shorter_fr) { - RegionRelationCheckResult::Ok - } else { - // If we are not in a context where we can't propagate errors, or we - // could not shrink `fr` to something smaller, then just report an - // error. - // - // Note: in this case, we use the unapproximated regions to report the - // error. This gives better error messages in some cases. - self.try_propagate_universal_region_error( - longer_fr, - shorter_fr, - propagated_outlives_requirements, - ) - } - } - - /// Attempt to propagate a region error (e.g. `'a: 'b`) that is not met to a closure's - /// creator. If we cannot, then the caller should report an error to the user. - fn try_propagate_universal_region_error( - &self, - longer_fr: RegionVid, - shorter_fr: RegionVid, - propagated_outlives_requirements: &mut Option<&mut Vec>>, - ) -> RegionRelationCheckResult { - if let Some(propagated_outlives_requirements) = propagated_outlives_requirements { - // Shrink `longer_fr` until we find some non-local regions. - // We'll call them `longer_fr-` -- they are ever so slightly smaller than - // `longer_fr`. - let longer_fr_minus = self.universal_region_relations.non_local_lower_bounds(longer_fr); - - debug!("try_propagate_universal_region_error: fr_minus={:?}", longer_fr_minus); - - // If we don't find a any non-local regions, we should error out as there is nothing - // to propagate. - if longer_fr_minus.is_empty() { - return RegionRelationCheckResult::Error; - } - - let best_blame = self.best_blame_constraint( - longer_fr, - NllRegionVariableOrigin::FreeRegion, - shorter_fr, - ); - let OutlivesConstraint { category, span, .. } = best_blame.constraint(); - - // Grow `shorter_fr` until we find some non-local regions. - // We will always find at least one: `'static`. We'll call - // them `shorter_fr+` -- they're ever so slightly larger - // than `shorter_fr`. - let shorter_fr_plus = - self.universal_region_relations.non_local_upper_bounds(shorter_fr); - debug!("try_propagate_universal_region_error: shorter_fr_plus={:?}", shorter_fr_plus); - - // We then create constraints `longer_fr-: shorter_fr+` that may or may not - // be propagated (see below). - let mut constraints = vec![]; - for fr_minus in longer_fr_minus { - for shorter_fr_plus in &shorter_fr_plus { - constraints.push((fr_minus, *shorter_fr_plus)); - } - } - - // We only need to propagate at least one of the constraints for - // soundness. However, we want to avoid arbitrary choices here - // and currently don't support returning OR constraints. - // - // If any of the `shorter_fr+` regions are already outlived by `longer_fr-`, - // we propagate only those. - // - // Consider this example (`'b: 'a` == `a -> b`), where we try to propagate `'d: 'a`: - // a --> b --> d - // \ - // \-> c - // Here, `shorter_fr+` of `'a` == `['b, 'c]`. - // Propagating `'d: 'b` is correct and should occur; `'d: 'c` is redundant because of - // `'d: 'b` and could reject valid code. - // - // So we filter the constraints to regions already outlived by `longer_fr-`, but if - // the filter yields an empty set, we fall back to the original one. - let subset: Vec<_> = constraints - .iter() - .filter(|&&(fr_minus, shorter_fr_plus)| { - self.eval_outlives(fr_minus, shorter_fr_plus) - }) - .copied() - .collect(); - let propagated_constraints = if subset.is_empty() { constraints } else { subset }; - debug!( - "try_propagate_universal_region_error: constraints={:?}", - propagated_constraints - ); - - assert!( - !propagated_constraints.is_empty(), - "Expected at least one constraint to propagate here" - ); - - for (fr_minus, fr_plus) in propagated_constraints { - // Push the constraint `long_fr-: shorter_fr+` - propagated_outlives_requirements.push(ClosureOutlivesRequirement { - subject: ClosureOutlivesSubject::Region(fr_minus), - outlived_free_region: fr_plus, - blame_span: *span, - category: *category, - }); - } - return RegionRelationCheckResult::Propagated; - } - - RegionRelationCheckResult::Error - } - - fn check_bound_universal_region( - &self, - longer_fr: RegionVid, - placeholder: ty::PlaceholderRegion<'tcx>, - errors_buffer: &mut RegionErrors<'tcx>, - ) { - debug!("check_bound_universal_region(fr={:?}, placeholder={:?})", longer_fr, placeholder,); - - let longer_fr_scc = self.constraint_sccs.scc(longer_fr); - debug!("check_bound_universal_region: longer_fr_scc={:?}", longer_fr_scc,); - - // If we have some bound universal region `'a`, then the only - // elements it can contain is itself -- we don't know anything - // else about it! - if let Some(error_element) = self - .scc_values - .elements_contained_in(longer_fr_scc) - .find(|e| *e != RegionElement::PlaceholderRegion(placeholder)) - { - let illegally_outlived_r = self.region_from_element(longer_fr, &error_element); - // Stop after the first error, it gets too noisy otherwise, and does not provide more information. - errors_buffer.push(RegionErrorKind::PlaceholderOutlivesIllegalRegion { - longer_fr, - illegally_outlived_r, - }); - } else { - debug!("check_bound_universal_region: all bounds satisfied"); - } - } - - pub(crate) fn constraint_path_between_regions( - &self, - from_region: RegionVid, - to_region: RegionVid, - ) -> Option>> { - if from_region == to_region { - bug!("Tried to find a path between {from_region:?} and itself!"); - } - self.constraint_path_to(from_region, |to| to == to_region, true).map(|o| o.0) - } - - /// Walks the graph of constraints (where `'a: 'b` is considered - /// an edge `'a -> 'b`) to find a path from `from_region` to - /// `to_region`. - /// - /// Returns: a series of constraints as well as the region `R` - /// that passed the target test. - /// If `include_static_outlives_all` is `true`, then the synthetic - /// outlives constraints `'static -> a` for every region `a` are - /// considered in the search, otherwise they are ignored. - #[instrument(skip(self, target_test), ret)] - pub(crate) fn constraint_path_to( - &self, - from_region: RegionVid, - target_test: impl Fn(RegionVid) -> bool, - include_placeholder_static: bool, - ) -> Option<(Vec>, RegionVid)> { - self.find_constraint_path_between_regions_inner( - true, - from_region, - &target_test, - include_placeholder_static, - ) - .or_else(|| { - self.find_constraint_path_between_regions_inner( - false, - from_region, - &target_test, - include_placeholder_static, - ) - }) - } - - /// The constraints we get from equating the hidden type of each use of an opaque - /// with its final hidden type may end up getting preferred over other, potentially - /// longer constraint paths. - /// - /// Given that we compute the final hidden type by relying on this existing constraint - /// path, this can easily end up hiding the actual reason for why we require these regions - /// to be equal. - /// - /// To handle this, we first look at the path while ignoring these constraints and then - /// retry while considering them. This is not perfect, as the `from_region` may have already - /// been partially related to its argument region, so while we rely on a member constraint - /// to get a complete path, the most relevant step of that path already existed before then. - fn find_constraint_path_between_regions_inner( - &self, - ignore_opaque_type_constraints: bool, - from_region: RegionVid, - target_test: impl Fn(RegionVid) -> bool, - include_placeholder_static: bool, - ) -> Option<(Vec>, RegionVid)> { - let mut context = IndexVec::from_elem(Trace::NotVisited, &self.definitions); - context[from_region] = Trace::StartRegion; - - let fr_static = self.universal_regions().fr_static; - - // Use a deque so that we do a breadth-first search. We will - // stop at the first match, which ought to be the shortest - // path (fewest constraints). - let mut deque = VecDeque::new(); - deque.push_back(from_region); - - while let Some(r) = deque.pop_front() { - debug!( - "constraint_path_to: from_region={:?} r={:?} value={}", - from_region, - r, - self.region_value_str(r), - ); - - // Check if we reached the region we were looking for. If so, - // we can reconstruct the path that led to it and return it. - if target_test(r) { - let mut result = vec![]; - let mut p = r; - // This loop is cold and runs at the end, which is why we delay - // `OutlivesConstraint` construction until now. - loop { - match context[p] { - Trace::FromGraph(c) => { - p = c.sup; - result.push(*c); - } - - Trace::FromStatic(sub) => { - let c = OutlivesConstraint { - sup: fr_static, - sub, - locations: Locations::All(DUMMY_SP), - span: DUMMY_SP, - category: ConstraintCategory::Internal, - variance_info: ty::VarianceDiagInfo::default(), - from_closure: false, - }; - p = c.sup; - result.push(c); - } - - Trace::StartRegion => { - result.reverse(); - return Some((result, r)); - } - - Trace::NotVisited => { - bug!("found unvisited region {:?} on path to {:?}", p, r) - } - } - } - } - - // Otherwise, walk over the outgoing constraints and - // enqueue any regions we find, keeping track of how we - // reached them. - - // A constraint like `'r: 'x` can come from our constraint - // graph. - - // Always inline this closure because it can be hot. - let mut handle_trace = #[inline(always)] - |sub, trace| { - if let Trace::NotVisited = context[sub] { - context[sub] = trace; - deque.push_back(sub); - } - }; - - // If this is the `'static` region and the graph's direction is normal, then set up the - // Edges iterator to return all regions (#53178). - if r == fr_static && self.constraint_graph.is_normal() { - for sub in self.constraint_graph.outgoing_edges_from_static() { - handle_trace(sub, Trace::FromStatic(sub)); - } - } else { - let edges = self.constraint_graph.outgoing_edges_from_graph(r, &self.constraints); - // This loop can be hot. - for constraint in edges { - match constraint.category { - ConstraintCategory::OutlivesUnnameablePlaceholder(_) - if !include_placeholder_static => - { - debug!("Ignoring illegal placeholder constraint: {constraint:?}"); - continue; - } - ConstraintCategory::OpaqueType if ignore_opaque_type_constraints => { - debug!("Ignoring member constraint: {constraint:?}"); - continue; - } - _ => {} - } - - debug_assert_eq!(constraint.sup, r); - handle_trace(constraint.sub, Trace::FromGraph(constraint)); - } - } - } - - None - } - - /// Finds some region R such that `fr1: R` and `R` is live at `location`. - #[instrument(skip(self), level = "trace", ret)] - pub(crate) fn find_sub_region_live_at(&self, fr1: RegionVid, location: Location) -> RegionVid { - trace!(scc = ?self.constraint_sccs.scc(fr1)); - trace!(universe = ?self.max_nameable_universe(self.constraint_sccs.scc(fr1))); - self.constraint_path_to(fr1, |r| { - trace!(?r, liveness_constraints=?self.liveness_constraints.pretty_print_live_points(r)); - self.liveness_constraints.is_live_at(r, location) - }, true).unwrap().1 - } - - /// Get the region outlived by `longer_fr` and live at `element`. - fn region_from_element( - &self, - longer_fr: RegionVid, - element: &RegionElement<'tcx>, - ) -> RegionVid { - match *element { - RegionElement::Location(l) => self.find_sub_region_live_at(longer_fr, l), - RegionElement::RootUniversalRegion(r) => r, - RegionElement::PlaceholderRegion(error_placeholder) => self - .definitions - .iter_enumerated() - .find_map(|(r, definition)| match definition.origin { - NllRegionVariableOrigin::Placeholder(p) if p == error_placeholder => Some(r), - _ => None, - }) - .unwrap(), - } - } - - /// Get the region definition of `r`. - pub(crate) fn region_definition(&self, r: RegionVid) -> &RegionDefinition<'tcx> { - &self.definitions[r] - } - - /// Check if the SCC of `r` contains `upper`, a free region. - pub(crate) fn upper_bound_in_region_scc(&self, r: RegionVid, upper: RegionVid) -> bool { - let r_scc = self.constraint_sccs.scc(r); - self.scc_values.contains_free_region(r_scc, upper) - } - - pub(crate) fn universal_regions(&self) -> &UniversalRegions<'tcx> { - &self.universal_region_relations.universal_regions - } - - /// Tries to find the best constraint to blame for the fact that - /// `R: from_region`, where `R` is some region that meets - /// `target_test`. This works by following the constraint graph, - /// creating a constraint path that forces `R` to outlive - /// `from_region`, and then finding the best choices within that - /// path to blame. - #[instrument(level = "debug", skip(self))] - pub(crate) fn best_blame_constraint( - &self, - from_region: RegionVid, - from_region_origin: NllRegionVariableOrigin<'tcx>, - to_region: RegionVid, - ) -> BestBlame<'tcx> { - assert!(from_region != to_region, "Trying to blame a region for itself!"); - - let path = self.constraint_path_between_regions(from_region, to_region).unwrap(); - - // If we are passing through a constraint added because we reached an unnameable placeholder `'unnameable`, - // redirect search towards `'unnameable`. - let due_to_placeholder_outlives = path.iter().find_map(|c| { - if let ConstraintCategory::OutlivesUnnameablePlaceholder(unnameable) = c.category { - Some(unnameable) - } else { - None - } - }); - - // Edge case: it's possible that `'from_region` is an unnameable placeholder. - let mut path = if let Some(unnameable) = due_to_placeholder_outlives - && unnameable != from_region - { - // We ignore the extra edges due to unnameable placeholders to get - // an explanation that was present in the original constraint graph. - self.constraint_path_to(from_region, |r| r == unnameable, false).unwrap().0 - } else { - path - }; - - debug!( - "path={:#?}", - path.iter() - .map(|c| format!( - "{:?} ({:?}: {:?})", - c, - self.constraint_sccs.scc(c.sup), - self.constraint_sccs.scc(c.sub), - )) - .collect::>() - ); - - // When reporting an error, there is typically a chain of constraints leading from some - // "source" region which must outlive some "target" region. - // In most cases, we prefer to "blame" the constraints closer to the target -- - // but there is one exception. When constraints arise from higher-ranked subtyping, - // we generally prefer to blame the source value, - // as the "target" in this case tends to be some type annotation that the user gave. - // Therefore, if we find that the region origin is some instantiation - // of a higher-ranked region, we start our search from the "source" point - // rather than the "target", and we also tweak a few other things. - // - // An example might be this bit of Rust code: - // - // ```rust - // let x: fn(&'static ()) = |_| {}; - // let y: for<'a> fn(&'a ()) = x; - // ``` - // - // In MIR, this will be converted into a combination of assignments and type ascriptions. - // In particular, the 'static is imposed through a type ascription: - // - // ```rust - // x = ...; - // AscribeUserType(x, fn(&'static ()) - // y = x; - // ``` - // - // We wind up ultimately with constraints like - // - // ```rust - // !a: 'temp1 // from the `y = x` statement - // 'temp1: 'temp2 - // 'temp2: 'static // from the AscribeUserType - // ``` - // - // and here we prefer to blame the source (the y = x statement). - let blame_source = match from_region_origin { - NllRegionVariableOrigin::FreeRegion => true, - NllRegionVariableOrigin::Placeholder(_) => false, - // `'existential: 'whatever` never results in a region error by itself. - // We may always infer it to `'static` afterall. This means while an error - // path may go through an existential, these existentials are never the - // `from_region`. - NllRegionVariableOrigin::Existential { name: _ } => { - unreachable!("existentials can outlive everything") - } - }; - - // To pick a constraint to blame, we organize constraints by how interesting we expect them - // to be in diagnostics, then pick the most interesting one closest to either the source or - // the target on our constraint path. - let constraint_interest = |constraint: &OutlivesConstraint<'tcx>| { - // Try to avoid blaming constraints from desugarings, since they may not clearly match - // match what users have written. As an exception, allow blaming returns generated by - // `?` desugaring, since the correspondence is fairly clear. - let category = if let Some(kind) = constraint.span.desugaring_kind() - && (kind != DesugaringKind::QuestionMark - || !matches!(constraint.category, ConstraintCategory::Return(_))) - { - ConstraintCategory::Boring - } else { - constraint.category - }; - - let interest = match category { - // Returns usually provide a type to blame and have specially written diagnostics, - // so prioritize them. - ConstraintCategory::Return(_) => 0, - // Unsizing coercions are interesting, since we have a note for that: - // `BorrowExplanation::add_object_lifetime_default_note`. - // FIXME(dianne): That note shouldn't depend on a coercion being blamed; see issue - // #131008 for an example of where we currently don't emit it but should. - // Once the note is handled properly, this case should be removed. Until then, it - // should be as limited as possible; the note is prone to false positives and this - // constraint usually isn't best to blame. - ConstraintCategory::Cast { - is_raw_ptr_dyn_type_cast: _, - unsize_to: Some(unsize_ty), - is_implicit_coercion: true, - } if to_region == self.universal_regions().fr_static - // Mirror the note's condition, to minimize how often this diverts blame. - && let ty::Adt(_, args) = unsize_ty.kind() - && args.iter().any(|arg| arg.as_type().is_some_and(|ty| ty.is_trait())) - // Mimic old logic for this, to minimize false positives in tests. - && !path - .iter() - .any(|c| matches!(c.category, ConstraintCategory::TypeAnnotation(_))) => - { - 1 - } - // Between other interesting constraints, order by their position on the `path`. - ConstraintCategory::Yield - | ConstraintCategory::UseAsConst - | ConstraintCategory::UseAsStatic - | ConstraintCategory::TypeAnnotation( - AnnotationSource::Ascription - | AnnotationSource::Declaration - | AnnotationSource::OpaqueCast, - ) - | ConstraintCategory::Cast { .. } - | ConstraintCategory::CallArgument(_) - | ConstraintCategory::CopyBound - | ConstraintCategory::SizedBound - | ConstraintCategory::Assignment - | ConstraintCategory::Usage - | ConstraintCategory::ClosureUpvar(_) => 2, - // Generic arguments are unlikely to be what relates regions together - ConstraintCategory::TypeAnnotation(AnnotationSource::GenericArg) => 3, - // We handle predicates and opaque types specially; don't prioritize them here. - ConstraintCategory::Predicate(_) | ConstraintCategory::OpaqueType => 4, - // `Boring` constraints can correspond to user-written code and have useful spans, - // but don't provide any other useful information for diagnostics. - ConstraintCategory::Boring => 5, - // `BoringNoLocation` constraints can point to user-written code, but are less - // specific, and are not used for relations that would make sense to blame. - ConstraintCategory::BoringNoLocation => 6, - // Do not blame internal constraints if we can avoid it. Never blame - // the `'region: 'static` constraints introduced by placeholder outlives. - ConstraintCategory::Internal => 7, - ConstraintCategory::OutlivesUnnameablePlaceholder(_) => 8, - ConstraintCategory::SolverRegionConstraint(_) => 9, - }; - - debug!("constraint {constraint:?} category: {category:?}, interest: {interest:?}"); - - interest - }; - - let best_choice = if blame_source { - path.iter().enumerate().rev().min_by_key(|(_, c)| constraint_interest(c)).unwrap().0 - } else { - path.iter().enumerate().min_by_key(|(_, c)| constraint_interest(c)).unwrap().0 - }; - - debug!(?best_choice, ?blame_source); - - let best_blame_idx = if let Some(next) = path.get(best_choice + 1) - && matches!(path[best_choice].category, ConstraintCategory::Return(_)) - && next.category == ConstraintCategory::OpaqueType - { - // The return expression is being influenced by the return type being - // impl Trait, point at the return type and not the return expr. - best_choice + 1 - } else if path[best_choice].category == ConstraintCategory::Return(ReturnConstraint::Normal) - && let Some(field) = path.iter().find_map(|p| { - if let ConstraintCategory::ClosureUpvar(f) = p.category { Some(f) } else { None } - }) - { - path[best_choice].category = - ConstraintCategory::Return(ReturnConstraint::ClosureUpvar(field)); - best_choice - } else { - best_choice - }; - - assert!( - !matches!( - path[best_blame_idx].category, - ConstraintCategory::OutlivesUnnameablePlaceholder(_) - ), - "Illegal placeholder constraint blamed; should have redirected to other region relation" - ); - - BestBlame { path, idx: best_blame_idx } - } - - pub(crate) fn universe_info(&self, universe: ty::UniverseIndex) -> UniverseInfo<'tcx> { - // Query canonicalization can create local superuniverses (for example in - // `InferCtx::query_response_instantiation_guess`), but they don't have an associated - // `UniverseInfo` explaining why they were created. - // This can cause ICEs if these causes are accessed in diagnostics, for example in issue - // #114907 where this happens via liveness and dropck outlives results. - // Therefore, we return a default value in case that happens, which should at worst emit a - // suboptimal error, instead of the ICE. - self.universe_causes.get(&universe).cloned().unwrap_or_else(UniverseInfo::other) - } - - /// Tries to find the terminator of the loop in which the region 'r' resides. - /// Returns the location of the terminator if found. - pub(crate) fn find_loop_terminator_location( - &self, - r: RegionVid, - body: &Body<'_>, - ) -> Option { - let scc = self.constraint_sccs.scc(r); - let locations = self.scc_values.locations_outlived_by(scc); - for location in locations { - let bb = &body[location.block]; - if let Some(terminator) = &bb.terminator - // terminator of a loop should be TerminatorKind::FalseUnwind - && let TerminatorKind::FalseUnwind { .. } = terminator.kind - { - return Some(location); - } - } - None - } - - /// Access to the SCC constraint graph. - /// This can be used to quickly under-approximate the regions which are equal to each other - /// and their relative orderings. - // This is `pub` because it's used by unstable external borrowck data users, see `consumers.rs`. - pub fn constraint_sccs(&self) -> &ConstraintSccs { - &self.constraint_sccs - } - - /// Returns the representative `RegionVid` for a given SCC. - /// See `RegionTracker` for how a region variable ID is chosen. - /// - /// It is a hacky way to manage checking regions for equality, - /// since we can 'canonicalize' each region to the representative - /// of its SCC and be sure that -- if they have the same repr -- - /// they *must* be equal (though not having the same repr does not - /// mean they are unequal). - fn scc_representative(&self, scc: ConstraintSccIndex) -> RegionVid { - self.scc_annotations[scc].representative.rvid() - } - - pub(crate) fn liveness_constraints(&self) -> &LivenessValues { - &self.liveness_constraints - } - - /// Returns whether the `loan_idx` is live at the given `location`: whether its issuing - /// region is contained within the type of a variable that is live at this point. - /// Note: for now, the sets of live loans is only available when using `-Zpolonius=next`. - pub(crate) fn is_loan_live_at(&self, loan_idx: BorrowIndex, location: Location) -> bool { - let point = self.liveness_constraints.point_from_location(location); - self.liveness_constraints.is_loan_live_at(loan_idx, point) - } -} - #[derive(Clone, Debug)] pub(crate) struct BestBlame<'tcx> { - /// See docs on [`RegionInferenceContext::best_blame_constraint`] for what this is. + /// See docs on [`RegionInferenceContextInner::best_blame_constraint`] for what this is. path: Vec>, /// Index into `path` of the constraint most relevant to report to users. idx: usize, @@ -1928,11 +275,11 @@ impl<'tcx> BestBlame<'tcx> { .path .iter() .find_map(|constraint| { - if let ConstraintCategory::Predicate(predicate_span) = constraint.category { + if let ConstraintCategory::Predicate(predicate_span, def_id) = constraint.category { // We currently do not store the `DefId` in the `ConstraintCategory` // for performances reasons. The error reporting code used by NLL only // uses the span, so this doesn't cause any problems at the moment. - Some(ObligationCauseCode::WhereClause(CRATE_DEF_ID.to_def_id(), predicate_span)) + Some(ObligationCauseCode::WhereClause(def_id, predicate_span)) } else { None } diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs index ad15c5750b092..e3240c2add95c 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs @@ -24,7 +24,7 @@ use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp; use tracing::{debug, instrument}; use super::reverse_sccs::ReverseSccGraph; -use crate::consumers::RegionInferenceContext; +use crate::region_infer::RegionInferenceContext; use crate::session_diagnostics::LifetimeMismatchOpaqueParam; use crate::type_check::canonical::fully_perform_op_raw; use crate::type_check::free_region_relations::UniversalRegionRelations; @@ -171,7 +171,7 @@ fn add_hidden_type<'tcx>( prev.span = prev.span.substitute_dummy(hidden_ty.span); } else { let (Ok(guar) | Err(guar)) = - prev.build_mismatch_error(&hidden_ty, tcx).map(|d| d.emit()); + prev.build_mismatch_error(&hidden_ty, tcx).map(|d| d.emit_err()); *prev = ty::DefinitionSiteHiddenType::new_error(tcx, guar); } } else { @@ -362,11 +362,11 @@ fn compute_definition_site_hidden_types_from_defining_uses<'tcx>( DefinitionSiteHiddenType::new_error(tcx, guar) }); - // Sometimes, when the hidden type is an inference variable, it can happen that - // the hidden type becomes the opaque type itself. In this case, this was an opaque - // usage of the opaque type and we can ignore it. This check is mirrored in typeck's - // writeback. if !rcx.infcx.tcx.use_typing_mode_post_typeck_until_borrowck() { + // Sometimes, when the hidden type is an inference variable, it can happen that + // the hidden type becomes the opaque type itself. In this case, this was an opaque + // usage of the opaque type and we can ignore it. This check is mirrored in typeck's + // writeback. if let &ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) = hidden_type.ty.skip_binder().kind() && def_id == opaque_type_key.def_id.to_def_id() @@ -374,32 +374,31 @@ fn compute_definition_site_hidden_types_from_defining_uses<'tcx>( { continue; } - } - // Check that all opaque types have the same region parameters if they have the same - // non-region parameters. This is necessary because within the new solver we perform - // various query operations modulo regions, and thus could unsoundly select some impls - // that don't hold. - // - // FIXME(-Znext-solver): This isn't necessary after all. We can remove this check again. - if let Some((prev_decl_key, prev_span)) = decls_modulo_regions.insert( - rcx.infcx.tcx.erase_and_anonymize_regions(opaque_type_key), - (opaque_type_key, hidden_type.span), - ) && let Some((arg1, arg2)) = std::iter::zip( - prev_decl_key.iter_captured_args(infcx.tcx).map(|(_, arg)| arg), - opaque_type_key.iter_captured_args(infcx.tcx).map(|(_, arg)| arg), - ) - .find(|(arg1, arg2)| arg1 != arg2) - { - errors.push(DeferredOpaqueTypeError::LifetimeMismatchOpaqueParam( - LifetimeMismatchOpaqueParam { - arg: arg1, - prev: arg2, - span: prev_span, - prev_span: hidden_type.span, - }, - )); + // Check that all opaque types have the same region parameters if they have the same + // non-region parameters. This is necessary because within the new solver we perform + // various query operations modulo regions, and thus could unsoundly select some impls + // that don't hold. + if let Some((prev_decl_key, prev_span)) = decls_modulo_regions.insert( + rcx.infcx.tcx.erase_and_anonymize_regions(opaque_type_key), + (opaque_type_key, hidden_type.span), + ) && let Some((arg1, arg2)) = std::iter::zip( + prev_decl_key.iter_captured_args(infcx.tcx).map(|(_, arg)| arg), + opaque_type_key.iter_captured_args(infcx.tcx).map(|(_, arg)| arg), + ) + .find(|(arg1, arg2)| arg1 != arg2) + { + errors.push(DeferredOpaqueTypeError::LifetimeMismatchOpaqueParam( + LifetimeMismatchOpaqueParam { + arg: arg1, + prev: arg2, + span: prev_span, + prev_span: hidden_type.span, + }, + )); + } } + add_hidden_type(tcx, hidden_types, opaque_type_key.def_id, hidden_type); } } diff --git a/compiler/rustc_borrowck/src/region_infer/region_context.rs b/compiler/rustc_borrowck/src/region_infer/region_context.rs new file mode 100644 index 0000000000000..327acb7da3fb8 --- /dev/null +++ b/compiler/rustc_borrowck/src/region_infer/region_context.rs @@ -0,0 +1,1715 @@ +use std::collections::VecDeque; +use std::ops::{Deref, DerefMut}; +use std::rc::Rc; + +use rustc_data_structures::frozen::Frozen; +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; +use rustc_errors::Diag; +use rustc_index::IndexVec; +use rustc_infer::infer::outlives::test_type_match; +use rustc_infer::infer::region_constraints::{VerifyBound, VerifyIfEq}; +use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin}; +use rustc_middle::mir::{ + AnnotationSource, BasicBlock, Body, ConstraintCategory, Location, ReturnConstraint, + TerminatorKind, +}; +use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable, UniverseIndex, fold_regions}; +use rustc_mir_dataflow::points::DenseLocationMap; +use rustc_span::hygiene::DesugaringKind; +use rustc_span::{DUMMY_SP, bug}; +use tracing::{debug, instrument, trace}; + +use crate::constraints::graph::NormalConstraintGraph; +use crate::constraints::{ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet}; +use crate::consumers::PoloniusOutput; +use crate::dataflow::BorrowIndex; +use crate::diagnostics::{RegionErrorKind, RegionErrors, UniverseInfo}; +use crate::handle_placeholders::{LoweredConstraints, RegionTracker}; +use crate::region_infer::values::{LivenessValues, RegionElement, RegionValues}; +use crate::region_infer::{ + BestBlame, ConstraintSccs, RegionDefinition, RegionRelationCheckResult, Trace, TypeTest, + sccs_info, +}; +use crate::type_check::Locations; +use crate::type_check::free_region_relations::UniversalRegionRelations; +use crate::universal_regions::UniversalRegions; +use crate::{ + BorrowckInferCtxt, ClosureOutlivesRequirement, ClosureOutlivesSubject, + ClosureOutlivesSubjectTy, ClosureRegionRequirements, +}; + +/// Contains the data for `RegionInferenceContext` and `UnsolvedRegionInferenceContext`. +pub struct RegionInferenceContextInner<'tcx> { + /// Contains the definition for every region variable. Region + /// variables are identified by their index (`RegionVid`). The + /// definition contains information about where the region came + /// from as well as its final inferred value. + pub(crate) definitions: Frozen>>, + + /// The liveness constraints added to each region. For most + /// regions, these start out empty and steadily grow, though for + /// each universally quantified region R they start out containing + /// the entire CFG and `end(R)`. + liveness_constraints: LivenessValues, + + /// The outlives constraints computed by the type-check. + pub(super) constraints: Frozen>, + + /// The constraint-set, but in graph form, making it easy to traverse + /// the constraints adjacent to a particular region. Used to construct + /// the SCC (see `constraint_sccs`) and for error reporting. + constraint_graph: Frozen, + + /// The SCC computed from `constraints` and the constraint + /// graph. We have an edge from SCC A to SCC B if `A: B`. Used to + /// compute the values of each region. + pub(super) constraint_sccs: ConstraintSccs, + + scc_annotations: IndexVec, + + /// Map universe indexes to information on why we created it. + universe_causes: FxIndexMap>, + + /// The final inferred values of the region variables; we compute + /// one value per SCC. To get the value for any given *region*, + /// you first find which scc it is a part of. + pub(super) scc_values: RegionValues<'tcx, ConstraintSccIndex>, + + /// Information about how the universally quantified regions in + /// scope on this function relate to one another. + pub(super) universal_region_relations: Frozen>, +} + +/// This contains data around region constraints and liveness, up to and after solving. +/// All data is immutable. +pub struct RegionInferenceContext<'tcx> { + inner: Frozen>, +} + +impl<'tcx> Deref for RegionInferenceContext<'tcx> { + type Target = RegionInferenceContextInner<'tcx>; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl<'tcx> RegionInferenceContext<'tcx> { + /// Performs region inference and report errors if we see any + /// unsatisfiable constraints. If this is a closure, returns the + /// region requirements to propagate to our creator, if any. + #[instrument( + skip(infcx, lowered_constraints, location_map, body, polonius_output), + level = "debug" + )] + pub(crate) fn solve( + infcx: &BorrowckInferCtxt<'tcx>, + lowered_constraints: LoweredConstraints<'tcx>, + universal_region_relations: Frozen>, + location_map: Rc, + body: &Body<'tcx>, + polonius_output: Option>, + ) -> (RegionInferenceContext<'tcx>, Option>, RegionErrors<'tcx>) + { + // 1. We first prepare the data needed for the `UnsolvedRegionInferenceContext` to do the + // solving work. + let universal_regions = &universal_region_relations.universal_regions; + + let LoweredConstraints { + constraint_sccs, + definitions, + outlives_constraints, + scc_annotations, + type_tests, + liveness_constraints, + universe_causes, + placeholder_indices, + } = lowered_constraints; + + debug!("universal_regions: {:#?}", universal_region_relations.universal_regions); + debug!("outlives constraints: {:#?}", outlives_constraints); + debug!("placeholder_indices: {:#?}", placeholder_indices); + debug!("type tests: {:#?}", type_tests); + + let constraint_graph = Frozen::freeze(outlives_constraints.graph(definitions.len())); + + if cfg!(debug_assertions) { + sccs_info(infcx, &constraint_sccs); + } + + let mut scc_values = + RegionValues::new(location_map, universal_regions.len(), placeholder_indices); + + // Initializes the region variables with their initial live points. + for (region, definition) in definitions.iter_enumerated() { + let scc = constraint_sccs.scc(region); + + // For each universally quantified region (lifetime parameter). The + // first N variables always correspond to the regions appearing in the + // function signature (both named and anonymous) and in where-clauses. + match definition.origin { + // For each free, universally quantified region X: + NllRegionVariableOrigin::FreeRegion => { + // Add `end(X)` into the set for X. + scc_values.add_free_region(scc, region); + } + + NllRegionVariableOrigin::Placeholder(placeholder) => { + scc_values.add_placeholder(scc, placeholder); + } + + NllRegionVariableOrigin::Existential { .. } => { + // For existential, regions, nothing to do. + } + } + + // Initially copy the liveness constraints of any region that + // has them, setting `scc_values[scc(region)] |= liveness_constraints[region]`. + // + // These values will later be propagated during + // [`UnsolvedRegionInferenceContext::propagate_constraints()`]. + // The values include any live-at-all-points constraints added previously in `liveness::generate`. + if let Some(liveness) = liveness_constraints.point_liveness(region) { + scc_values.merge_liveness(scc, liveness) + } + } + + let mut unsolved_regioncx = UnsolvedRegionInferenceContext { + inner: RegionInferenceContextInner { + definitions, + liveness_constraints, + constraints: outlives_constraints, + constraint_graph, + constraint_sccs, + scc_annotations, + universe_causes, + universal_region_relations, + scc_values, + }, + type_tests, + }; + + // 2. And now we can do the actual solving, the region inference. + let mir_def_id = body.source.def_id(); + unsolved_regioncx.propagate_constraints(); + + let mut errors_buffer = RegionErrors::new(infcx.tcx); + + // If this is a nested body, we propagate unsatisfied + // outlives constraints to the parent body instead of + // eagerly erroing. + let mut propagated_outlives_requirements = + infcx.tcx.is_typeck_child(mir_def_id).then(Vec::new); + + unsolved_regioncx.check_type_tests( + infcx, + propagated_outlives_requirements.as_mut(), + &mut errors_buffer, + ); + + debug!(?errors_buffer); + debug!(?propagated_outlives_requirements); + + // In Polonius mode, the errors about missing universal region relations are in the output + // and need to be emitted or propagated. Otherwise, we need to check whether the + // constraints were too strong, and if so, emit or propagate those errors. + if infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled() { + unsolved_regioncx.check_polonius_subset_errors( + propagated_outlives_requirements.as_mut(), + &mut errors_buffer, + polonius_output + .as_ref() + .expect("Polonius output is unavailable despite `-Z polonius`"), + ); + } else { + unsolved_regioncx.check_universal_regions( + propagated_outlives_requirements.as_mut(), + &mut errors_buffer, + ); + } + + debug!(?errors_buffer); + + let propagated_outlives_requirements = propagated_outlives_requirements.unwrap_or_default(); + if propagated_outlives_requirements.is_empty() { + ( + RegionInferenceContext { inner: Frozen::freeze(unsolved_regioncx.inner) }, + None, + errors_buffer, + ) + } else { + let num_external_vids = + unsolved_regioncx.universal_regions().num_global_and_external_regions(); + ( + RegionInferenceContext { inner: Frozen::freeze(unsolved_regioncx.inner) }, + Some(ClosureRegionRequirements { + num_external_vids, + outlives_requirements: propagated_outlives_requirements, + }), + errors_buffer, + ) + } + } + + /// Returns `true` if the region `r` contains the point `p`. + pub(crate) fn region_contains_point(&self, r: RegionVid, p: Location) -> bool { + let scc = self.constraint_sccs.scc(r); + self.scc_values.contains_point(scc, p) + } + + /// Returns the lowest statement index in `start..=end` which is not contained by `r`. + pub(crate) fn first_non_contained_inclusive( + &self, + r: RegionVid, + block: BasicBlock, + start: usize, + end: usize, + ) -> Option { + let scc = self.constraint_sccs.scc(r); + self.scc_values.first_non_contained_inclusive(scc, block, start, end) + } +} + +/// This contains data around region constraints and liveness, up to solving. +struct UnsolvedRegionInferenceContext<'tcx> { + inner: RegionInferenceContextInner<'tcx>, + + /// Type constraints that we check after solving. + type_tests: Vec>, +} + +impl<'tcx> Deref for UnsolvedRegionInferenceContext<'tcx> { + type Target = RegionInferenceContextInner<'tcx>; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl<'tcx> DerefMut for UnsolvedRegionInferenceContext<'tcx> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + +impl<'tcx> RegionInferenceContextInner<'tcx> { + /// Returns an iterator over all the region indices. + pub(crate) fn regions(&self) -> impl Iterator + 'tcx { + self.definitions.indices() + } + + /// Given a universal region in scope on the MIR, returns the + /// corresponding index. + /// + /// Panics if `r` is not a registered universal region, most notably + /// if it is a placeholder. Handling placeholders requires access to the + /// `MirTypeckRegionConstraints`. + pub(crate) fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid { + self.universal_regions().to_region_vid(r) + } + + /// Returns an iterator over all the outlives constraints. + pub(crate) fn outlives_constraints(&self) -> impl Iterator> { + self.constraints.outlives().iter().copied() + } + + /// Adds annotations for `#[rustc_regions]`; see `UniversalRegions::annotate`. + pub(crate) fn annotate(&self, tcx: TyCtxt<'tcx>, err: &mut Diag<'_>) { + self.universal_regions().annotate(tcx, err) + } + + /// Returns access to the value of `r` for debugging purposes. + pub(crate) fn region_value_str(&self, r: RegionVid) -> String { + let scc = self.constraint_sccs.scc(r); + self.scc_values.region_value_str(scc) + } + + pub(crate) fn placeholders_contained_in( + &self, + r: RegionVid, + ) -> impl Iterator> { + let scc = self.constraint_sccs.scc(r); + self.scc_values.placeholders_contained_in(scc) + } + + /// Like `universal_upper_bound`, but returns an approximation more suitable + /// for diagnostics. If `r` contains multiple disjoint universal regions + /// (e.g. 'a and 'b in `fn foo<'a, 'b> { ... }`, we pick the lower-numbered region. + /// This corresponds to picking named regions over unnamed regions + /// (e.g. picking early-bound regions over a closure late-bound region). + /// + /// This means that the returned value may not be a true upper bound, since + /// only 'static is known to outlive disjoint universal regions. + /// Therefore, this method should only be used in diagnostic code, + /// where displaying *some* named universal region is better than + /// falling back to 'static. + #[instrument(level = "debug", skip(self))] + pub(crate) fn approx_universal_upper_bound(&self, r: RegionVid) -> RegionVid { + debug!("{}", self.region_value_str(r)); + + // Find the smallest universal region that contains all other + // universal regions within `region`. + let mut lub = self.universal_regions().fr_fn_body; + let r_scc = self.constraint_sccs.scc(r); + let static_r = self.universal_regions().fr_static; + for ur in self.scc_values.universal_regions_outlived_by(r_scc) { + let new_lub = self.universal_region_relations.postdom_upper_bound(lub, ur); + debug!(?ur, ?lub, ?new_lub); + // The upper bound of two non-static regions is static: this + // means we know nothing about the relationship between these + // two regions. Pick a 'better' one to use when constructing + // a diagnostic + if ur != static_r && lub != static_r && new_lub == static_r { + // Prefer the region with an `external_name` - this + // indicates that the region is early-bound, so working with + // it can produce a nicer error. + if self.region_definition(ur).external_name.is_some() { + lub = ur; + } else if self.region_definition(lub).external_name.is_some() { + // Leave lub unchanged + } else { + // If we get here, we don't have any reason to prefer + // one region over the other. Just pick the + // one with the lower index for now. + lub = std::cmp::min(ur, lub); + } + } else { + lub = new_lub; + } + } + + debug!(?r, ?lub); + + lub + } + + /// The largest universe of any region nameable from this SCC. + pub(super) fn max_nameable_universe(&self, scc: ConstraintSccIndex) -> UniverseIndex { + self.scc_annotations[scc].max_nameable_universe() + } + + pub(crate) fn constraint_path_between_regions( + &self, + from_region: RegionVid, + to_region: RegionVid, + ) -> Option>> { + if from_region == to_region { + bug!("Tried to find a path between {from_region:?} and itself!"); + } + self.constraint_path_to(from_region, |to| to == to_region, true).map(|o| o.0) + } + + /// Walks the graph of constraints (where `'a: 'b` is considered + /// an edge `'a -> 'b`) to find a path from `from_region` to + /// `to_region`. + /// + /// Returns: a series of constraints as well as the region `R` + /// that passed the target test. + /// If `include_static_outlives_all` is `true`, then the synthetic + /// outlives constraints `'static -> a` for every region `a` are + /// considered in the search, otherwise they are ignored. + #[instrument(skip(self, target_test), ret)] + pub(crate) fn constraint_path_to( + &self, + from_region: RegionVid, + target_test: impl Fn(RegionVid) -> bool, + include_placeholder_static: bool, + ) -> Option<(Vec>, RegionVid)> { + self.find_constraint_path_between_regions_inner( + true, + from_region, + &target_test, + include_placeholder_static, + ) + .or_else(|| { + self.find_constraint_path_between_regions_inner( + false, + from_region, + &target_test, + include_placeholder_static, + ) + }) + } + + /// The constraints we get from equating the hidden type of each use of an opaque + /// with its final hidden type may end up getting preferred over other, potentially + /// longer constraint paths. + /// + /// Given that we compute the final hidden type by relying on this existing constraint + /// path, this can easily end up hiding the actual reason for why we require these regions + /// to be equal. + /// + /// To handle this, we first look at the path while ignoring these constraints and then + /// retry while considering them. This is not perfect, as the `from_region` may have already + /// been partially related to its argument region, so while we rely on a member constraint + /// to get a complete path, the most relevant step of that path already existed before then. + fn find_constraint_path_between_regions_inner( + &self, + ignore_opaque_type_constraints: bool, + from_region: RegionVid, + target_test: impl Fn(RegionVid) -> bool, + include_placeholder_static: bool, + ) -> Option<(Vec>, RegionVid)> { + let mut context = IndexVec::from_elem(Trace::NotVisited, &self.definitions); + context[from_region] = Trace::StartRegion; + + let fr_static = self.universal_regions().fr_static; + + // Use a deque so that we do a breadth-first search. We will + // stop at the first match, which ought to be the shortest + // path (fewest constraints). + let mut deque = VecDeque::new(); + deque.push_back(from_region); + + while let Some(r) = deque.pop_front() { + debug!( + "constraint_path_to: from_region={:?} r={:?} value={}", + from_region, + r, + self.region_value_str(r), + ); + + // Check if we reached the region we were looking for. If so, + // we can reconstruct the path that led to it and return it. + if target_test(r) { + let mut result = vec![]; + let mut p = r; + // This loop is cold and runs at the end, which is why we delay + // `OutlivesConstraint` construction until now. + loop { + match context[p] { + Trace::FromGraph(c) => { + p = c.sup; + result.push(*c); + } + + Trace::FromStatic(sub) => { + let c = OutlivesConstraint { + sup: fr_static, + sub, + locations: Locations::All(DUMMY_SP), + span: DUMMY_SP, + category: ConstraintCategory::Internal, + variance_info: ty::VarianceDiagInfo::default(), + from_closure: false, + }; + p = c.sup; + result.push(c); + } + + Trace::StartRegion => { + result.reverse(); + return Some((result, r)); + } + + Trace::NotVisited => { + bug!("found unvisited region {:?} on path to {:?}", p, r) + } + } + } + } + + // Otherwise, walk over the outgoing constraints and + // enqueue any regions we find, keeping track of how we + // reached them. + + // A constraint like `'r: 'x` can come from our constraint + // graph. + + // Always inline this closure because it can be hot. + let mut handle_trace = #[inline(always)] + |sub, trace| { + if let Trace::NotVisited = context[sub] { + context[sub] = trace; + deque.push_back(sub); + } + }; + + // If this is the `'static` region and the graph's direction is normal, then set up the + // Edges iterator to return all regions (#53178). + if r == fr_static && self.constraint_graph.is_normal() { + for sub in self.constraint_graph.outgoing_edges_from_static() { + handle_trace(sub, Trace::FromStatic(sub)); + } + } else { + let edges = self.constraint_graph.outgoing_edges_from_graph(r, &self.constraints); + // This loop can be hot. + for constraint in edges { + match constraint.category { + ConstraintCategory::OutlivesUnnameablePlaceholder(_) + if !include_placeholder_static => + { + debug!("Ignoring illegal placeholder constraint: {constraint:?}"); + continue; + } + ConstraintCategory::OpaqueType if ignore_opaque_type_constraints => { + debug!("Ignoring member constraint: {constraint:?}"); + continue; + } + _ => {} + } + + debug_assert_eq!(constraint.sup, r); + handle_trace(constraint.sub, Trace::FromGraph(constraint)); + } + } + } + + None + } + + /// Finds some region R such that `fr1: R` and `R` is live at `location`. + #[instrument(skip(self), level = "trace", ret)] + pub(crate) fn find_sub_region_live_at(&self, fr1: RegionVid, location: Location) -> RegionVid { + trace!(scc = ?self.constraint_sccs.scc(fr1)); + trace!(universe = ?self.max_nameable_universe(self.constraint_sccs.scc(fr1))); + self.constraint_path_to(fr1, |r| { + trace!(?r, liveness_constraints=?self.liveness_constraints.pretty_print_live_points(r)); + self.liveness_constraints.is_live_at(r, location) + }, true).unwrap().1 + } + + /// Get the region definition of `r`. + pub(crate) fn region_definition(&self, r: RegionVid) -> &RegionDefinition<'tcx> { + &self.definitions[r] + } + + /// Check if the SCC of `r` contains `upper`, a free region. + pub(crate) fn upper_bound_in_region_scc(&self, r: RegionVid, upper: RegionVid) -> bool { + let r_scc = self.constraint_sccs.scc(r); + self.scc_values.contains_free_region(r_scc, upper) + } + + pub(crate) fn universal_regions(&self) -> &UniversalRegions<'tcx> { + &self.universal_region_relations.universal_regions + } + + /// Tries to find the best constraint to blame for the fact that + /// `R: from_region`, where `R` is some region that meets + /// `target_test`. This works by following the constraint graph, + /// creating a constraint path that forces `R` to outlive + /// `from_region`, and then finding the best choices within that + /// path to blame. + #[instrument(level = "debug", skip(self))] + pub(crate) fn best_blame_constraint( + &self, + from_region: RegionVid, + from_region_origin: NllRegionVariableOrigin<'tcx>, + to_region: RegionVid, + ) -> BestBlame<'tcx> { + assert!(from_region != to_region, "Trying to blame a region for itself!"); + + let path = self.constraint_path_between_regions(from_region, to_region).unwrap(); + + // If we are passing through a constraint added because we reached an unnameable placeholder `'unnameable`, + // redirect search towards `'unnameable`. + let due_to_placeholder_outlives = path.iter().find_map(|c| { + if let ConstraintCategory::OutlivesUnnameablePlaceholder(unnameable) = c.category { + Some(unnameable) + } else { + None + } + }); + + // Edge case: it's possible that `'from_region` is an unnameable placeholder. + let mut path = if let Some(unnameable) = due_to_placeholder_outlives + && unnameable != from_region + { + // We ignore the extra edges due to unnameable placeholders to get + // an explanation that was present in the original constraint graph. + self.constraint_path_to(from_region, |r| r == unnameable, false).unwrap().0 + } else { + path + }; + + debug!( + "path={:#?}", + path.iter() + .map(|c| format!( + "{:?} ({:?}: {:?})", + c, + self.constraint_sccs.scc(c.sup), + self.constraint_sccs.scc(c.sub), + )) + .collect::>() + ); + + // When reporting an error, there is typically a chain of constraints leading from some + // "source" region which must outlive some "target" region. + // In most cases, we prefer to "blame" the constraints closer to the target -- + // but there is one exception. When constraints arise from higher-ranked subtyping, + // we generally prefer to blame the source value, + // as the "target" in this case tends to be some type annotation that the user gave. + // Therefore, if we find that the region origin is some instantiation + // of a higher-ranked region, we start our search from the "source" point + // rather than the "target", and we also tweak a few other things. + // + // An example might be this bit of Rust code: + // + // ```rust + // let x: fn(&'static ()) = |_| {}; + // let y: for<'a> fn(&'a ()) = x; + // ``` + // + // In MIR, this will be converted into a combination of assignments and type ascriptions. + // In particular, the 'static is imposed through a type ascription: + // + // ```rust + // x = ...; + // AscribeUserType(x, fn(&'static ()) + // y = x; + // ``` + // + // We wind up ultimately with constraints like + // + // ```rust + // !a: 'temp1 // from the `y = x` statement + // 'temp1: 'temp2 + // 'temp2: 'static // from the AscribeUserType + // ``` + // + // and here we prefer to blame the source (the y = x statement). + let blame_source = match from_region_origin { + NllRegionVariableOrigin::FreeRegion => true, + NllRegionVariableOrigin::Placeholder(_) => false, + // `'existential: 'whatever` never results in a region error by itself. + // We may always infer it to `'static` afterall. This means while an error + // path may go through an existential, these existentials are never the + // `from_region`. + NllRegionVariableOrigin::Existential { name: _ } => { + unreachable!("existentials can outlive everything") + } + }; + + // To pick a constraint to blame, we organize constraints by how interesting we expect them + // to be in diagnostics, then pick the most interesting one closest to either the source or + // the target on our constraint path. + let constraint_interest = |constraint: &OutlivesConstraint<'tcx>| { + // Try to avoid blaming constraints from desugarings, since they may not clearly match + // match what users have written. As an exception, allow blaming returns generated by + // `?` desugaring, since the correspondence is fairly clear. + let category = if let Some(kind) = constraint.span.desugaring_kind() + && (kind != DesugaringKind::QuestionMark + || !matches!(constraint.category, ConstraintCategory::Return(_))) + { + ConstraintCategory::Boring + } else { + constraint.category + }; + + let interest = match category { + // Returns usually provide a type to blame and have specially written diagnostics, + // so prioritize them. + ConstraintCategory::Return(_) => 0, + // Unsizing coercions are interesting, since we have a note for that: + // `BorrowExplanation::add_object_lifetime_default_note`. + // FIXME(dianne): That note shouldn't depend on a coercion being blamed; see issue + // #131008 for an example of where we currently don't emit it but should. + // Once the note is handled properly, this case should be removed. Until then, it + // should be as limited as possible; the note is prone to false positives and this + // constraint usually isn't best to blame. + ConstraintCategory::Cast { + is_raw_ptr_dyn_type_cast: _, + unsize_to: Some(unsize_ty), + is_implicit_coercion: true, + } if to_region == self.universal_regions().fr_static + // Mirror the note's condition, to minimize how often this diverts blame. + && let ty::Adt(_, args) = unsize_ty.kind() + && args.iter().any(|arg| arg.as_type().is_some_and(|ty| ty.is_trait())) + // Mimic old logic for this, to minimize false positives in tests. + && !path + .iter() + .any(|c| matches!(c.category, ConstraintCategory::TypeAnnotation(_))) => + { + 1 + } + // Between other interesting constraints, order by their position on the `path`. + ConstraintCategory::Yield + | ConstraintCategory::UseAsConst + | ConstraintCategory::UseAsStatic + | ConstraintCategory::TypeAnnotation( + AnnotationSource::Ascription + | AnnotationSource::Declaration + | AnnotationSource::OpaqueCast, + ) + | ConstraintCategory::Cast { .. } + | ConstraintCategory::CallArgument(_) + | ConstraintCategory::CopyBound + | ConstraintCategory::SizedBound + | ConstraintCategory::Assignment + | ConstraintCategory::Usage + | ConstraintCategory::ClosureUpvar(_) => 2, + // Generic arguments are unlikely to be what relates regions together + ConstraintCategory::TypeAnnotation(AnnotationSource::GenericArg) => 3, + // We handle predicates and opaque types specially; don't prioritize them here. + ConstraintCategory::Predicate(_, _) | ConstraintCategory::OpaqueType => 4, + // `Boring` constraints can correspond to user-written code and have useful spans, + // but don't provide any other useful information for diagnostics. + ConstraintCategory::Boring => 5, + // `BoringNoLocation` constraints can point to user-written code, but are less + // specific, and are not used for relations that would make sense to blame. + ConstraintCategory::BoringNoLocation => 6, + // Do not blame internal constraints if we can avoid it. Never blame + // the `'region: 'static` constraints introduced by placeholder outlives. + ConstraintCategory::Internal => 7, + ConstraintCategory::OutlivesUnnameablePlaceholder(_) => 8, + ConstraintCategory::SolverRegionConstraint(_) => 9, + }; + + debug!("constraint {constraint:?} category: {category:?}, interest: {interest:?}"); + + interest + }; + + let best_choice = if blame_source { + path.iter().enumerate().rev().min_by_key(|(_, c)| constraint_interest(c)).unwrap().0 + } else { + path.iter().enumerate().min_by_key(|(_, c)| constraint_interest(c)).unwrap().0 + }; + + debug!(?best_choice, ?blame_source); + + let best_blame_idx = if let Some(next) = path.get(best_choice + 1) + && matches!(path[best_choice].category, ConstraintCategory::Return(_)) + && next.category == ConstraintCategory::OpaqueType + { + // The return expression is being influenced by the return type being + // impl Trait, point at the return type and not the return expr. + best_choice + 1 + } else if path[best_choice].category == ConstraintCategory::Return(ReturnConstraint::Normal) + && let Some(field) = path.iter().find_map(|p| { + if let ConstraintCategory::ClosureUpvar(f) = p.category { Some(f) } else { None } + }) + { + path[best_choice].category = + ConstraintCategory::Return(ReturnConstraint::ClosureUpvar(field)); + best_choice + } else { + best_choice + }; + + assert!( + !matches!( + path[best_blame_idx].category, + ConstraintCategory::OutlivesUnnameablePlaceholder(_) + ), + "Illegal placeholder constraint blamed; should have redirected to other region relation" + ); + + BestBlame { path, idx: best_blame_idx } + } + + pub(crate) fn universe_info(&self, universe: ty::UniverseIndex) -> UniverseInfo<'tcx> { + // Query canonicalization can create local superuniverses (for example in + // `InferCtx::query_response_instantiation_guess`), but they don't have an associated + // `UniverseInfo` explaining why they were created. + // This can cause ICEs if these causes are accessed in diagnostics, for example in issue + // #114907 where this happens via liveness and dropck outlives results. + // Therefore, we return a default value in case that happens, which should at worst emit a + // suboptimal error, instead of the ICE. + self.universe_causes.get(&universe).cloned().unwrap_or_else(UniverseInfo::other) + } + + /// Tries to find the terminator of the loop in which the region 'r' resides. + /// Returns the location of the terminator if found. + pub(crate) fn find_loop_terminator_location( + &self, + r: RegionVid, + body: &Body<'_>, + ) -> Option { + let scc = self.constraint_sccs.scc(r); + let locations = self.scc_values.locations_outlived_by(scc); + for location in locations { + let bb = &body[location.block]; + if let Some(terminator) = &bb.terminator + // terminator of a loop should be TerminatorKind::FalseUnwind + && let TerminatorKind::FalseUnwind { .. } = terminator.kind + { + return Some(location); + } + } + None + } + + /// Access to the SCC constraint graph. + /// This can be used to quickly under-approximate the regions which are equal to each other + /// and their relative orderings. + // This is `pub` because it's used by unstable external borrowck data users, see `consumers.rs`. + pub(crate) fn constraint_sccs(&self) -> &ConstraintSccs { + &self.constraint_sccs + } + + pub(crate) fn liveness_constraints(&self) -> &LivenessValues { + &self.liveness_constraints + } + + /// Returns whether the `loan_idx` is live at the given `location`: whether its issuing + /// region is contained within the type of a variable that is live at this point. + /// Note: for now, the sets of live loans is only available when using `-Zpolonius=next`. + pub(crate) fn is_loan_live_at(&self, loan_idx: BorrowIndex, location: Location) -> bool { + let point = self.liveness_constraints.location_map().point_from_location(location); + self.liveness_constraints.is_loan_live_at(loan_idx, point) + } +} + +impl<'tcx> UnsolvedRegionInferenceContext<'tcx> { + /// Propagate the region constraints: this will grow the values + /// for each region variable until all the constraints are + /// satisfied. Note that some values may grow **too** large to be + /// feasible, but we check this later. + #[instrument(skip(self), level = "debug")] + fn propagate_constraints(&mut self) { + debug!("constraints={:#?}", { + let mut constraints: Vec<_> = self.outlives_constraints().collect(); + constraints.sort_by_key(|c| (c.sup, c.sub)); + constraints + .into_iter() + .map(|c| (c, self.constraint_sccs.scc(c.sup), self.constraint_sccs.scc(c.sub))) + .collect::>() + }); + + // To propagate constraints, we walk the DAG induced by the + // SCC. For each SCC `A`, we visit its successors and compute + // their values, then we union all those values to get our + // own. This one-shot approach works because iteration is in + // dependency order. I.e. a chain A: B: C will visit C, B, A. + for scc_a in self.constraint_sccs.all_sccs() { + // Walk each SCC `B` such that `A: B`... + for &scc_b in self.inner.constraint_sccs.successors(scc_a) { + debug!(?scc_b); + self.inner.scc_values.add_region(scc_a, scc_b); + } + } + } + + /// Returns `true` if all the placeholders in the value of `scc_b` are nameable + /// in `scc_a`. Used during constraint propagation, and only once + /// the value of `scc_b` has been computed. + fn can_name_all_placeholders( + &self, + scc_a: ConstraintSccIndex, + scc_b: ConstraintSccIndex, + ) -> bool { + self.scc_annotations[scc_a].can_name_all_placeholders(self.scc_annotations[scc_b]) + } + + /// Once regions have been propagated, this method is used to see + /// whether the "type tests" produced by typeck were satisfied; + /// type tests encode type-outlives relationships like `T: + /// 'a`. See `TypeTest` for more details. + fn check_type_tests( + &self, + infcx: &InferCtxt<'tcx>, + mut propagated_outlives_requirements: Option<&mut Vec>>, + errors_buffer: &mut RegionErrors<'tcx>, + ) { + let tcx = infcx.tcx; + + // Sometimes we register equivalent type-tests that would + // result in basically the exact same error being reported to + // the user. Avoid that. + let mut deduplicate_errors = FxIndexSet::default(); + let mut failed_type_tests = Vec::new(); + + for type_test in &self.type_tests { + debug!("check_type_test: {:?}", type_test); + + let generic_ty = type_test.generic_kind.to_ty(tcx); + if self.eval_verify_bound( + infcx, + generic_ty, + type_test.lower_bound, + &type_test.verify_bound, + ) { + continue; + } + + if let Some(propagated_outlives_requirements) = &mut propagated_outlives_requirements + && self.try_promote_type_test(infcx, type_test, propagated_outlives_requirements) + { + continue; + } + + // Type-test failed. Collect it so we can suppress redundant errors below. + let erased_generic_kind = infcx.tcx.erase_and_anonymize_regions(type_test.generic_kind); + failed_type_tests.push((erased_generic_kind, type_test)); + } + + // An async body can produce both `G: 'static` and `G: 'a` type-test failures at + // the same span, as in `tests/ui/async-await/spurious-static-bound-issue-115376.rs`. + // Reporting the weaker bound adds a redundant diagnostic and suggests a lifetime + // bound that cannot fix the missing `G: 'static` requirement. Keep the `'static` + // error and suppress weaker failures for the same erased generic kind and span. + // This is a diagnostic heuristic, using the same erasure as deduplication below. + // + // Collect all failed `'static` bounds before reporting errors so suppression does + // not depend on the order of the type tests. Compare SCCs because a lower-bound + // region can be equivalent to `'static` without being `fr_static` itself. + let static_scc = self.constraint_sccs.scc(self.universal_regions().fr_static); + let static_bound_errors: FxIndexSet<_> = failed_type_tests + .iter() + .filter_map(|&(erased_generic_kind, type_test)| { + if self.constraint_sccs.scc(type_test.lower_bound) == static_scc { + Some((erased_generic_kind, type_test.span)) + } else { + None + } + }) + .collect(); + + // If `G: 'static` failed at this span, then same-span `G: 'a` failures are weaker. + for (erased_generic_kind, type_test) in failed_type_tests { + if self.constraint_sccs.scc(type_test.lower_bound) != static_scc + && static_bound_errors.contains(&(erased_generic_kind, type_test.span)) + { + continue; + } + + // Skip duplicate-ish errors. + if deduplicate_errors.insert(( + erased_generic_kind, + type_test.lower_bound, + type_test.span, + )) { + debug!( + "check_type_test: reporting error for erased_generic_kind={:?}, \ + lower_bound_region={:?}, \ + type_test.span={:?}", + erased_generic_kind, type_test.lower_bound, type_test.span, + ); + + errors_buffer.push(RegionErrorKind::TypeTestError { type_test: type_test.clone() }); + } + } + } + + /// Invoked when we have some type-test (e.g., `T: 'X`) that we cannot + /// prove to be satisfied. If this is a closure, we will attempt to + /// "promote" this type-test into our `ClosureRegionRequirements` and + /// hence pass it up the creator. To do this, we have to phrase the + /// type-test in terms of external free regions, as local free + /// regions are not nameable by the closure's creator. + /// + /// Promotion works as follows: we first check that the type `T` + /// contains only regions that the creator knows about. If this is + /// true, then -- as a consequence -- we know that all regions in + /// the type `T` are free regions that outlive the closure body. If + /// false, then promotion fails. + /// + /// Once we've promoted T, we have to "promote" `'X` to some region + /// that is "external" to the closure. Generally speaking, a region + /// may be the union of some points in the closure body as well as + /// various free lifetimes. We can ignore the points in the closure + /// body: if the type T can be expressed in terms of external regions, + /// we know it outlives the points in the closure body. That + /// just leaves the free regions. + /// + /// The idea then is to lower the `T: 'X` constraint into multiple + /// bounds -- e.g., if `'X` is the union of two free lifetimes, + /// `'1` and `'2`, then we would create `T: '1` and `T: '2`. + #[instrument(level = "debug", skip(self, infcx, propagated_outlives_requirements))] + fn try_promote_type_test( + &self, + infcx: &InferCtxt<'tcx>, + type_test: &TypeTest<'tcx>, + propagated_outlives_requirements: &mut Vec>, + ) -> bool { + let tcx = infcx.tcx; + let TypeTest { generic_kind, lower_bound, span: blame_span, verify_bound: _ } = *type_test; + + let generic_ty = generic_kind.to_ty(tcx); + let Some(subject) = self.try_promote_type_test_subject(infcx, generic_ty) else { + return false; + }; + + let r_scc = self.constraint_sccs.scc(lower_bound); + debug!( + "lower_bound = {:?} r_scc={:?} universe={:?}", + lower_bound, + r_scc, + self.max_nameable_universe(r_scc) + ); + // If the type test requires that `T: 'a` where `'a` is a + // placeholder from another universe, that effectively requires + // `T: 'static`, so we have to propagate that requirement. + // + // It doesn't matter *what* universe because the promoted `T` will + // always be in the root universe. + if let Some(p) = self.scc_values.placeholders_contained_in(r_scc).next() { + debug!("encountered placeholder in higher universe: {:?}, requiring 'static", p); + let static_r = self.universal_regions().fr_static; + propagated_outlives_requirements.push(ClosureOutlivesRequirement { + subject, + outlived_free_region: static_r, + blame_span, + category: ConstraintCategory::Boring, + }); + + // we can return here -- the code below might push add'l constraints + // but they would all be weaker than this one. + return true; + } + + // For each region outlived by lower_bound find a non-local, + // universal region (it may be the same region) and add it to + // `ClosureOutlivesRequirement`. + let mut found_outlived_universal_region = false; + for ur in self.scc_values.universal_regions_outlived_by(r_scc) { + found_outlived_universal_region = true; + debug!("universal_region_outlived_by ur={:?}", ur); + let non_local_ub = self.universal_region_relations.non_local_upper_bounds(ur); + debug!(?non_local_ub); + + // This is slightly too conservative. To show T: '1, given `'2: '1` + // and `'3: '1` we only need to prove that T: '2 *or* T: '3, but to + // avoid potential non-determinism we approximate this by requiring + // T: '1 and T: '2. + for upper_bound in non_local_ub { + debug_assert!(self.universal_regions().is_universal_region(upper_bound)); + debug_assert!(!self.universal_regions().is_local_free_region(upper_bound)); + + let requirement = ClosureOutlivesRequirement { + subject, + outlived_free_region: upper_bound, + blame_span, + category: ConstraintCategory::Boring, + }; + debug!(?requirement, "adding closure requirement"); + propagated_outlives_requirements.push(requirement); + } + } + // If we succeed to promote the subject, i.e. it only contains non-local regions, + // and fail to prove the type test inside of the closure, the `lower_bound` has to + // also be at least as large as some universal region, as the type test is otherwise + // trivial. + assert!(found_outlived_universal_region); + true + } + + /// When we promote a type test `T: 'r`, we have to replace all region + /// variables in the type `T` with an equal universal region from the + /// closure signature. + /// This is not always possible, so this is a fallible process. + #[instrument(level = "debug", skip(self, infcx), ret)] + fn try_promote_type_test_subject( + &self, + infcx: &InferCtxt<'tcx>, + ty: Ty<'tcx>, + ) -> Option> { + let tcx = infcx.tcx; + let mut failed = false; + let ty = fold_regions(tcx, ty, |r, _depth| { + let r_vid = self.to_region_vid(r); + let r_scc = self.constraint_sccs.scc(r_vid); + + // The challenge is this. We have some region variable `r` + // whose value is a set of CFG points and universal + // regions. We want to find if that set is *equivalent* to + // any of the named regions found in the closure. + // To do so, we simply check every candidate `u_r` for equality. + self.scc_values + .universal_regions_outlived_by(r_scc) + .filter(|&u_r| !self.universal_regions().is_local_free_region(u_r)) + .find(|&u_r| self.eval_equal(u_r, r_vid)) + .map(|u_r| ty::Region::new_var(tcx, u_r)) + // In case we could not find a named region to map to, + // we will return `None` below. + .unwrap_or_else(|| { + failed = true; + r + }) + }); + + debug!("try_promote_type_test_subject: folded ty = {:?}", ty); + + // This will be true if we failed to promote some region. + if failed { + return None; + } + + Some(ClosureOutlivesSubject::Ty(ClosureOutlivesSubjectTy::bind(tcx, ty))) + } + + /// Tests if `test` is true when applied to `lower_bound` at + /// `point`. + fn eval_verify_bound( + &self, + infcx: &InferCtxt<'tcx>, + generic_ty: Ty<'tcx>, + lower_bound: RegionVid, + verify_bound: &VerifyBound<'tcx>, + ) -> bool { + debug!("eval_verify_bound(lower_bound={:?}, verify_bound={:?})", lower_bound, verify_bound); + + match verify_bound { + VerifyBound::IfEq(verify_if_eq_b) => { + self.eval_if_eq(infcx, generic_ty, lower_bound, *verify_if_eq_b) + } + + VerifyBound::IsEmpty => { + let lower_bound_scc = self.constraint_sccs.scc(lower_bound); + self.scc_values.elements_contained_in(lower_bound_scc).next().is_none() + } + + VerifyBound::OutlivedBy(r) => { + let r_vid = self.to_region_vid(*r); + self.eval_outlives(r_vid, lower_bound) + } + + VerifyBound::AnyBound(verify_bounds) => verify_bounds.iter().any(|verify_bound| { + self.eval_verify_bound(infcx, generic_ty, lower_bound, verify_bound) + }), + + VerifyBound::AllBounds(verify_bounds) => verify_bounds.iter().all(|verify_bound| { + self.eval_verify_bound(infcx, generic_ty, lower_bound, verify_bound) + }), + } + } + + fn eval_if_eq( + &self, + infcx: &InferCtxt<'tcx>, + generic_ty: Ty<'tcx>, + lower_bound: RegionVid, + verify_if_eq_b: ty::Binder<'tcx, VerifyIfEq<'tcx>>, + ) -> bool { + let generic_ty = self.normalize_to_scc_representatives(infcx.tcx, generic_ty); + let verify_if_eq_b = self.normalize_to_scc_representatives(infcx.tcx, verify_if_eq_b); + match test_type_match::extract_verify_if_eq(infcx.tcx, &verify_if_eq_b, generic_ty) { + Some(r) => { + let r_vid = self.to_region_vid(r); + self.eval_outlives(r_vid, lower_bound) + } + None => false, + } + } + + /// This is a conservative normalization procedure. It takes every + /// free region in `value` and replaces it with the + /// "representative" of its SCC (see `scc_representatives` field). + /// We are guaranteed that if two values normalize to the same + /// thing, then they are equal; this is a conservative check in + /// that they could still be equal even if they normalize to + /// different results. (For example, there might be two regions + /// with the same value that are not in the same SCC). + /// + /// N.B., this is not an ideal approach and I would like to revisit + /// it. However, it works pretty well in practice. In particular, + /// this is needed to deal with projection outlives bounds like + /// + /// ```text + /// >::Item: '1 + /// ``` + /// + /// In particular, this routine winds up being important when + /// there are bounds like `where >::Item: 'b` in the + /// environment. In this case, if we can show that `'0 == 'a`, + /// and that `'b: '1`, then we know that the clause is + /// satisfied. In such cases, particularly due to limitations of + /// the trait solver =), we usually wind up with a where-clause like + /// `T: Foo<'a>` in scope, which thus forces `'0 == 'a` to be added as + /// a constraint, and thus ensures that they are in the same SCC. + /// + /// So why can't we do a more correct routine? Well, we could + /// *almost* use the `relate_tys` code, but the way it is + /// currently setup it creates inference variables to deal with + /// higher-ranked things and so forth, and right now the inference + /// context is not permitted to make more inference variables. So + /// we use this kind of hacky solution. + fn normalize_to_scc_representatives(&self, tcx: TyCtxt<'tcx>, value: T) -> T + where + T: TypeFoldable>, + { + fold_regions(tcx, value, |r, _db| { + let vid = self.to_region_vid(r); + let scc = self.constraint_sccs.scc(vid); + let repr = self.scc_representative(scc); + ty::Region::new_var(tcx, repr) + }) + } + + /// Evaluate whether `sup_region == sub_region`. + /// + /// Panics if called before `solve()` executes, + // This is `pub` because it's used by unstable external borrowck data users, see `consumers.rs`. + pub(crate) fn eval_equal(&self, r1: RegionVid, r2: RegionVid) -> bool { + self.eval_outlives(r1, r2) && self.eval_outlives(r2, r1) + } + + /// Evaluate whether `sup_region: sub_region`. + /// + /// Panics if called before `solve()` executes, + // This is `pub` because it's used by unstable external borrowck data users, see `consumers.rs`. + #[instrument(skip(self), level = "debug", ret)] + pub(crate) fn eval_outlives(&self, sup_region: RegionVid, sub_region: RegionVid) -> bool { + debug!( + "sup_region's value = {:?} universal={:?}", + self.region_value_str(sup_region), + self.universal_regions().is_universal_region(sup_region), + ); + debug!( + "sub_region's value = {:?} universal={:?}", + self.region_value_str(sub_region), + self.universal_regions().is_universal_region(sub_region), + ); + + let sub_region_scc = self.constraint_sccs.scc(sub_region); + let sup_region_scc = self.constraint_sccs.scc(sup_region); + + if sub_region_scc == sup_region_scc { + debug!("{sup_region:?}: {sub_region:?} holds trivially; they are in the same SCC"); + return true; + } + + let fr_static = self.universal_regions().fr_static; + + // If we are checking that `'sup: 'sub`, and `'sub` contains + // some placeholder that `'sup` cannot name, then this is only + // true if `'sup` outlives static. + // + // Avoid infinite recursion if `sub_region` is already `'static` + if sub_region != fr_static + && !self.can_name_all_placeholders(sup_region_scc, sub_region_scc) + { + debug!( + "sub universe `{sub_region_scc:?}` is not nameable \ + by super `{sup_region_scc:?}`, promoting to static", + ); + + return self.eval_outlives(sup_region, fr_static); + } + + // Both the `sub_region` and `sup_region` consist of the union + // of some number of universal regions (along with the union + // of various points in the CFG; ignore those points for + // now). Therefore, the sup-region outlives the sub-region if, + // for each universal region R1 in the sub-region, there + // exists some region R2 in the sup-region that outlives R1. + let universal_outlives = + self.scc_values.universal_regions_outlived_by(sub_region_scc).all(|r1| { + self.scc_values + .universal_regions_outlived_by(sup_region_scc) + .any(|r2| self.universal_region_relations.outlives(r2, r1)) + }); + + if !universal_outlives { + debug!("sub region contains a universal region not present in super"); + return false; + } + + // Now we have to compare all the points in the sub region and make + // sure they exist in the sup region. + + if self.universal_regions().is_universal_region(sup_region) { + // Micro-opt: universal regions contain all points. + debug!("super is universal and hence contains all points"); + return true; + } + + debug!("comparison between points in sup/sub"); + + self.scc_values.contains_points(sup_region_scc, sub_region_scc) + } + + /// Once regions have been propagated, this method is used to see + /// whether any of the constraints were too strong. In particular, + /// we want to check for a case where a universally quantified + /// region exceeded its bounds. Consider: + /// ```compile_fail + /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x } + /// ``` + /// In this case, returning `x` requires `&'a u32 <: &'b u32` + /// and hence we establish (transitively) a constraint that + /// `'a: 'b`. The `propagate_constraints` code above will + /// therefore add `end('a)` into the region for `'b` -- but we + /// have no evidence that `'b` outlives `'a`, so we want to report + /// an error. + /// + /// If `propagated_outlives_requirements` is `Some`, then we will + /// push unsatisfied obligations into there. Otherwise, we'll + /// report them as errors. + fn check_universal_regions( + &self, + mut propagated_outlives_requirements: Option<&mut Vec>>, + errors_buffer: &mut RegionErrors<'tcx>, + ) { + for (fr, fr_definition) in self.definitions.iter_enumerated() { + debug!(?fr, ?fr_definition); + match fr_definition.origin { + NllRegionVariableOrigin::FreeRegion => { + // Go through each of the universal regions `fr` and check that + // they did not grow too large, accumulating any requirements + // for our caller into the `outlives_requirements` vector. + self.check_universal_region( + fr, + &mut propagated_outlives_requirements, + errors_buffer, + ); + } + + NllRegionVariableOrigin::Placeholder(placeholder) => { + self.check_bound_universal_region(fr, placeholder, errors_buffer); + } + + NllRegionVariableOrigin::Existential { .. } => { + // nothing to check here + } + } + } + } + + /// Checks if Polonius has found any unexpected free region relations. + /// + /// In Polonius terms, a "subset error" (or "illegal subset relation error") is the equivalent + /// of NLL's "checking if any region constraints were too strong": a placeholder origin `'a` + /// was unexpectedly found to be a subset of another placeholder origin `'b`, and means in NLL + /// terms that the "longer free region" `'a` outlived the "shorter free region" `'b`. + /// + /// More details can be found in this blog post by Niko: + /// + /// + /// In the canonical example + /// ```compile_fail + /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x } + /// ``` + /// returning `x` requires `&'a u32 <: &'b u32` and hence we establish (transitively) a + /// constraint that `'a: 'b`. It is an error that we have no evidence that this + /// constraint holds. + /// + /// If `propagated_outlives_requirements` is `Some`, then we will + /// push unsatisfied obligations into there. Otherwise, we'll + /// report them as errors. + fn check_polonius_subset_errors( + &self, + mut propagated_outlives_requirements: Option<&mut Vec>>, + errors_buffer: &mut RegionErrors<'tcx>, + polonius_output: &PoloniusOutput, + ) { + debug!( + "check_polonius_subset_errors: {} subset_errors", + polonius_output.subset_errors.len() + ); + + // Similarly to `check_universal_regions`: a free region relation, which was not explicitly + // declared ("known") was found by Polonius, so emit an error, or propagate the + // requirements for our caller into the `propagated_outlives_requirements` vector. + // + // Polonius doesn't model regions ("origins") as CFG-subsets or durations, but the + // `longer_fr` and `shorter_fr` terminology will still be used here, for consistency with + // the rest of the NLL infrastructure. The "subset origin" is the "longer free region", + // and the "superset origin" is the outlived "shorter free region". + // + // Note: Polonius will produce a subset error at every point where the unexpected + // `longer_fr`'s "placeholder loan" is contained in the `shorter_fr`. This can be helpful + // for diagnostics in the future, e.g. to point more precisely at the key locations + // requiring this constraint to hold. However, the error and diagnostics code downstream + // expects that these errors are not duplicated (and that they are in a certain order). + // Otherwise, diagnostics messages such as the ones giving names like `'1` to elided or + // anonymous lifetimes for example, could give these names differently, while others like + // the outlives suggestions or the debug output from `#[rustc_regions]` would be + // duplicated. The polonius subset errors are deduplicated here, while keeping the + // CFG-location ordering. + // We can iterate the HashMap here because the result is sorted afterwards. + #[allow(rustc::potential_query_instability)] + let mut subset_errors: Vec<_> = polonius_output + .subset_errors + .iter() + .flat_map(|(_location, subset_errors)| subset_errors.iter()) + .collect(); + subset_errors.sort(); + subset_errors.dedup(); + + for &(longer_fr, shorter_fr) in subset_errors.into_iter() { + debug!( + "check_polonius_subset_errors: subset_error longer_fr={:?},\ + shorter_fr={:?}", + longer_fr, shorter_fr + ); + + let propagated = self.try_propagate_universal_region_error( + longer_fr.into(), + shorter_fr.into(), + &mut propagated_outlives_requirements, + ); + if propagated == RegionRelationCheckResult::Error { + errors_buffer.push(RegionErrorKind::RegionError { + longer_fr: longer_fr.into(), + shorter_fr: shorter_fr.into(), + fr_origin: NllRegionVariableOrigin::FreeRegion, + is_reported: true, + }); + } + } + + // Handle the placeholder errors as usual, until the chalk-rustc-polonius triumvirate has + // a more complete picture on how to separate this responsibility. + for (fr, fr_definition) in self.definitions.iter_enumerated() { + match fr_definition.origin { + NllRegionVariableOrigin::FreeRegion => { + // handled by polonius above + } + + NllRegionVariableOrigin::Placeholder(placeholder) => { + self.check_bound_universal_region(fr, placeholder, errors_buffer); + } + + NllRegionVariableOrigin::Existential { .. } => { + // nothing to check here + } + } + } + } + + /// Checks the final value for the free region `fr` to see if it + /// grew too large. In particular, examine what `end(X)` points + /// wound up in `fr`'s final value; for each `end(X)` where `X != + /// fr`, we want to check that `fr: X`. If not, that's either an + /// error, or something we have to propagate to our creator. + /// + /// Things that are to be propagated are accumulated into the + /// `outlives_requirements` vector. + #[instrument(skip(self, propagated_outlives_requirements, errors_buffer), level = "debug")] + fn check_universal_region( + &self, + longer_fr: RegionVid, + propagated_outlives_requirements: &mut Option<&mut Vec>>, + errors_buffer: &mut RegionErrors<'tcx>, + ) { + let longer_fr_scc = self.constraint_sccs.scc(longer_fr); + + // Because this free region must be in the ROOT universe, we + // know it cannot contain any bound universes. + assert!(self.max_nameable_universe(longer_fr_scc).is_root()); + + // Only check all of the relations for the main representative of each + // SCC, otherwise just check that we outlive said representative. This + // reduces the number of redundant relations propagated out of + // closures. + // Note that the representative will be a universal region if there is + // one in this SCC, so we will always check the representative here. + let representative = self.scc_representative(longer_fr_scc); + if representative != longer_fr { + if let RegionRelationCheckResult::Error = self.check_universal_region_relation( + longer_fr, + representative, + propagated_outlives_requirements, + ) { + errors_buffer.push(RegionErrorKind::RegionError { + longer_fr, + shorter_fr: representative, + fr_origin: NllRegionVariableOrigin::FreeRegion, + is_reported: true, + }); + } + return; + } + + // Find every region `o` such that `fr: o` + // (because `fr` includes `end(o)`). + let mut error_reported = false; + for shorter_fr in self.scc_values.universal_regions_outlived_by(longer_fr_scc) { + if let RegionRelationCheckResult::Error = self.check_universal_region_relation( + longer_fr, + shorter_fr, + propagated_outlives_requirements, + ) { + // We only report the first region error. Subsequent errors are hidden so as + // not to overwhelm the user, but we do record them so as to potentially print + // better diagnostics elsewhere... + errors_buffer.push(RegionErrorKind::RegionError { + longer_fr, + shorter_fr, + fr_origin: NllRegionVariableOrigin::FreeRegion, + is_reported: !error_reported, + }); + + error_reported = true; + } + } + } + + /// Checks that we can prove that `longer_fr: shorter_fr`. If we can't we attempt to propagate + /// the constraint outward (e.g. to a closure environment), but if that fails, there is an + /// error. + fn check_universal_region_relation( + &self, + longer_fr: RegionVid, + shorter_fr: RegionVid, + propagated_outlives_requirements: &mut Option<&mut Vec>>, + ) -> RegionRelationCheckResult { + // If it is known that `fr: o`, carry on. + if self.universal_region_relations.outlives(longer_fr, shorter_fr) { + RegionRelationCheckResult::Ok + } else { + // If we are not in a context where we can't propagate errors, or we + // could not shrink `fr` to something smaller, then just report an + // error. + // + // Note: in this case, we use the unapproximated regions to report the + // error. This gives better error messages in some cases. + self.try_propagate_universal_region_error( + longer_fr, + shorter_fr, + propagated_outlives_requirements, + ) + } + } + + /// Attempt to propagate a region error (e.g. `'a: 'b`) that is not met to a closure's + /// creator. If we cannot, then the caller should report an error to the user. + fn try_propagate_universal_region_error( + &self, + longer_fr: RegionVid, + shorter_fr: RegionVid, + propagated_outlives_requirements: &mut Option<&mut Vec>>, + ) -> RegionRelationCheckResult { + if let Some(propagated_outlives_requirements) = propagated_outlives_requirements { + // Shrink `longer_fr` until we find some non-local regions. + // We'll call them `longer_fr-` -- they are ever so slightly smaller than + // `longer_fr`. + let longer_fr_minus = self.universal_region_relations.non_local_lower_bounds(longer_fr); + + debug!("try_propagate_universal_region_error: fr_minus={:?}", longer_fr_minus); + + // If we don't find a any non-local regions, we should error out as there is nothing + // to propagate. + if longer_fr_minus.is_empty() { + return RegionRelationCheckResult::Error; + } + + let best_blame = self.best_blame_constraint( + longer_fr, + NllRegionVariableOrigin::FreeRegion, + shorter_fr, + ); + let OutlivesConstraint { category, span, .. } = best_blame.constraint(); + + // Grow `shorter_fr` until we find some non-local regions. + // We will always find at least one: `'static`. We'll call + // them `shorter_fr+` -- they're ever so slightly larger + // than `shorter_fr`. + let shorter_fr_plus = + self.universal_region_relations.non_local_upper_bounds(shorter_fr); + debug!("try_propagate_universal_region_error: shorter_fr_plus={:?}", shorter_fr_plus); + + // We then create constraints `longer_fr-: shorter_fr+` that may or may not + // be propagated (see below). + let mut constraints = vec![]; + for fr_minus in longer_fr_minus { + for shorter_fr_plus in &shorter_fr_plus { + constraints.push((fr_minus, *shorter_fr_plus)); + } + } + + // We only need to propagate at least one of the constraints for + // soundness. However, we want to avoid arbitrary choices here + // and currently don't support returning OR constraints. + // + // If any of the `shorter_fr+` regions are already outlived by `longer_fr-`, + // we propagate only those. + // + // Consider this example (`'b: 'a` == `a -> b`), where we try to propagate `'d: 'a`: + // a --> b --> d + // \ + // \-> c + // Here, `shorter_fr+` of `'a` == `['b, 'c]`. + // Propagating `'d: 'b` is correct and should occur; `'d: 'c` is redundant because of + // `'d: 'b` and could reject valid code. + // + // So we filter the constraints to regions already outlived by `longer_fr-`, but if + // the filter yields an empty set, we fall back to the original one. + let subset: Vec<_> = constraints + .iter() + .filter(|&&(fr_minus, shorter_fr_plus)| { + self.eval_outlives(fr_minus, shorter_fr_plus) + }) + .copied() + .collect(); + let propagated_constraints = if subset.is_empty() { constraints } else { subset }; + debug!( + "try_propagate_universal_region_error: constraints={:?}", + propagated_constraints + ); + + assert!( + !propagated_constraints.is_empty(), + "Expected at least one constraint to propagate here" + ); + + for (fr_minus, fr_plus) in propagated_constraints { + // Push the constraint `long_fr-: shorter_fr+` + propagated_outlives_requirements.push(ClosureOutlivesRequirement { + subject: ClosureOutlivesSubject::Region(fr_minus), + outlived_free_region: fr_plus, + blame_span: *span, + category: *category, + }); + } + return RegionRelationCheckResult::Propagated; + } + + RegionRelationCheckResult::Error + } + + fn check_bound_universal_region( + &self, + longer_fr: RegionVid, + placeholder: ty::PlaceholderRegion<'tcx>, + errors_buffer: &mut RegionErrors<'tcx>, + ) { + debug!("check_bound_universal_region(fr={:?}, placeholder={:?})", longer_fr, placeholder,); + + let longer_fr_scc = self.constraint_sccs.scc(longer_fr); + debug!("check_bound_universal_region: longer_fr_scc={:?}", longer_fr_scc,); + + // If we have some bound universal region `'a`, then the only + // elements it can contain is itself -- we don't know anything + // else about it! + if let Some(error_element) = self + .scc_values + .elements_contained_in(longer_fr_scc) + .find(|e| *e != RegionElement::PlaceholderRegion(placeholder)) + { + let illegally_outlived_r = self.region_from_element(longer_fr, &error_element); + // Stop after the first error, it gets too noisy otherwise, and does not provide more information. + errors_buffer.push(RegionErrorKind::PlaceholderOutlivesIllegalRegion { + longer_fr, + illegally_outlived_r, + }); + } else { + debug!("check_bound_universal_region: all bounds satisfied"); + } + } + + /// Get the region outlived by `longer_fr` and live at `element`. + fn region_from_element( + &self, + longer_fr: RegionVid, + element: &RegionElement<'tcx>, + ) -> RegionVid { + match *element { + RegionElement::Location(l) => self.find_sub_region_live_at(longer_fr, l), + RegionElement::RootUniversalRegion(r) => r, + RegionElement::PlaceholderRegion(error_placeholder) => self + .definitions + .iter_enumerated() + .find_map(|(r, definition)| match definition.origin { + NllRegionVariableOrigin::Placeholder(p) if p == error_placeholder => Some(r), + _ => None, + }) + .unwrap(), + } + } + + /// Returns the representative `RegionVid` for a given SCC. + /// See `RegionTracker` for how a region variable ID is chosen. + /// + /// It is a hacky way to manage checking regions for equality, + /// since we can 'canonicalize' each region to the representative + /// of its SCC and be sure that -- if they have the same repr -- + /// they *must* be equal (though not having the same repr does not + /// mean they are unequal). + fn scc_representative(&self, scc: ConstraintSccIndex) -> RegionVid { + self.scc_annotations[scc].representative.rvid() + } +} diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index 35009c3bad485..709eddf6e985f 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs @@ -87,6 +87,7 @@ impl LivenessValues { /// Returns the liveness matrix of points where each region is live. Panics if the liveness /// values have been created without any per-point data (that is, for promoteds). + #[inline] pub(crate) fn points(&self) -> &SparseIntervalMatrix { if let LiveRegions::AtPoints(points) = &self.live_regions { points @@ -190,16 +191,6 @@ impl LivenessValues { ) } - #[inline] - pub(crate) fn point_from_location(&self, location: Location) -> PointIndex { - self.location_map.point_from_location(location) - } - - #[inline] - pub(crate) fn location_from_point(&self, point: PointIndex) -> Location { - self.location_map.to_location(point) - } - /// When using `-Zpolonius=next`, records the given live loans for the loan scopes and active /// loans dataflow computations. pub(crate) fn record_live_loans(&mut self, live_loans: LiveLoans) { @@ -213,6 +204,11 @@ impl LivenessValues { .expect("Accessing live loans requires `-Zpolonius=next`") .contains(point, loan_idx) } + + #[inline] + pub(crate) fn location_map(&self) -> &DenseLocationMap { + &self.location_map + } } /// Maps from `ty::PlaceholderRegion` values that are used in the rest of @@ -409,16 +405,6 @@ impl<'tcx, N: Idx> RegionValues<'tcx, N> { } } -/// For debugging purposes, returns a pretty-printed string of the given points. -pub(crate) fn pretty_print_points( - location_map: &DenseLocationMap, - points: impl IntoIterator, -) -> String { - pretty_print_region_elements( - points.into_iter().map(|p| location_map.to_location(p)).map(RegionElement::Location), - ) -} - /// For debugging purposes, returns a pretty-printed string of the given region elements. fn pretty_print_region_elements<'tcx>( elements: impl IntoIterator>, diff --git a/compiler/rustc_borrowck/src/session_diagnostics.rs b/compiler/rustc_borrowck/src/session_diagnostics.rs index a0f347679b05a..8935aed1e7e00 100644 --- a/compiler/rustc_borrowck/src/session_diagnostics.rs +++ b/compiler/rustc_borrowck/src/session_diagnostics.rs @@ -659,13 +659,13 @@ pub(crate) struct SimdIntrinsicArgConst { pub intrinsic: String, } -pub(crate) struct TailExprDropOrder)> { +pub(crate) struct TailExprDropOrder)> { pub borrowed: Span, pub callback: F, } -impl<'a, F: FnOnce(&mut Diag<'_, ()>)> Diagnostic<'a, ()> for TailExprDropOrder { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { +impl<'a, F: FnOnce(&mut Diag<'_>)> Diagnostic<'a> for TailExprDropOrder { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let Self { borrowed, callback } = self; let mut diag = Diag::new(dcx, level, "relative drop order changing in Rust 2024") .with_span_label( diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index 9e4a39873753c..b295a66ee74ee 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -142,15 +142,13 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { #[instrument(level = "debug", skip(self))] pub(super) fn normalize_and_prove_instantiated_clauses( &mut self, - // Keep this parameter for now, in case we start using - // it in `ConstraintCategory` at some point. - _def_id: DefId, + def_id: DefId, instantiated_clauses: ty::InstantiatedClauses<'tcx>, locations: Locations, ) { for (clause, span) in instantiated_clauses { debug!(?span, ?clause); - let category = ConstraintCategory::Predicate(span); + let category = ConstraintCategory::Predicate(span, def_id); let clause = self.normalize_with_category(clause, locations, category); self.prove_clause(clause, locations, category); } diff --git a/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs b/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs index 9c39645ec677d..0bb6ecb8e2049 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs @@ -82,7 +82,7 @@ impl<'a> Iterator for AppearancesIter<'a> { impl LocalUseMap { pub(crate) fn build( - live_locals: &[Local], + mut live_locals: impl Iterator, location_map: &DenseLocationMap, body: &Body<'_>, ) -> Self { @@ -94,13 +94,11 @@ impl LocalUseMap { appearances: IndexVec::new(), }; - if live_locals.is_empty() { - return local_use_map; - } + let Some(head) = live_locals.next() else { return local_use_map }; - let mut locals_with_use_data: DenseBitSet = - DenseBitSet::new_empty(body.local_decls.len()); - live_locals.iter().for_each(|&local| { + let mut locals_with_use_data = DenseBitSet::new_empty(body.local_decls.len()); + locals_with_use_data.insert(head); + live_locals.for_each(|local| { locals_with_use_data.insert(local); }); diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs index 2de6635e93ac7..735fd68a2b1d2 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs @@ -1,15 +1,18 @@ use itertools::{Either, Itertools}; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; +use rustc_index::interval::IntervalSet; use rustc_middle::mir::visit::{TyContext, Visitor}; use rustc_middle::mir::{Body, Local, Location, SourceInfo}; use rustc_middle::ty::relate::Relate; use rustc_middle::ty::{GenericArgsRef, Region, RegionVid, Ty, TyCtxt, TypeVisitable}; use rustc_mir_dataflow::move_paths::MoveData; -use rustc_mir_dataflow::points::DenseLocationMap; +use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex}; use rustc_span::span_bug; +use rustc_trait_selection::traits::outlives_for_liveness::FreeRegionsVisitor; use tracing::debug; use super::TypeChecker; +use crate::BorrowckInferCtxt; use crate::constraints::OutlivesConstraintSet; use crate::polonius::{PoloniusContext, record_live_region_variance}; use crate::region_infer::values::LivenessValues; @@ -18,6 +21,9 @@ use crate::universal_regions::UniversalRegions; mod local_use_map; mod trace; +pub(crate) use local_use_map::LocalUseMap; +pub(crate) use trace::LivenessComputation; + /// Combines liveness analysis with initialization analysis to /// determine which variables are live at which points, both due to /// ordinary uses and drops. Returns a set of (ty, location) pairs @@ -39,35 +45,76 @@ pub(super) fn generate<'tcx>( typeck.constraints.liveness_constraints.add_all_points(region); } - let mut free_regions = regions_that_outlive_free_regions( + let free_regions = regions_that_outlive_free_regions( typeck.infcx.num_region_vars(), &typeck.universal_regions, &typeck.constraints.outlives_constraints, ); - // NLLs can avoid computing some liveness data here because its constraints are - // location-insensitive, but that doesn't work in polonius: locals whose type contains a region - // that outlives a free region are not necessarily live everywhere in a flow-sensitive setting, - // unlike NLLs. - // We do record these regions in the polonius context, since they're used to differentiate - // relevant and boring locals, which is a key distinction used later in diagnostics. - // This additional liveness information is ultimately used for *loan* liveness, - // so we don't need to compute it when there are no loans. - // FIXME: this NLL optimization idea, to reduce work to relevant locals only, still makes sense - // for polonius, and should be investigated to improve liveness performance. - if typeck.tcx().sess.opts.unstable_opts.polonius.is_next_enabled() - && typeck.borrow_set.len() > 0 - { - let (_, boring_locals) = - compute_relevant_live_locals(typeck.tcx(), &free_regions, typeck.body); - typeck.polonius_context.as_mut().unwrap().boring_nll_locals = - boring_locals.into_iter().collect(); - free_regions = typeck.universal_regions.universal_regions_iter().collect(); - } let (relevant_live_locals, boring_locals) = compute_relevant_live_locals(typeck.tcx(), &free_regions, typeck.body); - trace::trace(typeck, location_map, move_data, &relevant_live_locals, &boring_locals); + // Under Polonius Alpha, a larger set of locals are considered relevant: specifically, + // locals containing regions *outliving* universal regions are relevant and only + // locals containing solely universal regions are considered boring. + // + // However, we don't actually need liveness information for *all* these locals, + // only when actually computing loans. So, we can defer computing the liveness + // until we try to propagate the loan, which is gated on `LocalizedConstraintGraph` + // traversal. + // + // Potentially in theory, we could defer computing liveness for *all* locals, + // but that's a much bigger refactor (many things rely on liveness of + // NLL-relevant locals). So, we only defer NLL-boring/Polonius-relevant locals + // for now. + // FIXME: this NLL optimization idea, to reduce work to relevant locals only, still makes sense + // for polonius, and should be investigated to improve liveness performance. + let deferred_locals = if typeck.polonius_context.is_none() || typeck.borrow_set.len() == 0 { + // If we aren't going to be using the additional liveness information, + // don't even bother computing the larger relevant set. + // Similarly, since this liveness information is ultimately used for *loan* + // liveness, we don't need to compute it when there are no loans. + FxIndexSet::default() + } else { + // As described above, we can defer computing liveness for the regions that are + // NLL-boring-and-polonius-relevant. Let's find these. + // + // Note that this is basically a simplified version of `compute_relevant_live_locals` + // avoiding the allocations that are unnecessary in our more limited use-case. + let tcx = typeck.tcx(); + let universal_regions = typeck.universal_regions; + let boring_nll_locals: FxHashSet<_> = boring_locals.iter().copied().collect(); + let deferred_polonius_locals = typeck + .body + .local_decls + .iter_enumerated() + .filter_map(|(local, decl)| { + // The polonius-relevant locals are the ones whose types do not only contain + // universal regions. + if boring_nll_locals.contains(&local) + && !tcx.all_free_regions_meet(&decl.ty, |r| { + universal_regions.is_universal_region(r.as_var()) + }) + { + Some(local) + } else { + None + } + }) + .collect(); + + typeck.polonius_context.as_mut().unwrap().boring_nll_locals = boring_nll_locals; + deferred_polonius_locals + }; + + trace::trace( + typeck, + location_map, + move_data, + &relevant_live_locals, + &boring_locals, + &deferred_locals, + ); // Mark regions that should be live where they appear within rvalues or within a call: like // args, regions, and types. @@ -152,7 +199,7 @@ fn record_regular_live_regions<'tcx>( tcx: TyCtxt<'tcx>, liveness_constraints: &mut LivenessValues, universal_regions: &UniversalRegions<'tcx>, - polonius_context: &mut Option, + polonius_context: &mut Option>, body: &Body<'tcx>, ) { let mut visitor = @@ -167,7 +214,7 @@ struct LiveVariablesVisitor<'a, 'tcx> { tcx: TyCtxt<'tcx>, liveness_constraints: &'a mut LivenessValues, universal_regions: &'a UniversalRegions<'tcx>, - polonius_context: &'a mut Option, + polonius_context: &'a mut Option>, } impl<'a, 'tcx> Visitor<'tcx> for LiveVariablesVisitor<'a, 'tcx> { @@ -229,3 +276,18 @@ impl<'a, 'tcx> LiveVariablesVisitor<'a, 'tcx> { } } } + +pub(crate) fn make_all_regions_live<'tcx>( + infcx: &BorrowckInferCtxt<'tcx>, + universal_regions: &UniversalRegions<'tcx>, + liveness: &mut LivenessValues, + value: impl TypeVisitable>, + live_at: &IntervalSet, +) { + debug!("make_all_regions_live(value={value:?})"); + value.visit_with(&mut FreeRegionsVisitor { + tcx: infcx.tcx, + param_env: infcx.param_env, + op: |r| liveness.add_points(universal_regions.to_region_vid(r), live_at), + }); +} diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 90126866cd500..05bd139934e4d 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -6,7 +6,7 @@ use rustc_infer::infer::canonical::QueryRegionConstraints; use rustc_infer::traits::TraitErrors; use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, Local, Location}; use rustc_middle::traits::query::DropckOutlivesResult; -use rustc_middle::ty::{Ty, TyCtxt, TypeVisitable, TypeVisitableExt}; +use rustc_middle::ty::{GenericArg, Ty, TypeVisitableExt}; use rustc_mir_dataflow::impls::MaybeInitializedPlaces; use rustc_mir_dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex}; use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex}; @@ -14,16 +14,17 @@ use rustc_mir_dataflow::{Analysis, MaybeReachable, ResultsCursor}; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::traits::ObligationCtxt; -use rustc_trait_selection::traits::outlives_for_liveness::FreeRegionsVisitor; use rustc_trait_selection::traits::query::dropck_outlives; use rustc_trait_selection::traits::query::type_op::{DropckOutlives, TypeOpOutput}; use tracing::debug; -use crate::BorrowckInferCtxt; -use crate::polonius::{self, record_live_region_variance}; -use crate::region_infer::values; +use crate::polonius::{DeferredRegionLiveness, LiveRegionVariances, record_live_region_variance}; +use crate::region_infer::values::LivenessValues; use crate::type_check::liveness::local_use_map::LocalUseMap; +use crate::type_check::liveness::make_all_regions_live; use crate::type_check::{NormalizeLocation, TypeChecker}; +use crate::universal_regions::UniversalRegions; +use crate::{BorrowckInferCtxt, polonius}; /// This is the heart of the liveness computation. For each variable X /// that requires a liveness computation, it walks over all the uses @@ -45,46 +46,48 @@ pub(super) fn trace<'tcx>( move_data: &MoveData<'tcx>, relevant_live_locals: &[Local], boring_locals: &[Local], + deferred_locals: &FxIndexSet, ) { let _timer = typeck.tcx().prof.generic_activity("borrowck_liveness_trace"); - let local_use_map = &LocalUseMap::build(&relevant_live_locals, location_map, typeck.body); - let cx = LivenessContext { - typeck, - flow_inits: None, + // The use map must also cover the deferred locals: their liveness is computed later, from + // this same map, when the loan liveness traversal first reaches one of their regions. + let use_map_locals = relevant_live_locals.iter().chain(deferred_locals).copied(); + let local_use_map = LocalUseMap::build(use_map_locals, location_map, typeck.body); + let comp = LivenessComputation::new( + typeck.infcx, + typeck.body, location_map, - local_use_map, move_data, - term_states: IndexVec::new(), - exit_states: IndexVec::new(), - drop_data: FxIndexMap::default(), - }; + &local_use_map, + ); - let mut results = LivenessResults::new(cx); + let mut results = LivenessResults::new(typeck, comp); - results.add_extra_drop_facts(relevant_live_locals); + results.record_legacy_polonius_drop_facts(relevant_live_locals, &deferred_locals); results.compute_for_all_locals(relevant_live_locals); - results.dropck_boring_locals(boring_locals); + let mut deferred_liveness = DeferredRegionLiveness::default(); + results.dropck_boring_locals(boring_locals, &deferred_locals, &mut deferred_liveness); + + if let Some(polonius_context) = &mut typeck.polonius_context { + polonius_context.deferred_liveness = deferred_liveness; + polonius_context.local_use_map = Some(local_use_map); + } } -/// Contextual state for the type-liveness coroutine. -struct LivenessContext<'a, 'typeck, 'tcx> { - /// Current type-checker, giving us our inference context etc. - /// - /// This also stores the body we're currently analyzing. - typeck: &'a mut TypeChecker<'typeck, 'tcx>, +pub(crate) struct LivenessComputation<'a, 'tcx> { + pub(crate) infcx: &'a BorrowckInferCtxt<'tcx>, + + pub(crate) body: &'a Body<'tcx>, /// Defines the `PointIndex` mapping - location_map: &'a DenseLocationMap, + pub(crate) location_map: &'a DenseLocationMap, /// Mapping to/from the various indices used for initialization tracking. move_data: &'a MoveData<'tcx>, - /// Cache for the results of `dropck_outlives` query. - drop_data: FxIndexMap, DropData<'tcx>>, - /// Results of dataflow tracking which variables (and paths) have been /// initialized. Computed lazily when needed by drop-liveness. flow_inits: Option>>, @@ -96,15 +99,6 @@ struct LivenessContext<'a, 'typeck, 'tcx> { // Caches for the results of `initialized_at_terminator` and `initialized_at_exit`. term_states: IndexVec>>>, exit_states: IndexVec>>>, -} - -struct DropData<'tcx> { - dropck_result: DropckOutlivesResult<'tcx>, - region_constraint_data: Option<&'tcx QueryRegionConstraints<'tcx>>, -} - -struct LivenessResults<'a, 'typeck, 'tcx> { - cx: LivenessContext<'a, 'typeck, 'tcx>, /// Set of points that define the current local. defs: DenseBitSet, @@ -125,43 +119,70 @@ struct LivenessResults<'a, 'typeck, 'tcx> { stack: Vec, } +struct LivenessResults<'a, 'typeck, 'tcx> { + /// Current type-checker, giving us our inference context etc. + typeck: &'a mut TypeChecker<'typeck, 'tcx>, + + /// Cache for the results of `dropck_outlives` query. + drop_data: FxIndexMap, DropData<'tcx>>, + + comp: LivenessComputation<'a, 'tcx>, +} + impl<'a, 'typeck, 'tcx> LivenessResults<'a, 'typeck, 'tcx> { - fn new(cx: LivenessContext<'a, 'typeck, 'tcx>) -> Self { - let num_points = cx.location_map.num_points(); - LivenessResults { - cx, - defs: DenseBitSet::new_empty(num_points), - use_live_at: IntervalSet::new(num_points), - drop_live_at: DenseBitSet::new_empty(num_points), - drop_locations: vec![], - stack: vec![], - } + fn new( + typeck: &'a mut TypeChecker<'typeck, 'tcx>, + comp: LivenessComputation<'a, 'tcx>, + ) -> Self { + LivenessResults { typeck, drop_data: FxIndexMap::default(), comp } } fn compute_for_all_locals(&mut self, relevant_live_locals: &[Local]) { for &local in relevant_live_locals { - self.reset_local_state(); - self.add_defs_for(local); - self.compute_use_live_points_for(local); - self.compute_drop_live_points_for(local); + self.compute_for_local(local); + } + } - let local_ty = self.cx.body().local_decls[local].ty; + fn compute_for_local(&mut self, local: Local) { + // If we end up needing to compute the drop data (because there are + // drop-live points), then we need to register region constraints and + // emit drop facts. + let mut computed_drop_data = None; + + self.comp.compute( + local, + self.typeck.universal_regions, + self.typeck.polonius_context.as_mut().map(|c| &mut c.live_region_variances), + &mut self.typeck.constraints.liveness_constraints, + || { + let local_ty = self.comp.body.local_decls[local].ty; + let local_span = self.comp.body.local_decls[local].source_info.span; + let drop_data = + dropck_local(&self.typeck.infcx, &mut self.drop_data, local_ty, local_span); + let drop_data = computed_drop_data.insert(drop_data); + &drop_data.dropck_result.kinds + }, + ); - if !self.use_live_at.is_empty() { - self.cx.add_use_live_facts_for(local_ty, &self.use_live_at); + if let Some(drop_data) = computed_drop_data { + if let Some(data) = &drop_data.region_constraint_data { + for &drop_location in &self.comp.drop_locations { + self.typeck.push_region_constraints( + drop_location.to_locations(), + ConstraintCategory::Boring, + data, + ); + } } - if !self.drop_live_at.is_empty() { - // `drop_live_at` is using a DenseBitSet, but `add_drop_live_facts_for` expects - // an IntervalSet. We thus convert between those two here. - let mut set: IntervalSet = - IntervalSet::new(self.drop_live_at.domain_size()); - for item in self.drop_live_at.iter() { - // We iterate the `drop_live_at` set from smallest to largest values, so - // we can use append to add things to the interval set at the end. - set.append(item); - } - self.cx.add_drop_live_facts_for(local, local_ty, &self.drop_locations, &set); + for &kind in &drop_data.dropck_result.kinds { + polonius::legacy::emit_drop_facts( + self.typeck.tcx(), + local, + &kind, + self.typeck.universal_regions, + self.typeck.polonius_facts, + ); } } } @@ -172,51 +193,264 @@ impl<'a, 'typeck, 'tcx> LivenessResults<'a, 'typeck, 'tcx> { /// These are all the locals which do not potentially reference a region local /// to this body. Locals which only reference free regions are always drop-live /// and can therefore safely be dropped. - fn dropck_boring_locals(&mut self, boring_locals: &[Local]) { + fn dropck_boring_locals( + &mut self, + boring_locals: &[Local], + deferred_locals: &FxIndexSet, + deferred_liveness: &mut DeferredRegionLiveness<'tcx>, + ) { for &local in boring_locals { - let local_ty = self.cx.body().local_decls[local].ty; - let local_span = self.cx.body().local_decls[local].source_info.span; - dropck_local(&self.cx.typeck.infcx, &mut self.cx.drop_data, local_ty, local_span); + let is_local_deferred = deferred_locals.contains(&local); + self.dropck_boring_local(local, is_local_deferred, deferred_liveness); } } - /// Add extra drop facts needed for Polonius. + fn dropck_boring_local( + &mut self, + local: Local, + is_local_deferred: bool, + deferred_liveness: &mut DeferredRegionLiveness<'tcx>, + ) { + let typeck = &mut *self.typeck; + let local_ty = self.comp.body.local_decls[local].ty; + let local_span = self.comp.body.local_decls[local].source_info.span; + + // If we had treated this as "relevant", we would have run `compute_for_local`. This + // in turn would have skipped calculating dropck *at all* for locals without drop-liveness. + // Calculating drop-liveness is expensive, but we can skip it when we know that there + // are *no* drops (which is relatively cheap). + if is_local_deferred && self.comp.local_use_map.drops(local).next().is_none() { + deferred_liveness.defer_local( + typeck.infcx.tcx, + typeck.universal_regions, + local, + local_ty, + &[], + ); + return; + } + + // We need to compute dropck for *all* boring locals because we report overflows. + // + // FIXME: there is an argument to be made that we don't need to do this for boring locals + // without drop-liveness, because we skip it for *relevant* locals without drop-liveness. + // But, this is preexisting even on NLL, so leaving it for now. + let drop_data = dropck_local(&typeck.infcx, &mut self.drop_data, local_ty, local_span); + + // We are done with *truly* boring locals. + if !is_local_deferred { + return; + } + + // If this local is deferred and has drop region constraints, we need to register + // them, but *only if the local is drop-live*. + // It doesn't really make sense to only check drop-liveness but defer use-liveness, + // so we just treat this as eager. + if drop_data.region_constraint_data.is_some() { + self.compute_for_local(local); + return; + } + + // The only other thing we need to do *eagerly* for deferred locals is to register + // legacy drop facts (because these facts are on `typeck`). + for &kind in &drop_data.dropck_result.kinds { + polonius::legacy::emit_drop_facts( + typeck.tcx(), + local, + &kind, + typeck.universal_regions, + typeck.polonius_facts, + ); + } + + // Finally, we mark that this local is deferred, including the drop kinds. + deferred_liveness.defer_local( + typeck.infcx.tcx, + typeck.universal_regions, + local, + local_ty, + &drop_data.dropck_result.kinds, + ); + } + + /// Add extra drop facts needed for Polonius Legacy. /// /// Add facts for all locals with free regions, since regions may outlive /// the function body only at certain nodes in the CFG. - fn add_extra_drop_facts(&mut self, relevant_live_locals: &[Local]) { - // This collect is more necessary than immediately apparent - // because these facts go into `add_drop_live_facts_for()`, - // which also writes to `polonius_facts`, and so this is genuinely - // a simultaneous overlapping mutable borrow. + fn record_legacy_polonius_drop_facts( + &mut self, + nll_relevant_locals: &[Local], + deferred_polonius_relevant_locals: &FxIndexSet, + ) { + // This is *all wonky* because this used to call a shared + // `add_drop_live_facts_for` function that was also used for regular + // relevant locals. Presumably, this can be cleaned up quite a bit. // FIXME for future hackers: investigate whether this is // actually necessary; these facts come from Polonius // and probably maybe plausibly does not need to go back in. // It may be necessary to just pick out the parts of // `add_drop_live_facts_for()` that make sense. - let Some(facts) = self.cx.typeck.polonius_facts.as_ref() else { return }; + let Some(facts) = self.typeck.polonius_facts.as_ref() else { return }; let facts_to_add: Vec<_> = { - let relevant_live_locals: FxIndexSet<_> = - relevant_live_locals.iter().copied().collect(); + let nll_relevant_locals: FxIndexSet<_> = nll_relevant_locals.iter().copied().collect(); facts .var_dropped_at .iter() .filter_map(|&(local, location_index)| { - let local_ty = self.cx.body().local_decls[local].ty; - if relevant_live_locals.contains(&local) || !local_ty.has_free_regions() { + let local_ty = self.comp.body.local_decls[local].ty; + if nll_relevant_locals.contains(&local) + || deferred_polonius_relevant_locals.contains(&local) + || !local_ty.has_free_regions() + { return None; } - let location = self.cx.typeck.location_table.to_location(location_index); + let location = self.typeck.location_table.to_location(location_index); Some((local, local_ty, location)) }) .collect() }; - let live_at = IntervalSet::new(self.cx.location_map.num_points()); + let live_at = IntervalSet::new(self.comp.location_map.num_points()); for (local, local_ty, location) in facts_to_add { - self.cx.add_drop_live_facts_for(local, local_ty, &[location], &live_at); + let local_span = self.comp.body.local_decls[local].source_info.span; + let drop_data = + dropck_local(&self.typeck.infcx, &mut self.drop_data, local_ty, local_span); + + if let Some(data) = &drop_data.region_constraint_data { + self.typeck.push_region_constraints( + location.to_locations(), + ConstraintCategory::Boring, + data, + ); + } + + for &kind in &drop_data.dropck_result.kinds { + make_all_regions_live( + self.typeck.infcx, + self.typeck.universal_regions, + &mut self.typeck.constraints.liveness_constraints, + kind, + &live_at, + ); + polonius::legacy::emit_drop_facts( + self.typeck.tcx(), + local, + &kind, + self.typeck.universal_regions, + self.typeck.polonius_facts, + ); + } + + if let Some(polonius_context) = self.typeck.polonius_context.as_mut() { + record_live_region_variance( + self.typeck.infcx.tcx, + &mut polonius_context.live_region_variances, + self.typeck.universal_regions, + local_ty, + ); + } + } + } +} + +enum InitAtLocation { + Terminator, + Exit, +} + +impl<'a, 'tcx> LivenessComputation<'a, 'tcx> { + pub(crate) fn new( + infcx: &'a BorrowckInferCtxt<'tcx>, + body: &'a Body<'tcx>, + location_map: &'a DenseLocationMap, + move_data: &'a MoveData<'tcx>, + local_use_map: &'a LocalUseMap, + ) -> Self { + let num_points = location_map.num_points(); + LivenessComputation { + infcx, + body, + location_map, + move_data, + flow_inits: None, + local_use_map, + term_states: IndexVec::new(), + exit_states: IndexVec::new(), + defs: DenseBitSet::new_empty(num_points), + use_live_at: IntervalSet::new(num_points), + drop_live_at: DenseBitSet::new_empty(num_points), + drop_locations: vec![], + stack: vec![], + } + } + + /// Compute for a given local the use- and drop-live points + pub(crate) fn compute<'drop_data>( + &mut self, + local: Local, + universal_regions: &UniversalRegions<'tcx>, + live_region_variances: Option<&mut LiveRegionVariances>, + liveness_constraints: &mut LivenessValues, + get_drop_args: impl FnOnce() -> &'drop_data [GenericArg<'tcx>], + ) where + 'tcx: 'drop_data, + { + let _timer = self.infcx.tcx.prof.generic_activity("borrowck_liveness_compute"); + + self.reset_local_state(); + self.add_defs_for(local); + self.compute_use_live_points_for(local); + self.compute_drop_live_points_for(local); + + let local_ty = self.body.local_decls[local].ty; + + // When using `-Zpolonius=next`, we also record the variance of regions in this live type. + // For dropck in particular, note that we walk the type and not its live components seen in + // the dropck results. See issue #160670. + let is_live_anywhere = !self.use_live_at.is_empty() || !self.drop_live_at.is_empty(); + if is_live_anywhere && let Some(live_region_variances) = live_region_variances { + record_live_region_variance( + self.infcx.tcx, + live_region_variances, + universal_regions, + local_ty, + ); + } + if !self.use_live_at.is_empty() { + make_all_regions_live( + self.infcx, + universal_regions, + liveness_constraints, + local_ty, + &self.use_live_at, + ); + } + if !self.drop_live_at.is_empty() { + let drop_data = get_drop_args(); + + // `compute_drop_live_points_for` computes `drop_live_at` as a `DenseBitSet`, but + // `make_all_regions_live` expects an `IntervalSet`. We thus convert between those two + // here. Using a `DenseBitSet` has better performance, but storing liveness as a dense + // matrix has worse performance. There's probably room here for some cleanup, but this + // works for now. + let mut drop_live_at = IntervalSet::new(self.drop_live_at.domain_size()); + for point in self.drop_live_at.iter() { + // We iterate the `drop_live_at` set from smallest to largest values, so + // we can use append to add things to the interval set at the end. + drop_live_at.append(point); + } + + for &kind in drop_data { + make_all_regions_live( + self.infcx, + universal_regions, + liveness_constraints, + kind, + &drop_live_at, + ); + } } } @@ -231,7 +465,7 @@ impl<'a, 'typeck, 'tcx> LivenessResults<'a, 'typeck, 'tcx> { /// Adds the definitions of `local` into `self.defs`. fn add_defs_for(&mut self, local: Local) { - for def in self.cx.local_use_map.defs(local) { + for def in self.local_use_map.defs(local) { debug!("- defined at {:?}", def); self.defs.insert(def); } @@ -246,14 +480,14 @@ impl<'a, 'typeck, 'tcx> LivenessResults<'a, 'typeck, 'tcx> { fn compute_use_live_points_for(&mut self, local: Local) { debug!("compute_use_live_points_for(local={:?})", local); - self.stack.extend(self.cx.local_use_map.uses(local)); + self.stack.extend(self.local_use_map.uses(local)); while let Some(p) = self.stack.pop() { // We are live in this block from the closest to us of: // // * Inclusively, the block start // * Exclusively, the previous definition (if it's in this block) // * Exclusively, the previous live_at setting (an optimization) - let block_start = self.cx.location_map.to_block_start(p); + let block_start = self.location_map.to_block_start(p); let previous_defs = self.defs.last_set_in(block_start..=p); let previous_live_at = self.use_live_at.last_set_in(block_start..=p); @@ -277,12 +511,12 @@ impl<'a, 'typeck, 'tcx> LivenessResults<'a, 'typeck, 'tcx> { // terminators of predecessor basic blocks. Push those onto the // stack so that the next iteration(s) will process them. - let block = self.cx.location_map.to_location(block_start).block; + let block = self.location_map.to_location(block_start).block; self.stack.extend( - self.cx.body().basic_blocks.predecessors()[block] + self.body.basic_blocks.predecessors()[block] .iter() - .map(|&pred_bb| self.cx.body().terminator_loc(pred_bb)) - .map(|pred_loc| self.cx.location_map.point_from_location(pred_loc)), + .map(|&pred_bb| self.body.terminator_loc(pred_bb)) + .map(|pred_loc| self.location_map.point_from_location(pred_loc)), ); } } @@ -300,15 +534,15 @@ impl<'a, 'typeck, 'tcx> LivenessResults<'a, 'typeck, 'tcx> { fn compute_drop_live_points_for(&mut self, local: Local) { debug!("compute_drop_live_points_for(local={:?})", local); - let Some(mpi) = self.cx.move_data.rev_lookup.find_local(local) else { return }; + let Some(mpi) = self.move_data.rev_lookup.find_local(local) else { return }; debug!("compute_drop_live_points_for: mpi = {:?}", mpi); // Find the drops where `local` is initialized. - for drop_point in self.cx.local_use_map.drops(local) { - let location = self.cx.location_map.to_location(drop_point); - debug_assert_eq!(self.cx.body().terminator_loc(location.block), location,); + for drop_point in self.local_use_map.drops(local) { + let location = self.location_map.to_location(drop_point); + debug_assert_eq!(self.body.terminator_loc(location.block), location,); - if self.cx.initialized_at_terminator(location.block, mpi) { + if self.initialized_at_terminator(location.block, mpi) { let inserted = self.drop_live_at.insert(drop_point); // Right now, we should not visit a drop_point twice. // If we do, this will trigger a debug assert so we know we can optimize. @@ -342,8 +576,8 @@ impl<'a, 'typeck, 'tcx> LivenessResults<'a, 'typeck, 'tcx> { fn compute_drop_live_points_for_block(&mut self, mpi: MovePathIndex, term_point: PointIndex) { debug!( "compute_drop_live_points_for_block(mpi={:?}, term_point={:?})", - self.cx.move_data.move_paths[mpi].place, - self.cx.location_map.to_location(term_point), + self.move_data.move_paths[mpi].place, + self.location_map.to_location(term_point), ); // We are only invoked with terminators where `mpi` is @@ -353,14 +587,14 @@ impl<'a, 'typeck, 'tcx> LivenessResults<'a, 'typeck, 'tcx> { // Otherwise, scan backwards through the statements in the // block. One of them may be either a definition or use // live point. - let term_location = self.cx.location_map.to_location(term_point); - debug_assert_eq!(self.cx.body().terminator_loc(term_location.block), term_location,); + let term_location = self.location_map.to_location(term_point); + debug_assert_eq!(self.body.terminator_loc(term_location.block), term_location,); let block = term_location.block; - let entry_point = self.cx.location_map.entry_point(term_location.block); + let entry_point = self.location_map.entry_point(term_location.block); for p in (entry_point..term_point).rev() { debug!( "compute_drop_live_points_for_block: p = {:?}", - self.cx.location_map.to_location(p) + self.location_map.to_location(p) ); if self.defs.contains(p) { @@ -379,7 +613,7 @@ impl<'a, 'typeck, 'tcx> LivenessResults<'a, 'typeck, 'tcx> { } } - let body = self.cx.typeck.body; + let body = self.body; for &pred_block in body.basic_blocks.predecessors()[block].iter() { debug!("compute_drop_live_points_for_block: pred_block = {:?}", pred_block,); @@ -401,13 +635,13 @@ impl<'a, 'typeck, 'tcx> LivenessResults<'a, 'typeck, 'tcx> { // terminator. *But*, in that case, the terminator is also // a *definition* of the variable, in which case we want // to stop the search anyhow. (But see Note 1 below.) - if !self.cx.initialized_at_exit(pred_block, mpi) { + if !self.initialized_at_exit(pred_block, mpi) { debug!("compute_drop_live_points_for_block: not initialized"); continue; } - let pred_term_loc = self.cx.body().terminator_loc(pred_block); - let pred_term_point = self.cx.location_map.point_from_location(pred_term_loc); + let pred_term_loc = self.body.terminator_loc(pred_block); + let pred_term_point = self.location_map.point_from_location(pred_term_loc); // If the terminator of this predecessor either *assigns* // our value or is a "normal use", then stop. @@ -463,17 +697,6 @@ impl<'a, 'typeck, 'tcx> LivenessResults<'a, 'typeck, 'tcx> { // for the call (`TMP = call()...`) and then a // `Drop(X)` followed by `X = TMP` to swap that with `X`. } -} - -enum InitAtLocation { - Terminator, - Exit, -} - -impl<'tcx> LivenessContext<'_, '_, 'tcx> { - fn body(&self) -> &Body<'tcx> { - self.typeck.body - } /// Returns `true` if the local variable (or some part of it) is initialized /// at the location defined by `init_at_location`. @@ -490,8 +713,8 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { // - there are relevant live locals // - there are drop points for these relevant live locals. let flow_inits = self.flow_inits.get_or_insert_with(|| { - let tcx = self.typeck.tcx(); - let body = self.typeck.body; + let tcx = self.infcx.tcx; + let body = self.body; // FIXME: reduce the `MaybeInitializedPlaces` domain to the useful `MovePath`s. // // This dataflow analysis computes maybe-initializedness of all move paths, which @@ -515,7 +738,7 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { InitAtLocation::Exit => &mut self.exit_states, }; let state = states.get_or_insert_with(block, || { - let terminator_location = self.typeck.body.terminator_loc(block); + let terminator_location = self.body.terminator_loc(block); match init_at_location { InitAtLocation::Terminator => { flow_inits.seek_before_primary_effect(terminator_location) @@ -548,109 +771,14 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { fn initialized_at_exit(&mut self, block: BasicBlock, mpi: MovePathIndex) -> bool { self.initialized_at(block, mpi, InitAtLocation::Exit) } +} - /// Stores the result that all regions in `value` are live for the - /// points `live_at`. - fn add_use_live_facts_for(&mut self, value: Ty<'tcx>, live_at: &IntervalSet) { - debug!("add_use_live_facts_for(value={:?})", value); - Self::make_all_regions_live(self.location_map, self.typeck, value, live_at); - - // When using `-Zpolonius=next`, we also record the variance of regions in this live type. - if let Some(polonius_context) = self.typeck.polonius_context.as_mut() { - record_live_region_variance( - self.typeck.infcx.tcx, - &mut polonius_context.live_region_variances, - self.typeck.universal_regions, - value, - ); - } - } - - /// Some variable with type `live_ty` is "drop live" at `location` - /// -- i.e., it may be dropped later. This means that *some* of - /// the regions in its type must be live at `location`. The - /// precise set will depend on the dropck constraints, and in - /// particular this takes `#[may_dangle]` into account. - fn add_drop_live_facts_for( - &mut self, - dropped_local: Local, - dropped_ty: Ty<'tcx>, - drop_locations: &[Location], - live_at: &IntervalSet, - ) { - debug!( - "add_drop_live_constraint(\ - dropped_local={:?}, \ - dropped_ty={:?}, \ - drop_locations={:?}, \ - live_at={:?})", - dropped_local, - dropped_ty, - drop_locations, - values::pretty_print_points(self.location_map, live_at.iter()), - ); - - let dropped_span = self.body().local_decls[dropped_local].source_info.span; - let drop_data = - dropck_local(&self.typeck.infcx, &mut self.drop_data, dropped_ty, dropped_span); - - if let Some(data) = &drop_data.region_constraint_data { - for &drop_location in drop_locations { - self.typeck.push_region_constraints( - drop_location.to_locations(), - ConstraintCategory::Boring, - data, - ); - } - } - - // All things in the `outlives` array may be touched by - // the destructor and must be live at this point. - for &kind in &drop_data.dropck_result.kinds { - Self::make_all_regions_live(self.location_map, self.typeck, kind, live_at); - polonius::legacy::emit_drop_facts( - self.typeck.tcx(), - dropped_local, - &kind, - self.typeck.universal_regions, - self.typeck.polonius_facts, - ); - } - - // For polonius: since the local is drop live, record the variance of the regions in its - // type, not the ones in the type's live components seen in the dropck results above. See - // issue #160670. - if let Some(polonius_context) = self.typeck.polonius_context.as_mut() { - record_live_region_variance( - self.typeck.infcx.tcx, - &mut polonius_context.live_region_variances, - self.typeck.universal_regions, - dropped_ty, - ); - } - } - - fn make_all_regions_live( - location_map: &DenseLocationMap, - typeck: &mut TypeChecker<'_, 'tcx>, - value: impl TypeVisitable>, - live_at: &IntervalSet, - ) { - debug!("make_all_regions_live(value={:?})", value); - debug!( - "make_all_regions_live: live_at={}", - values::pretty_print_points(location_map, live_at.iter()), - ); - - value.visit_with(&mut FreeRegionsVisitor { - tcx: typeck.tcx(), - param_env: typeck.infcx.param_env, - op: |r| { - let live_region_vid = typeck.universal_regions.to_region_vid(r); - typeck.constraints.liveness_constraints.add_points(live_region_vid, live_at); - }, - }); - } +/// Contains the results of computing dropck for a local. Namely, this includes +/// the dropped types, and overflows found, and the region constraints that must +/// hold at drop. +struct DropData<'tcx> { + dropck_result: DropckOutlivesResult<'tcx>, + region_constraint_data: Option<&'tcx QueryRegionConstraints<'tcx>>, } /// Computes the `DropData` for a given type, caching the result. diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 6418c73173df0..ab0d786c6f264 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -4,11 +4,11 @@ use std::rc::Rc; use std::{fmt, iter, mem}; use rustc_abi::FieldIdx; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::frozen::Frozen; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_index::{IndexSlice, IndexVec}; @@ -24,6 +24,7 @@ use rustc_middle::mir::*; use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::cast::CastTy; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::{ self, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, UserArgs, UserTypeAnnotationIndex, fold_regions, @@ -47,7 +48,7 @@ use crate::region_infer::values::{LivenessValues, PlaceholderIndex, PlaceholderI use crate::session_diagnostics::{MoveUnsized, SimdIntrinsicArgConst}; use crate::type_check::free_region_relations::{CreateResult, UniversalRegionRelations}; use crate::universal_regions::{DefiningTy, UniversalRegions}; -use crate::{BorrowCheckRootCtxt, BorrowckInferCtxt, DeferredClosureRequirements, path_utils}; +use crate::{BorrowckInferCtxt, DeferredClosureRequirements, path_utils}; macro_rules! span_mirbug { ($context:expr, $elem:expr, $($message:tt)*) => ({ @@ -94,7 +95,6 @@ mod relate_tys; /// - `move_data` -- move-data constructed when performing the maybe-init dataflow analysis /// - `location_map` -- map between MIR `Location` and `PointIndex` pub(crate) fn type_check<'tcx>( - root_cx: &BorrowCheckRootCtxt<'_, 'tcx>, infcx: &BorrowckInferCtxt<'tcx>, body: &Body<'tcx>, promoted: &IndexSlice>, @@ -145,7 +145,6 @@ pub(crate) fn type_check<'tcx>( let mut deferred_closure_requirements = Default::default(); let mut typeck = TypeChecker { - root_cx, infcx, last_span: body.span, body, @@ -227,7 +226,6 @@ enum FieldAccessError { /// way, it accrues region constraints -- these can later be used by /// NLL region checking. struct TypeChecker<'a, 'tcx> { - root_cx: &'a BorrowCheckRootCtxt<'a, 'tcx>, infcx: &'a BorrowckInferCtxt<'tcx>, last_span: Span, body: &'a Body<'tcx>, @@ -247,7 +245,7 @@ struct TypeChecker<'a, 'tcx> { constraints: &'a mut MirTypeckRegionConstraints<'tcx>, deferred_closure_requirements: &'a mut DeferredClosureRequirements<'tcx>, /// When using `-Zpolonius=next`, the liveness helper data used to create polonius constraints. - polonius_context: Option, + polonius_context: Option>, } /// Holder struct for passing results from MIR typeck to the rest of the non-lexical regions @@ -258,7 +256,7 @@ pub(crate) struct MirTypeckResults<'tcx> { pub(crate) region_bound_pairs: Frozen>, pub(crate) known_type_outlives_obligations: Frozen>>, pub(crate) deferred_closure_requirements: DeferredClosureRequirements<'tcx>, - pub(crate) polonius_context: Option, + pub(crate) polonius_context: Option>, } /// A collection of region constraints that must be satisfied for the @@ -320,7 +318,7 @@ impl<'tcx> MirTypeckRegionConstraints<'tcx> { /// required to hold. Normally, this is at a particular point which /// created the obligation, but for constraints that the user gave, we /// want the constraint to hold at all points. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub enum Locations { /// Indicates that a type constraint should always be true. This /// is particularly important in the new borrowck analysis for @@ -1363,7 +1361,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(*ty); match (cast_ty_from, cast_ty_to) { - (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Int(_))) => (), + (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Int)) => (), _ => { span_mirbug!( self, @@ -1381,7 +1379,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(*ty); match (cast_ty_from, cast_ty_to) { - (Some(CastTy::Int(_)), Some(CastTy::Ptr(_))) => (), + (Some(CastTy::Int), Some(CastTy::Ptr(_))) => (), _ => { span_mirbug!( self, @@ -1398,7 +1396,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(*ty); match (cast_ty_from, cast_ty_to) { - (Some(CastTy::Int(_)), Some(CastTy::Int(_))) => (), + (Some(from), Some(to)) if from.is_int_like() && to.is_int_like() => (), _ => { span_mirbug!( self, @@ -1415,7 +1413,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(*ty); match (cast_ty_from, cast_ty_to) { - (Some(CastTy::Int(_)), Some(CastTy::Float)) => (), + (Some(CastTy::Int), Some(CastTy::Float)) => (), _ => { span_mirbug!( self, @@ -1432,7 +1430,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(*ty); match (cast_ty_from, cast_ty_to) { - (Some(CastTy::Float), Some(CastTy::Int(_))) => (), + (Some(CastTy::Float), Some(CastTy::Int)) => (), _ => { span_mirbug!( self, @@ -1850,7 +1848,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { assert_eq!(tcx.trait_impl_of_assoc(def_id), None); self.prove_clauses( - args.terms().map(|t| ty::ClauseKind::WellFormed(t.into())), + args.terms().map(|t| ty::ClauseKind::WellFormed(t)), locations, ConstraintCategory::Boring, ); @@ -2715,7 +2713,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { args: GenericArgsRef<'tcx>, location: Location, ) -> ty::InstantiatedClauses<'tcx> { - let root_def_id = self.root_cx.root_def_id(); + let root_def_id = self.infcx.root_def_id; // We will have to handle propagated closure requirements for this closure, // but need to defer this until the nested body has been fully borrow checked. self.deferred_closure_requirements.push((def_id, args, location.to_locations())); diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index e494f363879cb..7a666726cecc3 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -14,10 +14,10 @@ use std::cell::Cell; use std::iter; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Diag; use rustc_hir::BodyOwnerKind; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_index::IndexVec; @@ -614,7 +614,7 @@ impl<'tcx> UniversalRegions<'tcx> { /// that this region imposes on others. The methods in this file /// handle the part about dumping the inference context internal /// state. - pub(crate) fn annotate(&self, tcx: TyCtxt<'tcx>, err: &mut Diag<'_, ()>) { + pub(crate) fn annotate(&self, tcx: TyCtxt<'tcx>, err: &mut Diag<'_>) { match self.defining_ty { DefiningTy::Closure(def_id, args) => { let v = with_no_trimmed_paths!( diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 5039d27a46fb4..845dc8ab544fc 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -314,7 +314,7 @@ fn expand_preparsed_asm( Ok(template_part) => template_part, Err(err) => { return ExpandResult::Ready(Err(match err { - Ok((err, _)) => err.emit(), + Ok((err, _)) => err.emit_err(), Err(guar) => guar, })); } @@ -407,7 +407,7 @@ fn expand_preparsed_asm( if let Some((label, span)) = err.secondary_label { e.span_label(span_in_template(span), label); } - let guar = e.emit(); + let guar = e.emit_err(); return ExpandResult::Ready(Err(guar)); } @@ -481,12 +481,10 @@ fn expand_preparsed_asm( Some(&idx) => Some(idx), None => { let span = arg.position_span; - ecx.dcx() - .create_err(diagnostics::AsmNoMatchedArgumentName { - name: name.to_owned(), - span: span_in_template(span), - }) - .emit(); + ecx.dcx().emit_err(diagnostics::AsmNoMatchedArgumentName { + name: name.to_owned(), + span: span_in_template(span), + }); None } } @@ -590,19 +588,13 @@ pub(super) fn expand_asm<'cx>( return ExpandResult::Retry(()); }; let expr = match mac { - Ok(inline_asm) => Box::new(ast::Expr { - id: ast::DUMMY_NODE_ID, - kind: ast::ExprKind::InlineAsm(Box::new(inline_asm)), - span: sp, - attrs: ast::AttrVec::new(), - tokens: None, - }), + Ok(inline_asm) => ecx.expr(sp, ast::ExprKind::InlineAsm(Box::new(inline_asm))), Err(guar) => DummyResult::raw_expr(sp, Some(guar)), }; MacEager::expr(expr) } Err(err) => { - let guar = err.emit(); + let guar = err.emit_err(); DummyResult::any(sp, guar) } }) @@ -620,19 +612,13 @@ pub(super) fn expand_naked_asm<'cx>( return ExpandResult::Retry(()); }; let expr = match mac { - Ok(inline_asm) => Box::new(ast::Expr { - id: ast::DUMMY_NODE_ID, - kind: ast::ExprKind::InlineAsm(Box::new(inline_asm)), - span: sp, - attrs: ast::AttrVec::new(), - tokens: None, - }), + Ok(inline_asm) => ecx.expr(sp, ast::ExprKind::InlineAsm(Box::new(inline_asm))), Err(guar) => DummyResult::raw_expr(sp, Some(guar)), }; MacEager::expr(expr) } Err(err) => { - let guar = err.emit(); + let guar = err.emit_err(); DummyResult::any(sp, guar) } }) @@ -650,22 +636,16 @@ pub(super) fn expand_global_asm<'cx>( return ExpandResult::Retry(()); }; match mac { - Ok(inline_asm) => MacEager::items(smallvec![Box::new(ast::Item { - attrs: ast::AttrVec::new(), - id: ast::DUMMY_NODE_ID, - kind: ast::ItemKind::GlobalAsm(Box::new(inline_asm)), - vis: ast::Visibility { - span: sp.shrink_to_lo(), - kind: ast::VisibilityKind::Inherited, - }, - span: sp, - tokens: None, - })]), + Ok(inline_asm) => MacEager::items(smallvec![ecx.item( + sp, + ast::AttrVec::new(), + ast::ItemKind::GlobalAsm(Box::new(inline_asm)) + )]), Err(guar) => DummyResult::any(sp, guar), } } Err(err) => { - let guar = err.emit(); + let guar = err.emit_err(); DummyResult::any(sp, guar) } }) diff --git a/compiler/rustc_builtin_macros/src/assert.rs b/compiler/rustc_builtin_macros/src/assert.rs index 106b67d1c8ec7..f5a582ddffcc5 100644 --- a/compiler/rustc_builtin_macros/src/assert.rs +++ b/compiler/rustc_builtin_macros/src/assert.rs @@ -1,8 +1,8 @@ mod context; use rustc_ast::token::Delimiter; -use rustc_ast::tokenstream::{DelimSpan, TokenStream}; -use rustc_ast::{DelimArgs, Expr, ExprKind, MacCall, Path, PathSegment, UnOp, token}; +use rustc_ast::tokenstream::TokenStream; +use rustc_ast::{Expr, ExprKind, Path, UnOp, token}; use rustc_ast_pretty::pprust; use rustc_errors::PResult; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult}; @@ -22,7 +22,7 @@ pub(crate) fn expand_assert<'cx>( let Assert { cond_expr, custom_message } = match parse_assert(cx, span, tts) { Ok(assert) => assert, Err(err) => { - let guar = err.emit(); + let guar = err.emit_err(); return ExpandResult::Ready(DummyResult::any(span, guar)); } }; @@ -34,14 +34,7 @@ pub(crate) fn expand_assert<'cx>( let panic_path = || { if use_panic_2021(span) { // On edition 2021, we always call `$crate::panic::panic_2021!()`. - Path { - span: call_site_span, - segments: cx - .std_path(&[sym::panic, sym::panic_2021]) - .into_iter() - .map(PathSegment::from_ident) - .collect(), - } + cx.path(call_site_span, cx.std_path(&[sym::panic, sym::panic_2021])) } else { // Before edition 2021, we call `panic!()` unqualified, // such that it calls either `std::panic!()` or `core::panic!()`. @@ -51,16 +44,9 @@ pub(crate) fn expand_assert<'cx>( // Simply uses the user provided message instead of generating custom outputs let expr = if let Some(tokens) = custom_message { - let then = cx.expr( + let then = cx.expr_macro_call( call_site_span, - ExprKind::MacCall(Box::new(MacCall { - path: panic_path(), - args: Box::new(DelimArgs { - dspan: DelimSpan::from_single(call_site_span), - delim: Delimiter::Parenthesis, - tokens, - }), - })), + cx.macro_call(call_site_span, panic_path(), Delimiter::Parenthesis, tokens), ); expr_if_not(cx, call_site_span, cond_expr, then, None) } diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs index ef39ca049b811..11c97da0db5ad 100644 --- a/compiler/rustc_builtin_macros/src/assert/context.rs +++ b/compiler/rustc_builtin_macros/src/assert/context.rs @@ -1,8 +1,8 @@ -use rustc_ast::token::{self, Delimiter, IdentIsRaw}; +use rustc_ast::token::{self, Delimiter, IdentKind}; use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree}; use rustc_ast::{ - BinOpKind, BorrowKind, DUMMY_NODE_ID, DelimArgs, Expr, ExprKind, ItemKind, MacCall, MethodCall, - Mutability, Path, PathSegment, Stmt, StructRest, UnOp, UseTree, UseTreeAndId, UseTreeKind, + BinOpKind, BorrowKind, DUMMY_NODE_ID, DelimArgs, Expr, ExprKind, ItemKind, MacCall, Mutability, + Path, Stmt, StructRest, UnOp, UseTree, UseTreeAndId, UseTreeKind, }; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; @@ -163,7 +163,7 @@ impl<'cx, 'a> Context<'cx, 'a> { let captures = self.capture_decls.iter().flat_map(|cap| { [ TokenTree::token_joint( - token::Ident(cap.ident.name, IdentIsRaw::No), + token::Ident(cap.ident.name, IdentKind::Normal), cap.ident.span, ), TokenTree::token_alone(token::Comma, self.span), @@ -321,7 +321,7 @@ impl<'cx, 'a> Context<'cx, 'a> { | ExprKind::Yeet(_) | ExprKind::Become(_) | ExprKind::Yield(_) - | ExprKind::DirectConstArg(_) + | ExprKind::GcaMacro(_) | ExprKind::UnsafeBinderCast(..) => {} } } @@ -382,20 +382,15 @@ impl<'cx, 'a> Context<'cx, 'a> { ); let try_capture_call = self .cx - .stmt_expr(expr_method_call( - self.cx, - PathSegment { - args: None, - id: DUMMY_NODE_ID, - ident: Ident::new(sym::try_capture, self.span), - }, - expr_paren(self.cx, self.span, self.cx.expr_addr_of(self.span, wrapper)), + .stmt_expr(self.cx.expr_method_call( + self.span, + self.cx.expr_paren(self.span, self.cx.expr_addr_of(self.span, wrapper)), + Ident::new(sym::try_capture, self.span), thin_vec![expr_addr_of_mut( self.cx, self.span, self.cx.expr_path(Path::from_ident(capture)), )], - self.span, )) .add_trailing_semicolon(); let local_bind_path = self.cx.expr_path(Path::from_ident(local_bind)); @@ -448,17 +443,3 @@ fn escape_to_fmt(s: &str) -> String { fn expr_addr_of_mut(cx: &ExtCtxt<'_>, sp: Span, e: Box) -> Box { cx.expr(sp, ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, e)) } - -fn expr_method_call( - cx: &ExtCtxt<'_>, - seg: PathSegment, - receiver: Box, - args: ThinVec>, - span: Span, -) -> Box { - cx.expr(span, ExprKind::MethodCall(Box::new(MethodCall { seg, receiver, args, span }))) -} - -fn expr_paren(cx: &ExtCtxt<'_>, sp: Span, e: Box) -> Box { - cx.expr(sp, ExprKind::Paren(e)) -} diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs index 5a9988e076b0d..21f424836e476 100644 --- a/compiler/rustc_builtin_macros/src/autodiff.rs +++ b/compiler/rustc_builtin_macros/src/autodiff.rs @@ -10,13 +10,12 @@ mod llvm_enzyme { use rustc_ast::expand::autodiff_attrs::{ DiffActivity, DiffMode, valid_input_activity, valid_ret_activity, valid_ty_for_activity, }; - use rustc_ast::token::{Lit, LitKind, Token, TokenKind}; + use rustc_ast::token::{IdentKind, Lit, LitKind, Token, TokenKind}; use rustc_ast::tokenstream::*; use rustc_ast::visit::AssocCtxt::*; use rustc_ast::{ - self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AssocItemKind, BindingMode, - FnRetTy, FnSig, GenericArg, GenericArgs, GenericParamKind, Generics, ItemKind, - MetaItemInner, PatKind, Path, PathSegment, TyKind, Visibility, + self as ast, AnonConst, FnRetTy, FnSig, GenericArg, GenericParamKind, Generics, ItemKind, + MetaItemInner, PatKind, TyKind, Visibility, }; use rustc_attr_ir::RustcAutodiff; use rustc_expand::base::{Annotatable, ExtCtxt}; @@ -154,7 +153,7 @@ mod llvm_enzyme { } fn meta_item_inner_to_ts(t: &MetaItemInner, ts: &mut Vec) { - let comma: Token = Token::new(TokenKind::Comma, Span::default()); + let comma = Token::new(TokenKind::Comma, Span::default()); let val = first_ident(t); let t = Token::from_ast_ident(val); ts.push(TokenTree::Token(t, Spacing::Joint)); @@ -264,7 +263,8 @@ mod llvm_enzyme { }; // Insert mode token - let mode_token = Token::new(TokenKind::Ident(mode_symbol, false.into()), Span::default()); + let mode_token = + Token::new(TokenKind::Ident(mode_symbol, IdentKind::Normal), Span::default()); ts.insert(0, TokenTree::Token(mode_token, Spacing::Joint)); ts.insert( 1, @@ -274,7 +274,7 @@ mod llvm_enzyme { // Now, if the user gave a width (vector aka batch-mode ad), then we copy it. // If it is not given, we default to 1 (scalar mode). let start_position; - let kind: LitKind = LitKind::Integer; + let kind = LitKind::Integer; let symbol; if meta_item_vec.len() >= 2 && let Some(width) = width(&meta_item_vec[1]) @@ -286,7 +286,7 @@ mod llvm_enzyme { symbol = sym::integer(1); } - let l: Lit = Lit { kind, symbol, suffix: None }; + let l = Lit { kind, symbol, suffix: None }; let t = Token::new(TokenKind::Literal(l), Span::default()); let comma = Token::new(TokenKind::Comma, Span::default()); ts.push(TokenTree::Token(t, Spacing::Joint)); @@ -299,15 +299,15 @@ mod llvm_enzyme { if !has_ret { // We don't want users to provide a return activity if the function doesn't return anything. // For simplicity, we just add a dummy token to the end of the list. - let t = Token::new(TokenKind::Ident(sym::None, false.into()), Span::default()); + let t = Token::new(TokenKind::Ident(sym::None, IdentKind::Normal), Span::default()); ts.push(TokenTree::Token(t, Spacing::Joint)); ts.push(TokenTree::Token(comma, Spacing::Alone)); } // We remove the last, trailing comma. ts.pop(); - let ts: TokenStream = TokenStream::from_iter(ts); + let ts = TokenStream::from_iter(ts); - let x: RustcAutodiff = from_ast(ecx, &meta_item_vec, has_ret, mode); + let x = from_ast(ecx, &meta_item_vec, has_ret, mode); if !x.is_active() { // We encountered an error, so we return the original item. // This allows us to potentially parse other attributes. @@ -345,8 +345,8 @@ mod llvm_enzyme { let mut rustc_ad_attr = Box::new(ast::NormalAttr::from_ident(Ident::with_dummy_span(sym::rustc_autodiff))); - let ts2: Vec = vec![TokenTree::Token( - Token::new(TokenKind::Ident(sym::never, false.into()), span), + let ts2 = vec![TokenTree::Token( + Token::new(TokenKind::Ident(sym::never, IdentKind::Normal), span), Spacing::Joint, )]; let never_arg = ast::DelimArgs { @@ -381,7 +381,7 @@ mod llvm_enzyme { let mut has_inline_never = false; // Don't add it multiple times: - let orig_annotatable: Annotatable = match item { + let orig_annotatable = match item { Annotatable::Item(ref mut iitem) => { if !iitem.attrs.iter().any(|a| same_attribute(&a.kind, &attr.kind)) { iitem.attrs.push(attr); @@ -438,7 +438,7 @@ mod llvm_enzyme { let d_annotatable = match &item { Annotatable::AssocItem(_, ctxt) => { - let assoc_item: AssocItemKind = ast::AssocItemKind::Fn(d_fn); + let assoc_item = ast::AssocItemKind::Fn(d_fn); let d_fn = Box::new(ast::AssocItem { attrs: d_attrs, id: ast::DUMMY_NODE_ID, @@ -458,12 +458,7 @@ mod llvm_enzyme { Annotatable::Stmt(_) => { let mut d_fn = ecx.item(span, d_attrs, ItemKind::Fn(d_fn)); d_fn.vis = vis; - - Annotatable::Stmt(Box::new(ast::Stmt { - id: ast::DUMMY_NODE_ID, - kind: ast::StmtKind::Item(d_fn), - span, - })) + Annotatable::Stmt(Box::new(ecx.stmt_item(span, d_fn))) } _ => { unreachable!("item kind checked previously") @@ -478,11 +473,11 @@ mod llvm_enzyme { fn assure_mut_ref(ty: &ast::Ty) -> ast::Ty { let mut ty = ty.clone(); match ty.kind { - TyKind::Ptr(ref mut mut_ty) => { - mut_ty.mutbl = ast::Mutability::Mut; + TyKind::Ptr(_, ref mut mutbl) => { + *mutbl = ast::Mutability::Mut; } - TyKind::Ref(_, ref mut mut_ty) => { - mut_ty.mutbl = ast::Mutability::Mut; + TyKind::Ref(_, _, ref mut mutbl) => { + *mutbl = ast::Mutability::Mut; } _ => { panic!("unsupported type: {:?}", ty); @@ -515,10 +510,15 @@ mod llvm_enzyme { .map(|param| { let ty = match ¶m.ty.kind { TyKind::ImplicitSelf => self_ty(), - TyKind::Ref(lt, mt) if matches!(mt.ty.kind, TyKind::ImplicitSelf) => ecx - .ty(span, TyKind::Ref(*lt, ast::MutTy { ty: self_ty(), mutbl: mt.mutbl })), - TyKind::Ptr(mt) if matches!(mt.ty.kind, TyKind::ImplicitSelf) => { - ecx.ty(span, TyKind::Ptr(ast::MutTy { ty: self_ty(), mutbl: mt.mutbl })) + TyKind::Ref(lt, inner_ty, mutbl) + if matches!(inner_ty.kind, TyKind::ImplicitSelf) => + { + ecx.ty(span, TyKind::Ref(*lt, self_ty(), *mutbl)) + } + TyKind::Ptr(inner_ty, mutbl) + if matches!(inner_ty.kind, TyKind::ImplicitSelf) => + { + ecx.ty(span, TyKind::Ptr(self_ty(), *mutbl)) } _ => param.ty.clone(), }; @@ -567,7 +567,7 @@ mod llvm_enzyme { let call_expr = ecx.expr_call( span, ecx.expr_path(enzyme_path), - vec![primal_fn_ptr, diff_path_expr, tuple_expr].into(), + thin_vec![primal_fn_ptr, diff_path_expr, tuple_expr], ); ecx.stmt_expr(call_expr) @@ -590,35 +590,20 @@ mod llvm_enzyme { GenericParamKind::Type { .. } => { let path = ast::Path::from_ident(p.ident); let ty = ecx.ty_path(path); - Some(AngleBracketedArg::Arg(GenericArg::Type(ty))) + Some(GenericArg::Type(ty)) } GenericParamKind::Const { .. } => { let expr = ecx.expr_path(ast::Path::from_ident(p.ident)); let anon_const = AnonConst { id: ast::DUMMY_NODE_ID, value: expr }; - Some(AngleBracketedArg::Arg(GenericArg::Const(anon_const))) + Some(GenericArg::Const(anon_const)) } GenericParamKind::Lifetime => None, }) - .collect::>(); + .collect::>(); - let args: AngleBracketedArgs = AngleBracketedArgs { span, args: generic_args }; - - let segment = PathSegment { - ident, - id: ast::DUMMY_NODE_ID, - args: Some(Box::new(GenericArgs::AngleBracketed(args))), - }; - - let segments = if is_impl { - thin_vec![ - PathSegment { ident: Ident::from_str("Self"), id: ast::DUMMY_NODE_ID, args: None }, - segment, - ] - } else { - thin_vec![segment] - }; - - let path = Path { span, segments }; + let idents = + if is_impl { vec![Ident::new(kw::SelfUpper, span), ident] } else { vec![ident] }; + let path = ecx.path_all(span, false, idents, generic_args); ecx.expr_path(path) } @@ -656,9 +641,7 @@ mod llvm_enzyme { assert!(sig.decl.inputs.len() == x.input_activity.len()); assert!(has_ret == x.has_ret_activity()); let mut d_decl = sig.decl.clone(); - let mut d_inputs = Vec::new(); - let mut new_inputs = Vec::new(); - let mut idents = Vec::new(); + let mut d_inputs = ThinVec::new(); let mut act_ret = ThinVec::new(); // We have two loops, a first one just to check the activities and types and possibly report @@ -723,15 +706,10 @@ mod llvm_enzyme { debug!("{:#?}", &shadow_arg.pat); panic!("not an ident?"); }; - let name: String = format!("d{}_{}", old_name, i); - new_inputs.push(name.clone()); + let name = format!("d{}_{}", old_name, i); let ident = Ident::from_str_and_span(&name, shadow_arg.pat.span); - *shadow_arg.pat = ast::Pat { - id: ast::DUMMY_NODE_ID, - kind: PatKind::Ident(BindingMode::NONE, ident, None), - span: shadow_arg.pat.span, - }; - d_inputs.push(shadow_arg.clone()); + *shadow_arg.pat = ecx.pat_ident(shadow_arg.pat.span, ident); + d_inputs.push(shadow_arg); } } DiffActivity::Dual @@ -754,15 +732,11 @@ mod llvm_enzyme { debug!("{:#?}", &shadow_arg.pat); panic!("not an ident?"); }; - let name: String = format!("b{}_{}", old_name, i); - new_inputs.push(name.clone()); + let name = format!("b{}_{}", old_name, i); let ident = Ident::from_str_and_span(&name, shadow_arg.pat.span); - *shadow_arg.pat = ast::Pat { - id: ast::DUMMY_NODE_ID, - kind: PatKind::Ident(BindingMode::NONE, ident, None), - span: shadow_arg.pat.span, - }; - d_inputs.push(shadow_arg.clone()); + + *shadow_arg.pat = ecx.pat_ident(shadow_arg.pat.span, ident); + d_inputs.push(shadow_arg); } } DiffActivity::Const => { @@ -772,11 +746,6 @@ mod llvm_enzyme { panic!("Should not happen"); } } - if let PatKind::Ident(_, ident, _) = arg.pat.kind { - idents.push(ident); - } else { - panic!("not an ident?"); - } } let active_only_ret = x.ret_activity == DiffActivity::ActiveOnly; @@ -797,33 +766,20 @@ mod llvm_enzyme { }; let name = "dret".to_string(); let ident = Ident::from_str_and_span(&name, ty.span); - let shadow_arg = ast::Param { - attrs: ThinVec::new(), - ty: ty.clone(), - pat: Box::new(ast::Pat { - id: ast::DUMMY_NODE_ID, - kind: PatKind::Ident(BindingMode::NONE, ident, None), - span: ty.span, - }), - id: ast::DUMMY_NODE_ID, - span: ty.span, - is_placeholder: false, - }; + let shadow_arg = ecx.param(ty.span, ident, ty); d_inputs.push(shadow_arg); - new_inputs.push(name); } _ => {} } } - d_decl.inputs = d_inputs.into(); + d_decl.inputs = d_inputs; if x.mode.is_fwd() { let ty = match d_decl.output { FnRetTy::Ty(ref ty) => ty.clone(), FnRetTy::Default(span) => { // We want to return std::hint::black_box(()). - let kind = TyKind::Tup(ThinVec::new()); - let ty = Box::new(rustc_ast::Ty { kind, id: ast::DUMMY_NODE_ID, span }); + let ty = ecx.ty_unit(span); d_decl.output = FnRetTy::Ty(ty.clone()); assert!(matches!(x.ret_activity, DiffActivity::None)); // this won't be used below, so any type would be fine. diff --git a/compiler/rustc_builtin_macros/src/cfg.rs b/compiler/rustc_builtin_macros/src/cfg.rs index b34d928146efd..ae3781380b90e 100644 --- a/compiler/rustc_builtin_macros/src/cfg.rs +++ b/compiler/rustc_builtin_macros/src/cfg.rs @@ -46,7 +46,7 @@ fn parse_cfg(cx: &ExtCtxt<'_>, span: Span, tts: TokenStream) -> Result { - bounds = smallvec![Path(path_std!(marker::Copy))]; + bounds = smallvec![path_std!(cx, span, marker::Copy)]; is_simple = true; substructure = combine_substructure(|c, s, sub| cs_clone_simple(c, s, sub, true)); } @@ -62,13 +61,12 @@ pub(crate) fn expand_deriving_clone( if is_simple { let trivial_def = TraitDef { span, - path: path_std!(clone::TrivialClone), + path: path_std!(cx, span, clone::TrivialClone), skip_path_as_bound: false, needs_copy_as_bound_if_packed: true, additional_bounds: bounds.clone(), supports_unions: true, methods: SmallVec::new(), - associated_types: SmallVec::new(), is_const, safety: Safety::Unsafe(DUMMY_SP), // `TrivialClone` is not part of an API guarantee, so it shouldn't @@ -81,7 +79,7 @@ pub(crate) fn expand_deriving_clone( let trait_def = TraitDef { span, - path: path_std!(clone::Clone), + path: path_std!(cx, span, clone::Clone), skip_path_as_bound: false, needs_copy_as_bound_if_packed: true, additional_bounds: bounds, @@ -91,12 +89,12 @@ pub(crate) fn expand_deriving_clone( generics: cx.empty_generics(span), explicit_self: true, nonself_args: SmallVec::new(), - ret_ty: Self_, + has_other_selflike_arg: false, + ret_ty: cx.ty_self(span), attributes: thin_vec![cx.attr_word(sym::inline, span)], fieldless_variants_strategy: FieldlessVariantsStrategy::Default, combine_substructure: substructure, }], - associated_types: SmallVec::new(), is_const, safety: Safety::Default, document: true, @@ -149,7 +147,7 @@ fn cs_clone_simple( &[sym::clone, sym::AssertParamIsCopy], ); } else { - match substr.fields { + match substr { StaticStruct(vdata, ..) => { process_variant(vdata); } @@ -171,17 +169,18 @@ fn cs_clone(cx: &ExtCtxt<'_>, trait_span: Span, substr: Substructure<'_>) -> Blo cx.expr_call_global(field.span, fn_path.clone(), args) }; + let self_ident = Ident::new(kw::SelfUpper, trait_span); let ctor_path; let all_fields; let vdata; - match substr.fields { + match substr { Struct(vdata_, af) => { - ctor_path = cx.path(trait_span, vec![substr.type_ident]); + ctor_path = cx.path(trait_span, vec![self_ident]); all_fields = af; vdata = vdata_; } EnumMatching(.., variant, af) => { - ctor_path = cx.path(trait_span, vec![substr.type_ident, variant.ident]); + ctor_path = cx.path(trait_span, vec![self_ident, variant.ident]); all_fields = af; vdata = &variant.data; } diff --git a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs index ab9037331050e..7d0db12a450d6 100644 --- a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs +++ b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs @@ -2,8 +2,8 @@ use ast::HasAttrs; use rustc_ast::mut_visit::MutVisitor; use rustc_ast::visit::{BoundKind, Visitor}; use rustc_ast::{ - self as ast, GenericArg, GenericBound, GenericParamKind, Generics, ItemKind, - TraitBoundModifiers, VariantData, WherePredicate, + self as ast, GenericArg, GenericBound, GenericParamKind, ItemKind, TraitBoundModifiers, + VariantData, WherePredicate, }; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_errors::E0802; @@ -12,12 +12,10 @@ use rustc_macros::Diagnostic; use rustc_span::{Ident, Span, Symbol, sym}; use thin_vec::{ThinVec, thin_vec}; +use crate::deriving::generic::*; +use crate::deriving::{new_path, path_std}; use crate::diagnostics; -macro_rules! path { - ($span:expr, $($part:ident)::*) => { vec![$(Ident::new(sym::$part, $span),)*] } -} - pub(crate) fn expand_deriving_coerce_pointee( cx: &ExtCtxt<'_>, span: Span, @@ -43,15 +41,8 @@ pub(crate) fn expand_deriving_coerce_pointee( }; // Convert generic parameters (from the struct) into generic args. - let self_params: Vec<_> = generics - .params - .iter() - .map(|p| match p.kind { - GenericParamKind::Lifetime => GenericArg::Lifetime(cx.lifetime(p.span(), p.ident)), - GenericParamKind::Type { .. } => GenericArg::Type(cx.ty_ident(p.span(), p.ident)), - GenericParamKind::Const { .. } => GenericArg::Const(cx.const_ident(p.span(), p.ident)), - }) - .collect(); + let self_params: Vec<_> = + generics.params.iter().map(|p| generic_param_to_arg(cx, p, p.span())).collect(); let type_params: Vec<_> = generics .params .iter() @@ -98,71 +89,30 @@ pub(crate) fn expand_deriving_coerce_pointee( let attrs = thin_vec![cx.attr_word(sym::automatically_derived, span),]; // # Validity assertion which will be checked later in `rustc_hir_analysis::coherence::builtins`. { - let trait_path = - cx.path_all(span, true, path!(span, core::marker::CoercePointeeValidated), vec![]); - let trait_ref = cx.trait_ref(trait_path); - push( - cx.item( - span, - attrs.clone(), - ast::ItemKind::Impl(ast::Impl { - generics: Generics { - params: generics - .params - .iter() - .map(|p| match &p.kind { - GenericParamKind::Lifetime => { - cx.lifetime_param(p.span(), p.ident, p.bounds.clone()) - } - GenericParamKind::Type { default: _ } => { - cx.typaram(p.span(), p.ident, p.bounds.clone(), None) - } - GenericParamKind::Const { ty, span: _, default: _ } => cx - .const_param( - p.span(), - p.ident, - p.bounds.clone(), - ty.clone(), - None, - ), - }) - .collect(), - where_clause: generics.where_clause.clone(), - span: generics.span, - }, - of_trait: Some(Box::new(ast::TraitImplHeader { - safety: ast::Safety::Default, - polarity: ast::ImplPolarity::Positive, - defaultness: ast::Defaultness::Implicit, - trait_ref, - })), - constness: ast::Const::No, - self_ty: self_type.clone(), - items: ThinVec::new(), - }), - ), - ); + let trait_path = path_std!(cx, span, marker::CoercePointeeValidated); + push(cx.item_trait_impl( + span, + attrs.clone(), + generics_without_defaults(generics), + ast::Safety::Default, + false, + cx.trait_ref(trait_path), + self_type.clone(), + ThinVec::new(), + )); } let mut add_impl_block = |generics, trait_symbol, trait_args| { - let mut parts = path!(span, core::ops); - parts.push(Ident::new(trait_symbol, span)); - let trait_path = cx.path_all(span, true, parts, trait_args); + let trait_path = new_path(cx, span, &[sym::ops, trait_symbol], trait_args); let trait_ref = cx.trait_ref(trait_path); - let item = cx.item( + let item = cx.item_trait_impl( span, attrs.clone(), - ast::ItemKind::Impl(ast::Impl { - generics, - of_trait: Some(Box::new(ast::TraitImplHeader { - safety: ast::Safety::Default, - polarity: ast::ImplPolarity::Positive, - defaultness: ast::Defaultness::Implicit, - trait_ref, - })), - constness: ast::Const::No, - self_ty: self_type.clone(), - items: ThinVec::new(), - }), + generics, + ast::Safety::Default, + false, + trait_ref, + self_type.clone(), + ThinVec::new(), ); push(item); }; @@ -196,7 +146,7 @@ pub(crate) fn expand_deriving_coerce_pointee( return; } let arg = GenericArg::Type(s_ty.clone()); - let unsize = cx.path_all(span, true, path!(span, core::marker::Unsize), vec![arg]); + let unsize = cx.path_all(span, true, cx.std_path(&[sym::marker, sym::Unsize]), vec![arg]); pointee.bounds.push(cx.trait_bound(unsize, false)); // Drop `#[pointee]` attribute since it should not be recognized outside `derive(CoercePointee)` pointee.attrs.retain(|attr| !attr.has_name(sym::pointee)); @@ -313,11 +263,11 @@ pub(crate) fn expand_deriving_coerce_pointee( } } - let extra_param = cx.typaram(span, Ident::new(sym::__S, span), self_bounds, None); + let extra_param = cx.typaram(Ident::new(sym::__S, span), self_bounds, None); impl_generics.params.insert(pointee_param_idx + 1, extra_param); // Add the impl blocks for `DispatchFromDyn` and `CoerceUnsized`. - let gen_args = vec![GenericArg::Type(alt_self_type)]; + let gen_args = vec![alt_self_type]; add_impl_block(impl_generics.clone(), sym::DispatchFromDyn, gen_args.clone()); add_impl_block(impl_generics, sym::CoerceUnsized, gen_args); } diff --git a/compiler/rustc_builtin_macros/src/deriving/const_param_ty.rs b/compiler/rustc_builtin_macros/src/deriving/const_param_ty.rs index 8034a3e9b0b16..c693cbb66fee9 100644 --- a/compiler/rustc_builtin_macros/src/deriving/const_param_ty.rs +++ b/compiler/rustc_builtin_macros/src/deriving/const_param_ty.rs @@ -14,13 +14,12 @@ pub(crate) fn expand_deriving_const_param_ty( ) { let trait_def = TraitDef { span, - path: path_std!(marker::ConstParamTy_), + path: path_std!(cx, span, marker::ConstParamTy_), skip_path_as_bound: false, needs_copy_as_bound_if_packed: false, - additional_bounds: smallvec![ty::Ty::Path(path_std!(cmp::Eq))], + additional_bounds: smallvec![path_std!(cx, span, cmp::Eq)], supports_unions: false, methods: SmallVec::new(), - associated_types: SmallVec::new(), is_const, safety: Safety::Default, document: true, diff --git a/compiler/rustc_builtin_macros/src/deriving/copy.rs b/compiler/rustc_builtin_macros/src/deriving/copy.rs index bae4bd98df465..4ce8598e96711 100644 --- a/compiler/rustc_builtin_macros/src/deriving/copy.rs +++ b/compiler/rustc_builtin_macros/src/deriving/copy.rs @@ -14,13 +14,12 @@ pub(crate) fn expand_deriving_copy( ) { let trait_def = TraitDef { span, - path: path_std!(marker::Copy), + path: path_std!(cx, span, marker::Copy), skip_path_as_bound: false, needs_copy_as_bound_if_packed: false, additional_bounds: SmallVec::new(), supports_unions: true, methods: SmallVec::new(), - associated_types: SmallVec::new(), is_const, safety: Safety::Default, document: true, diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs index e694502d607e8..2f3d2058773d8 100644 --- a/compiler/rustc_builtin_macros/src/deriving/debug.rs +++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs @@ -4,7 +4,6 @@ use rustc_session::config::FmtDebug; use rustc_span::{Ident, Span, Symbol, sym}; use thin_vec::{ThinVec, thin_vec}; -use crate::deriving::generic::ty::*; use crate::deriving::generic::*; use crate::deriving::path_std; @@ -16,11 +15,16 @@ pub(crate) fn expand_deriving_debug( is_const: bool, ) { // &mut ::std::fmt::Formatter - let fmtr = Ref(Box::new(Path(path_std!(fmt::Formatter))), ast::Mutability::Mut); + let fmtr = cx.ty_ref( + span, + cx.ty_path(path_std!(cx, span, fmt::Formatter)), + None, + ast::Mutability::Mut, + ); let trait_def = TraitDef { span, - path: path_std!(fmt::Debug), + path: path_std!(cx, span, fmt::Debug), skip_path_as_bound: false, needs_copy_as_bound_if_packed: true, additional_bounds: SmallVec::new(), @@ -30,13 +34,18 @@ pub(crate) fn expand_deriving_debug( generics: cx.empty_generics(span), explicit_self: true, nonself_args: smallvec![(fmtr, sym::character('f'))], - ret_ty: Path(path_std!(fmt::Result)), + has_other_selflike_arg: false, + ret_ty: cx.ty_path(path_std!(cx, span, fmt::Result)), attributes: thin_vec![cx.attr_word(sym::inline, span)], fieldless_variants_strategy: FieldlessVariantsStrategy::SpecializeIfAllVariantsFieldless, - combine_substructure: combine_substructure(show_substructure), + combine_substructure: combine_substructure(|cx, span, substr| show_substructure( + cx, + span, + substr, + item.kind.ident().unwrap() + )), }], - associated_types: SmallVec::new(), is_const, safety: Safety::Default, document: true, @@ -44,24 +53,30 @@ pub(crate) fn expand_deriving_debug( trait_def.expand(cx, item, push) } -fn show_substructure(cx: &ExtCtxt<'_>, span: Span, substr: Substructure<'_>) -> BlockOrExpr { - // We want to make sure we have the ctxt set so that we can use unstable methods - let span = cx.with_def_site_ctxt(span); +fn formatter_ident(cx: &ExtCtxt<'_>, span: Span) -> Box { + cx.expr_ident_sym(span, sym::character('f')) +} +fn show_substructure( + cx: &ExtCtxt<'_>, + span: Span, + substr: Substructure<'_>, + type_ident: Ident, +) -> BlockOrExpr { let fmt_detail = cx.sess.opts.unstable_opts.fmt_debug; if fmt_detail == FmtDebug::None { return BlockOrExpr::new_expr(cx.expr_ok(span, cx.expr_tuple(span, ThinVec::new()))); } - let (ident, vdata, fields) = match substr.fields { - Struct(vdata, fields) => (substr.type_ident, vdata, fields), + let (ident, vdata, fields) = match substr { + Struct(vdata, fields) => (type_ident, vdata, fields), EnumMatching(v, fields) => (v.ident, &v.data, fields), - AllFieldlessEnum(enum_def) => return show_fieldless_enum(cx, span, enum_def, substr), + AllFieldlessEnum(enum_def) => return show_fieldless_enum(cx, span, enum_def, type_ident), _ => cx.dcx().span_bug(span, "unexpected substructure in `derive(Debug)`"), }; let name = cx.expr_str(span, ident.name); - let fmt = substr.nonselflike_args[0].clone(); + let fmt = formatter_ident(cx, span); // Fieldless enums have been special-cased earlier if fmt_detail == FmtDebug::Shallow { @@ -85,13 +100,14 @@ fn show_substructure(cx: &ExtCtxt<'_>, span: Span, substr: Substructure<'_>) -> // The number of fields that can be handled without an array. const CUTOFF: usize = 5; - let expr_for_field = |field: &FieldInfo, index: usize| -> Box { - if index < fields.len() - 1 { - field.self_expr.clone() + let len = fields.len(); + let expr_for_field = |field: FieldInfo, index: usize| -> Box { + if index < len - 1 { + field.self_expr } else { // Unsized types need an extra indirection, but only the last field // may be unsized. - cx.expr_addr_of(field.span, field.self_expr.clone()) + cx.expr_addr_of(field.span, field.self_expr) } }; @@ -111,8 +127,7 @@ fn show_substructure(cx: &ExtCtxt<'_>, span: Span, substr: Substructure<'_>) -> let mut args = ThinVec::with_capacity(2 + fields.len() * args_per_field); args.extend([fmt, name]); - for i in 0..fields.len() { - let field = &fields[i]; + for (i, field) in fields.into_iter().enumerate() { if is_struct { let name = cx.expr_str(field.span, field.name.unwrap().name); args.push(name); @@ -128,8 +143,7 @@ fn show_substructure(cx: &ExtCtxt<'_>, span: Span, substr: Substructure<'_>) -> let mut name_exprs = ThinVec::with_capacity(fields.len()); let mut value_exprs = ThinVec::with_capacity(fields.len()); - for i in 0..fields.len() { - let field = &fields[i]; + for (i, field) in fields.into_iter().enumerate() { if is_struct { name_exprs.push(cx.expr_str(field.span, field.name.unwrap().name)); } @@ -185,9 +199,9 @@ fn show_substructure(cx: &ExtCtxt<'_>, span: Span, substr: Substructure<'_>) -> args.push(fmt); args.push(name); if is_struct { - args.push(cx.expr_ident(span, Ident::new(sym::names, span))); + args.push(cx.expr_ident_sym(span, sym::names)); } - args.push(cx.expr_ident(span, Ident::new(sym::values, span))); + args.push(cx.expr_ident_sym(span, sym::values)); let expr = cx.expr_call_global(span, fn_path_debug_internal, args); let mut stmts = ThinVec::with_capacity(2); @@ -216,14 +230,14 @@ fn show_fieldless_enum( cx: &ExtCtxt<'_>, span: Span, def: &EnumDef, - substr: Substructure<'_>, + type_ident: Ident, ) -> BlockOrExpr { - let fmt = substr.nonselflike_args[0].clone(); + let fmt = formatter_ident(cx, span); let arms = def .variants .iter() .map(|v| { - let variant_path = cx.path(span, vec![substr.type_ident, v.ident]); + let variant_path = cx.path(span, vec![type_ident, v.ident]); let pat = match &v.data { ast::VariantData::Tuple(fields, _) => { debug_assert!(fields.is_empty()); diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs index f9f9af4e9f012..28526bdef6b9d 100644 --- a/compiler/rustc_builtin_macros/src/deriving/default.rs +++ b/compiler/rustc_builtin_macros/src/deriving/default.rs @@ -7,8 +7,8 @@ use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym}; use smallvec::SmallVec; use thin_vec::{ThinVec, thin_vec}; -use crate::deriving::generic::ty::*; use crate::deriving::generic::*; +use crate::deriving::new_path; use crate::diagnostics; pub(crate) fn expand_deriving_default( @@ -22,7 +22,7 @@ pub(crate) fn expand_deriving_default( let trait_def = TraitDef { span, - path: Path::new(vec![kw::Default, sym::Default]), + path: new_path(cx, span, &[kw::Default, sym::Default], vec![]), skip_path_as_bound: has_a_default_variant(item), needs_copy_as_bound_if_packed: false, additional_bounds: SmallVec::new(), @@ -32,13 +32,14 @@ pub(crate) fn expand_deriving_default( generics: cx.empty_generics(span), explicit_self: false, nonself_args: SmallVec::new(), - ret_ty: Self_, + has_other_selflike_arg: false, + ret_ty: cx.ty_self(span), attributes: thin_vec![cx.attr_word(sym::inline, span)], fieldless_variants_strategy: FieldlessVariantsStrategy::Default, combine_substructure: combine_substructure(|cx, trait_span, substr| { - match substr.fields { + match substr { StaticStruct(variant_data) => { - default_struct_substructure(cx, trait_span, substr, variant_data) + default_struct_substructure(cx, trait_span, variant_data) } StaticEnum(enum_def) => { default_enum_substructure(cx, trait_span, enum_def, item.span) @@ -49,7 +50,6 @@ pub(crate) fn expand_deriving_default( } }), }], - associated_types: SmallVec::new(), is_const, safety: Safety::Default, document: true, @@ -66,26 +66,23 @@ fn default_call(cx: &ExtCtxt<'_>, span: Span) -> Box { fn default_struct_substructure( cx: &ExtCtxt<'_>, trait_span: Span, - substr: Substructure<'_>, variant_data: &VariantData, ) -> BlockOrExpr { let expr = match variant_data { - VariantData::Unit(_) => cx.expr_ident(trait_span, substr.type_ident), + VariantData::Unit(_) => cx.expr_ident(trait_span, Ident::new(kw::SelfUpper, trait_span)), VariantData::Tuple(fields, _) => { let exprs = fields .iter() .map(|field| default_call(cx, field.span.with_ctxt(trait_span.ctxt()))) .collect(); - cx.expr_call_ident(trait_span, substr.type_ident, exprs) + cx.expr_call_ident(trait_span, Ident::new(kw::SelfUpper, trait_span), exprs) } VariantData::Struct { fields, .. } => { let default_fields = fields .iter() .map(|field| { let span = field.span.with_ctxt(trait_span.ctxt()); - let value = if let Some(extras) = &field.extras - && let Some(default_val) = &extras.default - { + let value = if let Some(default_val) = field.default_value() { // We use the field default const expression. cx.expr( default_val.value.span, @@ -98,7 +95,7 @@ fn default_struct_substructure( cx.field_imm(span, field.ident.unwrap(), value) }) .collect(); - cx.expr_struct_ident(trait_span, substr.type_ident, default_fields) + cx.expr_struct_ident(trait_span, Ident::new(kw::SelfUpper, trait_span), default_fields) } }; BlockOrExpr::new_expr(expr) diff --git a/compiler/rustc_builtin_macros/src/deriving/eq.rs b/compiler/rustc_builtin_macros/src/deriving/eq.rs index eaf9298fc10c7..da38045994c7f 100644 --- a/compiler/rustc_builtin_macros/src/deriving/eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/eq.rs @@ -4,7 +4,6 @@ use rustc_expand::base::ExtCtxt; use rustc_span::{Span, sym}; use thin_vec::{ThinVec, thin_vec}; -use crate::deriving::generic::ty::*; use crate::deriving::generic::*; use crate::deriving::path_std; @@ -19,7 +18,7 @@ pub(crate) fn expand_deriving_eq( let trait_def = TraitDef { span, - path: path_std!(cmp::Eq), + path: path_std!(cx, span, cmp::Eq), skip_path_as_bound: false, needs_copy_as_bound_if_packed: true, additional_bounds: SmallVec::new(), @@ -29,7 +28,8 @@ pub(crate) fn expand_deriving_eq( generics: cx.empty_generics(span), explicit_self: true, nonself_args: smallvec![], - ret_ty: Unit, + has_other_selflike_arg: false, + ret_ty: cx.ty_unit(span), attributes: thin_vec![ // This method will never be called, so doing codegen etc. for it is unnecessary. // We prevent this by adding `#[inline]`, which improves compile-time. @@ -40,7 +40,6 @@ pub(crate) fn expand_deriving_eq( fieldless_variants_strategy: FieldlessVariantsStrategy::Unify, combine_substructure: combine_substructure(cs_total_eq_assert), }], - associated_types: SmallVec::new(), is_const, safety: Safety::Default, document: true, @@ -73,7 +72,7 @@ fn cs_total_eq_assert(cx: &ExtCtxt<'_>, trait_span: Span, substr: Substructure<' } }; - match substr.fields { + match substr { StaticStruct(vdata, ..) => { process_variant(vdata); } diff --git a/compiler/rustc_builtin_macros/src/deriving/from.rs b/compiler/rustc_builtin_macros/src/deriving/from.rs index 0b28df9850097..df95ccd99f3e6 100644 --- a/compiler/rustc_builtin_macros/src/deriving/from.rs +++ b/compiler/rustc_builtin_macros/src/deriving/from.rs @@ -5,9 +5,8 @@ use rustc_expand::base::{DummyResult, ExtCtxt}; use rustc_span::{Ident, Span, kw, sym}; use thin_vec::thin_vec; -use crate::deriving::generic::ty::{Path, PathKind, Ty}; use crate::deriving::generic::*; -use crate::deriving::pathvec; +use crate::deriving::{new_path, pathvec}; use crate::diagnostics; /// Generate an implementation of the `From` trait, provided that `item` @@ -47,13 +46,12 @@ pub(crate) fn expand_deriving_from( _ => cx.dcx().bug("Invalid derive(From) ADT input"), }; - let from_type = Ty::AstTy(match field { + let from_type = match field { Ok(ref field) => field.ty.clone(), Err(guar) => cx.ty(span, ast::TyKind::Err(guar)), - }); + }; - let path = - Path::new_(pathvec!(convert::From), vec![Box::new(from_type.clone())], PathKind::Std); + let path = new_path(cx, span, pathvec!(convert::From), vec![from_type.clone()]); // Generate code like this: // @@ -77,7 +75,8 @@ pub(crate) fn expand_deriving_from( generics: cx.empty_generics(span), explicit_self: false, nonself_args: smallvec![(from_type, sym::value)], - ret_ty: Ty::Self_, + has_other_selflike_arg: false, + ret_ty: cx.ty_self(span), attributes: thin_vec![cx.attr_word(sym::inline, span)], fieldless_variants_strategy: FieldlessVariantsStrategy::Default, combine_substructure: combine_substructure(|cx, span, substructure| { @@ -89,8 +88,8 @@ pub(crate) fn expand_deriving_from( }; let self_kw = Ident::new(kw::SelfUpper, span); - let expr: Box = match substructure.fields { - SubstructureFields::StaticStruct(variant) => match variant { + let expr: Box = match substructure { + StaticStruct(variant) => match variant { // Self { field: value } VariantData::Struct { .. } => cx.expr_struct_ident( span, @@ -98,14 +97,14 @@ pub(crate) fn expand_deriving_from( thin_vec![cx.field_imm( span, field.ident.unwrap(), - cx.expr_ident(span, Ident::new(sym::value, span)) + cx.expr_ident_sym(span, sym::value) )], ), // Self(value) VariantData::Tuple(_, _) => cx.expr_call_ident( span, self_kw, - thin_vec![cx.expr_ident(span, Ident::new(sym::value, span))], + thin_vec![cx.expr_ident_sym(span, sym::value)], ), variant => { cx.dcx().bug(format!("Invalid derive(From) ADT variant: {variant:?}")); @@ -116,7 +115,6 @@ pub(crate) fn expand_deriving_from( BlockOrExpr::new_expr(expr) }), }], - associated_types: SmallVec::new(), is_const, safety: Safety::Default, document: true, diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index a041e83a646ce..8b15920511aca 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -175,34 +175,31 @@ //! ``` use std::ops::Not; -use std::{iter, vec}; +use std::vec; -pub(crate) use SubstructureFields::*; +pub(crate) use Substructure::*; pub(crate) use rustc_ast as ast; -use rustc_ast::token::{IdentIsRaw, LitKind, Token, TokenKind}; +use rustc_ast::token::{IdentKind, LitKind, Token, TokenKind}; use rustc_ast::tokenstream::{DelimSpan, Spacing, TokenTree}; use rustc_ast::{ - AttrArgs, DelimArgs, EnumDef, Expr, GenericArg, GenericParamKind, Generics, Safety, SelfKind, - VariantData, + AttrArgs, DelimArgs, EnumDef, Expr, GenericArg, GenericParam, GenericParamKind, Generics, + Safety, SelfKind, VariantData, }; use rustc_attr_ir::{Attribute, AttributeKind, ReprPacked}; use rustc_attr_parsing::AttributeParser; use rustc_expand::base::ExtCtxt; -use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, respan, sym}; +use rustc_span::{Ident, Span, Symbol, kw, respan, sym}; pub(crate) use smallvec::{SmallVec, smallvec}; use thin_vec::{ThinVec, thin_vec}; -use ty::{Path, Ref, Self_, Ty}; use crate::{deriving, diagnostics}; -pub(crate) mod ty; - pub(crate) struct TraitDef<'a> { /// The span for the current #[derive(Foo)] header. pub span: Span, /// Path of the trait, including any type parameters - pub path: Path, + pub path: ast::Path, /// Whether to skip adding the current trait as a bound to the type parameters of the type. pub skip_path_as_bound: bool, @@ -212,15 +209,13 @@ pub(crate) struct TraitDef<'a> { /// Additional bounds required of any type parameters of the type, /// other than the current trait - pub additional_bounds: SmallVec<[Ty; 1]>, + pub additional_bounds: SmallVec<[ast::Path; 1]>, /// Can this trait be derived for unions? pub supports_unions: bool, pub methods: SmallVec<[MethodDef<'a>; 1]>, - pub associated_types: SmallVec<[(Ident, Ty); 1]>, - pub is_const: bool, /// The safety of the `impl`. @@ -240,10 +235,13 @@ pub(crate) struct MethodDef<'a> { pub explicit_self: bool, /// Arguments other than the self argument. - pub nonself_args: SmallVec<[(Ty, Symbol); 1]>, + pub nonself_args: SmallVec<[(Box, Symbol); 1]>, + + /// Whether this method has another arg of type `&self`. + pub has_other_selflike_arg: bool, /// Returns type - pub ret_ty: Ty, + pub ret_ty: Box, pub attributes: ast::AttrVec, @@ -268,16 +266,6 @@ pub(crate) enum FieldlessVariantsStrategy { SpecializeIfAllVariantsFieldless, } -/// All the data about the data structure/method being derived upon. -pub(crate) struct Substructure<'a> { - /// ident of self - pub type_ident: Ident, - /// Verbatim access to any non-selflike arguments, i.e. arguments that - /// don't have type `&Self`. - pub nonselflike_args: &'a [Box], - pub fields: SubstructureFields<'a>, -} - /// Summary of the relevant parts of a struct/enum field. pub(crate) struct FieldInfo { pub span: Span, @@ -287,14 +275,14 @@ pub(crate) struct FieldInfo { /// The expression corresponding to this field of `self` /// (specifically, a reference to it). pub self_expr: Box, - /// The expressions corresponding to references to this field in - /// the other selflike arguments. - pub other_selflike_exprs: Vec>, + /// The expression corresponding to a reference to this field in + /// the other selflike argument. + pub other_selflike_expr: Option>, pub maybe_scalar: bool, } /// A summary of the possible sets of fields. -pub(crate) enum SubstructureFields<'a> { +pub(crate) enum Substructure<'a> { /// A non-static method where `Self` is a struct. Struct(&'a ast::VariantData, Vec), @@ -303,15 +291,14 @@ pub(crate) enum SubstructureFields<'a> { /// variants has any fields). AllFieldlessEnum(&'a ast::EnumDef), - /// Matching variants of the enum: variant index, ast::Variant, + /// Matching variants of the enum: ast::Variant, /// fields: the field name is only non-`None` in the case of a struct /// variant. EnumMatching(&'a ast::Variant, Vec), - /// The discriminant of an enum. The first field is a `FieldInfo` for the discriminants, as - /// if they were fields. The second field is the expression to combine the + /// The discriminant of an enum. The field is the expression to combine the /// discriminant expression with; it will be `None` if no match is necessary. - EnumDiscr(FieldInfo, Option>), + EnumDiscr(Option>), /// A static method where `Self` is a struct. StaticStruct(&'a ast::VariantData), @@ -471,14 +458,30 @@ impl<'a> TraitDef<'a> { push: &mut dyn FnMut(Box), from_scratch: bool, ) { + let span = self.span; let is_packed = matches!( AttributeParser::parse_limited_sym(cx.sess, &item.attrs, &[sym::repr]), Some(Attribute::Parsed(AttributeKind::Repr { reprs, .. })) if reprs.iter().any(|(x, _)| matches!(x, ReprPacked(..))) ); let mut newitem = match &item.kind { - ast::ItemKind::Struct(ident, generics, struct_def) => { - self.expand_struct_def(cx, struct_def, *ident, generics, from_scratch, is_packed) + ast::ItemKind::Union(..) if !self.supports_unions => { + cx.dcx().emit_err(diagnostics::DeriveUnion { span }); + return; + } + ast::ItemKind::Struct(ident, generics, struct_def) + | ast::ItemKind::Union(ident, generics, struct_def) => { + let fields = struct_def.fields().iter(); + let methods = self.methods.iter().filter_map(|method_def| { + let body = if from_scratch || method_def.is_static() { + method_def.call_substructure_method(cx, span, StaticStruct(struct_def)) + } else { + method_def.expand_struct_method_body(cx, span, struct_def, is_packed) + }; + + method_def.create_method(cx, span, body) + }); + self.create_derived_impl(cx, *ident, generics, fields, methods, is_packed) } ast::ItemKind::Enum(ident, generics, enum_def) => { // We can skip generating the impl here, because `repr(packed)` @@ -488,22 +491,17 @@ impl<'a> TraitDef<'a> { if is_packed { return; } - self.expand_enum_def(cx, enum_def, *ident, generics, from_scratch) - } - ast::ItemKind::Union(ident, generics, struct_def) => { - if self.supports_unions { - self.expand_struct_def( - cx, - struct_def, - *ident, - generics, - from_scratch, - is_packed, - ) - } else { - cx.dcx().emit_err(diagnostics::DeriveUnion { span: self.span }); - return; - } + let fields = enum_def.variants.iter().flat_map(|variant| variant.data.fields()); + let methods = self.methods.iter().filter_map(|method_def| { + let body = if from_scratch || method_def.is_static() { + method_def.call_substructure_method(cx, span, StaticEnum(enum_def)) + } else { + method_def.expand_enum_method_body(cx, span, enum_def) + }; + + method_def.create_method(cx, span, body) + }); + self.create_derived_impl(cx, *ident, generics, fields, methods, is_packed) } _ => unreachable!(), }; @@ -567,38 +565,13 @@ impl<'a> TraitDef<'a> { cx: &ExtCtxt<'_>, type_ident: Ident, generics: &Generics, - field_tys: impl Iterator, + fields: impl Iterator, methods: impl Iterator>, is_packed: bool, ) -> Box { - let trait_path = self.path.to_path(cx, self.span, type_ident, generics); - - // Transform associated types from `deriving::ty::Ty` into `ast::AssocItem` - let associated_types = self.associated_types.iter().map(|&(ident, ref type_def)| { - Box::new(ast::AssocItem { - id: ast::DUMMY_NODE_ID, - span: self.span, - vis: ast::Visibility { - span: self.span.shrink_to_lo(), - kind: ast::VisibilityKind::Inherited, - }, - attrs: ast::AttrVec::new(), - kind: ast::AssocItemKind::Type(Box::new(ast::TyAlias { - defaultness: ast::Defaultness::Implicit, - ident, - generics: Generics::default(), - after_where_clause: ast::WhereClause::default(), - bounds: ThinVec::new(), - ty: Some(type_def.to_ty(cx, self.span, type_ident, generics)), - })), - tokens: None, - }) - }); - let mut where_clause = ast::WhereClause::default(); where_clause.span = generics.where_clause.span; let ctxt = self.span.ctxt(); - let span = generics.span.with_ctxt(ctxt); // Create the generic parameters let params: ThinVec<_> = generics @@ -613,13 +586,11 @@ impl<'a> TraitDef<'a> { let bounds: ThinVec<_> = self .additional_bounds .iter() - .map(|p| { - cx.trait_bound(p.to_path(cx, span, type_ident, generics), self.is_const) - }) + .map(|p| cx.trait_bound(ast::Path { span, ..p.clone() }, self.is_const)) .chain( // Add a bound for the current trait. self.skip_path_as_bound.not().then(|| { - let mut trait_path = trait_path.clone(); + let mut trait_path = self.path.clone(); trait_path.span = span; cx.trait_bound(trait_path, self.is_const) }), @@ -627,11 +598,8 @@ impl<'a> TraitDef<'a> { .chain({ // Add a `Copy` bound if required. if is_packed && self.needs_copy_as_bound_if_packed { - let p = deriving::path_std!(marker::Copy); - Some(cx.trait_bound( - p.to_path(cx, span, type_ident, generics), - self.is_const, - )) + let p = deriving::path_std!(cx, span, marker::Copy); + Some(cx.trait_bound(p, self.is_const)) } else { None } @@ -642,7 +610,7 @@ impl<'a> TraitDef<'a> { ) .collect(); - cx.typaram(span, param.ident, bounds, None) + cx.typaram(param.ident, bounds, None) } GenericParamKind::Const { ty, span, .. } => { let const_nodefault_kind = GenericParamKind::Const { @@ -683,8 +651,8 @@ impl<'a> TraitDef<'a> { .collect(); if !ty_param_names.is_empty() { - for field_ty in field_tys { - let field_ty_params = find_type_parameters(field_ty, &ty_param_names, cx); + for field in fields { + let field_ty_params = find_type_parameters(&field.ty, &ty_param_names, cx); for field_ty_param in field_ty_params { // if we have already handled this type, skip it @@ -697,26 +665,18 @@ impl<'a> TraitDef<'a> { let mut bounds: ThinVec<_> = self .additional_bounds .iter() - .map(|p| { - cx.trait_bound( - p.to_path(cx, self.span, type_ident, generics), - self.is_const, - ) - }) + .map(|p| cx.trait_bound(p.clone(), self.is_const)) .collect(); // Require the current trait. if !self.skip_path_as_bound { - bounds.push(cx.trait_bound(trait_path.clone(), self.is_const)); + bounds.push(cx.trait_bound(self.path.clone(), self.is_const)); } // Add a `Copy` bound if required. if is_packed && self.needs_copy_as_bound_if_packed { - let p = deriving::path_std!(marker::Copy); - bounds.push(cx.trait_bound( - p.to_path(cx, self.span, type_ident, generics), - self.is_const, - )); + let p = deriving::path_std!(cx, self.span, marker::Copy); + bounds.push(cx.trait_bound(p, self.is_const)); } if !bounds.is_empty() { @@ -740,33 +700,21 @@ impl<'a> TraitDef<'a> { } } - let trait_generics = Generics { params, where_clause, span }; + let trait_generics = Generics { params, where_clause, span: generics.span.with_ctxt(ctxt) }; // Create the reference to the trait. - let trait_ref = cx.trait_ref(trait_path); + let trait_ref = cx.trait_ref(self.path.clone()); let self_params: Vec<_> = generics .params .iter() - .map(|param| match param.kind { - GenericParamKind::Lifetime => { - GenericArg::Lifetime(cx.lifetime(param.ident.span.with_ctxt(ctxt), param.ident)) - } - GenericParamKind::Type { .. } => { - GenericArg::Type(cx.ty_ident(param.ident.span.with_ctxt(ctxt), param.ident)) - } - GenericParamKind::Const { .. } => { - GenericArg::Const(cx.const_ident(param.ident.span.with_ctxt(ctxt), param.ident)) - } - }) + .map(|param| generic_param_to_arg(cx, param, param.ident.span.with_ctxt(ctxt))) .collect(); // Create the type of `self`. let path = cx.path_all(type_ident.span.with_ctxt(ctxt), false, vec![type_ident], self_params); let self_type = cx.ty_path(path); - let rustc_const_unstable = - cx.path_ident(self.span, Ident::new(sym::rustc_const_unstable, self.span)); let mut attrs = thin_vec![cx.attr_word(sym::automatically_derived, self.span),]; @@ -774,6 +722,10 @@ impl<'a> TraitDef<'a> { // Other crates don't need stability attributes, so adding them is not useful, but libcore needs them // on all const trait impls. if self.is_const && cx.ecfg.features.staged_api() { + let rustc_const_unstable = + cx.path_ident(self.span, Ident::new(sym::rustc_const_unstable, self.span)); + + // #[rustc_const_unstable(feature = "derive_const", issue = "118304")] attrs.push( cx.attr_nested( rustc_ast::AttrItem { @@ -783,11 +735,11 @@ impl<'a> TraitDef<'a> { dspan: DelimSpan::from_single(self.span), delim: rustc_ast::token::Delimiter::Parenthesis, tokens: [ - TokenKind::Ident(sym::feature, IdentIsRaw::No), + TokenKind::Ident(sym::feature, IdentKind::Normal), TokenKind::Eq, TokenKind::lit(LitKind::Str, sym::derive_const, None), TokenKind::Comma, - TokenKind::Ident(sym::issue, IdentIsRaw::No), + TokenKind::Ident(sym::issue, IdentKind::Normal), TokenKind::Eq, TokenKind::lit(LitKind::Str, sym::derive_const_issue, None), ] @@ -808,227 +760,80 @@ impl<'a> TraitDef<'a> { attrs.push(cx.attr_nested_word(sym::doc, sym::hidden, self.span)); } - cx.item( + cx.item_trait_impl( self.span, attrs, - ast::ItemKind::Impl(ast::Impl { - generics: trait_generics, - of_trait: Some(Box::new(ast::TraitImplHeader { - safety: self.safety, - polarity: ast::ImplPolarity::Positive, - defaultness: ast::Defaultness::Implicit, - trait_ref, - })), - constness: if self.is_const { ast::Const::Yes(DUMMY_SP) } else { ast::Const::No }, - self_ty: self_type, - items: methods.chain(associated_types).collect(), - }), + trait_generics, + self.safety, + self.is_const, + trait_ref, + self_type, + methods.collect(), ) } - - fn expand_struct_def( - &self, - cx: &ExtCtxt<'_>, - struct_def: &'a VariantData, - type_ident: Ident, - generics: &Generics, - from_scratch: bool, - is_packed: bool, - ) -> Box { - let field_tys = struct_def.fields().iter().map(|field| &*field.ty); - - let methods = self.methods.iter().map(|method_def| { - let ArgDetails { explicit_self, selflike_args, nonselflike_args, nonself_arg_tys } = - method_def.extract_arg_details(cx, self, type_ident, generics); - - let body = if from_scratch || method_def.is_static() { - method_def.call_substructure_method( - cx, - self, - type_ident, - &nonselflike_args, - StaticStruct(struct_def), - ) - } else { - method_def.expand_struct_method_body( - cx, - self, - struct_def, - type_ident, - &selflike_args, - &nonselflike_args, - is_packed, - ) - }; - - method_def.create_method( - cx, - self, - type_ident, - generics, - explicit_self, - nonself_arg_tys, - body, - ) - }); - - self.create_derived_impl(cx, type_ident, generics, field_tys, methods, is_packed) - } - - fn expand_enum_def( - &self, - cx: &ExtCtxt<'_>, - enum_def: &'a EnumDef, - type_ident: Ident, - generics: &Generics, - from_scratch: bool, - ) -> Box { - let field_tys = enum_def - .variants - .iter() - .flat_map(|variant| variant.data.fields()) - .map(|field| &*field.ty); - - let methods = self.methods.iter().filter_map(|method_def| { - let ArgDetails { explicit_self, selflike_args, nonselflike_args, nonself_arg_tys } = - method_def.extract_arg_details(cx, self, type_ident, generics); - - let body = if from_scratch || method_def.is_static() { - method_def.call_substructure_method( - cx, - self, - type_ident, - &nonselflike_args, - StaticEnum(enum_def), - ) - } else { - method_def.expand_enum_method_body( - cx, - self, - enum_def, - type_ident, - selflike_args, - &nonselflike_args, - ) - }; - - // `assert_fields_are_eq` has an empty default implementation - if body.0.is_empty() && body.1.is_none() && method_def.name == sym::assert_fields_are_eq - { - return None; - } - - Some(method_def.create_method( - cx, - self, - type_ident, - generics, - explicit_self, - nonself_arg_tys, - body, - )) - }); - - let is_packed = false; // enums are never packed - self.create_derived_impl(cx, type_ident, generics, field_tys, methods, is_packed) - } -} - -struct ArgDetails { - /// The `&self` arg, if present. - explicit_self: Option, - /// Expressions for `&self` (if present) and also any other - /// args with the same type (e.g. the `other` arg in `PartialEq::eq`). - selflike_args: ThinVec>, - /// Expressions for all the remaining args. - nonselflike_args: Vec>, - /// Additional information about all the args other than `&self`. - nonself_arg_tys: Vec<(Ident, Box)>, } impl<'a> MethodDef<'a> { fn call_substructure_method( &self, cx: &ExtCtxt<'_>, - trait_: &TraitDef<'_>, - type_ident: Ident, - nonselflike_args: &[Box], - fields: SubstructureFields<'_>, + span: Span, + substructure: Substructure<'_>, ) -> BlockOrExpr { - let span = trait_.span; - let substructure = Substructure { type_ident, nonselflike_args, fields }; - let f: &CombineSubstructureFunc<'_> = &self.combine_substructure; - f(cx, span, substructure) + (self.combine_substructure)(cx, span, substructure) } fn is_static(&self) -> bool { !self.explicit_self } - fn extract_arg_details( - &self, - cx: &ExtCtxt<'_>, - trait_: &TraitDef<'_>, - type_ident: Ident, - generics: &Generics, - ) -> ArgDetails { - let mut selflike_args = ThinVec::new(); - let mut nonselflike_args = Vec::new(); - let mut nonself_arg_tys = Vec::new(); - let span = trait_.span; - - let explicit_self = self.explicit_self.then(|| { - // This constructs a fresh `self` path. - selflike_args.push(cx.expr_self(span)); - respan(span, SelfKind::Region(None, ast::Mutability::Not)) - }); - - for (ty, name) in self.nonself_args.iter() { - let ast_ty = ty.to_ty(cx, span, type_ident, generics); - let ident = Ident::new(*name, span); - nonself_arg_tys.push((ident, ast_ty)); - - let arg_expr = cx.expr_ident(span, ident); + /// Expressions for `&self` and also any other + /// args with the same type (e.g. the `other` arg in `PartialEq::eq`). + fn get_selflike_args(&self, cx: &ExtCtxt<'_>, span: Span) -> ThinVec> { + assert!(self.explicit_self); - match ty { - // Selflike (`&Self`) arguments only occur in non-static methods. - Ref(Self_, _) if !self.is_static() => selflike_args.push(arg_expr), - Self_ => cx.dcx().span_bug(span, "`Self` in non-return position"), - _ => nonselflike_args.push(arg_expr), - } + let self_expr = cx.expr_self(span); + if self.has_other_selflike_arg { + thin_vec![self_expr, cx.expr_ident_sym(span, self.nonself_args[0].1)] + } else { + thin_vec![self_expr] } - - ArgDetails { explicit_self, selflike_args, nonselflike_args, nonself_arg_tys } } fn create_method( &self, cx: &ExtCtxt<'_>, - trait_: &TraitDef<'_>, - type_ident: Ident, - generics: &Generics, - explicit_self: Option, - nonself_arg_tys: Vec<(Ident, Box)>, + span: Span, body: BlockOrExpr, - ) -> Box { - let span = trait_.span; + ) -> Option> { + // `assert_fields_are_eq` has an empty default implementation + if body.0.is_empty() && body.1.is_none() && self.name == sym::assert_fields_are_eq { + return None; + } // Create the generics that aren't for `Self`. let fn_generics = self.generics.clone(); - let args = { - let self_arg = explicit_self.map(|explicit_self| { - let ident = Ident::new(kw::SelfLower, span); - ast::Param::from_self(ast::AttrVec::default(), explicit_self, ident) - }); - let nonself_args = - nonself_arg_tys.into_iter().map(|(name, ty)| cx.param(span, name, ty)); - self_arg.into_iter().chain(nonself_args).collect() - }; + let self_arg = self.explicit_self.then(|| { + let ident = Ident::new(kw::SelfLower, span); + ast::Param::from_self( + ast::AttrVec::default(), + respan(span, SelfKind::Region(None, ast::Mutability::Not)), + ident, + ) + }); + let args = self_arg + .into_iter() + .chain(self.nonself_args.iter().map(|(ty, name)| { + let ast_ty = ty.clone(); + let ident = Ident::new(*name, span); + cx.param(span, ident, ast_ty) + })) + .collect(); - let ret_type = if let Ty::Unit = &self.ret_ty { + let ret_type = if self.ret_ty.kind.is_unit() { ast::FnRetTy::Default(span) } else { - ast::FnRetTy::Ty(self.ret_ty.to_ty(cx, span, type_ident, generics)) + ast::FnRetTy::Ty(self.ret_ty.clone()) }; let method_ident = Ident::new(self.name, span); @@ -1041,7 +846,7 @@ impl<'a> MethodDef<'a> { let defaultness = ast::Defaultness::Implicit; // Create the method. - Box::new(ast::AssocItem { + Some(Box::new(ast::AssocItem { id: ast::DUMMY_NODE_ID, attrs: self.attributes.clone(), span, @@ -1057,7 +862,7 @@ impl<'a> MethodDef<'a> { eii_impl: None, })), tokens: None, - }) + })) } /// The normal case uses field access. @@ -1098,24 +903,15 @@ impl<'a> MethodDef<'a> { fn expand_struct_method_body<'b>( &self, cx: &ExtCtxt<'_>, - trait_: &TraitDef<'b>, + span: Span, struct_def: &'b VariantData, - type_ident: Ident, - selflike_args: &[Box], - nonselflike_args: &[Box], is_packed: bool, ) -> BlockOrExpr { - assert!(selflike_args.len() == 1 || selflike_args.len() == 2); + let selflike_args = self.get_selflike_args(cx, span); let selflike_fields = - trait_.create_struct_field_access_fields(cx, selflike_args, struct_def, is_packed); - self.call_substructure_method( - cx, - trait_, - type_ident, - nonselflike_args, - Struct(struct_def, selflike_fields), - ) + create_struct_field_access_fields(span, cx, &selflike_args, struct_def, is_packed); + self.call_substructure_method(cx, span, Struct(struct_def, selflike_fields)) } /// ``` @@ -1156,18 +952,9 @@ impl<'a> MethodDef<'a> { fn expand_enum_method_body<'b>( &self, cx: &ExtCtxt<'_>, - trait_: &TraitDef<'b>, + span: Span, enum_def: &'b EnumDef, - type_ident: Ident, - mut selflike_args: ThinVec>, - nonselflike_args: &[Box], ) -> BlockOrExpr { - assert!( - !selflike_args.is_empty(), - "static methods must use `expand_static_enum_method_body`", - ); - - let span = trait_.span; let variants = &enum_def.variants; // Traits that unify fieldless variants always use the discriminant(s). @@ -1178,54 +965,18 @@ impl<'a> MethodDef<'a> { // `match *self {}`. This produces machine code identical to `unsafe { // core::intrinsics::unreachable() }` while being safe and stable. if variants.is_empty() { - selflike_args.truncate(1); - let match_arg = cx.expr_deref(span, selflike_args.pop().unwrap()); + let match_arg = cx.expr_deref(span, cx.expr_self(span)); let match_arms = ThinVec::new(); let expr = cx.expr_match(span, match_arg, match_arms); return BlockOrExpr(ThinVec::new(), Some(expr)); } - let prefixes = iter::once("__self".to_string()) - .chain((1..selflike_args.len()).map(|arg_count| format!("__arg{arg_count}"))) - .collect::>(); - - // Build a series of let statements mapping each selflike_arg - // to its discriminant value. - // - // e.g. for `PartialEq::eq` builds two statements: - // ``` - // let __self_discr = ::core::intrinsics::discriminant_value(self); - // let __arg1_discr = ::core::intrinsics::discriminant_value(other); - // ``` - let get_discr_pieces = |cx: &ExtCtxt<'_>| { - let discr_idents: Vec<_> = prefixes - .iter() - .map(|name| Ident::from_str_and_span(&format!("{name}_discr"), span)) - .collect(); - - let mut discr_exprs: Vec<_> = discr_idents - .iter() - .map(|&ident| cx.expr_addr_of(span, cx.expr_ident(span, ident))) - .collect(); - - let self_expr = discr_exprs.remove(0); - let other_selflike_exprs = discr_exprs; - let discr_field = - FieldInfo { span, name: None, self_expr, other_selflike_exprs, maybe_scalar: true }; - - let discr_let_stmts: ThinVec<_> = iter::zip(&discr_idents, &selflike_args) - .map(|(&ident, selflike_arg)| { - let variant_value = deriving::call_intrinsic( - cx, - span, - sym::discriminant_value, - thin_vec![selflike_arg.clone()], - ); - cx.stmt_let(span, false, ident, variant_value) - }) - .collect(); + let selflike_args = self.get_selflike_args(cx, span); - (discr_field, discr_let_stmts) + let prefixes: &[&str] = match selflike_args.len() { + 1 => &["__self"], + 2 => &["__self", "__arg1"], + _ => unreachable!(), }; // There are some special cases involving fieldless enums where no @@ -1238,38 +989,17 @@ impl<'a> MethodDef<'a> { // If the type is fieldless and the trait uses the discriminant and // there are multiple variants, we need just an operation on // the discriminant(s). - let (discr_field, mut discr_let_stmts) = get_discr_pieces(cx); - let mut discr_check = self.call_substructure_method( - cx, - trait_, - type_ident, - nonselflike_args, - EnumDiscr(discr_field, None), - ); - discr_let_stmts.append(&mut discr_check.0); - return BlockOrExpr(discr_let_stmts, discr_check.1); + return self.call_substructure_method(cx, span, EnumDiscr(None)); } FieldlessVariantsStrategy::SpecializeIfAllVariantsFieldless => { - return self.call_substructure_method( - cx, - trait_, - type_ident, - nonselflike_args, - AllFieldlessEnum(enum_def), - ); + return self.call_substructure_method(cx, span, AllFieldlessEnum(enum_def)); } FieldlessVariantsStrategy::Default => (), } } else if let [variant] = variants.as_slice() { // If there is a single variant, we don't need an operation on // the discriminant(s). Just use the most degenerate result. - return self.call_substructure_method( - cx, - trait_, - type_ident, - nonselflike_args, - EnumMatching(variant, Vec::new()), - ); + return self.call_substructure_method(cx, span, EnumMatching(variant, Vec::new())); } } @@ -1285,12 +1015,13 @@ impl<'a> MethodDef<'a> { // A single arm has form (&VariantK, &VariantK, ...) => BodyK // (see "Final wrinkle" note below for why.) - let fields = trait_.create_struct_pattern_fields(cx, &variant.data, &prefixes); + let fields = create_struct_pattern_fields(span, cx, &variant.data, &prefixes); - let sp = variant.span.with_ctxt(trait_.span.ctxt()); - let variant_path = cx.path(sp, vec![type_ident, variant.ident]); + let sp = variant.span.with_ctxt(span.ctxt()); + let variant_path = + cx.path(sp, vec![Ident::new(kw::SelfUpper, span), variant.ident]); let mut subpats = - trait_.create_struct_patterns(cx, variant_path, &variant.data, &prefixes); + create_struct_patterns(span, cx, variant_path, &variant.data, &prefixes); // `(VariantK, VariantK, ...)` or just `VariantK`. let single_pat = if subpats.len() == 1 { @@ -1308,15 +1039,8 @@ impl<'a> MethodDef<'a> { // Self arg, assuming all are instances of VariantK. // Build up code associated with such a case. let substructure = EnumMatching(variant, fields); - let arm_expr = self - .call_substructure_method( - cx, - trait_, - type_ident, - nonselflike_args, - substructure, - ) - .into_expr(cx, span); + let arm_expr = + self.call_substructure_method(cx, span, substructure).into_expr(cx, span); cx.arm(span, single_pat, arm_expr) }) @@ -1326,23 +1050,15 @@ impl<'a> MethodDef<'a> { let first_fieldless = variants.iter().find(|v| v.data.fields().is_empty()); let default = match first_fieldless { Some(v) if unify_fieldless_variants => { - // We need a default case that handles all the fieldless - // variants. The index and actual variant aren't meaningful in - // this case, so just use dummy values. + // We need a default case that handles all the fieldless variants. Some( - self.call_substructure_method( - cx, - trait_, - type_ident, - nonselflike_args, - EnumMatching(v, Vec::new()), - ) - .into_expr(cx, span), + self.call_substructure_method(cx, span, EnumMatching(v, Vec::new())) + .into_expr(cx, span), ) } _ if variants.len() > 1 && selflike_args.len() > 1 => { // Because we know that all the arguments will match if we reach - // the match expression we add the unreachable intrinsics as the + // the match expression we add the unreachable intrinsic as the // result of the default which should help llvm in optimizing it. Some(deriving::call_unreachable(cx, span)) } @@ -1364,7 +1080,7 @@ impl<'a> MethodDef<'a> { let match_arg = if selflike_args.len() == 1 { selflike_args.pop().unwrap() } else { - cx.expr(span, ast::ExprKind::Tup(selflike_args)) + cx.expr_tuple(span, selflike_args) }; cx.expr_match(span, match_arg, match_arms) }; @@ -1373,18 +1089,8 @@ impl<'a> MethodDef<'a> { // to add a discriminant check operation before the match. Otherwise, the match // is enough. if unify_fieldless_variants && variants.len() > 1 { - let (discr_field, mut discr_let_stmts) = get_discr_pieces(cx); - // Combine a discriminant check with the match. - let mut discr_check_plus_match = self.call_substructure_method( - cx, - trait_, - type_ident, - nonselflike_args, - EnumDiscr(discr_field, Some(get_match_expr(selflike_args))), - ); - discr_let_stmts.append(&mut discr_check_plus_match.0); - BlockOrExpr(discr_let_stmts, discr_check_plus_match.1) + self.call_substructure_method(cx, span, EnumDiscr(Some(get_match_expr(selflike_args)))) } else { BlockOrExpr(ThinVec::new(), Some(get_match_expr(selflike_args))) } @@ -1392,197 +1098,157 @@ impl<'a> MethodDef<'a> { } // general helper methods. -impl<'a> TraitDef<'a> { - fn create_struct_patterns( - &self, - cx: &ExtCtxt<'_>, - struct_path: ast::Path, - struct_def: &'a VariantData, - prefixes: &[String], - ) -> ThinVec { +fn create_struct_patterns( + span: Span, + cx: &ExtCtxt<'_>, + struct_path: ast::Path, + struct_def: &VariantData, + prefixes: &[&str], +) -> ThinVec { + prefixes + .iter() + .map(|prefix| { + let pieces_iter = struct_def.fields().iter().enumerate().map(|(i, struct_field)| { + let ident = mk_pattern_ident(span, prefix, i); + let path = ident.with_span_pos(struct_field.span); + (struct_field.ident, cx.pat_ident(path.span, path)) + }); + + let struct_path = struct_path.clone(); + match *struct_def { + VariantData::Struct { .. } => { + let field_pats = pieces_iter + .map(|(ident, pat)| ast::PatField { + ident: ident.expect("a braced struct with unnamed fields in `derive`"), + is_shorthand: false, + attrs: ast::AttrVec::new(), + id: ast::DUMMY_NODE_ID, + span: pat.span.with_ctxt(span.ctxt()), + pat: Box::new(pat), + is_placeholder: false, + }) + .collect(); + cx.pat_struct(span, struct_path, field_pats) + } + VariantData::Tuple(..) => { + let subpats = pieces_iter.map(|(_, subpat)| subpat).collect(); + cx.pat_tuple_struct(span, struct_path, subpats) + } + VariantData::Unit(..) => cx.pat_path(span, struct_path), + } + }) + .collect() +} + +fn create_fields(span: Span, struct_def: &VariantData, mk_exprs: F) -> Vec +where + F: Fn(usize, &ast::FieldDef, Span) -> Vec>, +{ + struct_def + .fields() + .iter() + .enumerate() + .map(|(i, struct_field)| { + // For this field, get an expr for each selflike_arg. E.g. for + // `PartialEq::eq`, one for each of `&self` and `other`. + let span = struct_field.span.with_ctxt(span.ctxt()); + let mut exprs: Vec<_> = mk_exprs(i, struct_field, span); + let self_expr = exprs.remove(0); + debug_assert!(exprs.len() <= 1); + FieldInfo { + span, + name: struct_field.ident, + self_expr, + other_selflike_expr: exprs.pop(), + maybe_scalar: struct_field.ty.peel_refs().kind.maybe_scalar(), + } + }) + .collect() +} + +fn mk_pattern_ident(span: Span, prefix: &str, i: usize) -> Ident { + Ident::from_str_and_span(&format!("{prefix}_{i}"), span) +} + +fn create_struct_pattern_fields( + span: Span, + cx: &ExtCtxt<'_>, + struct_def: &VariantData, + prefixes: &[&str], +) -> Vec { + create_fields(span, struct_def, |i, _struct_field, sp| { prefixes .iter() .map(|prefix| { - let pieces_iter = - struct_def.fields().iter().enumerate().map(|(i, struct_field)| { - let sp = struct_field.span.with_ctxt(self.span.ctxt()); - let ident = self.mk_pattern_ident(prefix, i); - let path = ident.with_span_pos(sp); - (struct_field.ident, cx.pat_ident(path.span, path)) - }); - - let struct_path = struct_path.clone(); - match *struct_def { - VariantData::Struct { .. } => { - let field_pats = pieces_iter - .map(|(ident, pat)| ast::PatField { - ident: ident - .expect("a braced struct with unnamed fields in `derive`"), - is_shorthand: false, - attrs: ast::AttrVec::new(), - id: ast::DUMMY_NODE_ID, - span: pat.span.with_ctxt(self.span.ctxt()), - pat: Box::new(pat), - is_placeholder: false, - }) - .collect(); - cx.pat_struct(self.span, struct_path, field_pats) - } - VariantData::Tuple(..) => { - let subpats = pieces_iter.map(|(_, subpat)| subpat).collect(); - cx.pat_tuple_struct(self.span, struct_path, subpats) - } - VariantData::Unit(..) => cx.pat_path(self.span, struct_path), - } + let ident = mk_pattern_ident(span, prefix, i); + cx.expr_path(cx.path_ident(sp, ident)) }) .collect() - } + }) +} - fn create_fields(&self, struct_def: &'a VariantData, mk_exprs: F) -> Vec - where - F: Fn(usize, &ast::FieldDef, Span) -> Vec>, - { - struct_def - .fields() +fn create_struct_field_access_fields( + span: Span, + cx: &ExtCtxt<'_>, + selflike_args: &[Box], + struct_def: &VariantData, + is_packed: bool, +) -> Vec { + create_fields(span, struct_def, |i, struct_field, sp| { + selflike_args .iter() - .enumerate() - .map(|(i, struct_field)| { - // For this field, get an expr for each selflike_arg. E.g. for - // `PartialEq::eq`, one for each of `&self` and `other`. - let sp = struct_field.span.with_ctxt(self.span.ctxt()); - let mut exprs: Vec<_> = mk_exprs(i, struct_field, sp); - let self_expr = exprs.remove(0); - let other_selflike_exprs = exprs; - FieldInfo { - span: sp.with_ctxt(self.span.ctxt()), - name: struct_field.ident, - self_expr, - other_selflike_exprs, - maybe_scalar: struct_field.ty.peel_refs().kind.maybe_scalar(), + .map(|selflike_arg| { + // Note: we must use `struct_field.span` rather than `sp` in the + // `unwrap_or_else` case otherwise the hygiene is wrong and we get + // "field `0` of struct `Point` is private" errors on tuple + // structs. + let mut field_expr = cx.expr( + sp, + ast::ExprKind::Field( + selflike_arg.clone(), + struct_field.ident.unwrap_or_else(|| { + Ident::from_str_and_span(&i.to_string(), struct_field.span) + }), + ), + ); + if is_packed { + // Fields in packed structs are wrapped in a block, e.g. `&{self.0}`, + // causing a copy instead of a (potentially misaligned) reference. + field_expr = cx.expr_block( + cx.block(struct_field.span, thin_vec![cx.stmt_expr(field_expr)]), + ); } + cx.expr_addr_of(sp, field_expr) }) .collect() - } - - fn mk_pattern_ident(&self, prefix: &str, i: usize) -> Ident { - Ident::from_str_and_span(&format!("{prefix}_{i}"), self.span) - } - - fn create_struct_pattern_fields( - &self, - cx: &ExtCtxt<'_>, - struct_def: &'a VariantData, - prefixes: &[String], - ) -> Vec { - self.create_fields(struct_def, |i, _struct_field, sp| { - prefixes - .iter() - .map(|prefix| { - let ident = self.mk_pattern_ident(prefix, i); - cx.expr_path(cx.path_ident(sp, ident)) - }) - .collect() - }) - } - - fn create_struct_field_access_fields( - &self, - cx: &ExtCtxt<'_>, - selflike_args: &[Box], - struct_def: &'a VariantData, - is_packed: bool, - ) -> Vec { - self.create_fields(struct_def, |i, struct_field, sp| { - selflike_args - .iter() - .map(|selflike_arg| { - // Note: we must use `struct_field.span` rather than `sp` in the - // `unwrap_or_else` case otherwise the hygiene is wrong and we get - // "field `0` of struct `Point` is private" errors on tuple - // structs. - let mut field_expr = cx.expr( - sp, - ast::ExprKind::Field( - selflike_arg.clone(), - struct_field.ident.unwrap_or_else(|| { - Ident::from_str_and_span(&i.to_string(), struct_field.span) - }), - ), - ); - if is_packed { - // Fields in packed structs are wrapped in a block, e.g. `&{self.0}`, - // causing a copy instead of a (potentially misaligned) reference. - field_expr = cx.expr_block( - cx.block(struct_field.span, thin_vec![cx.stmt_expr(field_expr)]), - ); - } - cx.expr_addr_of(sp, field_expr) - }) - .collect() - }) - } + }) } -/// The function passed to `cs_fold` is called repeatedly with a value of this -/// type. It describes one part of the code generation. The result is always an -/// expression. -pub(crate) enum CsFold { - /// The basic case: a field expression for one or more selflike args. E.g. - /// for `PartialEq::eq` this is something like `self.x == other.x`. - Single(FieldInfo), - - /// The combination of two field expressions. E.g. for `PartialEq::eq` this - /// is something like ` && `. - Combine(Span, Box, Box), - - // The fallback case for a struct or enum variant with no fields. - Fieldless, +pub(crate) fn generic_param_to_arg(cx: &ExtCtxt<'_>, p: &GenericParam, span: Span) -> GenericArg { + match p.kind { + GenericParamKind::Lifetime => GenericArg::Lifetime(cx.lifetime(span, p.ident)), + GenericParamKind::Type { .. } => GenericArg::Type(cx.ty_ident(span, p.ident)), + GenericParamKind::Const { .. } => GenericArg::Const(cx.const_ident(span, p.ident)), + } } -/// Folds over fields, combining the expressions for each field in a sequence. -/// Statics may not be folded over. -pub(crate) fn cs_fold( - use_foldl: bool, - cx: &ExtCtxt<'_>, - trait_span: Span, - substructure: Substructure<'_>, - mut f: F, -) -> Box -where - F: FnMut(&ExtCtxt<'_>, CsFold) -> Box, -{ - match substructure.fields { - EnumMatching(.., all_fields) | Struct(_, all_fields) => { - let mut fields = all_fields.into_iter(); - - let base_field = if use_foldl { fields.next() } else { fields.next_back() }; - - let Some(base_field) = base_field else { - return f(cx, CsFold::Fieldless); - }; - - let base_expr = f(cx, CsFold::Single(base_field)); - - let op = |old, field: FieldInfo| { - let span = field.span; - let new = f(cx, CsFold::Single(field)); - f(cx, CsFold::Combine(span, old, new)) - }; - - if use_foldl { fields.fold(base_expr, op) } else { fields.rfold(base_expr, op) } - } - EnumDiscr(discr_field, match_expr) => { - let discr_check_expr = f(cx, CsFold::Single(discr_field)); - if let Some(match_expr) = match_expr { - if use_foldl { - f(cx, CsFold::Combine(trait_span, discr_check_expr, match_expr)) - } else { - f(cx, CsFold::Combine(trait_span, match_expr, discr_check_expr)) - } - } else { - discr_check_expr - } - } - _ => cx.dcx().span_bug(trait_span, "unexpected substructure in `derive`"), +pub(crate) fn generics_without_defaults(g: &Generics) -> Generics { + Generics { + params: g + .params + .iter() + .map(|p| { + let mut p = p.clone(); + match &mut p.kind { + ast::GenericParamKind::Const { default, .. } => *default = None, + ast::GenericParamKind::Type { default } => *default = None, + ast::GenericParamKind::Lifetime => {} + }; + p.attrs.clear(); + p + }) + .collect(), + where_clause: g.where_clause.clone(), + span: g.span, } } diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs b/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs deleted file mode 100644 index 98ad5b8faf52b..0000000000000 --- a/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs +++ /dev/null @@ -1,133 +0,0 @@ -//! A mini version of ast::Ty, which is easier to use, and features an explicit `Self` type to use -//! when specifying impls to be derived. - -pub(crate) use Ty::*; -use rustc_ast::{self as ast, GenericArg, GenericParamKind, Generics, TyKind}; -use rustc_expand::base::ExtCtxt; -use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw}; -use thin_vec::ThinVec; - -/// A path, e.g., `::std::option::Option::` (global). Has support -/// for type parameters. -#[derive(Clone)] -pub(crate) struct Path { - path: Vec, - params: Vec>, - kind: PathKind, -} - -#[derive(Clone)] -pub(crate) enum PathKind { - Local, - Std, -} - -impl Path { - pub(crate) fn new(path: Vec) -> Path { - Path::new_(path, Vec::new(), PathKind::Std) - } - pub(crate) fn new_local(path: Symbol) -> Path { - Path::new_(vec![path], Vec::new(), PathKind::Local) - } - pub(crate) fn new_(path: Vec, params: Vec>, kind: PathKind) -> Path { - Path { path, params, kind } - } - - pub(crate) fn to_path( - &self, - cx: &ExtCtxt<'_>, - span: Span, - self_ty: Ident, - self_generics: &Generics, - ) -> ast::Path { - let mut idents = self.path.iter().map(|s| Ident::new(*s, span)).collect::>(); - let tys = self.params.iter().map(|t| t.to_ty(cx, span, self_ty, self_generics)); - let params = tys.map(GenericArg::Type).collect(); - - if let PathKind::Std = self.kind { - let def_site = cx.with_def_site_ctxt(DUMMY_SP); - idents.insert(0, Ident::new(kw::DollarCrate, def_site)); - } - cx.path_all(span, false, idents, params) - } -} - -/// A type. Supports pointers, Self, literals, unit or an arbitrary AST path. -#[derive(Clone)] -pub(crate) enum Ty { - Self_, - /// A reference. - Ref(Box, ast::Mutability), - /// `mod::mod::Type<[lifetime], [Params...]>`, including a plain type - /// parameter, and things like `i32` - Path(Path), - /// For () return types. - Unit, - /// An arbitrary type. - AstTy(Box), -} - -pub(crate) fn self_ref() -> Ty { - Ref(Box::new(Self_), ast::Mutability::Not) -} - -impl Ty { - pub(crate) fn to_ty( - &self, - cx: &ExtCtxt<'_>, - span: Span, - self_ty: Ident, - self_generics: &Generics, - ) -> Box { - match self { - Ref(ty, mutbl) => { - let raw_ty = ty.to_ty(cx, span, self_ty, self_generics); - cx.ty_ref(span, raw_ty, None, *mutbl) - } - Path(p) => cx.ty_path(p.to_path(cx, span, self_ty, self_generics)), - Self_ => cx.ty_path(self.to_path(cx, span, self_ty, self_generics)), - Unit => { - let ty = ast::TyKind::Tup(ThinVec::new()); - cx.ty(span, ty) - } - AstTy(ty) => ty.clone(), - } - } - - pub(crate) fn to_path( - &self, - cx: &ExtCtxt<'_>, - span: Span, - self_ty: Ident, - generics: &Generics, - ) -> ast::Path { - match self { - Self_ => { - let params: Vec<_> = generics - .params - .iter() - .map(|param| match param.kind { - GenericParamKind::Lifetime => { - GenericArg::Lifetime(ast::Lifetime { id: param.id, ident: param.ident }) - } - GenericParamKind::Type { .. } => { - GenericArg::Type(cx.ty_ident(span, param.ident)) - } - GenericParamKind::Const { .. } => { - GenericArg::Const(cx.const_ident(span, param.ident)) - } - }) - .collect(); - - cx.path_all(span, false, vec![self_ty], params) - } - Path(p) => p.to_path(cx, span, self_ty, generics), - AstTy(ty) => match &ty.kind { - TyKind::Path(_, path) => path.clone(), - _ => cx.dcx().span_bug(span, "non-path in a path in generic `derive`"), - }, - Ref(..) => cx.dcx().span_bug(span, "ref in a path in generic `derive`"), - Unit => cx.dcx().span_bug(span, "unit in a path in generic `derive`"), - } - } -} diff --git a/compiler/rustc_builtin_macros/src/deriving/hash.rs b/compiler/rustc_builtin_macros/src/deriving/hash.rs index b6a87851254fc..3e0722988155c 100644 --- a/compiler/rustc_builtin_macros/src/deriving/hash.rs +++ b/compiler/rustc_builtin_macros/src/deriving/hash.rs @@ -1,11 +1,10 @@ use rustc_ast::{Mutability, Safety}; use rustc_expand::base::ExtCtxt; -use rustc_span::{Ident, Span, sym}; +use rustc_span::{Ident, Span, kw, sym}; use thin_vec::{ThinVec, thin_vec}; -use crate::deriving::generic::ty::*; use crate::deriving::generic::*; -use crate::deriving::path_std; +use crate::deriving::{call_discriminant_value, path_std}; pub(crate) fn expand_deriving_hash( cx: &ExtCtxt<'_>, @@ -14,15 +13,15 @@ pub(crate) fn expand_deriving_hash( push: &mut dyn FnMut(Box), is_const: bool, ) { - let path = path_std!(hash::Hash); + let path = path_std!(cx, span, hash::Hash); - let typaram = sym::__H; + let typaram = Ident::new(sym::__H, span); - let arg = Path::new_local(typaram); + let arg = cx.ty_path(cx.path_ident(span, typaram)); let param = { - let path = cx.path_all(span, false, cx.std_path(&[sym::hash, sym::Hasher]), Vec::new()); - cx.typaram(span, Ident::new(typaram, span), thin_vec![cx.trait_bound(path, false)], None) + let path = path_std!(cx, span, hash::Hasher); + cx.typaram(typaram, thin_vec![cx.trait_bound(path, false)], None) }; let generics = ast::Generics { @@ -42,13 +41,13 @@ pub(crate) fn expand_deriving_hash( name: sym::hash, generics, explicit_self: true, - nonself_args: smallvec![(Ref(Box::new(Path(arg)), Mutability::Mut), sym::state)], - ret_ty: Unit, + nonself_args: smallvec![(cx.ty_ref(span, arg, None, Mutability::Mut), sym::state)], + has_other_selflike_arg: false, + ret_ty: cx.ty_unit(span), attributes: thin_vec![cx.attr_word(sym::inline, span)], fieldless_variants_strategy: FieldlessVariantsStrategy::Unify, combine_substructure: combine_substructure(hash_substructure), }], - associated_types: SmallVec::new(), is_const, safety: Safety::Default, document: true, @@ -57,29 +56,29 @@ pub(crate) fn expand_deriving_hash( hash_trait_def.expand(cx, item, push); } -fn hash_substructure(cx: &ExtCtxt<'_>, trait_span: Span, substr: Substructure<'_>) -> BlockOrExpr { - let [state_expr] = substr.nonselflike_args else { - cx.dcx().span_bug(trait_span, "incorrect number of arguments in `derive(Hash)`"); - }; +fn hash_substructure(cx: &ExtCtxt<'_>, span: Span, substr: Substructure<'_>) -> BlockOrExpr { let call_hash = |span, expr| { let strs = cx.std_path(&[sym::hash, sym::Hash, sym::hash]); let hash_path = cx.expr_path(cx.path_global(span, strs)); - let expr = cx.expr_call(span, hash_path, thin_vec![expr, state_expr.clone()]); + let expr = + cx.expr_call(span, hash_path, thin_vec![expr, cx.expr_ident_sym(span, sym::state)]); cx.stmt_expr(expr) }; - let (stmts, match_expr) = match substr.fields { + let (stmts, match_expr) = match substr { Struct(_, fields) | EnumMatching(.., fields) => { let stmts = fields.into_iter().map(|field| call_hash(field.span, field.self_expr)).collect(); (stmts, None) } - EnumDiscr(discr_field, match_expr) => { - assert!(discr_field.other_selflike_exprs.is_empty()); - let stmts = thin_vec![call_hash(discr_field.span, discr_field.self_expr)]; + EnumDiscr(match_expr) => { + let stmts = thin_vec![call_hash( + span, + cx.expr_addr_of(span, call_discriminant_value(cx, span, kw::SelfLower)) + )]; (stmts, match_expr) } - _ => cx.dcx().span_bug(trait_span, "unexpected substructure in `derive(Hash)`"), + _ => cx.dcx().span_bug(span, "unexpected substructure in `derive(Hash)`"), }; BlockOrExpr::new_mixed(stmts, match_expr) diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs index a1b1d56664f22..c4af6ac16bdc6 100644 --- a/compiler/rustc_builtin_macros/src/deriving/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs @@ -1,17 +1,19 @@ //! The compiler code necessary to implement the `#[derive]` extensions. +use std::iter::once; + use rustc_ast as ast; use rustc_ast::{GenericArg, MetaItem}; use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, MultiItemModifier}; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Ident, Span, Symbol, kw, sym}; use thin_vec::{ThinVec, thin_vec}; macro pathvec($($rest:ident)::+) {{ - vec![ $( sym::$rest ),+ ] + &[ $( sym::$rest ),+ ] }} -macro path_std($($x:tt)*) { - generic::ty::Path::new( pathvec!( $($x)* ) ) +macro path_std($cx: expr, $span: expr, $($x:tt)*) { + new_path($cx, $span, pathvec!( $($x)* ), vec![] ) } pub(crate) mod clone; @@ -87,6 +89,11 @@ fn call_intrinsic( cx.expr_call_global(span, path, args) } +/// Constructs an expression that calls the `discriminant_value` intrinsic. +fn call_discriminant_value(cx: &ExtCtxt<'_>, span: Span, arg: Symbol) -> Box { + call_intrinsic(cx, span, sym::discriminant_value, thin_vec![cx.expr_ident_sym(span, arg)]) +} + /// Constructs an expression that calls the `unreachable` intrinsic. fn call_unreachable(cx: &ExtCtxt<'_>, span: Span) -> Box { let call = call_intrinsic(cx, span, sym::unreachable, ThinVec::new()); @@ -110,3 +117,11 @@ fn assert_ty_bounds( let assert_path = cx.path_all(span, true, cx.std_path(assert_path), vec![GenericArg::Type(ty)]); stmts.push(cx.stmt_let_type_only(span, cx.ty_path(assert_path))); } + +fn new_path(cx: &ExtCtxt<'_>, span: Span, path: &[Symbol], params: Vec>) -> ast::Path { + let idents = path.iter().map(|s| Ident::new(*s, span)); + let params = params.into_iter().map(GenericArg::Type).collect(); + + let idents = once(Ident::new(kw::DollarCrate, span)).chain(idents).collect(); + cx.path_all(span, false, idents, params) +} diff --git a/compiler/rustc_builtin_macros/src/deriving/ord.rs b/compiler/rustc_builtin_macros/src/deriving/ord.rs index 3c2d95a299a51..c20bfc81bd409 100644 --- a/compiler/rustc_builtin_macros/src/deriving/ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/ord.rs @@ -1,10 +1,10 @@ use rustc_ast::Safety; use rustc_expand::base::ExtCtxt; -use rustc_span::{Ident, Span, sym}; +use rustc_span::{Span, sym}; use thin_vec::thin_vec; -use crate::deriving::generic::ty::*; use crate::deriving::generic::*; +use crate::deriving::partial_ord::{OrdlikeDerive, cmp_body, discr_data_order}; use crate::deriving::path_std; pub(crate) fn expand_deriving_ord( @@ -14,9 +14,11 @@ pub(crate) fn expand_deriving_ord( push: &mut dyn FnMut(Box), is_const: bool, ) { + let discr_then_data = discr_data_order(item); + let trait_def = TraitDef { span, - path: path_std!(cmp::Ord), + path: path_std!(cx, span, cmp::Ord), skip_path_as_bound: false, needs_copy_as_bound_if_packed: true, additional_bounds: SmallVec::new(), @@ -25,13 +27,18 @@ pub(crate) fn expand_deriving_ord( name: sym::cmp, generics: cx.empty_generics(span), explicit_self: true, - nonself_args: smallvec![(self_ref(), sym::other)], - ret_ty: Path(path_std!(cmp::Ordering)), + nonself_args: smallvec![(cx.ty_self_ref(span), sym::other)], + has_other_selflike_arg: true, + ret_ty: cx.ty_path(path_std!(cx, span, cmp::Ordering)), attributes: thin_vec![cx.attr_word(sym::inline, span)], fieldless_variants_strategy: FieldlessVariantsStrategy::Unify, - combine_substructure: combine_substructure(cs_cmp), + combine_substructure: combine_substructure(|cx, span, substr| cs_cmp( + cx, + span, + substr, + discr_then_data + )), }], - associated_types: SmallVec::new(), is_const, safety: Safety::Default, document: true, @@ -40,11 +47,12 @@ pub(crate) fn expand_deriving_ord( trait_def.expand(cx, item, push) } -pub(crate) fn cs_cmp(cx: &ExtCtxt<'_>, span: Span, substr: Substructure<'_>) -> BlockOrExpr { - let test_id = Ident::new(sym::cmp, span); - let equal_path = cx.path_global(span, cx.std_path(&[sym::cmp, sym::Ordering, sym::Equal])); - let cmp_path = cx.std_path(&[sym::cmp, sym::Ord, sym::cmp]); - +pub(crate) fn cs_cmp( + cx: &ExtCtxt<'_>, + span: Span, + substr: Substructure<'_>, + discr_then_data: bool, +) -> BlockOrExpr { // Builds: // // match ::core::cmp::Ord::cmp(&self.x, &other.x) { @@ -52,29 +60,6 @@ pub(crate) fn cs_cmp(cx: &ExtCtxt<'_>, span: Span, substr: Substructure<'_>) -> // ::core::cmp::Ord::cmp(&self.y, &other.y), // cmp => cmp, // } - let expr = cs_fold( - // foldr nests the if-elses correctly, leaving the first field - // as the outermost one, and the last as the innermost. - false, - cx, - span, - substr, - |cx, fold| match fold { - CsFold::Single(field) => { - let [other_expr] = &field.other_selflike_exprs[..] else { - cx.dcx().span_bug(field.span, "not exactly 2 arguments in `derive(Ord)`"); - }; - let args = thin_vec![field.self_expr.clone(), other_expr.clone()]; - cx.expr_call_global(field.span, cmp_path.clone(), args) - } - CsFold::Combine(span, expr1, expr2) => { - let eq_arm = cx.arm(span, cx.pat_path(span, equal_path.clone()), expr1); - let neq_arm = - cx.arm(span, cx.pat_ident(span, test_id), cx.expr_ident(span, test_id)); - cx.expr_match(span, expr2, thin_vec![eq_arm, neq_arm]) - } - CsFold::Fieldless => cx.expr_path(equal_path.clone()), - }, - ); + let expr = cmp_body(cx, span, substr, discr_then_data, OrdlikeDerive::Ord); BlockOrExpr::new_expr(expr) } diff --git a/compiler/rustc_builtin_macros/src/deriving/partial_eq.rs b/compiler/rustc_builtin_macros/src/deriving/partial_eq.rs index be48c0532e596..377a32dcca4f0 100644 --- a/compiler/rustc_builtin_macros/src/deriving/partial_eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/partial_eq.rs @@ -1,11 +1,10 @@ use rustc_ast::{BinOpKind, BorrowKind, Expr, ExprKind, Mutability, Safety}; use rustc_expand::base::ExtCtxt; -use rustc_span::{Span, sym}; +use rustc_span::{Ident, Span, kw, sym}; use thin_vec::thin_vec; -use crate::deriving::generic::ty::*; -use crate::deriving::generic::{self, *}; -use crate::deriving::path_std; +use crate::deriving::generic::*; +use crate::deriving::{call_discriminant_value, path_std}; /// Expands a `#[derive(PartialEq)]` attribute into an implementation for the /// target item. @@ -18,18 +17,17 @@ pub(crate) fn expand_deriving_partial_eq( ) { let structural_trait_def = TraitDef { span, - path: path_std!(marker::StructuralPartialEq), + path: path_std!(cx, span, marker::StructuralPartialEq), skip_path_as_bound: true, // crucial! needs_copy_as_bound_if_packed: false, // The `StructuralPartialEq` impl must have the *same* bounds as the `PartialEq` impl, // or it will apply in situations where it should not, such as in the bug // . - additional_bounds: smallvec![ty::Ty::Path(path_std!(cmp::PartialEq))], + additional_bounds: smallvec![path_std!(cx, span, cmp::PartialEq)], // We really don't support unions, but that's already checked by the impl generated below; // a second check here would lead to redundant error messages. supports_unions: true, methods: SmallVec::new(), - associated_types: SmallVec::new(), is_const: false, safety: Safety::Default, document: true, @@ -42,8 +40,9 @@ pub(crate) fn expand_deriving_partial_eq( name: sym::eq, generics: cx.empty_generics(span), explicit_self: true, - nonself_args: smallvec![(self_ref(), sym::other)], - ret_ty: Path(generic::ty::Path::new_local(sym::bool)), + nonself_args: smallvec![(cx.ty_self_ref(span), sym::other)], + has_other_selflike_arg: true, + ret_ty: cx.ty_path(cx.path_ident(span, Ident::new(sym::bool, span))), attributes: thin_vec![cx.attr_word(sym::inline, span)], fieldless_variants_strategy: FieldlessVariantsStrategy::Unify, combine_substructure: combine_substructure(get_substructure_equality_expr), @@ -51,13 +50,12 @@ pub(crate) fn expand_deriving_partial_eq( let trait_def = TraitDef { span, - path: path_std!(cmp::PartialEq), + path: path_std!(cx, span, cmp::PartialEq), skip_path_as_bound: false, needs_copy_as_bound_if_packed: true, additional_bounds: SmallVec::new(), supports_unions: false, methods, - associated_types: SmallVec::new(), is_const, safety: Safety::Default, document: true, @@ -121,12 +119,20 @@ fn get_substructure_equality_expr( span: Span, substructure: Substructure<'_>, ) -> BlockOrExpr { - use SubstructureFields::*; - - BlockOrExpr::new_expr(match substructure.fields { + BlockOrExpr::new_expr(match substructure { EnumMatching(.., fields) | Struct(.., fields) => { - let combine = move |acc, field| { - let rhs = get_field_equality_expr(cx, field); + let combine = move |acc, field: &FieldInfo| { + let rhs = field + .other_selflike_expr + .as_ref() + .expect("not exactly 2 arguments in `derive(PartialEq)`"); + + let rhs = cx.expr_binary( + field.span, + BinOpKind::Eq, + wrap_block_expr(cx, peel_refs(&field.self_expr)), + wrap_block_expr(cx, peel_refs(rhs)), + ); match acc { // Combine the previous comparison with the current field // using logical AND. @@ -146,44 +152,21 @@ fn get_substructure_equality_expr( // If there are no fields, treat as always equal. .unwrap_or_else(|| cx.expr_bool(span, true)) } - EnumDiscr(disc, match_expr) => { - let lhs = get_field_equality_expr(cx, &disc); + EnumDiscr(match_expr) => { + let self_expr = call_discriminant_value(cx, span, kw::SelfLower); + let other_selflike_expr = call_discriminant_value(cx, span, sym::other); + let lhs = cx.expr_binary(span, BinOpKind::Eq, self_expr, other_selflike_expr); let Some(match_expr) = match_expr else { return BlockOrExpr::new_expr(lhs); }; // Compare the discriminant first (cheaper), then the rest of the // fields. - cx.expr_binary(disc.span, BinOpKind::And, lhs, match_expr.clone()) + cx.expr_binary(span, BinOpKind::And, lhs, match_expr) } _ => cx.dcx().span_bug(span, "unexpected substructure in `derive(PartialEq)`"), }) } -/// Generates an equality comparison expression for a single struct or enum -/// field. -/// -/// This function produces an AST expression that compares the `self` and -/// `other` values for a field using `==`. It removes any leading references -/// from both sides for readability. If the field is a block expression, it is -/// wrapped in parentheses to ensure valid syntax. -/// -/// # Panics -/// -/// Panics if there are not exactly two arguments to compare (should be `self` -/// and `other`). -fn get_field_equality_expr(cx: &ExtCtxt<'_>, field: &FieldInfo) -> Box { - let [rhs] = &field.other_selflike_exprs[..] else { - cx.dcx().span_bug(field.span, "not exactly 2 arguments in `derive(PartialEq)`"); - }; - - cx.expr_binary( - field.span, - BinOpKind::Eq, - wrap_block_expr(cx, peel_refs(&field.self_expr)), - wrap_block_expr(cx, peel_refs(rhs)), - ) -} - /// Removes all leading immutable references from an expression. /// /// This is used to strip away any number of leading `&` from an expression diff --git a/compiler/rustc_builtin_macros/src/deriving/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/partial_ord.rs index fe5e48b11367d..8c3e2ee58f8e2 100644 --- a/compiler/rustc_builtin_macros/src/deriving/partial_ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/partial_ord.rs @@ -1,11 +1,10 @@ -use rustc_ast::{ExprKind, ItemKind, PatKind, Safety, ast}; +use rustc_ast::{Expr, ItemKind, Safety, ast}; use rustc_expand::base::ExtCtxt; -use rustc_span::{Ident, Span, sym}; +use rustc_span::{Ident, Span, kw, sym}; use thin_vec::thin_vec; -use crate::deriving::generic::ty::*; use crate::deriving::generic::*; -use crate::deriving::{path_std, pathvec}; +use crate::deriving::{call_discriminant_value, new_path, path_std, pathvec}; pub(crate) fn expand_deriving_partial_ord( cx: &ExtCtxt<'_>, @@ -14,37 +13,18 @@ pub(crate) fn expand_deriving_partial_ord( push: &mut dyn FnMut(Box), is_const: bool, ) { - let ordering_ty = Path(path_std!(cmp::Ordering)); - let ret_ty = - Path(Path::new_(pathvec!(option::Option), vec![Box::new(ordering_ty)], PathKind::Std)); + let ordering_ty = cx.ty_path(path_std!(cx, span, cmp::Ordering)); + let ret_ty = cx.ty_path(new_path(cx, span, pathvec!(option::Option), vec![ordering_ty])); // Order in which to perform matching - let discr_then_data = if let ItemKind::Enum(_, _, def) = &item.kind { - let dataful: Vec = def.variants.iter().map(|v| !v.data.fields().is_empty()).collect(); - match dataful.iter().filter(|&&b| b).count() { - // No data, placing the discriminant check first makes codegen simpler - 0 => true, - 1..=2 => false, - _ => (0..dataful.len() - 1).any(|i| { - if dataful[i] - && let Some(idx) = dataful[i + 1..].iter().position(|v| *v) - { - idx >= 2 - } else { - false - } - }), - } - } else { - true - }; + let discr_then_data = discr_data_order(item); let container_id = cx.current_expansion.id.expn_data().parent.expect_local(); let has_derive_ord = cx.resolver.has_derive_ord(container_id); let default_substructure = combine_substructure(|cx, span, substr| cs_partial_cmp(cx, span, substr, discr_then_data)); let simple_substructure = combine_substructure(|cx, span, _| { - cs_partial_cmp_simple(cx, span, cx.expr_ident(span, Ident::new(sym::other, span))) + cs_partial_cmp_simple(cx, span, cx.expr_ident_sym(span, sym::other)) }); let is_simple = match &item.kind { // For unit structs/zero-variant enums, the default generated code is better. @@ -73,7 +53,8 @@ pub(crate) fn expand_deriving_partial_ord( name: sym::partial_cmp, generics: cx.empty_generics(span), explicit_self: true, - nonself_args: smallvec![(self_ref(), sym::other)], + nonself_args: smallvec![(cx.ty_self_ref(span), sym::other)], + has_other_selflike_arg: true, ret_ty, attributes: thin_vec![cx.attr_word(sym::inline, span)], fieldless_variants_strategy: FieldlessVariantsStrategy::Unify, @@ -82,13 +63,12 @@ pub(crate) fn expand_deriving_partial_ord( let trait_def = TraitDef { span, - path: path_std!(cmp::PartialOrd), + path: path_std!(cx, span, cmp::PartialOrd), skip_path_as_bound: false, needs_copy_as_bound_if_packed: true, additional_bounds: smallvec![], supports_unions: false, methods: smallvec![partial_cmp_def], - associated_types: SmallVec::new(), is_const, safety: Safety::Default, document: true, @@ -96,6 +76,28 @@ pub(crate) fn expand_deriving_partial_ord( trait_def.expand_ext(cx, item, push, is_simple) } +pub(crate) fn discr_data_order(item: &ast::Item) -> bool { + if let ItemKind::Enum(_, _, def) = &item.kind { + let dataful: Vec = def.variants.iter().map(|v| !v.data.fields().is_empty()).collect(); + match dataful.iter().filter(|&&b| b).count() { + // No data, placing the discriminant check first makes codegen simpler + 0 => true, + 1..=2 => false, + _ => (0..dataful.len() - 1).any(|i| { + if dataful[i] + && let Some(idx) = dataful[i + 1..].iter().position(|v| *v) + { + idx >= 2 + } else { + false + } + }), + } + } else { + true + } +} + // Special case for the type deriving both `PartialOrd` and `Ord`. Builds: // ``` // Some(::core::cmp::Ord::cmp(self, other)) @@ -113,10 +115,6 @@ fn cs_partial_cmp( substr: Substructure<'_>, discr_then_data: bool, ) -> BlockOrExpr { - let test_id = Ident::new(sym::cmp, span); - let equal_path = cx.path_global(span, cx.std_path(&[sym::cmp, sym::Ordering, sym::Equal])); - let partial_cmp_path = cx.std_path(&[sym::cmp, sym::PartialOrd, sym::partial_cmp]); - // Builds: // // match ::core::cmp::PartialOrd::partial_cmp(&self.x, &other.x) { @@ -124,73 +122,121 @@ fn cs_partial_cmp( // ::core::cmp::PartialOrd::partial_cmp(&self.y, &other.y), // cmp => cmp, // } - let expr = cs_fold( - // foldr nests the if-elses correctly, leaving the first field - // as the outermost one, and the last as the innermost. - false, - cx, - span, - substr, - |cx, fold| match fold { - CsFold::Single(field) => { - let [other_expr] = &field.other_selflike_exprs[..] else { - cx.dcx() - .span_bug(field.span, "not exactly 2 arguments in `derive(PartialOrd)`"); - }; - let args = thin_vec![field.self_expr.clone(), other_expr.clone()]; - cx.expr_call_global(field.span, partial_cmp_path.clone(), args) - } - CsFold::Combine(span, mut expr1, expr2) => { - // When the item is an enum, this expands to - // ``` - // match (expr2) { - // Some(Ordering::Equal) => expr1, - // cmp => cmp - // } - // ``` - // where `expr2` is `partial_cmp(self_discr, other_discr)`, and `expr1` is a `match` - // against the enum variants. This means that we begin by comparing the enum discriminants, - // before either inspecting their contents (if they match), or returning - // the `cmp::Ordering` of comparing the enum discriminants. - // ``` - // match partial_cmp(self_discr, other_discr) { - // Some(Ordering::Equal) => match (self, other) { - // (Self::A(self_0), Self::A(other_0)) => partial_cmp(self_0, other_0), - // (Self::B(self_0), Self::B(other_0)) => partial_cmp(self_0, other_0), - // _ => Some(Ordering::Equal) - // } - // cmp => cmp - // } - // ``` - // If we have any certain enum layouts, flipping this results in better codegen - // ``` - // match (self, other) { - // (Self::A(self_0), Self::A(other_0)) => partial_cmp(self_0, other_0), - // _ => partial_cmp(self_discr, other_discr) - // } - // ``` - // Reference: https://github.com/rust-lang/rust/pull/103659#issuecomment-1328126354 + let expr = cmp_body(cx, span, substr, discr_then_data, OrdlikeDerive::PartialOrd); + BlockOrExpr::new_expr(expr) +} - if !discr_then_data - && let ExprKind::Match(_, arms, _) = &mut expr1.kind - && let Some(last) = arms.last_mut() - && let PatKind::Wild = last.pat.kind - { - last.body = Some(expr2); - expr1 - } else { - let eq_arm = cx.arm( - span, - cx.pat_some(span, cx.pat_path(span, equal_path.clone())), - expr1, - ); - let neq_arm = - cx.arm(span, cx.pat_ident(span, test_id), cx.expr_ident(span, test_id)); - cx.expr_match(span, expr2, thin_vec![eq_arm, neq_arm]) +#[derive(PartialEq)] +pub(crate) enum OrdlikeDerive { + PartialOrd, + Ord, +} + +pub(crate) fn cmp_body( + cx: &ExtCtxt<'_>, + span: Span, + substructure: Substructure<'_>, + discr_then_data: bool, + derive: OrdlikeDerive, +) -> Box { + let is_partial_ord = derive == OrdlikeDerive::PartialOrd; + let method_path = if is_partial_ord { + cx.std_path(&[sym::cmp, sym::PartialOrd, sym::partial_cmp]) + } else { + cx.std_path(&[sym::cmp, sym::Ord, sym::cmp]) + }; + let equal_path = cx.path_global(span, cx.std_path(&[sym::cmp, sym::Ordering, sym::Equal])); + + // The combination of two field expressions. E.g. for `Ord::cmp` this + // is something like ` && `. + let combine = |span, mut expr1: Box, expr2| { + // For `PartialOrd` (`Ord` works the same but without the `Some` wrapping), + // when the item is an enum, this expands to + // ``` + // match (expr2) { + // Some(Ordering::Equal) => expr1, + // cmp => cmp + // } + // ``` + // where `expr2` is `partial_cmp(self_discr, other_discr)`, and `expr1` is a `match` + // against the enum variants. This means that we begin by comparing the enum discriminants, + // before either inspecting their contents (if they match), or returning + // the `cmp::Ordering` of comparing the enum discriminants. + // ``` + // match partial_cmp(self_discr, other_discr) { + // Some(Ordering::Equal) => match (self, other) { + // (Self::A(self_0), Self::A(other_0)) => partial_cmp(self_0, other_0), + // (Self::B(self_0), Self::B(other_0)) => partial_cmp(self_0, other_0), + // _ => Some(Ordering::Equal) + // } + // cmp => cmp + // } + // ``` + // If we have any certain enum layouts, flipping this results in better codegen + // ``` + // match (self, other) { + // (Self::A(self_0), Self::A(other_0)) => partial_cmp(self_0, other_0), + // _ => partial_cmp(self_discr, other_discr) + // } + // ``` + // Reference: https://github.com/rust-lang/rust/pull/103659#issuecomment-1328126354 + + if !discr_then_data + && let ast::ExprKind::Match(_, arms, _) = &mut expr1.kind + && let Some(last) = arms.last_mut() + && let ast::PatKind::Wild = last.pat.kind + { + last.body = Some(expr2); + expr1 + } else { + let eq_pat = cx.pat_path(span, equal_path.clone()); + let eq_arm = cx.arm( + span, + if is_partial_ord { cx.pat_some(span, eq_pat) } else { eq_pat }, + expr1, + ); + let cmp_ident = Ident::new(sym::cmp, span); + let neq_arm = + cx.arm(span, cx.pat_ident(span, cmp_ident), cx.expr_ident(span, cmp_ident)); + cx.expr_match(span, expr2, thin_vec![eq_arm, neq_arm]) + } + }; + + match substructure { + EnumMatching(.., all_fields) | Struct(_, all_fields) => { + let op = |old, field: FieldInfo| { + // The basic case: a field expression for one or more selflike args. E.g. + // for `Ord::cmp` this is something like `Ord::cmp(&self.x, &other.x)`. + let other_expr = + field.other_selflike_expr.expect("not exactly 2 arguments in `derive`"); + let args = thin_vec![field.self_expr, other_expr]; + let new = cx.expr_call_global(field.span, method_path.clone(), args); + match old { + Some(old) => Some(combine(field.span, old, new)), + None => Some(new), } + }; + + all_fields.into_iter().rfold(None, op).unwrap_or_else(|| { + // The fallback case for a struct or enum variant with no fields. + let mut expr = cx.expr_path(equal_path); + if is_partial_ord { + expr = cx.expr_some(span, expr) + }; + expr + }) + } + EnumDiscr(match_expr) => { + let self_expr = cx.expr_addr_of(span, call_discriminant_value(cx, span, kw::SelfLower)); + let other_expr = cx.expr_addr_of(span, call_discriminant_value(cx, span, sym::other)); + let args = thin_vec![self_expr, other_expr]; + let discr_check_expr = cx.expr_call_global(span, method_path, args); + if let Some(match_expr) = match_expr { + combine(span, match_expr, discr_check_expr) + } else { + discr_check_expr } - CsFold::Fieldless => cx.expr_some(span, cx.expr_path(equal_path.clone())), - }, - ); - BlockOrExpr::new_expr(expr) + } + _ => cx.dcx().span_bug(span, "unexpected substructure in `derive`"), + } } diff --git a/compiler/rustc_builtin_macros/src/deriving/reborrow.rs b/compiler/rustc_builtin_macros/src/deriving/reborrow.rs index dc45b1a896bc9..05ad76dbd1ff9 100644 --- a/compiler/rustc_builtin_macros/src/deriving/reborrow.rs +++ b/compiler/rustc_builtin_macros/src/deriving/reborrow.rs @@ -1,13 +1,12 @@ -use rustc_ast::{self as ast, AttrArgs, GenericArg, GenericParamKind, Generics, ItemKind, token}; +use rustc_ast::{self as ast, AttrArgs, Generics, ItemKind, token}; use rustc_errors::E0802; use rustc_expand::base::ExtCtxt; use rustc_macros::Diagnostic; use rustc_span::{Ident, Span, Symbol, sym}; use thin_vec::ThinVec; -macro_rules! path { - ($span:expr, $($part:ident)::*) => { vec![$(Ident::new(sym::$part, $span),)*] } -} +use crate::deriving::generic::*; +use crate::deriving::new_path; pub(crate) fn expand_deriving_reborrow( cx: &ExtCtxt<'_>, @@ -37,15 +36,7 @@ pub(crate) fn expand_deriving_coerce_shared( return; }; - push_marker_impl( - cx, - span, - ident, - generics, - sym::CoerceShared, - vec![GenericArg::Type(target)], - push, - ); + push_marker_impl(cx, span, ident, generics, sym::CoerceShared, vec![target], push); } fn struct_def<'a>( @@ -114,78 +105,28 @@ fn push_marker_impl( ident: Ident, generics: &Generics, trait_name: Symbol, - trait_args: Vec, + trait_args: Vec>, push: &mut dyn FnMut(Box), ) { - let mut trait_parts = path!(span, core::marker); - trait_parts.push(Ident::new(trait_name, span)); - let trait_path = cx.path_all(span, true, trait_parts, trait_args); + let trait_path = new_path(cx, span, &[sym::core, sym::marker, trait_name], trait_args); let trait_ref = cx.trait_ref(trait_path); - let self_params: Vec<_> = generics - .params - .iter() - .map(|param| match param.kind { - GenericParamKind::Lifetime => { - GenericArg::Lifetime(cx.lifetime(param.span(), param.ident)) - } - GenericParamKind::Type { .. } => { - GenericArg::Type(cx.ty_ident(param.span(), param.ident)) - } - GenericParamKind::Const { .. } => { - GenericArg::Const(cx.const_ident(param.span(), param.ident)) - } - }) - .collect(); + let self_params: Vec<_> = + generics.params.iter().map(|p| generic_param_to_arg(cx, p, p.span())).collect(); let self_ty = cx.ty_path(cx.path_all(span, false, vec![ident], self_params)); - push(cx.item( + push(cx.item_trait_impl( span, thin_vec::thin_vec![cx.attr_word(sym::automatically_derived, span)], - ast::ItemKind::Impl(ast::Impl { - generics: impl_generics(cx, generics), - of_trait: Some(Box::new(ast::TraitImplHeader { - safety: ast::Safety::Default, - polarity: ast::ImplPolarity::Positive, - defaultness: ast::Defaultness::Implicit, - trait_ref, - })), - constness: ast::Const::No, - self_ty, - items: ThinVec::new(), - }), + generics_without_defaults(generics), + ast::Safety::Default, + false, + trait_ref, + self_ty, + ThinVec::new(), )); } -fn impl_generics(cx: &ExtCtxt<'_>, generics: &Generics) -> Generics { - // Rebuild the generic parameter declarations because defaults are allowed on structs but - // rejected on impls. Preserve lifetime, type, and const parameters and their bounds, const - // parameter types, and the where-clause, while omitting type and const defaults. - Generics { - params: generics - .params - .iter() - .map(|param| match ¶m.kind { - GenericParamKind::Lifetime => { - cx.lifetime_param(param.span(), param.ident, param.bounds.clone()) - } - GenericParamKind::Type { default: _ } => { - cx.typaram(param.span(), param.ident, param.bounds.clone(), None) - } - GenericParamKind::Const { ty, span: _, default: _ } => cx.const_param( - param.span(), - param.ident, - param.bounds.clone(), - ty.clone(), - None, - ), - }) - .collect(), - where_clause: generics.where_clause.clone(), - span: generics.span, - } -} - #[derive(Diagnostic)] #[diag("`derive({$trait_name})` is only supported for structs, not {$kind}s", code = E0802)] struct UnsupportedItem { diff --git a/compiler/rustc_builtin_macros/src/diagnostics.rs b/compiler/rustc_builtin_macros/src/diagnostics.rs index 604341c3e7bbf..62f0063a7fbd3 100644 --- a/compiler/rustc_builtin_macros/src/diagnostics.rs +++ b/compiler/rustc_builtin_macros/src/diagnostics.rs @@ -542,9 +542,9 @@ pub(crate) struct EnvNotDefinedWithUserMessage { } // Hand-written implementation to support custom user messages. -impl<'a, G> Diagnostic<'a, G> for EnvNotDefinedWithUserMessage { +impl<'a> Diagnostic<'a> for EnvNotDefinedWithUserMessage { #[track_caller] - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let mut diag = Diag::new(dcx, level, self.msg_from_user.to_string()); diag.span(self.span); diag @@ -773,7 +773,7 @@ pub(crate) struct FormatUnusedArg { // Allow the singular form to be a subdiagnostic of the multiple-unused // form of diagnostic. impl Subdiagnostic for FormatUnusedArg { - fn add_to_diag(self, diag: &mut Diag<'_, G>) { + fn add_to_diag(self, diag: &mut Diag<'_>) { diag.span_label( self.span, msg!( @@ -957,8 +957,8 @@ pub(crate) struct AsmClobberNoReg { pub(crate) clobbers: Vec, } -impl<'a, G> Diagnostic<'a, G> for AsmClobberNoReg { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { +impl<'a> Diagnostic<'a> for AsmClobberNoReg { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { Diag::new( dcx, level, diff --git a/compiler/rustc_builtin_macros/src/edition_panic.rs b/compiler/rustc_builtin_macros/src/edition_panic.rs index ac5c43c660088..4c99ef9a9da26 100644 --- a/compiler/rustc_builtin_macros/src/edition_panic.rs +++ b/compiler/rustc_builtin_macros/src/edition_panic.rs @@ -1,6 +1,5 @@ use rustc_ast::token::Delimiter; -use rustc_ast::tokenstream::{DelimSpan, TokenStream}; -use rustc_ast::*; +use rustc_ast::tokenstream::TokenStream; use rustc_expand::base::*; use rustc_span::edition::Edition; use rustc_span::{Span, sym}; @@ -44,26 +43,15 @@ fn expand<'cx>( ) -> MacroExpanderResult<'cx> { let sp = cx.with_call_site_ctxt(sp); - ExpandResult::Ready(MacEager::expr( - cx.expr( + ExpandResult::Ready(MacEager::expr(cx.expr_macro_call( + sp, + cx.macro_call( sp, - ExprKind::MacCall(Box::new(MacCall { - path: Path { - span: sp, - segments: cx - .std_path(&[sym::panic, mac]) - .into_iter() - .map(PathSegment::from_ident) - .collect(), - }, - args: Box::new(DelimArgs { - dspan: DelimSpan::from_single(sp), - delim: Delimiter::Parenthesis, - tokens: tts, - }), - })), + cx.path(sp, cx.std_path(&[sym::panic, mac])), + Delimiter::Parenthesis, + tts, ), - )) + ))) } pub(crate) fn use_panic_2021(mut span: Span) -> bool { diff --git a/compiler/rustc_builtin_macros/src/eii.rs b/compiler/rustc_builtin_macros/src/eii.rs index 61b1c50180a6b..ed37599c9e85a 100644 --- a/compiler/rustc_builtin_macros/src/eii.rs +++ b/compiler/rustc_builtin_macros/src/eii.rs @@ -331,7 +331,7 @@ fn generate_default_impl( foreign_item_name.span, // prefix self to explicitly escape the const block generated below // NOTE: this is why EIIs can't be used on statements - vec![Ident::from_str_and_span("self", foreign_item_name.span), foreign_item_name], + vec![Ident::new(kw::SelfLower, foreign_item_name.span), foreign_item_name], )), }); diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index f501110fc97d5..d4817d7ad25ac 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -265,7 +265,7 @@ fn make_format_args( } } } - err.emit() + err.emit_err() } Err(guar) => guar, }; @@ -339,7 +339,9 @@ fn make_format_args( parse::Suggestion::UsePositional => { let captured_arg_span = fmt_span.from_inner(InnerSpan::new(err.span.start, err.span.end)); - if let Ok(arg) = ecx.source_map().span_to_snippet(captured_arg_span) { + if is_source_literal + && let Ok(arg) = ecx.source_map().span_to_snippet(captured_arg_span) + { let span = match args.unnamed_args().last() { Some(arg) => arg.expr.span, None => fmt_span, @@ -360,17 +362,21 @@ fn make_format_args( } } parse::Suggestion::ReorderFormatParameter(span, replacement) => { - let span = fmt_span.from_inner(InnerSpan::new(span.start, span.end)); - e.sugg_ = - Some(diagnostics::InvalidFormatStringSuggestion::ReorderFormatParameter { - span, - replacement, - }); + if is_source_literal { + let span = fmt_span.from_inner(InnerSpan::new(span.start, span.end)); + e.sugg_ = + Some(diagnostics::InvalidFormatStringSuggestion::ReorderFormatParameter { + span, + replacement, + }); + } } parse::Suggestion::AddMissingColon(span) => { - let span = fmt_span.from_inner(InnerSpan::new(span.start, span.end)); - e.sugg_ = - Some(diagnostics::InvalidFormatStringSuggestion::AddMissingColon { span }); + if is_source_literal { + let span = fmt_span.from_inner(InnerSpan::new(span.start, span.end)); + e.sugg_ = + Some(diagnostics::InvalidFormatStringSuggestion::AddMissingColon { span }); + } } parse::Suggestion::UseRustDebugPrintingMacro => { // This targets `println!("{=}", x);` and `println!("{0=}", x);` @@ -1107,7 +1113,7 @@ fn report_invalid_references( // for `println!("{7:7$}", 1);` indexes.sort(); indexes.dedup(); - let span: MultiSpan = if !parser.is_source_literal || parser.arg_places.is_empty() { + let span = if !parser.is_source_literal || parser.arg_places.is_empty() { MultiSpan::from_span(fmt_span) } else { MultiSpan::from_spans(invalid_refs.iter().filter_map(|&(_, span, _, _)| span).collect()) @@ -1156,7 +1162,7 @@ fn expand_format_args_impl<'cx>( } } Err(err) => { - let guar = err.emit(); + let guar = err.emit_err(); DummyResult::any(sp, guar) } }) diff --git a/compiler/rustc_builtin_macros/src/direct_const_arg.rs b/compiler/rustc_builtin_macros/src/gca.rs similarity index 64% rename from compiler/rustc_builtin_macros/src/direct_const_arg.rs rename to compiler/rustc_builtin_macros/src/gca.rs index 6af503e9a4681..9e117e06ea9fd 100644 --- a/compiler/rustc_builtin_macros/src/direct_const_arg.rs +++ b/compiler/rustc_builtin_macros/src/gca.rs @@ -10,8 +10,7 @@ pub(crate) fn expand<'cx>( span: Span, tts: TokenStream, ) -> MacroExpanderResult<'cx> { - let ExpandResult::Ready(expr) = get_single_expr_from_tts(cx, span, tts, "direct_const_arg!") - else { + let ExpandResult::Ready(expr) = get_single_expr_from_tts(cx, span, tts, "gca!") else { return ExpandResult::Retry(()); }; let expr = match expr { @@ -19,16 +18,9 @@ pub(crate) fn expand<'cx>( Err(err) => return ExpandResult::Ready(DummyResult::any(span, err)), }; - let id = ast::DUMMY_NODE_ID; ExpandResult::Ready(Box::new(base::MacEager { - expr: Some(Box::new(ast::Expr { - id, - kind: ast::ExprKind::DirectConstArg(expr.clone()), - span, - attrs: Default::default(), - tokens: None, - })), - ty: Some(Box::new(ast::Ty { id, kind: ast::TyKind::DirectConstArg(expr), span })), + expr: Some(cx.expr(span, ast::ExprKind::GcaMacro(expr.clone()))), + ty: Some(cx.ty(span, ast::TyKind::GcaMacro(expr))), ..Default::default() })) } diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index d6293f8b015d0..fada1c477ac88 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -131,8 +131,8 @@ impl AllocFnFactory<'_, '_> { // disambiguated somehow. Currently the generated code would // fail to compile with "identifier is bound more than once in // this parameter list". - let size = Ident::from_str_and_span("size", self.span); - let align = Ident::from_str_and_span("align", self.span); + let size = Ident::new(sym::size, self.span); + let align = Ident::new(sym::align, self.span); let usize = self.cx.path_ident(self.span, Ident::new(sym::usize, self.span)); let ty_usize = self.cx.ty_path(usize); diff --git a/compiler/rustc_builtin_macros/src/iter.rs b/compiler/rustc_builtin_macros/src/iter.rs index 86bf347cd9848..59fabe966b7e1 100644 --- a/compiler/rustc_builtin_macros/src/iter.rs +++ b/compiler/rustc_builtin_macros/src/iter.rs @@ -12,7 +12,7 @@ pub(crate) fn expand<'cx>( let closure = match parse_closure(cx, sp, tts) { Ok(parsed) => parsed, Err(err) => { - return ExpandResult::Ready(DummyResult::any(sp, err.emit())); + return ExpandResult::Ready(DummyResult::any(sp, err.emit_err())); } }; diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 57759bb113401..c564e14776c91 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -34,12 +34,12 @@ mod define_opaque; mod derive; mod deriving; mod diagnostics; -mod direct_const_arg; mod edition_panic; mod eii; mod env; mod format; mod format_foreign; +mod gca; mod global_allocator; mod iter; mod log_syntax; @@ -83,11 +83,11 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { concat_bytes: concat_bytes::expand_concat_bytes, const_format_args: format::expand_format_args, core_panic: edition_panic::expand_panic, - direct_const_arg: direct_const_arg::expand, env: env::expand_env, file: source_util::expand_file, format_args: format::expand_format_args, format_args_nl: format::expand_format_args_nl, + gca: gca::expand, global_asm: asm::expand_global_asm, include: source_util::expand_include, include_bytes: source_util::expand_include_bytes, diff --git a/compiler/rustc_builtin_macros/src/offload.rs b/compiler/rustc_builtin_macros/src/offload.rs index 4111843b0c9d8..4da4ce4e4a505 100644 --- a/compiler/rustc_builtin_macros/src/offload.rs +++ b/compiler/rustc_builtin_macros/src/offload.rs @@ -1,5 +1,5 @@ use rustc_ast::ast; -use rustc_ast::token::{Delimiter, Token, TokenKind}; +use rustc_ast::token::{Delimiter, IdentKind, Token, TokenKind}; use rustc_ast::tokenstream::{DelimSpan, Spacing, TokenStream, TokenTree}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_session::config::Offload; @@ -148,8 +148,8 @@ pub(crate) fn expand_kernel( } // inline(never) attr - let ts: Vec = vec![TokenTree::Token( - Token::new(TokenKind::Ident(sym::never, false.into()), span), + let ts = vec![TokenTree::Token( + Token::new(TokenKind::Ident(sym::never, IdentKind::Normal), span), Spacing::Joint, )]; diff --git a/compiler/rustc_builtin_macros/src/pattern_type.rs b/compiler/rustc_builtin_macros/src/pattern_type.rs index 215baf099416b..4d34ba963e4e8 100644 --- a/compiler/rustc_builtin_macros/src/pattern_type.rs +++ b/compiler/rustc_builtin_macros/src/pattern_type.rs @@ -14,7 +14,7 @@ pub(crate) fn expand<'cx>( let (ty, pat) = match parse_pat_ty(cx, tts) { Ok(parsed) => parsed, Err(err) => { - return ExpandResult::Ready(DummyResult::any(sp, err.emit())); + return ExpandResult::Ready(DummyResult::any(sp, err.emit_err())); } }; diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs index c35e56a8e850b..ecf4f7e7fb1af 100644 --- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs +++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs @@ -231,12 +231,10 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { if !self.is_proc_macro_crate { let path = &attr.get_normal_item().path; - self.dcx - .create_err(diagnostics::AttributeOnlyUsableWithCrateType { - span: path.span, - path: &pprust::path_to_string(&attr.get_normal_item().path), - }) - .emit(); + self.dcx.emit_err(diagnostics::AttributeOnlyUsableWithCrateType { + span: path.span, + path: &pprust::path_to_string(&attr.get_normal_item().path), + }); return; } @@ -341,6 +339,7 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> Box { let mut decls_static = cx.item_static( span, + ast::AttrVec::new(), Ident::new(sym::_DECLS, span), cx.ty_ref( span, diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index 37b2f49c3596d..61fa31b5de254 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -120,7 +120,7 @@ pub(crate) fn expand_include<'cx>( let path = match cx.sess.resolve_path(path.as_str(), sp) { Ok(path) => path, Err(err) => { - let guar = err.emit(); + let guar = err.emit_err(); return ExpandResult::Ready(DummyResult::any(sp, guar)); } }; @@ -225,7 +225,7 @@ pub(crate) fn expand_include_str( Err(utf8err) => { let mut err = cx.dcx().struct_span_err(sp, format!("`{path}` wasn't a utf-8 file")); utf8_error(cx.source_map(), path.as_str(), None, &mut err, utf8err, &bytes[..]); - DummyResult::any(sp, err.emit()) + DummyResult::any(sp, err.emit_err()) } }, Err(dummy) => dummy, @@ -270,7 +270,7 @@ fn load_binary_file( let resolved_path = match cx.sess.resolve_path(original_path, macro_span) { Ok(path) => path, Err(err) => { - let guar = err.emit(); + let guar = err.emit_err(); return Err(DummyResult::any(macro_span, guar)); } }; @@ -306,7 +306,7 @@ fn load_binary_file( ); } } - let guar = err.emit(); + let guar = err.emit_err(); Err(DummyResult::any(macro_span, guar)) } } diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index 857d0b774879d..8e99d532029db 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -3,7 +3,7 @@ use std::{assert_matches, iter}; -use rustc_ast::{self as ast, GenericParamKind, Mutability, Safety, attr, join_path_idents}; +use rustc_ast::{self as ast, GenericParamKind, Mutability, attr, join_path_idents}; use rustc_ast_pretty::pprust; use rustc_attr_ir::{Attribute, AttributeKind}; use rustc_attr_parsing::AttributeParser; @@ -167,11 +167,7 @@ pub(crate) fn expand_test_or_bench( let should_panic_path = |name| { cx.path( sp, - vec![ - test_ident, - Ident::from_str_and_span("ShouldPanic", sp), - Ident::from_str_and_span(name, sp), - ], + vec![test_ident, Ident::new(sym::ShouldPanic, sp), Ident::from_str_and_span(name, sp)], ) }; @@ -179,11 +175,7 @@ pub(crate) fn expand_test_or_bench( let test_type_path = |name| { cx.path( sp, - vec![ - test_ident, - Ident::from_str_and_span("TestType", sp), - Ident::from_str_and_span(name, sp), - ], + vec![test_ident, Ident::new(sym::TestType, sp), Ident::from_str_and_span(name, sp)], ) }; @@ -210,8 +202,9 @@ pub(crate) fn expand_test_or_bench( thin_vec![ // #[coverage(off)] // |__bench_fn_name| self::test::assert_test_result( - coverage_off(cx.lambda1( + coverage_off(cx.closure( sp, + vec![bencher_param], cx.expr_call( sp, cx.expr_path(test_path("assert_test_result")), @@ -224,7 +217,6 @@ pub(crate) fn expand_test_or_bench( ), ], ), - bencher_param, )), // ) ], ) @@ -235,8 +227,9 @@ pub(crate) fn expand_test_or_bench( thin_vec![ // #[coverage(off)] // || { - coverage_off(cx.lambda0( + coverage_off(cx.closure( sp, + vec![], // test::assert_test_result( cx.expr_call( sp, @@ -263,76 +256,74 @@ pub(crate) fn expand_test_or_bench( let location_info = get_location_info(cx, fn_); - let mut test_const = - cx.item( + // static $ident: test::TestDescAndFn = + // We use a static because these things only exist to have references taken + // to them for the test case array. No reason to introduce tons of promoteds for that. + // Promoteds have the advantage that they can be merged to save space, but every one + // of these points to a different function so that will not happen. + let mut test_const = cx.item_static( + sp, + thin_vec![ + // #[cfg(test)] + cx.attr_nested_word(sym::cfg, sym::test, attr_sp), + // #[rustc_test_marker = "test_case_sort_key"] + cx.attr_name_value_str(sym::rustc_test_marker, test_path_symbol, attr_sp), + // #[doc(hidden)] + cx.attr_nested_word(sym::doc, sym::hidden, attr_sp), + ], + Ident::new(fn_.ident.name, sp), + cx.ty(sp, ast::TyKind::Path(None, test_path("TestDescAndFn"))), + Mutability::Not, + // test::TestDescAndFn { + cx.expr_struct( sp, + test_path("TestDescAndFn"), thin_vec![ - // #[cfg(test)] - cx.attr_nested_word(sym::cfg, sym::test, attr_sp), - // #[rustc_test_marker = "test_case_sort_key"] - cx.attr_name_value_str(sym::rustc_test_marker, test_path_symbol, attr_sp), - // #[doc(hidden)] - cx.attr_nested_word(sym::doc, sym::hidden, attr_sp), - ], - // static $ident: test::TestDescAndFn = - // We use a static because these things only exist to have references taken - // to them for the test case array. No reason to introduce tons of promoteds for that. - // Promoteds have the advantage that they can be merged to save space, but every one - // of these points to a different function so that will not happen. - ast::ItemKind::Static( - ast::StaticItem { - ident: Ident::new(fn_.ident.name, sp), - ty: cx.ty(sp, ast::TyKind::Path(None, test_path("TestDescAndFn"))), - safety: Safety::Default, - mutability: Mutability::Not, - define_opaque: None, - eii_impl: None, - // test::TestDescAndFn { - expr: Some( - cx.expr_struct( - sp, - test_path("TestDescAndFn"), - thin_vec![ - // desc: test::TestDesc { - field( - "desc", - cx.expr_struct(sp, test_path("TestDesc"), thin_vec![ - // name: "path::to::test" - field( - "name", - cx.expr_call( - sp, - cx.expr_path(test_path("StaticTestName")), - thin_vec![cx.expr_str(sp, test_path_symbol)], - ), - ), - // ignore: true | false - field("ignore", cx.expr_bool(sp, should_ignore(&item)),), - // ignore_message: Some("...") | None - field( - "ignore_message", - if let Some(msg) = should_ignore_message(&item) { - cx.expr_some(sp, cx.expr_str(sp, msg)) - } else { - cx.expr_none(sp) - }, + // desc: test::TestDesc { + field( + "desc", + cx.expr_struct( + sp, + test_path("TestDesc"), + thin_vec![ + // name: "path::to::test" + field( + "name", + cx.expr_call( + sp, + cx.expr_path(test_path("StaticTestName")), + thin_vec![cx.expr_str(sp, test_path_symbol)], ), - // source_file: - field("source_file", cx.expr_str(sp, location_info.0)), - // start_line: start line of the test fn identifier. - field("start_line", cx.expr_usize(sp, location_info.1)), - // start_col: start column of the test fn identifier. - field("start_col", cx.expr_usize(sp, location_info.2)), - // end_line: end line of the test fn identifier. - field("end_line", cx.expr_usize(sp, location_info.3)), - // end_col: end column of the test fn identifier. - field("end_col", cx.expr_usize(sp, location_info.4)), - // compile_fail: true | false - field("compile_fail", cx.expr_bool(sp, false)), - // no_run: true | false - field("no_run", cx.expr_bool(sp, false)), - // should_panic: ... - field("should_panic", match should_panic(cx, &item) { + ), + // ignore: true | false + field("ignore", cx.expr_bool(sp, should_ignore(&item)),), + // ignore_message: Some("...") | None + field( + "ignore_message", + if let Some(msg) = should_ignore_message(&item) { + cx.expr_some(sp, cx.expr_str(sp, msg)) + } else { + cx.expr_none(sp) + }, + ), + // source_file: + field("source_file", cx.expr_str(sp, location_info.0)), + // start_line: start line of the test fn identifier. + field("start_line", cx.expr_usize(sp, location_info.1)), + // start_col: start column of the test fn identifier. + field("start_col", cx.expr_usize(sp, location_info.2)), + // end_line: end line of the test fn identifier. + field("end_line", cx.expr_usize(sp, location_info.3)), + // end_col: end column of the test fn identifier. + field("end_col", cx.expr_usize(sp, location_info.4)), + // compile_fail: true | false + field("compile_fail", cx.expr_bool(sp, false)), + // no_run: true | false + field("no_run", cx.expr_bool(sp, false)), + // should_panic: ... + field( + "should_panic", + match should_panic(cx, &item) { // test::ShouldPanic::No ShouldPanic::No => { cx.expr_path(should_panic_path("No")) @@ -347,9 +338,12 @@ pub(crate) fn expand_test_or_bench( cx.expr_path(should_panic_path("YesWithMessage")), thin_vec![cx.expr_str(sp, sym)], ), - },), - // test_type: ... - field("test_type", match test_type(cx) { + }, + ), + // test_type: ... + field( + "test_type", + match test_type(cx) { // test::TestType::UnitTest TestType::UnitTest => { cx.expr_path(test_type_path("UnitTest")) @@ -362,19 +356,18 @@ pub(crate) fn expand_test_or_bench( TestType::Unknown => { cx.expr_path(test_type_path("Unknown")) } - },), - // }, - ],), - ), - // testfn: test::StaticTestFn(...) | test::StaticBenchFn(...) - field("testfn", test_fn), // } - ], - ), // } + }, + ), + // }, + ], ), - } - .into(), - ), - ); + ), + // testfn: test::StaticTestFn(...) | test::StaticBenchFn(...) + field("testfn", test_fn), // } + ], + // } + ), + ); test_const.vis.kind = ast::VisibilityKind::Public; // extern crate test @@ -411,10 +404,10 @@ fn not_testable_error(cx: &ExtCtxt<'_>, is_bench: bool, attr_sp: Span, item: Opt let level = match item.map(|i| &i.kind) { // These were a warning before #92959 and need to continue being that to avoid breaking // stable user code (#94508). - Some(ast::ItemKind::MacCall(_)) => Level::Warning, + Some(ast::ItemKind::MacCall(_)) => Level::Warning(None), _ => Level::Error, }; - let mut err = Diag::<()>::new(dcx, level, msg); + let mut err = Diag::new(dcx, level, msg); err.span(attr_sp); if let Some(item) = item { err.span_label( diff --git a/compiler/rustc_builtin_macros/src/test_binder_constraints.rs b/compiler/rustc_builtin_macros/src/test_binder_constraints.rs index 0c7482672df46..3e70e83bb0762 100644 --- a/compiler/rustc_builtin_macros/src/test_binder_constraints.rs +++ b/compiler/rustc_builtin_macros/src/test_binder_constraints.rs @@ -1,5 +1,5 @@ use rustc_ast::tokenstream::TokenStream; -use rustc_ast::{AttrVec, VisibilityKind, ast, token}; +use rustc_ast::{AttrVec, ast, token}; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult}; use rustc_span::Span; use smallvec::SmallVec; @@ -19,22 +19,15 @@ pub(crate) fn expand<'cx>( let item = match p.parse_test_binder_constraints() { Ok(expr) => expr, Err(diag) => { - let guar = diag.emit(); + let guar = diag.emit_err(); return ExpandResult::Ready(DummyResult::any(span, guar)); } }; if p.token != token::Eof { cx.dcx().emit_err(diagnostics::OnlyOneArgument { span: p.token.span, name }); } - let item = Box::new(ast::Item { - attrs: AttrVec::default(), - id: ast::DUMMY_NODE_ID, - span, - vis: ast::Visibility { kind: VisibilityKind::Inherited, span: span.shrink_to_lo() }, - kind: ast::ItemKind::TestBinderConstraints(item), - tokens: None, - }); - rustc_expand::base::ExpandResult::Ready(Box::new(MacEager { + let item = cx.item(span, AttrVec::default(), ast::ItemKind::TestBinderConstraints(item)); + ExpandResult::Ready(Box::new(MacEager { expr: None, items: Some(SmallVec::from_buf([item])), ty: None, diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index 677db66530ecb..d6167d30558a3 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -324,7 +324,7 @@ fn add_main(cx: &mut TestCtxt<'_>, c: &mut ast::Crate) { // pub fn main() -> ExitCode { ... } let main_ret_ty = if cx.test_runner.is_none() { // Built-in runner has return type `ExitCode`. - let exit_code_path = vec![test_ident, Ident::from_str_and_span("ExitCode", sp)]; + let exit_code_path = vec![test_ident, Ident::new(sym::ExitCode, sp)]; ecx.ty(sp, ast::TyKind::Path(None, ecx.path(sp, exit_code_path))) } else { // User-defined runners have return type `()`. @@ -364,7 +364,12 @@ fn add_main(cx: &mut TestCtxt<'_>, c: &mut ast::Crate) { }); // Integrate the new item into existing module structures. - let items = AstFragment::Items(smallvec![test_extern_stmt, main]); + // `extern crate test;` is only needed with the default runner. + let items = AstFragment::Items(if cx.test_runner.is_none() { + smallvec![test_extern_stmt, main] + } else { + smallvec![main] + }); c.items.extend(cx.ext_cx.monotonic_expander().fully_expand_fragment(items).make_items()); } diff --git a/compiler/rustc_builtin_macros/src/util.rs b/compiler/rustc_builtin_macros/src/util.rs index 80fa3e3b8ac8d..7b990c5af080f 100644 --- a/compiler/rustc_builtin_macros/src/util.rs +++ b/compiler/rustc_builtin_macros/src/util.rs @@ -106,7 +106,7 @@ pub(crate) fn expr_to_spanned_string<'a>( Ok(ast::LitKind::ByteStr(..)) => { let mut err = cx.dcx().struct_span_err(expr.span, err_msg); let span = expr.span.shrink_to_lo(); - err.span_suggestion( + err.span_suggestion_short( span.with_hi(span.lo() + BytePos(1)), "consider removing the leading `b`", "", @@ -136,7 +136,7 @@ pub(crate) fn expr_to_string( ) -> ExpandResult, ()> { expr_to_spanned_string(cx, expr, err_msg).map(|res| { res.map_err(|err| match err { - Ok((err, _)) => err.emit(), + Ok((err, _)) => err.emit_err(), Err(guar) => guar, }) .map(|ExprToSpannedString { symbol, style, .. }| (symbol, style)) @@ -157,7 +157,7 @@ pub(crate) fn check_zero_tts(cx: &ExtCtxt<'_>, span: Span, tts: TokenStream, nam pub(crate) fn parse_expr(p: &mut parser::Parser<'_>) -> Result, ErrorGuaranteed> { let guar = match p.parse_expr() { Ok(expr) => return Ok(expr), - Err(err) => err.emit(), + Err(err) => err.emit_err(), }; while p.token != token::Eof { p.bump(); @@ -191,7 +191,7 @@ pub(crate) fn get_single_str_spanned_from_tts( }; expr_to_spanned_string(cx, ret, "argument must be a string literal").map(|res| { res.map_err(|err| match err { - Ok((err, _)) => err.emit(), + Ok((err, _)) => err.emit_err(), Err(guar) => guar, }) .map(|ExprToSpannedString { symbol, span, .. }| (symbol, span)) diff --git a/compiler/rustc_builtin_macros/src/view_type.rs b/compiler/rustc_builtin_macros/src/view_type.rs index 090603f4f1253..69bd8f2727fc3 100644 --- a/compiler/rustc_builtin_macros/src/view_type.rs +++ b/compiler/rustc_builtin_macros/src/view_type.rs @@ -15,7 +15,7 @@ pub(crate) fn expand<'cx>( let (ty, pat) = match parse_view_ty(cx, tts) { Ok(parsed) => parsed, Err(err) => { - return ExpandResult::Ready(DummyResult::any(sp, err.emit())); + return ExpandResult::Ready(DummyResult::any(sp, err.emit_err())); } }; diff --git a/compiler/rustc_codegen_cranelift/build_system/stdarch_skip/aarch64_core.txt b/compiler/rustc_codegen_cranelift/build_system/stdarch_skip/aarch64_core.txt new file mode 100644 index 0000000000000..3a028cd380029 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/build_system/stdarch_skip/aarch64_core.txt @@ -0,0 +1,301 @@ +vaba_s16 +vaba_s32 +vaba_s8 +vaba_u16 +vaba_u32 +vaba_u8 +vabaq_s16 +vabaq_s32 +vabaq_s8 +vabaq_u16 +vabaq_u32 +vabaq_u8 +vhadd_s16 +vhadd_s32 +vhadd_s8 +vhadd_u16 +vhadd_u32 +vhadd_u8 +vhaddq_s16 +vhaddq_s32 +vhaddq_s8 +vhaddq_u16 +vhaddq_u32 +vhaddq_u8 +vhsub_s16 +vhsub_s32 +vhsub_s8 +vhsub_u16 +vhsub_u32 +vhsub_u8 +vhsubq_s16 +vhsubq_s32 +vhsubq_s8 +vhsubq_u16 +vhsubq_u32 +vhsubq_u8 +vld1_f16_x2 +vld1_f16_x3 +vld1_f16_x4 +vld1_f32_x2 +vld1_f32_x3 +vld1_f32_x4 +vld1_f64_x2 +vld1_f64_x3 +vld1_f64_x4 +vld1_p16_x2 +vld1_p16_x3 +vld1_p16_x4 +vld1_p64_x2 +vld1_p64_x3 +vld1_p64_x4 +vld1_p8_x2 +vld1_p8_x3 +vld1_p8_x4 +vld1_s16_x2 +vld1_s16_x3 +vld1_s16_x4 +vld1_s32_x2 +vld1_s32_x3 +vld1_s32_x4 +vld1_s64_x2 +vld1_s64_x3 +vld1_s64_x4 +vld1_s8_x2 +vld1_s8_x3 +vld1_s8_x4 +vld1_u16_x2 +vld1_u16_x3 +vld1_u16_x4 +vld1_u32_x2 +vld1_u32_x3 +vld1_u32_x4 +vld1_u64_x2 +vld1_u64_x3 +vld1_u64_x4 +vld1_u8_x2 +vld1_u8_x3 +vld1_u8_x4 +vld1q_f16_x2 +vld1q_f16_x3 +vld1q_f16_x4 +vld1q_f32_x2 +vld1q_f32_x3 +vld1q_f32_x4 +vld1q_f64_x2 +vld1q_f64_x3 +vld1q_f64_x4 +vld1q_p16_x2 +vld1q_p16_x3 +vld1q_p16_x4 +vld1q_p64_x2 +vld1q_p64_x3 +vld1q_p64_x4 +vld1q_p8_x2 +vld1q_p8_x3 +vld1q_p8_x4 +vld1q_s16_x2 +vld1q_s16_x3 +vld1q_s16_x4 +vld1q_s32_x2 +vld1q_s32_x3 +vld1q_s32_x4 +vld1q_s64_x2 +vld1q_s64_x3 +vld1q_s64_x4 +vld1q_s8_x2 +vld1q_s8_x3 +vld1q_s8_x4 +vld1q_u16_x2 +vld1q_u16_x3 +vld1q_u16_x4 +vld1q_u32_x2 +vld1q_u32_x3 +vld1q_u32_x4 +vld1q_u64_x2 +vld1q_u64_x3 +vld1q_u64_x4 +vld1q_u8_x2 +vld1q_u8_x3 +vld1q_u8_x4 +vld2_f16 +vld2_f32 +vld2_lane_f16 +vld2_lane_f32 +vld2_lane_s16 +vld2_lane_s32 +vld2_lane_s64 +vld2_lane_s8 +vld2_lane_u16 +vld2_lane_u32 +vld2_lane_u64 +vld2_lane_u8 +vld2_p16 +vld2_p8 +vld2_s16 +vld2_s32 +vld2_s64 +vld2_s8 +vld2_u16 +vld2_u32 +vld2_u64 +vld2_u8 +vld2q_f16 +vld2q_f32 +vld2q_f64 +vld2q_lane_f16 +vld2q_lane_f32 +vld2q_lane_s16 +vld2q_lane_s32 +vld2q_lane_s64 +vld2q_lane_s8 +vld2q_lane_u16 +vld2q_lane_u32 +vld2q_lane_u64 +vld2q_lane_u8 +vld2q_p16 +vld2q_p8 +vld2q_s16 +vld2q_s32 +vld2q_s64 +vld2q_s8 +vld2q_u16 +vld2q_u32 +vld2q_u64 +vld2q_u8 +vld3_f16 +vld3_lane_f16 +vld3_lane_f32 +vld3_lane_s16 +vld3_lane_s32 +vld3_lane_s64 +vld3_lane_s8 +vld3_lane_u16 +vld3_lane_u32 +vld3_lane_u64 +vld3_lane_u8 +vld3q_f16 +vld3q_lane_f16 +vld3q_lane_f32 +vld3q_lane_s16 +vld3q_lane_s32 +vld3q_lane_s64 +vld3q_lane_s8 +vld3q_lane_u16 +vld3q_lane_u32 +vld3q_lane_u64 +vld3q_lane_u8 +vld4_f16 +vld4_lane_f16 +vld4_lane_f32 +vld4_lane_s16 +vld4_lane_s32 +vld4_lane_s64 +vld4_lane_s8 +vld4_lane_u16 +vld4_lane_u32 +vld4_lane_u64 +vld4_lane_u8 +vld4q_f16 +vld4q_lane_f16 +vld4q_lane_f32 +vld4q_lane_s16 +vld4q_lane_s32 +vld4q_lane_s64 +vld4q_lane_s8 +vld4q_lane_u16 +vld4q_lane_u32 +vld4q_lane_u64 +vld4q_lane_u8 +vqtbl2_p8 +vqtbl2_s8 +vqtbl2_u8 +vqtbl2q_p8 +vqtbl2q_s8 +vqtbl2q_u8 +vqtbl3_p8 +vqtbl3_s8 +vqtbl3_u8 +vqtbl3q_p8 +vqtbl3q_s8 +vqtbl3q_u8 +vqtbl4_p8 +vqtbl4_s8 +vqtbl4_u8 +vqtbl4q_p8 +vqtbl4q_s8 +vqtbl4q_u8 +vqtbx1_p8 +vqtbx1_s8 +vqtbx1_u8 +vqtbx1q_p8 +vqtbx1q_s8 +vqtbx1q_u8 +vqtbx2_p8 +vqtbx2_s8 +vqtbx2_u8 +vqtbx2q_p8 +vqtbx2q_s8 +vqtbx2q_u8 +vqtbx3_p8 +vqtbx3_s8 +vqtbx3_u8 +vqtbx3q_p8 +vqtbx3q_s8 +vqtbx3q_u8 +vqtbx4_p8 +vqtbx4_s8 +vqtbx4_u8 +vqtbx4q_p8 +vqtbx4q_s8 +vqtbx4q_u8 +vrhadd_s16 +vrhadd_s32 +vrhadd_s8 +vrhadd_u16 +vrhadd_u32 +vrhadd_u8 +vrhaddq_s16 +vrhaddq_s32 +vrhaddq_s8 +vrhaddq_u16 +vrhaddq_u32 +vrhaddq_u8 +vsli_n_p16 +vsli_n_p8 +vsli_n_s16 +vsli_n_s32 +vsli_n_s64 +vsli_n_s8 +vsli_n_u16 +vsli_n_u32 +vsli_n_u64 +vsli_n_u8 +vsliq_n_p16 +vsliq_n_p8 +vsliq_n_s16 +vsliq_n_s32 +vsliq_n_s64 +vsliq_n_s8 +vsliq_n_u16 +vsliq_n_u32 +vsliq_n_u64 +vsliq_n_u8 +vtbl3_p8 +vtbl3_s8 +vtbl3_u8 +vtbl4_p8 +vtbl4_s8 +vtbl4_u8 +vtbx1_p8 +vtbx1_s8 +vtbx1_u8 +vtbx2_p8 +vtbx2_s8 +vtbx2_u8 +vtbx3_p8 +vtbx3_s8 +vtbx3_u8 +vtbx4_p8 +vtbx4_s8 +vtbx4_u8 diff --git a/compiler/rustc_codegen_cranelift/build_system/stdarch_skip/aarch64_intrinsics_clang.txt b/compiler/rustc_codegen_cranelift/build_system/stdarch_skip/aarch64_intrinsics_clang.txt new file mode 100644 index 0000000000000..5f11867fd3ee9 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/build_system/stdarch_skip/aarch64_intrinsics_clang.txt @@ -0,0 +1 @@ +vcvth_s16_f16 diff --git a/compiler/rustc_codegen_cranelift/build_system/stdarch_skip/aarch64_intrinsics_common.txt b/compiler/rustc_codegen_cranelift/build_system/stdarch_skip/aarch64_intrinsics_common.txt new file mode 100644 index 0000000000000..a452f0c16b593 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/build_system/stdarch_skip/aarch64_intrinsics_common.txt @@ -0,0 +1,1173 @@ +__jcvt +vaba_s16 +vaba_s32 +vaba_s8 +vaba_u16 +vaba_u32 +vaba_u8 +vabal_high_s16 +vabal_high_s32 +vabal_high_s8 +vabal_high_u16 +vabal_high_u32 +vabal_high_u8 +vabal_s16 +vabal_s32 +vabal_s8 +vabal_u16 +vabal_u32 +vabal_u8 +vabaq_s16 +vabaq_s32 +vabaq_s8 +vabaq_u16 +vabaq_u32 +vabaq_u8 +vabd_f16 +vabd_f32 +vabd_f64 +vabd_s16 +vabd_s32 +vabd_s8 +vabd_u16 +vabd_u32 +vabd_u8 +vabdd_f64 +vabdh_f16 +vabdl_high_s16 +vabdl_high_s32 +vabdl_high_s8 +vabdl_high_u16 +vabdl_high_u32 +vabdl_high_u8 +vabdl_s16 +vabdl_s32 +vabdl_s8 +vabdl_u16 +vabdl_u32 +vabdl_u8 +vabdq_f16 +vabdq_f32 +vabdq_f64 +vabdq_s16 +vabdq_s32 +vabdq_s8 +vabdq_u16 +vabdq_u32 +vabdq_u8 +vabds_f32 +vabsd_s64 +vaddlv_s16 +vaddlv_s32 +vaddlv_s8 +vaddlv_u16 +vaddlv_u32 +vaddlv_u8 +vaddlvq_s16 +vaddlvq_s32 +vaddlvq_s8 +vaddlvq_u16 +vaddlvq_u32 +vaddvq_f64 +vamax_f16 +vamaxq_f16 +vamin_f16 +vaminq_f16 +vcadd_rot270_f16 +vcadd_rot270_f32 +vcadd_rot90_f16 +vcadd_rot90_f32 +vcaddq_rot270_f16 +vcaddq_rot270_f32 +vcaddq_rot270_f64 +vcaddq_rot90_f16 +vcaddq_rot90_f32 +vcaddq_rot90_f64 +vcage_f16 +vcage_f32 +vcage_f64 +vcaged_f64 +vcageh_f16 +vcageq_f16 +vcageq_f32 +vcageq_f64 +vcages_f32 +vcagt_f16 +vcagt_f32 +vcagt_f64 +vcagtd_f64 +vcagth_f16 +vcagtq_f16 +vcagtq_f32 +vcagtq_f64 +vcagts_f32 +vcale_f16 +vcale_f32 +vcale_f64 +vcaled_f64 +vcaleh_f16 +vcaleq_f16 +vcaleq_f32 +vcaleq_f64 +vcales_f32 +vcalt_f16 +vcalt_f32 +vcalt_f64 +vcaltd_f64 +vcalth_f16 +vcaltq_f16 +vcaltq_f32 +vcaltq_f64 +vcalts_f32 +vcmla_f16 +vcmla_f32 +vcmla_lane_f16 +vcmla_lane_f32 +vcmla_laneq_f16 +vcmla_laneq_f32 +vcmla_rot180_f16 +vcmla_rot180_f32 +vcmla_rot180_lane_f16 +vcmla_rot180_lane_f32 +vcmla_rot180_laneq_f16 +vcmla_rot180_laneq_f32 +vcmla_rot270_f16 +vcmla_rot270_f32 +vcmla_rot270_lane_f16 +vcmla_rot270_lane_f32 +vcmla_rot270_laneq_f16 +vcmla_rot270_laneq_f32 +vcmla_rot90_f16 +vcmla_rot90_f32 +vcmla_rot90_lane_f16 +vcmla_rot90_lane_f32 +vcmla_rot90_laneq_f16 +vcmla_rot90_laneq_f32 +vcmlaq_f16 +vcmlaq_f32 +vcmlaq_f64 +vcmlaq_lane_f16 +vcmlaq_lane_f32 +vcmlaq_laneq_f16 +vcmlaq_laneq_f32 +vcmlaq_rot180_f16 +vcmlaq_rot180_f32 +vcmlaq_rot180_f64 +vcmlaq_rot180_lane_f16 +vcmlaq_rot180_lane_f32 +vcmlaq_rot180_laneq_f16 +vcmlaq_rot180_laneq_f32 +vcmlaq_rot270_f16 +vcmlaq_rot270_f32 +vcmlaq_rot270_f64 +vcmlaq_rot270_lane_f16 +vcmlaq_rot270_lane_f32 +vcmlaq_rot270_laneq_f16 +vcmlaq_rot270_laneq_f32 +vcmlaq_rot90_f16 +vcmlaq_rot90_f32 +vcmlaq_rot90_f64 +vcmlaq_rot90_lane_f16 +vcmlaq_rot90_lane_f32 +vcmlaq_rot90_laneq_f16 +vcmlaq_rot90_laneq_f32 +vcvt_n_f16_s16 +vcvt_n_f16_u16 +vcvt_n_f32_s32 +vcvt_n_f32_u32 +vcvt_n_f64_s64 +vcvt_n_f64_u64 +vcvt_n_s16_f16 +vcvt_n_s32_f32 +vcvt_n_s64_f64 +vcvt_n_u16_f16 +vcvt_n_u32_f32 +vcvt_n_u64_f64 +vcvt_s32_f32 +vcvt_s64_f64 +vcvt_u32_f32 +vcvt_u64_f64 +vcvta_s16_f16 +vcvta_s32_f32 +vcvta_s64_f64 +vcvta_u16_f16 +vcvta_u32_f32 +vcvta_u64_f64 +vcvtad_s64_f64 +vcvtad_u64_f64 +vcvtah_s16_f16 +vcvtah_s32_f16 +vcvtah_s64_f16 +vcvtah_u16_f16 +vcvtah_u32_f16 +vcvtah_u64_f16 +vcvtaq_s16_f16 +vcvtaq_s32_f32 +vcvtaq_s64_f64 +vcvtaq_u16_f16 +vcvtaq_u32_f32 +vcvtaq_u64_f64 +vcvtas_s32_f32 +vcvtas_u32_f32 +vcvtd_n_f64_s64 +vcvtd_n_f64_u64 +vcvtd_n_s64_f64 +vcvtd_n_u64_f64 +vcvth_n_f16_s16 +vcvth_n_f16_s32 +vcvth_n_f16_s64 +vcvth_n_f16_u16 +vcvth_n_f16_u32 +vcvth_n_f16_u64 +vcvth_n_s16_f16 +vcvth_n_s32_f16 +vcvth_n_s64_f16 +vcvth_n_u16_f16 +vcvth_n_u32_f16 +vcvth_n_u64_f16 +vcvtm_s16_f16 +vcvtm_s32_f32 +vcvtm_s64_f64 +vcvtm_u16_f16 +vcvtm_u32_f32 +vcvtm_u64_f64 +vcvtmd_s64_f64 +vcvtmd_u64_f64 +vcvtmh_s16_f16 +vcvtmh_s32_f16 +vcvtmh_s64_f16 +vcvtmh_u16_f16 +vcvtmh_u32_f16 +vcvtmh_u64_f16 +vcvtmq_s16_f16 +vcvtmq_s32_f32 +vcvtmq_s64_f64 +vcvtmq_u16_f16 +vcvtmq_u32_f32 +vcvtmq_u64_f64 +vcvtms_s32_f32 +vcvtms_u32_f32 +vcvtn_s16_f16 +vcvtn_s32_f32 +vcvtn_s64_f64 +vcvtn_u16_f16 +vcvtn_u32_f32 +vcvtn_u64_f64 +vcvtnd_s64_f64 +vcvtnd_u64_f64 +vcvtnh_s16_f16 +vcvtnh_s32_f16 +vcvtnh_s64_f16 +vcvtnh_u16_f16 +vcvtnh_u32_f16 +vcvtnh_u64_f16 +vcvtnq_s16_f16 +vcvtnq_s64_f64 +vcvtnq_u16_f16 +vcvtnq_u32_f32 +vcvtnq_u64_f64 +vcvtns_s32_f32 +vcvtns_u32_f32 +vcvtp_s16_f16 +vcvtp_s32_f32 +vcvtp_s64_f64 +vcvtp_u16_f16 +vcvtp_u32_f32 +vcvtp_u64_f64 +vcvtpd_s64_f64 +vcvtpd_u64_f64 +vcvtph_s16_f16 +vcvtph_s32_f16 +vcvtph_s64_f16 +vcvtph_u16_f16 +vcvtph_u32_f16 +vcvtph_u64_f16 +vcvtpq_s16_f16 +vcvtpq_s32_f32 +vcvtpq_s64_f64 +vcvtpq_u16_f16 +vcvtpq_u32_f32 +vcvtpq_u64_f64 +vcvtps_s32_f32 +vcvtps_u32_f32 +vcvtq_n_f16_s16 +vcvtq_n_f16_u16 +vcvtq_n_f32_s32 +vcvtq_n_f32_u32 +vcvtq_n_f64_s64 +vcvtq_n_f64_u64 +vcvtq_n_s16_f16 +vcvtq_n_s32_f32 +vcvtq_n_s64_f64 +vcvtq_n_u16_f16 +vcvtq_n_u32_f32 +vcvtq_n_u64_f64 +vcvtq_s64_f64 +vcvtq_u32_f32 +vcvtq_u64_f64 +vcvts_n_f32_s32 +vcvts_n_f32_u32 +vcvts_n_s32_f32 +vcvts_n_u32_f32 +vcvtx_f32_f64 +vcvtx_high_f32_f64 +vcvtxd_f32_f64 +vdot_lane_s32 +vdot_lane_u32 +vdot_laneq_s32 +vdot_laneq_u32 +vdot_s32 +vdot_u32 +vdotq_lane_s32 +vdotq_lane_u32 +vdotq_laneq_s32 +vdotq_laneq_u32 +vdotq_s32 +vdotq_u32 +vfmlal_high_f16 +vfmlal_lane_high_f16 +vfmlal_lane_low_f16 +vfmlal_laneq_high_f16 +vfmlal_laneq_low_f16 +vfmlal_low_f16 +vfmlalq_high_f16 +vfmlalq_lane_high_f16 +vfmlalq_lane_low_f16 +vfmlalq_laneq_high_f16 +vfmlalq_laneq_low_f16 +vfmlalq_low_f16 +vfmlsl_high_f16 +vfmlsl_lane_high_f16 +vfmlsl_lane_low_f16 +vfmlsl_laneq_high_f16 +vfmlsl_laneq_low_f16 +vfmlsl_low_f16 +vfmlslq_high_f16 +vfmlslq_lane_high_f16 +vfmlslq_lane_low_f16 +vfmlslq_laneq_high_f16 +vfmlslq_laneq_low_f16 +vfmlslq_low_f16 +vhadd_s16 +vhadd_s32 +vhadd_s8 +vhadd_u16 +vhadd_u32 +vhadd_u8 +vhaddq_s16 +vhaddq_s32 +vhaddq_s8 +vhaddq_u16 +vhaddq_u32 +vhaddq_u8 +vhsub_s16 +vhsub_s32 +vhsub_s8 +vhsub_u16 +vhsub_u32 +vhsub_u8 +vhsubq_s16 +vhsubq_s32 +vhsubq_s8 +vhsubq_u16 +vhsubq_u32 +vhsubq_u8 +vld1_dup_f16 +vld1_f16 +vld1_f16_x2 +vld1_f16_x3 +vld1_f16_x4 +vld1_lane_f16 +vld1q_dup_f16 +vld1q_f16 +vld1q_f16_x2 +vld1q_f16_x3 +vld1q_f16_x4 +vld1q_lane_f16 +vld2_dup_f16 +vld2_f16 +vld2_lane_f16 +vld2q_dup_f16 +vld2q_f16 +vld2q_lane_f16 +vld3_dup_f16 +vld3_f16 +vld3_lane_f16 +vld3q_dup_f16 +vld3q_f16 +vld3q_lane_f16 +vld4_dup_f16 +vld4_f16 +vld4_lane_f16 +vld4q_dup_f16 +vld4q_f16 +vld4q_lane_f16 +vluti2_lane_f16 +vluti2_laneq_f16 +vluti2q_lane_f16 +vluti2q_laneq_f16 +vluti4q_lane_f16_x2 +vluti4q_laneq_f16_x2 +vmax_f16 +vmaxh_f16 +vmaxnm_f16 +vmaxnm_f32 +vmaxnm_f64 +vmaxnmh_f16 +vmaxnmq_f16 +vmaxnmq_f32 +vmaxnmq_f64 +vmaxnmv_f16 +vmaxnmv_f32 +vmaxnmvq_f16 +vmaxnmvq_f32 +vmaxnmvq_f64 +vmaxq_f16 +vmaxv_f16 +vmaxv_f32 +vmaxvq_f16 +vmaxvq_f32 +vmaxvq_f64 +vmin_f16 +vminh_f16 +vminnm_f16 +vminnm_f32 +vminnm_f64 +vminnmh_f16 +vminnmq_f16 +vminnmq_f32 +vminnmq_f64 +vminnmv_f16 +vminnmv_f32 +vminnmvq_f16 +vminnmvq_f32 +vminnmvq_f64 +vminq_f16 +vminv_f16 +vminv_f32 +vminvq_f16 +vminvq_f32 +vminvq_f64 +vmmlaq_s32 +vmmlaq_u32 +vmul_p8 +vmulq_p8 +vmulx_f16 +vmulx_f32 +vmulx_f64 +vmulx_lane_f16 +vmulx_lane_f32 +vmulx_lane_f64 +vmulx_laneq_f16 +vmulx_laneq_f32 +vmulx_laneq_f64 +vmulx_n_f16 +vmulxd_f64 +vmulxd_lane_f64 +vmulxd_laneq_f64 +vmulxh_f16 +vmulxh_lane_f16 +vmulxh_laneq_f16 +vmulxq_f16 +vmulxq_f32 +vmulxq_f64 +vmulxq_lane_f16 +vmulxq_lane_f32 +vmulxq_lane_f64 +vmulxq_laneq_f16 +vmulxq_laneq_f32 +vmulxq_laneq_f64 +vmulxq_n_f16 +vmulxs_f32 +vmulxs_lane_f32 +vmulxs_laneq_f32 +vpadd_f16 +vpadd_f32 +vpaddq_f16 +vpmax_f16 +vpmaxnm_f16 +vpmaxnm_f32 +vpmaxnmq_f16 +vpmaxnmq_f32 +vpmaxnmq_f64 +vpmaxnmqd_f64 +vpmaxnms_f32 +vpmaxq_f16 +vpmaxqd_f64 +vpmaxs_f32 +vpmin_f16 +vpminnm_f16 +vpminnm_f32 +vpminnmq_f16 +vpminnmq_f32 +vpminnmq_f64 +vpminnmqd_f64 +vpminnms_f32 +vpminq_f16 +vpminqd_f64 +vpmins_f32 +vqabs_s16 +vqabs_s32 +vqabs_s64 +vqabs_s8 +vqabsb_s8 +vqabsd_s64 +vqabsh_s16 +vqabsq_s16 +vqabsq_s32 +vqabsq_s64 +vqabsq_s8 +vqabss_s32 +vqaddd_s64 +vqaddd_u64 +vqadds_s32 +vqadds_u32 +vqdmlal_high_lane_s16 +vqdmlal_high_lane_s32 +vqdmlal_high_laneq_s16 +vqdmlal_high_laneq_s32 +vqdmlal_high_n_s16 +vqdmlal_high_n_s32 +vqdmlal_high_s16 +vqdmlal_high_s32 +vqdmlal_lane_s16 +vqdmlal_lane_s32 +vqdmlal_laneq_s16 +vqdmlal_laneq_s32 +vqdmlal_n_s16 +vqdmlal_n_s32 +vqdmlal_s16 +vqdmlal_s32 +vqdmlalh_lane_s16 +vqdmlalh_laneq_s16 +vqdmlalh_s16 +vqdmlals_lane_s32 +vqdmlals_laneq_s32 +vqdmlals_s32 +vqdmlsl_high_lane_s16 +vqdmlsl_high_lane_s32 +vqdmlsl_high_laneq_s16 +vqdmlsl_high_laneq_s32 +vqdmlsl_high_n_s16 +vqdmlsl_high_n_s32 +vqdmlsl_high_s16 +vqdmlsl_high_s32 +vqdmlsl_lane_s16 +vqdmlsl_lane_s32 +vqdmlsl_laneq_s16 +vqdmlsl_laneq_s32 +vqdmlsl_n_s16 +vqdmlsl_n_s32 +vqdmlsl_s16 +vqdmlsl_s32 +vqdmlslh_lane_s16 +vqdmlslh_laneq_s16 +vqdmlslh_s16 +vqdmlsls_lane_s32 +vqdmlsls_laneq_s32 +vqdmlsls_s32 +vqdmull_high_lane_s16 +vqdmull_high_lane_s32 +vqdmull_high_laneq_s16 +vqdmull_high_laneq_s32 +vqdmull_high_n_s16 +vqdmull_high_n_s32 +vqdmull_high_s16 +vqdmull_high_s32 +vqdmull_lane_s16 +vqdmull_lane_s32 +vqdmull_laneq_s16 +vqdmull_laneq_s32 +vqdmull_n_s16 +vqdmull_n_s32 +vqdmull_s16 +vqdmull_s32 +vqdmullh_lane_s16 +vqdmullh_laneq_s16 +vqdmullh_s16 +vqdmulls_lane_s32 +vqdmulls_laneq_s32 +vqdmulls_s32 +vqmovn_high_s16 +vqmovn_high_s32 +vqmovn_high_s64 +vqmovn_high_u16 +vqmovn_high_u32 +vqmovn_high_u64 +vqmovn_s16 +vqmovn_s32 +vqmovn_s64 +vqmovn_u16 +vqmovn_u32 +vqmovn_u64 +vqmovnd_s64 +vqmovnd_u64 +vqmovnh_s16 +vqmovnh_u16 +vqmovns_s32 +vqmovns_u32 +vqmovun_high_s16 +vqmovun_high_s32 +vqmovun_high_s64 +vqmovun_s16 +vqmovun_s32 +vqmovun_s64 +vqmovund_s64 +vqmovunh_s16 +vqmovuns_s32 +vqneg_s16 +vqneg_s32 +vqneg_s64 +vqneg_s8 +vqnegb_s8 +vqnegd_s64 +vqnegh_s16 +vqnegq_s16 +vqnegq_s32 +vqnegq_s64 +vqnegq_s8 +vqnegs_s32 +vqrdmlah_lane_s16 +vqrdmlah_lane_s32 +vqrdmlah_laneq_s16 +vqrdmlah_laneq_s32 +vqrdmlah_s16 +vqrdmlah_s32 +vqrdmlahh_lane_s16 +vqrdmlahh_laneq_s16 +vqrdmlahh_s16 +vqrdmlahq_lane_s16 +vqrdmlahq_lane_s32 +vqrdmlahq_laneq_s16 +vqrdmlahq_laneq_s32 +vqrdmlahq_s16 +vqrdmlahq_s32 +vqrdmlahs_lane_s32 +vqrdmlahs_laneq_s32 +vqrdmlahs_s32 +vqrdmlsh_lane_s16 +vqrdmlsh_lane_s32 +vqrdmlsh_laneq_s16 +vqrdmlsh_laneq_s32 +vqrdmlsh_s16 +vqrdmlsh_s32 +vqrdmlshh_lane_s16 +vqrdmlshh_laneq_s16 +vqrdmlshh_s16 +vqrdmlshq_lane_s16 +vqrdmlshq_lane_s32 +vqrdmlshq_laneq_s16 +vqrdmlshq_laneq_s32 +vqrdmlshq_s16 +vqrdmlshq_s32 +vqrdmlshs_lane_s32 +vqrdmlshs_laneq_s32 +vqrdmlshs_s32 +vqrdmulh_lane_s16 +vqrdmulh_lane_s32 +vqrdmulh_laneq_s16 +vqrdmulh_laneq_s32 +vqrdmulh_n_s16 +vqrdmulh_n_s32 +vqrdmulh_s16 +vqrdmulh_s32 +vqrdmulhh_lane_s16 +vqrdmulhh_laneq_s16 +vqrdmulhh_s16 +vqrdmulhq_lane_s16 +vqrdmulhq_lane_s32 +vqrdmulhq_laneq_s16 +vqrdmulhq_laneq_s32 +vqrdmulhq_n_s16 +vqrdmulhq_n_s32 +vqrdmulhq_s16 +vqrdmulhq_s32 +vqrdmulhs_lane_s32 +vqrdmulhs_laneq_s32 +vqrdmulhs_s32 +vqrshl_s16 +vqrshl_s32 +vqrshl_s64 +vqrshl_s8 +vqrshl_u16 +vqrshl_u32 +vqrshl_u64 +vqrshl_u8 +vqrshlb_s8 +vqrshlb_u8 +vqrshld_s64 +vqrshld_u64 +vqrshlh_s16 +vqrshlh_u16 +vqrshlq_s16 +vqrshlq_s32 +vqrshlq_s64 +vqrshlq_s8 +vqrshlq_u16 +vqrshlq_u32 +vqrshlq_u64 +vqrshlq_u8 +vqrshls_s32 +vqrshls_u32 +vqrshrn_high_n_s16 +vqrshrn_high_n_s32 +vqrshrn_high_n_s64 +vqrshrn_high_n_u16 +vqrshrn_high_n_u32 +vqrshrn_high_n_u64 +vqrshrn_n_s16 +vqrshrn_n_s32 +vqrshrn_n_s64 +vqrshrn_n_u16 +vqrshrn_n_u32 +vqrshrn_n_u64 +vqrshrnd_n_s64 +vqrshrnd_n_u64 +vqrshrnh_n_s16 +vqrshrnh_n_u16 +vqrshrns_n_s32 +vqrshrns_n_u32 +vqrshrun_high_n_s16 +vqrshrun_high_n_s32 +vqrshrun_high_n_s64 +vqrshrun_n_s16 +vqrshrun_n_s32 +vqrshrun_n_s64 +vqrshrund_n_s64 +vqrshrunh_n_s16 +vqrshruns_n_s32 +vqshl_n_s16 +vqshl_n_s32 +vqshl_n_s64 +vqshl_n_s8 +vqshl_n_u16 +vqshl_n_u32 +vqshl_n_u64 +vqshl_n_u8 +vqshl_s16 +vqshl_s32 +vqshl_s64 +vqshl_s8 +vqshl_u16 +vqshl_u32 +vqshl_u64 +vqshl_u8 +vqshlb_n_s8 +vqshlb_n_u8 +vqshlb_s8 +vqshlb_u8 +vqshld_n_s64 +vqshld_n_u64 +vqshld_s64 +vqshld_u64 +vqshlh_n_s16 +vqshlh_n_u16 +vqshlh_s16 +vqshlh_u16 +vqshlq_n_s16 +vqshlq_n_s32 +vqshlq_n_s64 +vqshlq_n_s8 +vqshlq_n_u16 +vqshlq_n_u32 +vqshlq_n_u64 +vqshlq_n_u8 +vqshlq_s16 +vqshlq_s32 +vqshlq_s64 +vqshlq_s8 +vqshlq_u16 +vqshlq_u32 +vqshlq_u64 +vqshlq_u8 +vqshls_n_s32 +vqshls_n_u32 +vqshls_s32 +vqshls_u32 +vqshlu_n_s16 +vqshlu_n_s32 +vqshlu_n_s64 +vqshlu_n_s8 +vqshlub_n_s8 +vqshlud_n_s64 +vqshluh_n_s16 +vqshluq_n_s16 +vqshluq_n_s32 +vqshluq_n_s64 +vqshluq_n_s8 +vqshlus_n_s32 +vqshrn_high_n_s16 +vqshrn_high_n_s32 +vqshrn_high_n_s64 +vqshrn_high_n_u16 +vqshrn_high_n_u32 +vqshrn_high_n_u64 +vqshrn_n_s16 +vqshrn_n_s32 +vqshrn_n_s64 +vqshrn_n_u16 +vqshrn_n_u32 +vqshrn_n_u64 +vqshrnd_n_s64 +vqshrnd_n_u64 +vqshrnh_n_s16 +vqshrnh_n_u16 +vqshrns_n_s32 +vqshrns_n_u32 +vqshrun_high_n_s16 +vqshrun_high_n_s32 +vqshrun_high_n_s64 +vqshrun_n_s16 +vqshrun_n_s32 +vqshrun_n_s64 +vqshrund_n_s64 +vqshrunh_n_s16 +vqshruns_n_s32 +vqsubd_s64 +vqsubd_u64 +vqsubs_s32 +vqsubs_u32 +vqtbl2_p8 +vqtbl2_s8 +vqtbl2_u8 +vqtbl2q_p8 +vqtbl2q_s8 +vqtbl2q_u8 +vqtbl3_p8 +vqtbl3_s8 +vqtbl3_u8 +vqtbl3q_p8 +vqtbl3q_s8 +vqtbl3q_u8 +vqtbl4_p8 +vqtbl4_s8 +vqtbl4_u8 +vqtbl4q_p8 +vqtbl4q_s8 +vqtbl4q_u8 +vqtbx1_p8 +vqtbx1_s8 +vqtbx1_u8 +vqtbx1q_p8 +vqtbx1q_s8 +vqtbx1q_u8 +vqtbx2_p8 +vqtbx2_s8 +vqtbx2_u8 +vqtbx2q_p8 +vqtbx2q_s8 +vqtbx2q_u8 +vqtbx3_p8 +vqtbx3_s8 +vqtbx3_u8 +vqtbx3q_p8 +vqtbx3q_s8 +vqtbx3q_u8 +vqtbx4_p8 +vqtbx4_s8 +vqtbx4_u8 +vqtbx4q_p8 +vqtbx4q_s8 +vqtbx4q_u8 +vraddhn_high_s16 +vraddhn_high_s32 +vraddhn_high_s64 +vraddhn_high_u16 +vraddhn_high_u32 +vraddhn_high_u64 +vraddhn_s16 +vraddhn_s32 +vraddhn_s64 +vraddhn_u16 +vraddhn_u32 +vraddhn_u64 +vrecpe_f16 +vrecpe_f32 +vrecpe_f64 +vrecpe_u32 +vrecped_f64 +vrecpeh_f16 +vrecpeq_f16 +vrecpeq_f64 +vrecpeq_u32 +vrecpes_f32 +vrecps_f16 +vrecps_f32 +vrecps_f64 +vrecpsd_f64 +vrecpsh_f16 +vrecpsq_f16 +vrecpsq_f64 +vrecpss_f32 +vrecpxd_f64 +vrecpxh_f16 +vrecpxs_f32 +vreinterpretq_f16_p128 +vrhadd_s16 +vrhadd_s32 +vrhadd_s8 +vrhadd_u16 +vrhadd_u32 +vrhadd_u8 +vrhaddq_s16 +vrhaddq_s32 +vrhaddq_s8 +vrhaddq_u16 +vrhaddq_u32 +vrhaddq_u8 +vrnd32x_f32 +vrnd32x_f64 +vrnd32xq_f32 +vrnd32xq_f64 +vrnd32z_f32 +vrnd32z_f64 +vrnd32zq_f32 +vrnd32zq_f64 +vrnd64x_f32 +vrnd64x_f64 +vrnd64xq_f32 +vrnd64xq_f64 +vrnd64z_f32 +vrnd64z_f64 +vrnd64zq_f32 +vrnd64zq_f64 +vrndi_f16 +vrndi_f32 +vrndi_f64 +vrndih_f16 +vrndiq_f16 +vrndiq_f32 +vrndiq_f64 +vrndn_f16 +vrndnh_f16 +vrndnq_f16 +vrndns_f32 +vrshl_s16 +vrshl_s32 +vrshl_s64 +vrshl_s8 +vrshl_u16 +vrshl_u32 +vrshl_u64 +vrshl_u8 +vrshld_s64 +vrshld_u64 +vrshlq_s16 +vrshlq_s32 +vrshlq_s64 +vrshlq_s8 +vrshlq_u16 +vrshlq_u32 +vrshlq_u64 +vrshlq_u8 +vrshr_n_s16 +vrshr_n_s32 +vrshr_n_s64 +vrshr_n_s8 +vrshr_n_u16 +vrshr_n_u32 +vrshr_n_u64 +vrshr_n_u8 +vrshrd_n_s64 +vrshrd_n_u64 +vrshrn_high_n_s16 +vrshrn_high_n_s32 +vrshrn_high_n_s64 +vrshrn_high_n_u16 +vrshrn_high_n_u32 +vrshrn_high_n_u64 +vrshrn_n_s16 +vrshrn_n_s32 +vrshrn_n_s64 +vrshrn_n_u16 +vrshrn_n_u32 +vrshrn_n_u64 +vrshrq_n_s16 +vrshrq_n_s32 +vrshrq_n_s64 +vrshrq_n_s8 +vrshrq_n_u16 +vrshrq_n_u32 +vrshrq_n_u64 +vrshrq_n_u8 +vrsqrte_f16 +vrsqrte_f32 +vrsqrte_f64 +vrsqrte_u32 +vrsqrted_f64 +vrsqrteh_f16 +vrsqrteq_f16 +vrsqrteq_f32 +vrsqrteq_f64 +vrsqrteq_u32 +vrsqrtes_f32 +vrsqrts_f16 +vrsqrts_f32 +vrsqrts_f64 +vrsqrtsd_f64 +vrsqrtsh_f16 +vrsqrtsq_f16 +vrsqrtsq_f32 +vrsqrtsq_f64 +vrsqrtss_f32 +vrsra_n_s16 +vrsra_n_s32 +vrsra_n_s64 +vrsra_n_s8 +vrsra_n_u16 +vrsra_n_u32 +vrsra_n_u64 +vrsra_n_u8 +vrsrad_n_s64 +vrsrad_n_u64 +vrsraq_n_s16 +vrsraq_n_s32 +vrsraq_n_s64 +vrsraq_n_s8 +vrsraq_n_u16 +vrsraq_n_u32 +vrsraq_n_u64 +vrsraq_n_u8 +vrsubhn_high_s16 +vrsubhn_high_s32 +vrsubhn_high_s64 +vrsubhn_high_u16 +vrsubhn_high_u32 +vrsubhn_high_u64 +vrsubhn_s16 +vrsubhn_s32 +vrsubhn_s64 +vrsubhn_u16 +vrsubhn_u32 +vrsubhn_u64 +vscale_f16 +vscaleq_f16 +vshl_s16 +vshl_s32 +vshl_s64 +vshl_s8 +vshl_u16 +vshl_u32 +vshl_u64 +vshl_u8 +vshld_s64 +vshld_u64 +vshlq_s16 +vshlq_s32 +vshlq_s64 +vshlq_s8 +vshlq_u16 +vshlq_u32 +vshlq_u64 +vshlq_u8 +vsli_n_p16 +vsli_n_p64 +vsli_n_p8 +vsli_n_s16 +vsli_n_s32 +vsli_n_s64 +vsli_n_s8 +vsli_n_u16 +vsli_n_u32 +vsli_n_u64 +vsli_n_u8 +vslid_n_s64 +vslid_n_u64 +vsliq_n_p16 +vsliq_n_p64 +vsliq_n_p8 +vsliq_n_s16 +vsliq_n_s32 +vsliq_n_s64 +vsliq_n_s8 +vsliq_n_u16 +vsliq_n_u32 +vsliq_n_u64 +vsliq_n_u8 +vsm3partw1q_u32 +vsm3partw2q_u32 +vsm3ss1q_u32 +vsm3tt1aq_u32 +vsm3tt1bq_u32 +vsm3tt2aq_u32 +vsm3tt2bq_u32 +vsm4ekeyq_u32 +vsm4eq_u32 +vsqadd_u16 +vsqadd_u32 +vsqadd_u64 +vsqadd_u8 +vsqaddb_u8 +vsqaddd_u64 +vsqaddh_u16 +vsqaddq_u16 +vsqaddq_u32 +vsqaddq_u64 +vsqaddq_u8 +vsqadds_u32 +vst1_f16 +vst1_f16_x2 +vst1_f16_x3 +vst1_f16_x4 +vst1_lane_f16 +vst1q_f16 +vst1q_f16_x2 +vst1q_f16_x3 +vst1q_f16_x4 +vst1q_lane_f16 +vst2_f16 +vst2_lane_f16 +vst2q_f16 +vst2q_lane_f16 +vst3_f16 +vst3_lane_f16 +vst3q_f16 +vst3q_lane_f16 +vst4_f16 +vst4_lane_f16 +vst4q_f16 +vst4q_lane_f16 +vsudot_lane_s32 +vsudot_laneq_s32 +vsudotq_lane_s32 +vsudotq_laneq_s32 +vtbl2_p8 +vtbl2_s8 +vtbl2_u8 +vtbl3_p8 +vtbl3_s8 +vtbl3_u8 +vtbl4_p8 +vtbl4_s8 +vtbl4_u8 +vtbx1_p8 +vtbx1_s8 +vtbx1_u8 +vtbx2_p8 +vtbx2_s8 +vtbx2_u8 +vtbx3_p8 +vtbx3_s8 +vtbx3_u8 +vtbx4_p8 +vtbx4_s8 +vtbx4_u8 +vuqadd_s16 +vuqadd_s32 +vuqadd_s64 +vuqadd_s8 +vuqaddb_s8 +vuqaddd_s64 +vuqaddh_s16 +vuqaddq_s16 +vuqaddq_s32 +vuqaddq_s64 +vuqaddq_s8 +vuqadds_s32 +vusdot_lane_s32 +vusdot_laneq_s32 +vusdot_s32 +vusdotq_lane_s32 +vusdotq_laneq_s32 +vusdotq_s32 +vusmmlaq_s32 diff --git a/compiler/rustc_codegen_cranelift/build_system/tests.rs b/compiler/rustc_codegen_cranelift/build_system/tests.rs index 5ae76dc1e591e..3b9ae2608abfe 100644 --- a/compiler/rustc_codegen_cranelift/build_system/tests.rs +++ b/compiler/rustc_codegen_cranelift/build_system/tests.rs @@ -1,4 +1,5 @@ use std::ffi::OsStr; +use std::fs; use std::path::PathBuf; use std::process::Command; @@ -108,7 +109,6 @@ const BASE_SYSROOT_SUITE: &[TestCase] = &[ }), TestCase::build_bin_and_run("aot.issue-72793", "example/issue-72793.rs", &[]), TestCase::build_bin("aot.issue-59326", "example/issue-59326.rs"), - TestCase::build_bin_and_run("aot.neon", "example/neon.rs", &[]), TestCase::build_bin_and_run("aot.gen_block_iterate", "example/gen_block_iterate.rs", &[]), TestCase::build_bin_and_run("aot.raw-dylib", "example/raw-dylib.rs", &[]), TestCase::custom("test.sysroot", &|runner| { @@ -182,6 +182,12 @@ static SYSROOT_TESTS_SRC: RelPath = RelPath::build("sysroot_tests"); static SYSROOT_TESTS: CargoProject = CargoProject::new(SYSROOT_TESTS_SRC, "sysroot_tests_target"); +static STDARCH_SRC: RelPath = RelPath::build("stdarch"); + +static STDARCH: CargoProject = CargoProject::new(STDARCH_SRC, "stdarch_target"); +static STDARCH_RUST_PROGRAMS: CargoProject = + CargoProject::new(RelPath::build("stdarch/rust_programs"), "stdarch_rust_programs_target"); + const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[ TestCase::custom("test.rust-random/rand", &|runner| { RAND_REPO.patch(&runner.dirs); @@ -283,6 +289,116 @@ const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[ spawn_and_wait(test_cmd); } }), + TestCase::custom("test.stdarch-core", &|runner| { + let target = runner.target_compiler.target.as_str(); + let (arch, _) = target.split_once('-').unwrap(); + + // FIXME: Add support for remaining architectures. + if !["aarch64"].contains(&arch) { + eprintln!("Skipping `stdarch-core` tests: unsupported target"); + return; + } + + apply_patches( + &runner.dirs, + "stdarch", + &runner.stdlib_source.join("library/stdarch"), + &STDARCH_SRC.to_path(&runner.dirs), + ); + + STDARCH.clean(&runner.dirs); + + let skip = format!("build_system/stdarch_skip/{arch}_core.txt"); + let skip = fs::read_to_string(runner.dirs.source_dir.join(skip)).unwrap_or_default(); + + let skip_functions = skip + .lines() + .map(str::trim) + .filter(|line| !line.is_empty() && !line.starts_with('#')) + .collect::>() + .join(","); + + if runner.is_native { + let mut test_cmd = STDARCH.test(&runner.target_compiler, &runner.dirs); + + test_cmd.env("TARGET", target); + test_cmd.env("STDARCH_TEST_SKIP_FUNCTION", &skip_functions); + + test_cmd.args(["-p", "core_arch", "--tests", "--", "-q"]); + spawn_and_wait(test_cmd); + } else { + eprintln!("Cross-Compiling: Not running tests"); + let mut build_cmd = STDARCH.build(&runner.target_compiler, &runner.dirs); + + build_cmd.env("TARGET", target); + + build_cmd.args(["-p", "core_arch", "--tests"]); + spawn_and_wait(build_cmd); + } + }), + TestCase::custom("test.stdarch-intrinsics", &|runner| { + let target = runner.target_compiler.target.as_str(); + let (arch, _) = target.split_once('-').unwrap(); + + // FIXME: Add support for remaining architectures. + let input = match arch { + "aarch64" => "intrinsics_data/arm_intrinsics.json", + _ => { + eprintln!("Skipping `stdarch-intrinsics` tests: unsupported target"); + return; + } + }; + + apply_patches( + &runner.dirs, + "stdarch", + &runner.stdlib_source.join("library/stdarch"), + &STDARCH_SRC.to_path(&runner.dirs), + ); + + STDARCH_RUST_PROGRAMS.clean(&runner.dirs); + + if !runner.is_native { + eprintln!("Cross-Compiling: Not running tests"); + return; + } + + let cc = if target.contains("apple") { "clang" } else { "gcc" }; + + let mut generate_cmd = STDARCH.run(&runner.target_compiler, &runner.dirs); + generate_cmd.current_dir(STDARCH.source_dir(&runner.dirs)); + generate_cmd.args([ + "-p", + "intrinsic-test", + "--", + input, + "--target", + target, + "--cc-arg-style", + cc, + "--skip", + &format!("crates/intrinsic-test/missing_{arch}_common.txt"), + "--skip", + &format!("crates/intrinsic-test/missing_{arch}_{cc}.txt"), + ]); + + for skip in [ + format!("build_system/stdarch_skip/{arch}_intrinsics_common.txt"), + format!("build_system/stdarch_skip/{arch}_intrinsics_{cc}.txt"), + ] { + let skip = runner.dirs.source_dir.join(skip); + + if skip.exists() { + generate_cmd.arg("--skip").arg(skip); + } + } + + spawn_and_wait(generate_cmd); + + let mut test_cmd = STDARCH_RUST_PROGRAMS.test(&runner.target_compiler, &runner.dirs); + test_cmd.args(["--workspace", "--tests", "--no-fail-fast", "--", "-q"]); + spawn_and_wait(test_cmd); + }), ]; pub(crate) fn run_tests( diff --git a/compiler/rustc_codegen_cranelift/config.txt b/compiler/rustc_codegen_cranelift/config.txt index ca126f4e39e29..c1068e6257d73 100644 --- a/compiler/rustc_codegen_cranelift/config.txt +++ b/compiler/rustc_codegen_cranelift/config.txt @@ -22,7 +22,6 @@ aot.float-minmax-pass aot.powi_libcall_signature aot.issue-72793 aot.issue-59326 -aot.neon aot.gen_block_iterate aot.raw-dylib test.sysroot @@ -32,3 +31,5 @@ test.rust-random/rand test.regex test.graviola test.portable-simd +test.stdarch-core +test.stdarch-intrinsics diff --git a/compiler/rustc_codegen_cranelift/example/mini_core.rs b/compiler/rustc_codegen_cranelift/example/mini_core.rs index 08adec96a079b..d6d82a9d2af69 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core.rs @@ -497,7 +497,7 @@ pub trait FnMut: FnOnce { pub fn panic(_msg: &'static str) -> ! { unsafe { libc::puts("Panicking\n\0" as *const str as *const i8); - intrinsics::abort(); + intrinsics::abort_immediate(); } } @@ -539,7 +539,7 @@ fn panic_bounds_check(index: usize, len: usize) -> ! { len, index, ); - intrinsics::abort(); + intrinsics::abort_immediate(); } } @@ -548,7 +548,7 @@ fn panic_bounds_check(index: usize, len: usize) -> ! { fn panic_cannot_unwind() -> ! { unsafe { libc::puts("panic in a function that cannot unwind\n\0" as *const str as *const i8); - intrinsics::abort(); + intrinsics::abort_immediate(); } } @@ -688,7 +688,7 @@ pub union MaybeUninit { pub mod intrinsics { #[rustc_intrinsic] - pub fn abort() -> !; + pub fn abort_immediate() -> !; #[rustc_intrinsic] pub const fn size_of() -> usize; #[rustc_intrinsic] diff --git a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs index 9a710caae9cd1..c53ec2ec6710d 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs @@ -602,11 +602,11 @@ pub enum E2 { #[allow(unreachable_patterns)] fn check_niche_behavior() { if let E1::V2 { .. } = (E1::V1 { f: true }) { - intrinsics::abort(); + intrinsics::abort_immediate(); } if let E2::V1 { .. } = E2::V3:: { - intrinsics::abort(); + intrinsics::abort_immediate(); } } diff --git a/compiler/rustc_codegen_cranelift/example/neon.rs b/compiler/rustc_codegen_cranelift/example/neon.rs deleted file mode 100644 index ba63333daa339..0000000000000 --- a/compiler/rustc_codegen_cranelift/example/neon.rs +++ /dev/null @@ -1,963 +0,0 @@ -// Most of these tests are copied from https://github.com/japaric/stdsimd/blob/0f4413d01c4f0c3ffbc5a69e9a37fbc7235b31a9/coresimd/arm/neon.rs - -#![cfg_attr(target_arch = "aarch64", feature(portable_simd))] - -#[cfg(target_arch = "aarch64")] -use std::arch::aarch64::*; -#[cfg(target_arch = "aarch64")] -use std::mem::transmute; -#[cfg(target_arch = "aarch64")] -use std::simd::*; - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "crc")] -unsafe fn test_crc32() { - assert!(std::arch::is_aarch64_feature_detected!("crc")); - - let a: u32 = 42; - let b: u64 = 0xdeadbeef; - - assert_eq!(__crc32b(a, b as u8), 0xEB0E363F); - assert_eq!(__crc32h(a, b as u16), 0x9A54BD80); - assert_eq!(__crc32w(a, b as u32), 0xF491F059); - assert_eq!(__crc32d(a, b as u64), 0xD14BBEA6); - - assert_eq!(__crc32cb(a, b as u8), 0xF67C32D8); - assert_eq!(__crc32ch(a, b as u16), 0x479108B8); - assert_eq!(__crc32cw(a, b as u32), 0x979F49F8); - assert_eq!(__crc32cd(a, b as u64), 0x0E6BE593); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmin_s8() { - let a = i8x8::from([1, -2, 3, -4, 5, 6, 7, 8]); - let b = i8x8::from([0, 3, 2, 5, 4, 7, 6, 9]); - let e = i8x8::from([-2, -4, 5, 7, 0, 2, 4, 6]); - let r: i8x8 = unsafe { transmute(vpmin_s8(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmin_s16() { - let a = i16x4::from([1, 2, 3, -4]); - let b = i16x4::from([0, 3, 2, 5]); - let e = i16x4::from([1, -4, 0, 2]); - let r: i16x4 = unsafe { transmute(vpmin_s16(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmin_s32() { - let a = i32x2::from([1, -2]); - let b = i32x2::from([0, 3]); - let e = i32x2::from([-2, 0]); - let r: i32x2 = unsafe { transmute(vpmin_s32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmin_u8() { - let a = u8x8::from([1, 2, 3, 4, 5, 6, 7, 8]); - let b = u8x8::from([0, 3, 2, 5, 4, 7, 6, 9]); - let e = u8x8::from([1, 3, 5, 7, 0, 2, 4, 6]); - let r: u8x8 = unsafe { transmute(vpmin_u8(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmin_u16() { - let a = u16x4::from([1, 2, 3, 4]); - let b = u16x4::from([0, 3, 2, 5]); - let e = u16x4::from([1, 3, 0, 2]); - let r: u16x4 = unsafe { transmute(vpmin_u16(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmin_u32() { - let a = u32x2::from([1, 2]); - let b = u32x2::from([0, 3]); - let e = u32x2::from([1, 0]); - let r: u32x2 = unsafe { transmute(vpmin_u32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmin_f32() { - let a = f32x2::from([1., -2.]); - let b = f32x2::from([0., 3.]); - let e = f32x2::from([-2., 0.]); - let r: f32x2 = unsafe { transmute(vpmin_f32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmax_s8() { - let a = i8x8::from([1, -2, 3, -4, 5, 6, 7, 8]); - let b = i8x8::from([0, 3, 2, 5, 4, 7, 6, 9]); - let e = i8x8::from([1, 3, 6, 8, 3, 5, 7, 9]); - let r: i8x8 = unsafe { transmute(vpmax_s8(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmax_s16() { - let a = i16x4::from([1, 2, 3, -4]); - let b = i16x4::from([0, 3, 2, 5]); - let e = i16x4::from([2, 3, 3, 5]); - let r: i16x4 = unsafe { transmute(vpmax_s16(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmax_s32() { - let a = i32x2::from([1, -2]); - let b = i32x2::from([0, 3]); - let e = i32x2::from([1, 3]); - let r: i32x2 = unsafe { transmute(vpmax_s32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmax_u8() { - let a = u8x8::from([1, 2, 3, 4, 5, 6, 7, 8]); - let b = u8x8::from([0, 3, 2, 5, 4, 7, 6, 9]); - let e = u8x8::from([2, 4, 6, 8, 3, 5, 7, 9]); - let r: u8x8 = unsafe { transmute(vpmax_u8(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmax_u16() { - let a = u16x4::from([1, 2, 3, 4]); - let b = u16x4::from([0, 3, 2, 5]); - let e = u16x4::from([2, 4, 3, 5]); - let r: u16x4 = unsafe { transmute(vpmax_u16(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmax_u32() { - let a = u32x2::from([1, 2]); - let b = u32x2::from([0, 3]); - let e = u32x2::from([2, 3]); - let r: u32x2 = unsafe { transmute(vpmax_u32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpmax_f32() { - let a = f32x2::from([1., -2.]); - let b = f32x2::from([0., 3.]); - let e = f32x2::from([1., 3.]); - let r: f32x2 = unsafe { transmute(vpmax_f32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpadd_s16() { - let a = i16x4::from([1, 2, 3, 4]); - let b = i16x4::from([0, -1, -2, -3]); - let r: i16x4 = unsafe { transmute(vpadd_s16(transmute(a), transmute(b))) }; - let e = i16x4::from([3, 7, -1, -5]); - assert_eq!(r, e); -} -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpadd_s32() { - let a = i32x2::from([1, 2]); - let b = i32x2::from([0, -1]); - let r: i32x2 = unsafe { transmute(vpadd_s32(transmute(a), transmute(b))) }; - let e = i32x2::from([3, -1]); - assert_eq!(r, e); -} -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpadd_s8() { - let a = i8x8::from([1, 2, 3, 4, 5, 6, 7, 8]); - let b = i8x8::from([0, -1, -2, -3, -4, -5, -6, -7]); - let r: i8x8 = unsafe { transmute(vpadd_s8(transmute(a), transmute(b))) }; - let e = i8x8::from([3, 7, 11, 15, -1, -5, -9, -13]); - assert_eq!(r, e); -} -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpadd_u16() { - let a = u16x4::from([1, 2, 3, 4]); - let b = u16x4::from([30, 31, 32, 33]); - let r: u16x4 = unsafe { transmute(vpadd_u16(transmute(a), transmute(b))) }; - let e = u16x4::from([3, 7, 61, 65]); - assert_eq!(r, e); -} -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpadd_u32() { - let a = u32x2::from([1, 2]); - let b = u32x2::from([30, 31]); - let r: u32x2 = unsafe { transmute(vpadd_u32(transmute(a), transmute(b))) }; - let e = u32x2::from([3, 61]); - assert_eq!(r, e); -} -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpadd_u8() { - let a = u8x8::from([1, 2, 3, 4, 5, 6, 7, 8]); - let b = u8x8::from([30, 31, 32, 33, 34, 35, 36, 37]); - let r: u8x8 = unsafe { transmute(vpadd_u8(transmute(a), transmute(b))) }; - let e = u8x8::from([3, 7, 11, 15, 61, 65, 69, 73]); - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vqsub_u8() { - let a = u8x8::from([1, 2, 3, 4, 5, 6, 7, 0xff]); - let b = u8x8::from([30, 1, 1, 1, 34, 0xff, 36, 37]); - let r: u8x8 = unsafe { transmute(vqsub_u8(transmute(a), transmute(b))) }; - let e = u8x8::from([0, 1, 2, 3, 0, 0, 0, 218]); - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vqadd_u8() { - let a = u8x8::from([1, 2, 3, 4, 5, 6, 7, 0xff]); - let b = u8x8::from([30, 1, 1, 1, 34, 0xff, 36, 37]); - let r: u8x8 = unsafe { transmute(vqadd_u8(transmute(a), transmute(b))) }; - let e = u8x8::from([31, 3, 4, 5, 39, 0xff, 43, 0xff]); - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vmaxq_f32() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.fmax.v4f32 - let a = f32x4::from([0., -1., 2., -3.]); - let b = f32x4::from([-4., 5., -6., 7.]); - let e = f32x4::from([0., 5., 2., 7.]); - let r: f32x4 = unsafe { transmute(vmaxq_f32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vminq_f32() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.fmin.v4f32 - let a = f32x4::from([0., -1., 2., -3.]); - let b = f32x4::from([-4., 5., -6., 7.]); - let e = f32x4::from([-4., -1., -6., -3.]); - let r: f32x4 = unsafe { transmute(vminq_f32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vaddvq_f32() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.faddv.f32.v4f32 - let a = f32x4::from([0., 1., 2., 3.]); - let e = 6f32; - let r = unsafe { vaddvq_f32(transmute(a)) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vrndnq_f32() { - // llvm intrinsic: llvm.roundeven.v4f32 - let a = f32x4::from([0.1, -1.9, 4.5, 5.5]); - let e = f32x4::from([0., -2., 4., 6.]); - let r: f32x4 = unsafe { transmute(vrndnq_f32(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "aes")] -unsafe fn test_vaeseq_u8() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.aese - let a = u8x16::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - let b = u8x16::from([16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]); - let e = u8x16::from([ - 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, - 0xca, - ]); - let r: u8x16 = unsafe { transmute(vaeseq_u8(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "aes")] -unsafe fn test_vaesdq_u8() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.aesd - let a = u8x16::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - let b = u8x16::from([16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]); - let e = u8x16::from([ - 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, - 0x7c, - ]); - let r: u8x16 = unsafe { transmute(vaesdq_u8(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "aes")] -unsafe fn test_vaesmcq_u8() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.aesmc - let a = u8x16::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - let e = u8x16::from([2, 7, 0, 5, 6, 3, 4, 1, 10, 15, 8, 13, 14, 11, 12, 9]); - let r: u8x16 = unsafe { transmute(vaesmcq_u8(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "aes")] -unsafe fn test_vaesimcq_u8() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.aesimc - let a = u8x16::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - let e = u8x16::from([10, 15, 8, 13, 14, 11, 12, 9, 2, 7, 0, 5, 6, 3, 4, 1]); - let r: u8x16 = unsafe { transmute(vaesimcq_u8(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha2")] -unsafe fn test_vsha1cq_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha1c - let a = u32x4::from([0, 1, 2, 3]); - let b = 8; - let c = u32x4::from([4, 5, 6, 7]); - let e = u32x4::from([0x40072911, 0x40003948, 0x80000072, 0x80000003]); - let r: u32x4 = unsafe { transmute(vsha1cq_u32(transmute(a), b, transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha2")] -fn test_vsha1h_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha1h - let a = 8; - let e = 0x00000002; - let r = vsha1h_u32(a); - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha2")] -unsafe fn test_vsha1mq_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha1m - let a = u32x4::from([0, 1, 2, 3]); - let b = 8; - let c = u32x4::from([4, 5, 6, 7]); - let e = u32x4::from([0x4007a107, 0x00003d08, 0x0000007a, 0xc0000003]); - let r: u32x4 = unsafe { transmute(vsha1mq_u32(transmute(a), b, transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha2")] -unsafe fn test_vsha1pq_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha1p - let a = u32x4::from([0, 1, 2, 3]); - let b = 8; - let c = u32x4::from([4, 5, 6, 7]); - let e = u32x4::from([0x80062d18, 0x4000315c, 0x90000062, 0x00000003]); - let r: u32x4 = unsafe { transmute(vsha1pq_u32(transmute(a), b, transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha2")] -unsafe fn test_vsha1su0q_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha1su0 - let a = u32x4::from([0, 1, 2, 3]); - let b = u32x4::from([4, 5, 6, 7]); - let c = u32x4::from([8, 9, 10, 11]); - let e = u32x4::from([0x0000000a, 0x0000000b, 0x0000000c, 0x0000000d]); - let r: u32x4 = unsafe { transmute(vsha1su0q_u32(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha2")] -unsafe fn test_vsha1su1q_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha1su1 - let a = u32x4::from([0, 1, 2, 3]); - let b = u32x4::from([4, 5, 6, 7]); - let e = u32x4::from([0x0000000a, 0x0000000e, 0x0000000a, 0x00000012]); - let r: u32x4 = unsafe { transmute(vsha1su1q_u32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha2")] -unsafe fn test_vsha256hq_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha256h - let a = u32x4::from([0, 1, 2, 3]); - let b = u32x4::from([4, 5, 6, 7]); - let c = u32x4::from([8, 9, 10, 11]); - let e = u32x4::from([0x27bb4ae0, 0xd8f61f7c, 0xb7c1ecdc, 0x10800215]); - let r: u32x4 = unsafe { transmute(vsha256hq_u32(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha2")] -unsafe fn test_vsha256h2q_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha256h2 - let a = u32x4::from([0, 1, 2, 3]); - let b = u32x4::from([4, 5, 6, 7]); - let c = u32x4::from([8, 9, 10, 11]); - let e = u32x4::from([0x6989ee0d, 0x4b055920, 0x52800a12, 0x00000014]); - let r: u32x4 = unsafe { transmute(vsha256h2q_u32(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha2")] -unsafe fn test_vsha256su0q_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha256su0 - let a = u32x4::from([0, 1, 2, 3]); - let b = u32x4::from([4, 5, 6, 7]); - let e = u32x4::from([0x02004000, 0x04008001, 0x0600c002, 0x08010003]); - let r: u32x4 = unsafe { transmute(vsha256su0q_u32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha2")] -unsafe fn test_vsha256su1q_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha256su1 - let a = u32x4::from([0, 1, 2, 3]); - let b = u32x4::from([4, 5, 6, 7]); - let c = u32x4::from([8, 9, 10, 11]); - let e = u32x4::from([0x00044005, 0x0004e007, 0xa802211b, 0xec036145]); - let r: u32x4 = unsafe { transmute(vsha256su1q_u32(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vsha512hq_u64() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha512h - let a = u64x2::from([0, 1]); - let b = u64x2::from([2, 3]); - let c = u64x2::from([4, 5]); - let e = u64x2::from([0x001c805053800005, 0x0015400002800003]); - let r: u64x2 = unsafe { transmute(vsha512hq_u64(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vsha512h2q_u64() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha512h2 - let a = u64x2::from([0, 1]); - let b = u64x2::from([2, 3]); - let c = u64x2::from([4, 5]); - let e = u64x2::from([0x401000514a000405, 0x0000004108000005]); - let r: u64x2 = unsafe { transmute(vsha512h2q_u64(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vsha512su0q_u64() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha512su0 - let a = u64x2::from([0, 1]); - let b = u64x2::from([2, 3]); - let e = u64x2::from([0x8100000000000000, 0x0200000000000002]); - let r: u64x2 = unsafe { transmute(vsha512su0q_u64(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vsha512su1q_u64() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.sha512su1 - let a = u64x2::from([0, 1]); - let b = u64x2::from([2, 3]); - let c = u64x2::from([4, 5]); - let e = u64x2::from([0x0000400000000014, 0x000060000000001e]); - let r: u64x2 = unsafe { transmute(vsha512su1q_u64(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_veor3q_s8() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.eor3s.v16i8 - let a = i8x16::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - let b = i8x16::from([16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]); - let c = i8x16::from([32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47]); - let e = i8x16::from([48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63]); - let r: i8x16 = unsafe { transmute(veor3q_s8(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_veor3q_s16() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.eor3s.v8i16 - let a = i16x8::from([0, 1, 2, 3, 4, 5, 6, 7]); - let b = i16x8::from([8, 9, 10, 11, 12, 13, 14, 15]); - let c = i16x8::from([16, 17, 18, 19, 20, 21, 22, 23]); - let e = i16x8::from([24, 25, 26, 27, 28, 29, 30, 31]); - let r: i16x8 = unsafe { transmute(veor3q_s16(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_veor3q_s32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.eor3s.v4i32 - let a = i32x4::from([0, 1, 2, 3]); - let b = i32x4::from([4, 5, 6, 7]); - let c = i32x4::from([8, 9, 10, 11]); - let e = i32x4::from([12, 13, 14, 15]); - let r: i32x4 = unsafe { transmute(veor3q_s32(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_veor3q_s64() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.eor3s.v2i64 - let a = i64x2::from([0, 1]); - let b = i64x2::from([2, 3]); - let c = i64x2::from([4, 5]); - let e = i64x2::from([6, 7]); - let r: i64x2 = unsafe { transmute(veor3q_s64(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_veor3q_u8() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.eor3u.v16i8 - let a = u8x16::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - let b = u8x16::from([16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]); - let c = u8x16::from([32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47]); - let e = u8x16::from([48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63]); - let r: u8x16 = unsafe { transmute(veor3q_u8(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_veor3q_u16() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.eor3u.v8i16 - let a = u16x8::from([0, 1, 2, 3, 4, 5, 6, 7]); - let b = u16x8::from([8, 9, 10, 11, 12, 13, 14, 15]); - let c = u16x8::from([16, 17, 18, 19, 20, 21, 22, 23]); - let e = u16x8::from([24, 25, 26, 27, 28, 29, 30, 31]); - let r: u16x8 = unsafe { transmute(veor3q_u16(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_veor3q_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.eor3u.v4i32 - let a = u32x4::from([0, 1, 2, 3]); - let b = u32x4::from([4, 5, 6, 7]); - let c = u32x4::from([8, 9, 10, 11]); - let e = u32x4::from([12, 13, 14, 15]); - let r: u32x4 = unsafe { transmute(veor3q_u32(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_veor3q_u64() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.eor3u.v2i64 - let a = u64x2::from([0, 1]); - let b = u64x2::from([2, 3]); - let c = u64x2::from([4, 5]); - let e = u64x2::from([6, 7]); - let r: u64x2 = unsafe { transmute(veor3q_u64(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vbcaxq_s8() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.bcaxs.v16i8 - let a = i8x16::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - let b = i8x16::from([16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]); - let c = i8x16::from([32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47]); - let e = i8x16::from([16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]); - let r: i8x16 = unsafe { transmute(vbcaxq_s8(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vbcaxq_s16() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.bcaxs.v8i16 - let a = i16x8::from([0, 1, 2, 3, 4, 5, 6, 7]); - let b = i16x8::from([8, 9, 10, 11, 12, 13, 14, 15]); - let c = i16x8::from([16, 17, 18, 19, 20, 21, 22, 23]); - let e = i16x8::from([8, 9, 10, 11, 12, 13, 14, 15]); - let r: i16x8 = unsafe { transmute(vbcaxq_s16(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vbcaxq_s32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.bcaxs.v4i32 - let a = i32x4::from([0, 1, 2, 3]); - let b = i32x4::from([4, 5, 6, 7]); - let c = i32x4::from([8, 9, 10, 11]); - let e = i32x4::from([4, 5, 6, 7]); - let r: i32x4 = unsafe { transmute(vbcaxq_s32(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vbcaxq_s64() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.bcaxs.v2i64 - let a = i64x2::from([0, 1]); - let b = i64x2::from([2, 3]); - let c = i64x2::from([4, 5]); - let e = i64x2::from([2, 3]); - let r: i64x2 = unsafe { transmute(vbcaxq_s64(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vbcaxq_u8() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.bcaxu.v16i8 - let a = u8x16::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - let b = u8x16::from([16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]); - let c = u8x16::from([32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47]); - let e = u8x16::from([16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]); - let r: u8x16 = unsafe { transmute(vbcaxq_u8(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vbcaxq_u16() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.bcaxu.v8i16 - let a = u16x8::from([0, 1, 2, 3, 4, 5, 6, 7]); - let b = u16x8::from([8, 9, 10, 11, 12, 13, 14, 15]); - let c = u16x8::from([16, 17, 18, 19, 20, 21, 22, 23]); - let e = u16x8::from([8, 9, 10, 11, 12, 13, 14, 15]); - let r: u16x8 = unsafe { transmute(vbcaxq_u16(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vbcaxq_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.bcaxu.v4i32 - let a = u32x4::from([0, 1, 2, 3]); - let b = u32x4::from([4, 5, 6, 7]); - let c = u32x4::from([8, 9, 10, 11]); - let e = u32x4::from([4, 5, 6, 7]); - let r: u32x4 = unsafe { transmute(vbcaxq_u32(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vbcaxq_u64() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.bcaxu.v2i64 - let a = u64x2::from([0, 1]); - let b = u64x2::from([2, 3]); - let c = u64x2::from([4, 5]); - let e = u64x2::from([2, 3]); - let r: u64x2 = unsafe { transmute(vbcaxq_u64(transmute(a), transmute(b), transmute(c))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vrax1q_u64() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.rax1 - let a = u64x2::from([0, 1]); - let b = u64x2::from([2, 3]); - let e = u64x2::from([4, 7]); - let r: u64x2 = unsafe { transmute(vrax1q_u64(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "sha3")] -unsafe fn test_vxarq_u64() { - // AArch64 llvm intrinsic: llvm.aarch64.crypto.xar - let a = u64x2::from([0, 1]); - let b = u64x2::from([2, 3]); - let e = u64x2::from([4, 4]); - let r: u64x2 = unsafe { transmute(vxarq_u64::<63>(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -#[target_feature(enable = "aes")] -fn test_vmull_p64() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.pmull64 - let a: u64 = 3; - let b: u64 = 6; - let e: u128 = 10; - let r: u128 = vmull_p64(a, b); - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vmull_p8() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.pmull.v8i16 - let a = u8x8::from([0, 1, 2, 3, 4, 5, 6, 7]); - let b = u8x8::from([8, 9, 10, 11, 12, 13, 14, 15]); - let e = u16x8::from([0x0000, 0x0009, 0x0014, 0x001d, 0x0030, 0x0039, 0x0024, 0x002d]); - let r: u16x8 = unsafe { transmute(vmull_p8(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vqdmulh_s16() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.sqdmulh.v4i16 - let a = i16x4::from([1, 2, 4, 8]); - let b = i16x4::from([16384, 16384, 16384, 16384]); - let e = i16x4::from([0, 1, 2, 4]); - let r: i16x4 = unsafe { transmute(vqdmulh_s16(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vqdmulh_s32() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.sqdmulh.v2i32 - let a = i32x2::from([1, 2]); - let b = i32x2::from([1073741824, 1073741824]); - let e = i32x2::from([0, 1]); - let r: i32x2 = unsafe { transmute(vqdmulh_s32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vqdmulhq_s16() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.sqdmulh.v8i16 - let a = i16x8::from([1, 2, 4, 8, 16, 32, 64, 128]); - let b = i16x8::from([16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384]); - let e = i16x8::from([0, 1, 2, 4, 8, 16, 32, 64]); - let r: i16x8 = unsafe { transmute(vqdmulhq_s16(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vqdmulhq_s32() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.sqdmulh.v4i32 - let a = i32x4::from([1, 2, 4, 8]); - let b = i32x4::from([1073741824, 1073741824, 1073741824, 1073741824]); - let e = i32x4::from([0, 1, 2, 4]); - let r: i32x4 = unsafe { transmute(vqdmulhq_s32(transmute(a), transmute(b))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddl_s8() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.saddlp.v4i16.v8i8 - let a = i8x8::from([1, 2, 3, 4, -5, -6, -7, -8]); - let e = i16x4::from([3, 7, -11, -15]); - let r: i16x4 = unsafe { transmute(vpaddl_s8(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddl_s16() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.saddlp.v2i32.v4i16 - let a = i16x4::from([1, 2, -3, -4]); - let e = i32x2::from([3, -7]); - let r: i32x2 = unsafe { transmute(vpaddl_s16(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddl_s32() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.saddlp.v1i64.v2i32 - let a = i32x2::from([1, -2]); - let e = i64x1::from([-1]); - let r: i64x1 = unsafe { transmute(vpaddl_s32(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddlq_s8() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.saddlp.v8i16.v16i8 - let a = i8x16::from([1, 2, 3, 4, 5, 6, 7, 8, -9, -10, -11, -12, -13, -14, -15, -16]); - let e = i16x8::from([3, 7, 11, 15, -19, -23, -27, -31]); - let r: i16x8 = unsafe { transmute(vpaddlq_s8(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddlq_s16() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.saddlp.v4i32.v8i16 - let a = i16x8::from([1, 2, 3, 4, -5, -6, -7, -8]); - let e = i32x4::from([3, 7, -11, -15]); - let r: i32x4 = unsafe { transmute(vpaddlq_s16(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddlq_s32() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.saddlp.v2i64.v4i32 - let a = i32x4::from([1, 2, -3, -4]); - let e = i64x2::from([3, -7]); - let r: i64x2 = unsafe { transmute(vpaddlq_s32(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddl_u8() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.uaddlp.v4i16.v8i8 - let a = u8x8::from([255, 254, 253, 252, 251, 250, 249, 248]); - let e = u16x4::from([509, 505, 501, 497]); - let r: u16x4 = unsafe { transmute(vpaddl_u8(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddl_u16() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.uaddlp.v2i32.v4i16 - let a = u16x4::from([65535, 65534, 65533, 65532]); - let e = u32x2::from([131069, 131065]); - let r: u32x2 = unsafe { transmute(vpaddl_u16(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddl_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.uaddlp.v1i64.v2i32 - let a = u32x2::from([4294967295, 4294967294]); - let e = u64x1::from([8589934589]); - let r: u64x1 = unsafe { transmute(vpaddl_u32(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddlq_u8() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.uaddlp.v8i16.v16i8 - let a = u8x16::from([ - 255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, 244, 243, 242, 241, 240, - ]); - let e = u16x8::from([509, 505, 501, 497, 493, 489, 485, 481]); - let r: u16x8 = unsafe { transmute(vpaddlq_u8(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddlq_u16() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.uaddlp.v4i32.v8i16 - let a = u16x8::from([65535, 65534, 65533, 65532, 65531, 65530, 65529, 65528]); - let e = u32x4::from([131069, 131065, 131061, 131057]); - let r: u32x4 = unsafe { transmute(vpaddlq_u16(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -unsafe fn test_vpaddlq_u32() { - // AArch64 llvm intrinsic: llvm.aarch64.neon.uaddlp.v2i64.v4i32 - let a = u32x4::from([4294967295, 4294967294, 4294967293, 4294967292]); - let e = u64x2::from([8589934589, 8589934585]); - let r: u64x2 = unsafe { transmute(vpaddlq_u32(transmute(a))) }; - assert_eq!(r, e); -} - -#[cfg(target_arch = "aarch64")] -fn main() { - unsafe { - test_vpmin_s8(); - test_vpmin_s16(); - test_vpmin_s32(); - test_vpmin_u8(); - test_vpmin_u16(); - test_vpmin_u32(); - test_vpmin_f32(); - test_vpmax_s8(); - test_vpmax_s16(); - test_vpmax_s32(); - test_vpmax_u8(); - test_vpmax_u16(); - test_vpmax_u32(); - test_vpmax_f32(); - - test_vpadd_s16(); - test_vpadd_s32(); - test_vpadd_s8(); - test_vpadd_u16(); - test_vpadd_u32(); - test_vpadd_u8(); - - test_vqsub_u8(); - test_vqadd_u8(); - - test_vmaxq_f32(); - test_vminq_f32(); - test_vaddvq_f32(); - test_vrndnq_f32(); - - test_crc32(); - - test_vaeseq_u8(); - test_vaesdq_u8(); - test_vaesmcq_u8(); - test_vaesimcq_u8(); - - test_vsha1cq_u32(); - test_vsha1h_u32(); - test_vsha1mq_u32(); - test_vsha1pq_u32(); - test_vsha1su0q_u32(); - test_vsha1su1q_u32(); - - test_vsha256hq_u32(); - test_vsha256h2q_u32(); - test_vsha256su0q_u32(); - test_vsha256su1q_u32(); - - if std::arch::is_aarch64_feature_detected!("sha3") { - test_vsha512hq_u64(); - test_vsha512h2q_u64(); - test_vsha512su0q_u64(); - test_vsha512su1q_u64(); - - test_veor3q_s8(); - test_veor3q_s16(); - test_veor3q_s32(); - test_veor3q_s64(); - test_veor3q_u8(); - test_veor3q_u16(); - test_veor3q_u32(); - test_veor3q_u64(); - - test_vbcaxq_s8(); - test_vbcaxq_s16(); - test_vbcaxq_s32(); - test_vbcaxq_s64(); - test_vbcaxq_u8(); - test_vbcaxq_u16(); - test_vbcaxq_u32(); - test_vbcaxq_u64(); - - test_vrax1q_u64(); - test_vxarq_u64(); - } - - test_vmull_p64(); - test_vmull_p8(); - - test_vqdmulh_s16(); - test_vqdmulh_s32(); - test_vqdmulhq_s16(); - test_vqdmulhq_s32(); - - test_vpaddl_s8(); - test_vpaddl_s16(); - test_vpaddl_s32(); - test_vpaddlq_s8(); - test_vpaddlq_s16(); - test_vpaddlq_s32(); - - test_vpaddl_u8(); - test_vpaddl_u16(); - test_vpaddl_u32(); - test_vpaddlq_u8(); - test_vpaddlq_u16(); - test_vpaddlq_u32(); - } -} - -#[cfg(not(target_arch = "aarch64"))] -fn main() {} diff --git a/compiler/rustc_codegen_cranelift/example/std_example.rs b/compiler/rustc_codegen_cranelift/example/std_example.rs index e1287683d495d..5fc5f17682cc8 100644 --- a/compiler/rustc_codegen_cranelift/example/std_example.rs +++ b/compiler/rustc_codegen_cranelift/example/std_example.rs @@ -294,6 +294,19 @@ unsafe fn test_simd() { if is_x86_feature_detected!("avx512f") { test_zmm_roundtrip(); } + + #[cfg(not(jit))] + { + if is_x86_feature_detected!("pclmulqdq") { + test_mm_clmulepi64_si128(); + } + if is_x86_feature_detected!("vpclmulqdq") { + test_mm256_clmulepi64_epi128(); + } + if is_x86_feature_detected!("vpclmulqdq") && is_x86_feature_detected!("avx512f") { + test_mm512_clmulepi64_epi128(); + } + } } } @@ -395,6 +408,14 @@ pub fn assert_eq_m256i(a: __m256i, b: __m256i) { } } +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "avx512f")] +pub fn assert_eq_m512i(a: __m512i, b: __m512i) { + unsafe { + assert_eq!(std::mem::transmute::<_, [u64; 8]>(a), std::mem::transmute::<_, [u64; 8]>(b)) + } +} + #[cfg(target_arch = "x86_64")] #[target_feature(enable = "sse2")] unsafe fn test_mm_cvtsi128_si64() { @@ -537,6 +558,62 @@ unsafe fn test_mm256_permutevar8x32_epi32() { assert_eq_m256i(r, e); } +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "pclmulqdq")] +#[cfg(not(jit))] +unsafe fn test_mm_clmulepi64_si128() { + // Constants taken from https://software.intel.com/sites/default/files/managed/72/cc/clmul-wp-rev-2.02-2014-04-20.pdf + let a = _mm_set_epi64x(0x7b5b546573745665, 0x63746f725d53475d); + let b = _mm_set_epi64x(0x4869285368617929, 0x5b477565726f6e5d); + let r00 = _mm_set_epi64x(0x1d4d84c85c3440c0, 0x929633d5d36f0451u64.cast_signed()); + let r01 = _mm_set_epi64x(0x1bd17c8d556ab5a1, 0x7fa540ac2a281315); + let r10 = _mm_set_epi64x(0x1a2bf6db3a30862f, 0xbabf262df4b7d5c9u64.cast_signed()); + let r11 = _mm_set_epi64x(0x1d1e1f2c592e7c45, 0xd66ee03e410fd4edu64.cast_signed()); + + assert_eq_m128i(_mm_clmulepi64_si128::<0x00>(a, b), r00); + assert_eq_m128i(_mm_clmulepi64_si128::<0x10>(a, b), r01); + assert_eq_m128i(_mm_clmulepi64_si128::<0x01>(a, b), r10); + assert_eq_m128i(_mm_clmulepi64_si128::<0x11>(a, b), r11); + + let a0 = _mm_set_epi64x(0x0000000000000000, 0x8000000000000000u64.cast_signed()); + let r = _mm_set_epi64x(0x4000000000000000, 0x0000000000000000); + assert_eq_m128i(_mm_clmulepi64_si128::<0x00>(a0, a0), r); +} + +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "vpclmulqdq")] +#[cfg(not(jit))] +unsafe fn test_mm256_clmulepi64_epi128() { + let a = _mm256_setr_epi64x(1, 2, 3, 4); + let b = _mm256_setr_epi64x(5, 6, 7, 8); + + assert_eq_m256i(_mm256_clmulepi64_epi128::<0x00>(a, b), _mm256_setr_epi64x(5, 0, 9, 0)); + assert_eq_m256i(_mm256_clmulepi64_epi128::<0x01>(a, b), _mm256_setr_epi64x(10, 0, 28, 0)); + assert_eq_m256i(_mm256_clmulepi64_epi128::<0x10>(a, b), _mm256_setr_epi64x(6, 0, 24, 0)); + assert_eq_m256i(_mm256_clmulepi64_epi128::<0x11>(a, b), _mm256_setr_epi64x(12, 0, 32, 0)); +} + +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "vpclmulqdq,avx512f")] +#[cfg(not(jit))] +unsafe fn test_mm512_clmulepi64_epi128() { + let a = _mm512_setr_epi64(1, 2, 3, 4, 5, 6, 7, 8); + let b = _mm512_setr_epi64(9, 10, 11, 12, 13, 14, 15, 16); + + assert_eq_m512i( + _mm512_clmulepi64_epi128::<0x00>(a, b), + _mm512_setr_epi64(9, 0, 29, 0, 57, 0, 45, 0), + ); + assert_eq_m512i( + _mm512_clmulepi64_epi128::<0x10>(a, b), + _mm512_setr_epi64(10, 0, 20, 0, 54, 0, 112, 0), + ); + assert_eq_m512i( + _mm512_clmulepi64_epi128::<0x11>(a, b), + _mm512_setr_epi64(20, 0, 48, 0, 36, 0, 128, 0), + ); +} + #[cfg(target_arch = "x86_64")] #[target_feature(enable = "avx2")] #[cfg(not(jit))] diff --git a/compiler/rustc_codegen_cranelift/josh-sync.toml b/compiler/rustc_codegen_cranelift/josh-sync.toml index 2de9f9489cce3..d8b49ebac131d 100644 --- a/compiler/rustc_codegen_cranelift/josh-sync.toml +++ b/compiler/rustc_codegen_cranelift/josh-sync.toml @@ -1,3 +1,4 @@ repo = "rustc_codegen_cranelift" filter = ":~(history=\"keep-trivial-merges,no-splice\")[:rev(<=5e120485964f4857f1ad70f7d661fd244d087668:prefix=compiler/rustc_codegen_cranelift,<=7bd21608dfab11ea536f7be8936cc7dfac5864fb:SQUASH)]:/compiler/rustc_codegen_cranelift" filter-version = 2 +pull-mode = "Nightly" diff --git a/compiler/rustc_codegen_cranelift/patches/0001-stdarch-Disable-SVE.patch b/compiler/rustc_codegen_cranelift/patches/0001-stdarch-Disable-SVE.patch new file mode 100644 index 0000000000000..e7038699bc998 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/patches/0001-stdarch-Disable-SVE.patch @@ -0,0 +1,51 @@ +From e90274ba5bc4bee03a24e8c134ea223497c3a46b Mon Sep 17 00:00:00 2001 +From: Cathal Mullan +Date: Sat, 13 Jun 2026 20:32:02 +0100 +Subject: [PATCH] Disable SVE + +Cranelift doesn't support scalable vectors. +--- + crates/core_arch/src/aarch64/mod.rs | 8 ++++---- + crates/intrinsic-test/src/arm/mod.rs | 1 + + 2 files changed, 5 insertions(+), 4 deletions(-) + +diff --git a/crates/core_arch/src/aarch64/mod.rs b/crates/core_arch/src/aarch64/mod.rs +index 34fa5c8..a870e31 100644 +--- a/crates/core_arch/src/aarch64/mod.rs ++++ b/crates/core_arch/src/aarch64/mod.rs +@@ -19,16 +19,16 @@ mod neon; + pub use self::neon::*; + + // The rest of `core_arch::aarch64` is available on `arm64ec` but SVE is not supported on `arm64ec`. +-#[cfg(any(all(target_arch = "aarch64", target_endian = "little"), doc))] ++#[cfg(all(not(test), any(all(target_arch = "aarch64", target_endian = "little"), doc)))] + mod sve; +-#[cfg(any(all(target_arch = "aarch64", target_endian = "little"), doc))] ++#[cfg(all(not(test), any(all(target_arch = "aarch64", target_endian = "little"), doc)))] + #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] + pub use self::sve::*; + + // The rest of `core_arch::aarch64` is available on `arm64ec` but SVE is not supported on `arm64ec`. +-#[cfg(any(all(target_arch = "aarch64", target_endian = "little"), doc))] ++#[cfg(all(not(test), any(all(target_arch = "aarch64", target_endian = "little"), doc)))] + mod sve2; +-#[cfg(any(all(target_arch = "aarch64", target_endian = "little"), doc))] ++#[cfg(all(not(test), any(all(target_arch = "aarch64", target_endian = "little"), doc)))] + #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] + pub use self::sve2::*; + +diff --git a/crates/intrinsic-test/src/arm/mod.rs b/crates/intrinsic-test/src/arm/mod.rs +index 9c2d0f3..3825f41 100644 +--- a/crates/intrinsic-test/src/arm/mod.rs ++++ b/crates/intrinsic-test/src/arm/mod.rs +@@ -66,6 +66,7 @@ impl SupportedArchitecture for Arm { + + let intrinsics = intrinsics + .into_iter() ++ .filter(|i| i.extension != "SVE" && i.extension != "SVE2") + // Skip intrinsics that don't return a value. + .filter(|i| i.results.kind() != TypeKind::Void) + // Skip bfloat intrinsics - not currently supported +-- +2.54.0 + diff --git a/compiler/rustc_codegen_cranelift/patches/0029-sysroot_tests-Disable-complex-type-tests.patch b/compiler/rustc_codegen_cranelift/patches/0029-sysroot_tests-Disable-complex-type-tests.patch new file mode 100644 index 0000000000000..d27810a988492 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/patches/0029-sysroot_tests-Disable-complex-type-tests.patch @@ -0,0 +1,32 @@ +From 20d296e9a8b837e586fcc8fc648d4ab9687a2a47 Mon Sep 17 00:00:00 2001 +From: Folkert de Vries +Date: Mon, 28 Sep 2026 19:14:22 +0200 +Subject: [PATCH] sysroot_tests: Disable complex type tests + +--- + coretests/tests/num/complex.rs | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/coretests/tests/num/complex.rs b/coretests/tests/num/complex.rs +index d73d04b1afc..289c0baec17 100644 +--- a/coretests/tests/num/complex.rs ++++ b/coretests/tests/num/complex.rs +@@ -75,6 +75,7 @@ fn complex_negation() { + } + + #[test] ++#[cfg_attr(target_os = "windows", ignore = "hits an ABI issue with cranelift on windows")] + fn complex_multiplication() { + #[cfg(target_has_reliable_f16)] + assert_eq!(Complex::new(1.0f16, 2.0) * Complex::new(3.0, 4.0), Complex::new(-5.0, 10.0)); +@@ -105,6 +106,7 @@ fn complex_multiplication() { + } + + #[test] ++#[cfg_attr(target_os = "windows", ignore = "hits an ABI issue with cranelift on windows")] + fn complex_div() { + #[cfg(target_has_reliable_f16)] + assert_eq!(Complex::new(2.0f16, 11.0) / Complex::new(2.0, 1.0), Complex::new(3.0, 4.0)); +-- +2.43.0 + diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain.toml b/compiler/rustc_codegen_cranelift/rust-toolchain.toml index d7e313a7a33ed..a6468283e1270 100644 --- a/compiler/rustc_codegen_cranelift/rust-toolchain.toml +++ b/compiler/rustc_codegen_cranelift/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2026-09-11" +channel = "nightly-2026-09-30" components = ["rust-src", "rustc-dev", "llvm-tools", "rustfmt"] profile = "minimal" diff --git a/compiler/rustc_codegen_cranelift/scripts/rustup.sh b/compiler/rustc_codegen_cranelift/scripts/rustup.sh index e26be307328f0..8bd41f98ce550 100755 --- a/compiler/rustc_codegen_cranelift/scripts/rustup.sh +++ b/compiler/rustc_codegen_cranelift/scripts/rustup.sh @@ -2,75 +2,34 @@ set -e -TOOLCHAIN=${TOOLCHAIN:-$(date +%Y-%m-%d)} - -function check_git_fixed_subtree() { - if [[ ! -e ./git-fixed-subtree.sh ]]; then - echo "Missing git-fixed-subtree.sh. Please run the following commands to download it:" - echo "curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/bjorn3/git/tqc-subtree-portable/contrib/subtree/git-subtree.sh -o git-fixed-subtree.sh" - echo "chmod u+x git-fixed-subtree.sh" - exit 1 - fi - if [[ ! -x ./git-fixed-subtree.sh ]]; then - echo "git-fixed-subtree.sh is not executable. Please run the following command to make it executable:" - echo "chmod u+x git-fixed-subtree.sh" - exit 1 - fi -} +export GIT_CONFIG_GLOBAL=$(pwd)/../rust/josh.gitconfig +export RUSTC_GIT=../rust case $1 in "prepare") - echo "=> Installing new nightly" - rustup toolchain install --profile minimal "nightly-${TOOLCHAIN}" # Sanity check to see if the nightly exists - sed -i "s/\"nightly-.*\"/\"nightly-${TOOLCHAIN}\"/" rust-toolchain.toml - echo "=> Uninstalling all old nightlies" - for nightly in $(rustup toolchain list | grep nightly | grep -v "$TOOLCHAIN" | grep -v nightly-x86_64); do + for nightly in $(RUSTUP_AUTO_INSTALL=0 rustup toolchain list | grep nightly | grep -v nightly-x86_64); do rustup toolchain uninstall "$nightly" done + echo "=> Installing new nightly" + rustup toolchain install + ./clean_all.sh ./y.sh prepare ;; - "commit") - git add rust-toolchain.toml - git commit -m "Rustup to $(rustc -V)" - ;; "push") - check_git_fixed_subtree - - cg_clif=$(pwd) - pushd ../rust - git pull origin main + username=${2:-bjorn3} branch=sync_cg_clif-$(date +%Y-%m-%d) - git checkout -b "$branch" - "$cg_clif/git-fixed-subtree.sh" pull --prefix=compiler/rustc_codegen_cranelift/ https://github.com/rust-lang/rustc_codegen_cranelift.git main - git push -u my "$branch" - - # immediately merge the merge commit into cg_clif to prevent merge conflicts when syncing - # from rust-lang/rust later - "$cg_clif/git-fixed-subtree.sh" push --prefix=compiler/rustc_codegen_cranelift/ "$cg_clif" sync_from_rust - popd - git merge sync_from_rust + rustc-josh-sync push "$branch" "$username" ;; "pull") - check_git_fixed_subtree - - RUST_VERS=$(curl "https://static.rust-lang.org/dist/$TOOLCHAIN/channel-rust-nightly-git-commit-hash.txt") - echo "Pulling $RUST_VERS ($TOOLCHAIN)" - - cg_clif=$(pwd) - pushd ../rust - git fetch origin main - git -c advice.detachedHead=false checkout "$RUST_VERS" - "$cg_clif/git-fixed-subtree.sh" push --prefix=compiler/rustc_codegen_cranelift/ "$cg_clif" sync_from_rust - popd - git merge sync_from_rust -m "Sync from rust $RUST_VERS" - git branch -d sync_from_rust + git checkout -b sync_from_rust + rustc-josh-sync pull ;; *) echo "Unknown command '$1'" - echo "Usage: ./rustup.sh prepare|commit" + echo "Usage: ./rustup.sh prepare|pull|push [fork]" ;; esac diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh index 5a1374fe56e09..648f68588e04b 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh @@ -164,6 +164,7 @@ rm -r tests/run-make/const-trait-stable-toolchain # same rm -r tests/run-make/print-request-help-stable-unstable # same rm -r tests/run-make/issue-149402-suggest-unresolve # same rm -r tests/run-make/const-destruct-stable-toolchain # same +rm -r tests/run-make/unstable-target-feature # same rm -r tests/run-make/incr-add-rust-src-component rm tests/ui/errors/remap-path-prefix-sysroot.rs # different sysroot source path rm -r tests/run-make/export # something about rustc version mismatches diff --git a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs index 1c552ca1a9c32..48ffc43c5cfa1 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs @@ -3,7 +3,7 @@ use cranelift_codegen::ir::ArgumentPurpose; use rustc_abi::{Reg, RegKind}; use rustc_target::callconv::{ - ArgAbi, ArgAttributes, ArgExtension as RustcArgExtension, CastTarget, PassMode, + ArgAbi, ArgAttributes, ArgExtension as RustcArgExtension, CastTarget, IndirectMode, PassMode, }; use smallvec::{SmallVec, smallvec}; @@ -126,8 +126,12 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { assert_eq!(pad_i32_count, 0, "padding support not yet implemented"); cast_target_to_abi_params(cast).into_iter().map(|(_, param)| param).collect() } - PassMode::Indirect { attrs, meta_attrs: None, on_stack } => { - if on_stack { + PassMode::Indirect { attrs, meta_attrs: None, address_space: _, mode } => { + assert!( + mode != IndirectMode::AmdgpuKernelArg, + "unsupported amdgpu kernel argument" + ); + if mode == IndirectMode::OnStack { // Abi requires aligning struct size to pointer size let size = self.layout.size.align_to(tcx.data_layout.pointer_align().abi); let size = u32::try_from(size.bytes()).unwrap(); @@ -139,8 +143,8 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { smallvec![apply_attrs_to_abi_param(AbiParam::new(pointer_ty(tcx)), attrs)] } } - PassMode::Indirect { attrs, meta_attrs: Some(meta_attrs), on_stack } => { - assert!(!on_stack); + PassMode::Indirect { attrs, meta_attrs: Some(meta_attrs), address_space: _, mode } => { + assert!(mode == IndirectMode::Pointer); smallvec![ apply_attrs_to_abi_param(AbiParam::new(pointer_ty(tcx)), attrs), apply_attrs_to_abi_param(AbiParam::new(pointer_ty(tcx)), meta_attrs), @@ -184,8 +188,8 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { None, cast_target_to_abi_params(cast).into_iter().map(|(_, param)| param).collect(), ), - PassMode::Indirect { attrs, meta_attrs: None, on_stack } => { - assert!(!on_stack); + PassMode::Indirect { attrs, meta_attrs: None, address_space: _, mode } => { + assert!(mode == IndirectMode::Pointer); ( Some(apply_attrs_to_abi_param( AbiParam::special(pointer_ty(tcx), ArgumentPurpose::StructReturn), @@ -194,7 +198,7 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { vec![], ) } - PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { + PassMode::Indirect { attrs: _, meta_attrs: Some(_), address_space: _, mode: _ } => { unreachable!("unsized return value") } } @@ -324,7 +328,7 @@ pub(super) fn cvalue_for_param<'tcx>( PassMode::Cast { ref cast, .. } => { from_casted_value(fx, &block_params, arg_abi.layout, cast) } - PassMode::Indirect { attrs, meta_attrs: None, on_stack: _ } => { + PassMode::Indirect { attrs, meta_attrs: None, address_space: _, mode: _ } => { assert_eq!(block_params.len(), 1, "{:?}", block_params); if let Some(pointee_align) = attrs.pointee_align && pointee_align < arg_abi.layout.align.abi @@ -342,7 +346,7 @@ pub(super) fn cvalue_for_param<'tcx>( CValue::by_ref(Pointer::new(block_params[0]), arg_abi.layout) } } - PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { + PassMode::Indirect { attrs: _, meta_attrs: Some(_), address_space: _, mode: _ } => { assert_eq!(block_params.len(), 2, "{:?}", block_params); CValue::by_ref_unsized(Pointer::new(block_params[0]), block_params[1], arg_abi.layout) } diff --git a/compiler/rustc_codegen_cranelift/src/abi/returning.rs b/compiler/rustc_codegen_cranelift/src/abi/returning.rs index 36087f96dd776..7f4ee9435b506 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/returning.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/returning.rs @@ -17,12 +17,12 @@ pub(super) fn codegen_return_param<'tcx>( let is_ssa = ssa_analyzed[RETURN_PLACE].is_ssa(fx, fx.fn_abi.ret.layout.ty); (super::make_local_place(fx, RETURN_PLACE, fx.fn_abi.ret.layout, is_ssa), smallvec![]) } - PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => { + PassMode::Indirect { attrs: _, meta_attrs: None, address_space: _, mode: _ } => { let ret_param = block_params_iter.next().unwrap(); assert_eq!(fx.bcx.func.dfg.value_type(ret_param), fx.pointer_type); (CPlace::for_ptr(Pointer::new(ret_param), fx.fn_abi.ret.layout), smallvec![ret_param]) } - PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { + PassMode::Indirect { attrs: _, meta_attrs: Some(_), address_space: _, mode: _ } => { unreachable!("unsized return value") } }; @@ -50,7 +50,7 @@ pub(super) fn codegen_with_call_return_arg<'tcx>( ) { let (ret_temp_place, return_ptr) = match ret_arg_abi.mode { PassMode::Ignore => (None, None), - PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => { + PassMode::Indirect { attrs: _, meta_attrs: None, address_space: _, mode: _ } => { if let Some(ret_ptr) = ret_place.try_to_ptr() { // This is an optimization to prevent unnecessary copies of the return value when // the return place is already a memory place as opposed to a register. @@ -61,7 +61,7 @@ pub(super) fn codegen_with_call_return_arg<'tcx>( (Some(place), Some(place.to_ptr().get_addr(fx))) } } - PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { + PassMode::Indirect { attrs: _, meta_attrs: Some(_), address_space: _, mode: _ } => { unreachable!("unsized return value") } PassMode::Direct(_) | PassMode::Pair(_, _) | PassMode::Cast { .. } => (None, None), @@ -86,14 +86,14 @@ pub(super) fn codegen_with_call_return_arg<'tcx>( super::pass_mode::from_casted_value(fx, &results, ret_place.layout(), cast); ret_place.write_cvalue(fx, result); } - PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => { + PassMode::Indirect { attrs: _, meta_attrs: None, address_space: _, mode: _ } => { if let Some(ret_temp_place) = ret_temp_place { // If ret_temp_place is None, it is not necessary to copy the return value. let ret_temp_value = ret_temp_place.to_cvalue(fx); ret_place.write_cvalue(fx, ret_temp_value); } } - PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { + PassMode::Indirect { attrs: _, meta_attrs: Some(_), address_space: _, mode: _ } => { unreachable!("unsized return value") } } @@ -102,10 +102,11 @@ pub(super) fn codegen_with_call_return_arg<'tcx>( /// Codegen a return instruction with the right return value(s) if any. pub(crate) fn codegen_return(fx: &mut FunctionCx<'_, '_, '_>) { match fx.fn_abi.ret.mode { - PassMode::Ignore | PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => { + PassMode::Ignore + | PassMode::Indirect { attrs: _, meta_attrs: None, address_space: _, mode: _ } => { fx.bcx.ins().return_(&[]); } - PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { + PassMode::Indirect { attrs: _, meta_attrs: Some(_), address_space: _, mode: _ } => { unreachable!("unsized return value") } PassMode::Direct(_) => { diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 506037c151148..e752189130290 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -5,13 +5,14 @@ use cranelift_codegen::ir::UserFuncName; use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use cranelift_module::ModuleError; use rustc_ast::InlineAsmOptions; +use rustc_attr_ir::lang_items::LangItem; use rustc_codegen_ssa::base::is_call_from_compiler_builtins_to_upstream_monomorphization; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_errors::DiagCtxtHandle; -use rustc_hir::attrs::lang_items::LangItem; use rustc_index::IndexVec; use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::adjustment::PointerCoercion; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::layout::{FnAbiOf, HasTypingEnv as _}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_session::config::OutputFilenames; diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index d31c8fc810b2c..30bd2f28af53f 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -35,6 +35,7 @@ pub(crate) fn scalar_to_clif_type(tcx: TyCtxt<'_>, scalar: Scalar) -> Type { }, Primitive::Float(float) => match float { Float::F16 => types::F16, + Float::F16B => bug!("f16b is not supported by the Cranelift codegen backend"), Float::F32 => types::F32, Float::F64 => types::F64, Float::F128 => types::F128, diff --git a/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs b/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs index ca9157daae584..8e339167a5b12 100644 --- a/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs +++ b/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs @@ -135,8 +135,8 @@ builtin_functions! { // float intrinsics fn __powisf2(a: f32, b: i32) -> f32; fn __powidf2(a: f64, b: i32) -> f64; - // FIXME(f16_f128): `compiler-builtins` doesn't currently support `__powitf2` on MSVC. - // fn __powitf2(a: f128, b: i32) -> f128; + #[cfg(not(all(target_os = "windows", target_env = "gnu")))] + fn __powitf2(a: f128, b: i32) -> f128; fn powf(a: f32, b: f32) -> f32; fn pow(a: f64, b: f64) -> f64; fn expf(f: f32) -> f32; diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index e2b0035361733..f1e4fd818ebeb 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -293,8 +293,8 @@ fn data_id_for_static( .abi .bytes(); - let linkage = if import_linkage == rustc_hir::attrs::Linkage::ExternalWeak - || import_linkage == rustc_hir::attrs::Linkage::WeakAny + let linkage = if import_linkage == rustc_attr_ir::Linkage::ExternalWeak + || import_linkage == rustc_attr_ir::Linkage::WeakAny { Linkage::Preemptible } else { @@ -344,8 +344,8 @@ fn data_id_for_static( let linkage = if definition { crate::linkage::get_static_linkage(tcx, def_id) - } else if attrs.linkage == Some(rustc_hir::attrs::Linkage::ExternalWeak) - || attrs.linkage == Some(rustc_hir::attrs::Linkage::WeakAny) + } else if attrs.linkage == Some(rustc_attr_ir::Linkage::ExternalWeak) + || attrs.linkage == Some(rustc_attr_ir::Linkage::WeakAny) { Linkage::Preemptible } else { diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs index 18a0632a0939d..2eb4725fc5b20 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs @@ -3,6 +3,7 @@ use gimli::write::{AttributeValue, UnitEntryId}; use rustc_codegen_ssa::debuginfo::type_names; use rustc_data_structures::fx::FxHashMap; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Ty, TyCtxt}; diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index cd82df386f5ce..a62dff641cfec 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -10,6 +10,7 @@ use std::time::Instant; use cranelift_object::{ObjectBuilder, ObjectModule}; use rustc_ast::expand::allocator::AllocatorMethod; +use rustc_attr_ir::Linkage as RLinkage; use rustc_codegen_ssa::back::lto::ThinModule; use rustc_codegen_ssa::back::write::{ CodegenContext, FatLtoInput, ModuleConfig, SharedEmitter, TargetMachineFactoryFn, ThinLtoInput, @@ -18,7 +19,6 @@ use rustc_codegen_ssa::traits::{ExtraBackendMethods, WriteBackendMethods}; use rustc_codegen_ssa::{CompiledModule, ModuleCodegen, ModuleKind}; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_errors::{DiagCtxt, DiagCtxtHandle}; -use rustc_hir::attrs::Linkage as RLinkage; use rustc_middle::dep_graph::WorkProduct; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mono::{MonoItem, MonoItemData, Visibility}; @@ -140,7 +140,6 @@ fn emit_module( bytecode: None, assembly: None, llvm_ir: None, - links_from_incr_cache: Vec::new(), }) } diff --git a/compiler/rustc_codegen_cranelift/src/inline_asm.rs b/compiler/rustc_codegen_cranelift/src/inline_asm.rs index cd2b06cc1defb..b2bf856e7bcee 100644 --- a/compiler/rustc_codegen_cranelift/src/inline_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/inline_asm.rs @@ -5,7 +5,7 @@ use std::fmt::Write; use cranelift_codegen::isa::CallConv; use rustc_abi::CanonAbi; use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece}; -use rustc_hir::attrs::lang_items::LangItem; +use rustc_attr_ir::lang_items::LangItem; use rustc_middle::mir::interpret::{GlobalAlloc, PointerArithmetic, Scalar as ConstScalar}; use rustc_middle::ty::layout::FnAbiOf; use rustc_span::sym; diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs index 136e98964bc35..f3763b5895eba 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs @@ -855,42 +855,59 @@ pub(super) fn codegen_x86_llvm_intrinsic_call<'tcx>( ); } - "llvm.x86.pclmulqdq" => { + "llvm.x86.pclmulqdq" | "llvm.x86.pclmulqdq.256" | "llvm.x86.pclmulqdq.512" => { // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_clmulepi64_si128&ig_expand=772 + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_clmulepi64_epi128&ig_expand=752 + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm512_clmulepi64_epi128&ig_expand=753 intrinsic_args!(fx, args => (a, b, _imm8); intrinsic); - let a = a.load_scalar(fx); - let b = b.load_scalar(fx); + let (lane_count, intrinsic_name) = match intrinsic { + "llvm.x86.pclmulqdq" => (1, "_mm_clmulepi64_si128"), + "llvm.x86.pclmulqdq.256" => (2, "_mm256_clmulepi64_epi128"), + "llvm.x86.pclmulqdq.512" => (4, "_mm512_clmulepi64_epi128"), + _ => unreachable!(), + }; - let imm8 = - if let Some(imm8) = crate::constant::mir_operand_get_const_val(fx, &args[2].node) { - imm8 - } else { - fx.tcx.dcx().span_fatal( - span, - "Index argument for `_mm_clmulepi64_si128` is not a constant", - ); - }; + let Some(imm8) = crate::constant::mir_operand_get_const_val(fx, &args[2].node) else { + fx.tcx.dcx().span_fatal( + span, + format!("Index argument for `{intrinsic_name}` is not a constant"), + ) + }; let imm8 = imm8.to_u8(); - codegen_inline_asm_inner( - fx, - &[InlineAsmTemplatePiece::String(format!("pclmulqdq xmm0, xmm1, {imm8}").into())], - &[ - CInlineAsmOperand::InOut { - reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::xmm0)), - _late: true, - in_value: a, - out_place: Some(ret), - }, - CInlineAsmOperand::In { - reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::xmm1)), - value: b, - }, - ], - InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, - ); + // Cranelift does not currently support 256-bit or 512-bit SSA values, so we use the + // 128-bit instruction on each 128-bit lane. + for lane in 0..lane_count { + let a = a.value_typed_lane(fx, fx.tcx.types.u128, lane).load_scalar(fx); + let b = b.value_typed_lane(fx, fx.tcx.types.u128, lane).load_scalar(fx); + let ret_lane = ret.place_typed_lane(fx, fx.tcx.types.u128, lane); + + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String( + format!("pclmulqdq xmm0, xmm1, {imm8}").into(), + )], + &[ + CInlineAsmOperand::InOut { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86( + X86InlineAsmReg::xmm0, + )), + _late: true, + in_value: a, + out_place: Some(ret_lane), + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86( + X86InlineAsmReg::xmm1, + )), + value: b, + }, + ], + InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, + ); + } } "llvm.x86.aesni.aeskeygenassist" => { diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index cf1c1f027e7f5..9c3182c944dc6 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -469,7 +469,7 @@ fn codegen_regular_intrinsic_call<'tcx>( let usize_layout = fx.layout_of(fx.tcx.types.usize); match intrinsic { - sym::abort => { + sym::abort_immediate => { fx.bcx.set_cold_block(fx.bcx.current_block().unwrap()); fx.bcx.ins().trap(TrapCode::user(2).unwrap()); return Ok(()); diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs index 270ea4e88a999..f04c32a70390a 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs @@ -3,6 +3,7 @@ use cranelift_codegen::ir::immediates::Offset32; use rustc_abi::Endian; use rustc_middle::ty::SimdAlign; +use rustc_middle::ty::consts::ConstExt; use super::*; use crate::prelude::*; diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index d4773e8199aed..a30dd1c3ebef3 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -12,6 +12,7 @@ extern crate rustc_abi; extern crate rustc_ast; +extern crate rustc_attr_ir; extern crate rustc_codegen_ssa; extern crate rustc_const_eval; extern crate rustc_data_structures; @@ -196,6 +197,7 @@ impl CodegenBackend for CraneliftCodegenBackend { // available in Cranelift. has_reliable_f16: has_reliable_f16_f128, has_reliable_f16_math: has_reliable_f16_f128, + has_reliable_f16b: false, has_reliable_f128: has_reliable_f16_f128, has_reliable_f128_math, } diff --git a/compiler/rustc_codegen_cranelift/src/linkage.rs b/compiler/rustc_codegen_cranelift/src/linkage.rs index e373268d76b76..aac3e2cf54b99 100644 --- a/compiler/rustc_codegen_cranelift/src/linkage.rs +++ b/compiler/rustc_codegen_cranelift/src/linkage.rs @@ -1,4 +1,4 @@ -use rustc_hir::attrs::Linkage as RLinkage; +use rustc_attr_ir::Linkage as RLinkage; use rustc_middle::mono::{MonoItem, Visibility}; use crate::prelude::*; diff --git a/compiler/rustc_codegen_cranelift/src/main_shim.rs b/compiler/rustc_codegen_cranelift/src/main_shim.rs index b69db582a04ca..75dbfb1228d91 100644 --- a/compiler/rustc_codegen_cranelift/src/main_shim.rs +++ b/compiler/rustc_codegen_cranelift/src/main_shim.rs @@ -1,5 +1,5 @@ use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; -use rustc_hir::attrs::lang_items::LangItem; +use rustc_attr_ir::lang_items::LangItem; use rustc_middle::ty::{AssocTag, GenericArg, Unnormalized}; use rustc_session::config::EntryFnType; use rustc_span::{DUMMY_SP, Ident}; diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs index 48fb0f6c7d4a8..efc89075cdeb2 100644 --- a/compiler/rustc_codegen_cranelift/src/unsize.rs +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -3,6 +3,7 @@ //! [`PointerCoercion::Unsize`]: `rustc_middle::ty::adjustment::PointerCoercion::Unsize` use rustc_codegen_ssa::base::validate_trivial_unsize; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::layout::HasTypingEnv; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; diff --git a/compiler/rustc_codegen_gcc/.cspell.json b/compiler/rustc_codegen_gcc/.cspell.json deleted file mode 100644 index 556432d69a41b..0000000000000 --- a/compiler/rustc_codegen_gcc/.cspell.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "allowCompoundWords": true, - "dictionaries": ["cpp", "rust-extra", "rustc_codegen_gcc"], - "dictionaryDefinitions": [ - { - "name": "rust-extra", - "path": "tools/cspell_dicts/rust.txt", - "addWords": true - }, - { - "name": "rustc_codegen_gcc", - "path": "tools/cspell_dicts/rustc_codegen_gcc.txt", - "addWords": true - } - ], - "files": [ - "src/**/*.rs" - ], - "ignorePaths": [ - "src/intrinsic/archs.rs", - "src/intrinsic/old_archs.rs", - "src/intrinsic/llvm.rs" - ], - "ignoreRegExpList": [ - "/(FIXME|NOTE|TODO)\\([^)]+\\)/", - "__builtin_\\w*" - ] -} diff --git a/compiler/rustc_codegen_gcc/.github/workflows/ci.yml b/compiler/rustc_codegen_gcc/.github/workflows/ci.yml index fa9535a3729c3..f37123c124c74 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/ci.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/ci.yml @@ -26,16 +26,18 @@ jobs: - { gcc: "gcc-15.deb" } - { gcc: "gcc-15-without-int128.deb" } commands: [ - "--std-tests", + "--std-tests --alloc-tests", # FIXME: re-enable asm tests when GCC can emit in the right syntax. # "--asm-tests", "--test-libcore", "--extended-rand-tests", - "--extended-regex-example-tests", + "--extended-regex-example-tests --test-libcore-doctests", "--extended-regex-tests", "--test-successful-rustc --nb-parts 2 --current-part 0", "--test-successful-rustc --nb-parts 2 --current-part 1", - "--projects", + "--projects --nb-parts 2 --current-part 0", + "--projects --nb-parts 2 --current-part 1", + "--gcc-asm-tests --test-release-libcore", ] steps: @@ -50,10 +52,13 @@ jobs: - name: Install packages # `llvm-14-tools` is needed to install the `FileCheck` binary which is used for asm tests. - run: sudo apt-get install ninja-build ripgrep llvm-14-tools llvm + run: | + sudo apt-get update + sudo apt-get install ninja-build ripgrep llvm-14-tools llvm - - name: Install rustfmt & clippy - run: rustup component add rustfmt clippy + - name: Install the libraries needed to build librsvg + if: ${{ contains(matrix.commands, '--projects') }} + run: sudo apt-get install libcairo2-dev libpango1.0-dev libfontconfig1-dev libfreetype-dev libharfbuzz-dev libxml2-dev libglib2.0-dev - name: Download artifact run: curl -LO https://github.com/rust-lang/gcc/releases/latest/download/${{ matrix.libgccjit_version.gcc }} @@ -88,16 +93,17 @@ jobs: - name: Check formatting run: ./y.sh fmt --check - - name: clippy - run: | - cargo clippy --all-targets -- -D warnings - cargo clippy --all-targets --no-default-features -- -D warnings - cargo clippy --manifest-path build_system/Cargo.toml --all-targets -- -D warnings + - name: Check todo + run: ./y.sh check-todo + + - name: Check lints + run: ./y.sh clippy - name: Build run: | ./y.sh build --sysroot ./y.sh test --cargo-tests + CARGO_TEST_FLAGS="-Zmir-preserve-ub" ./y.sh test --cargo-tests -- mir_preserve_ub_empty_switch - name: Run y.sh cargo build run: | @@ -127,13 +133,6 @@ jobs: - uses: actions/checkout@v4 - run: python tools/check_intrinsics_duplicates.py - spell_check: - runs-on: ubuntu-24.04 - steps: - - uses: actions/checkout@v4 - - uses: crate-ci/typos@v1.32.0 - - uses: streetsidesoftware/cspell-action@v7 - build_system: runs-on: ubuntu-24.04 steps: diff --git a/compiler/rustc_codegen_gcc/.github/workflows/failures.yml b/compiler/rustc_codegen_gcc/.github/workflows/failures.yml index 2c9e4950706b2..52e96726129b3 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/failures.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/failures.yml @@ -98,7 +98,14 @@ jobs: if: matrix.libgccjit_version.gcc != 'libgccjit12.so' id: tests run: | - ${{ matrix.libgccjit_version.env_extra }} ./y.sh test --release --clean --build-sysroot --test-failing-rustc ${{ matrix.libgccjit_version.extra }} 2>&1 | tee output_log + # Without this, `tee` masks the exit status of `y.sh test`. + set -o pipefail + status=0 + ${{ matrix.libgccjit_version.env_extra }} ./y.sh test --release --clean --build-sysroot --test-failing-rustc ${{ matrix.libgccjit_version.extra }} 2>&1 | tee output_log || status=$? + # This suite runs the tests known to fail, so only a build system error must fail the job. + if [ "$status" -ne 0 ] && [ "$status" -ne 2 ]; then + exit "$status" + fi rg --text "test result" output_log >> $GITHUB_STEP_SUMMARY - name: Run failing ui pattern tests for ICE @@ -106,7 +113,14 @@ jobs: if: matrix.libgccjit_version.gcc != 'libgccjit12.so' id: ui-tests run: | - ${{ matrix.libgccjit_version.env_extra }} ./y.sh test --release --test-failing-ui-pattern-tests ${{ matrix.libgccjit_version.extra }} 2>&1 | tee output_log_ui + # Without this, `tee` masks the exit status of `y.sh test`. + set -o pipefail + status=0 + ${{ matrix.libgccjit_version.env_extra }} ./y.sh test --release --test-failing-ui-pattern-tests ${{ matrix.libgccjit_version.extra }} 2>&1 | tee output_log_ui || status=$? + # This suite runs tests that fail, so only a build system error must fail the job here. + if [ "$status" -ne 0 ] && [ "$status" -ne 2 ]; then + exit "$status" + fi if grep -q "the compiler unexpectedly panicked" output_log_ui; then echo "Error: 'the compiler unexpectedly panicked' found in output logs. CI Error!!" exit 1 diff --git a/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml b/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml index 66f30b147b4c0..4afb74a1f8cdd 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml @@ -21,7 +21,7 @@ jobs: fail-fast: false matrix: cargo_runner: [ - "sde -future -rtm_mode full --", + "sde -cpuid-in /home/runner/work/rustc_codegen_gcc/rustc_codegen_gcc/tests/cpuid.def -rtm_mode full --", "", ] @@ -32,6 +32,10 @@ jobs: - name: Setup rust toolchain run: rustup show + # The stdarch-gen-common tests run rustfmt. + - name: Install rustfmt + run: rustup component add rustfmt + - name: Setup rust cache uses: Swatinem/rust-cache@v2 @@ -42,8 +46,14 @@ jobs: - name: Install more recent binutils run: | echo "deb http://archive.ubuntu.com/ubuntu plucky main universe" | sudo tee /etc/apt/sources.list.d/plucky-copies.list - sudo apt-get update + sudo apt-get update -o Acquire::Retries=3 sudo apt-get install binutils + installed="$(dpkg-query --showformat='${Version}' --show binutils)" + echo "Installed binutils: $installed" + if dpkg --compare-versions "$installed" lt "2.44"; then + echo "::error::binutils upgrade failed (got $installed, need >= 2.44); the apt fetch probably failed" + exit 1 + fi - name: Install Intel Software Development Emulator if: ${{ matrix.cargo_runner }} @@ -51,10 +61,9 @@ jobs: mkdir intel-sde cd intel-sde version=10.8.0-2026-03-15 - url_path=915934 dir=sde-external-$version-lin file=$dir.tar.xz - wget https://downloadmirror.intel.com/$url_path/$file + wget http://ci-mirrors.rust-lang.org/$file tar xvf $file sudo mkdir /usr/share/intel-sde sudo cp -r $dir/* /usr/share/intel-sde @@ -90,14 +99,17 @@ jobs: - name: Run stdarch tests if: ${{ !matrix.cargo_runner }} run: | - CHANNEL=release TARGET=x86_64-unknown-linux-gnu CG_RUSTFLAGS="-Ainternal_features" ./y.sh cargo test --manifest-path build/build_sysroot/sysroot_src/library/stdarch/Cargo.toml + # FIXME: remove --skip test_tile_ and --skip --skip test__tile when it's implemented. + # FIXME: remove --skip test_mm_srav_epi64 when it's fixed upstream. + ./y.sh test --release --stdarch-tests -- --skip test_tile_ --skip test__tile --skip test_mm_srav_epi64 - name: Run stdarch tests if: ${{ matrix.cargo_runner }} run: | # FIXME: these tests fail when the sysroot is compiled with LTO because of a missing symbol in proc-macro. - # FIXME: remove --skip test_tile_ when it's implemented. - STDARCH_TEST_SKIP_FUNCTION="xsave,xsaveopt,xsave64,xsaveopt64" STDARCH_TEST_EVERYTHING=1 CHANNEL=release CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER="${{ matrix.cargo_runner }}" TARGET=x86_64-unknown-linux-gnu CG_RUSTFLAGS="-Ainternal_features" ./y.sh cargo test --manifest-path build/build_sysroot/sysroot_src/library/stdarch/Cargo.toml -- --skip rtm --skip tbm --skip sse4a --skip test_tile_ + # FIXME: remove --skip test_tile_ and --skip --skip test__tile when it's implemented. + # FIXME: remove --skip test_mm_srav_epi64 when it's fixed upstream. + STDARCH_TEST_SKIP_FUNCTION="xsave,xsaveopt,xsave64,xsaveopt64" STDARCH_TEST_EVERYTHING=1 CHANNEL=release CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER="${{ matrix.cargo_runner }}" TARGET=x86_64-unknown-linux-gnu CG_RUSTFLAGS="-Ainternal_features" ./y.sh cargo test --manifest-path build/build_sysroot/sysroot_src/library/stdarch/Cargo.toml -- --skip rtm --skip tbm --skip sse4a --skip test_tile_ --skip test__tile --skip test_mm_srav_epi64 # Summary job for the merge queue. # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! diff --git a/compiler/rustc_codegen_gcc/.gitignore b/compiler/rustc_codegen_gcc/.gitignore index 8f73d3eb972a0..13bd0d0ffde9b 100644 --- a/compiler/rustc_codegen_gcc/.gitignore +++ b/compiler/rustc_codegen_gcc/.gitignore @@ -7,7 +7,7 @@ perf.data.old *.events *.string* gimple* -*asm +*_asm res test-backend projects @@ -20,4 +20,5 @@ llvm build_system/target config.toml build -rustlantis \ No newline at end of file +rustlantis +stuff/ diff --git a/compiler/rustc_codegen_gcc/CONTRIBUTING.md b/compiler/rustc_codegen_gcc/CONTRIBUTING.md index 8f81ecca445a8..c5c2a783b1ee7 100644 --- a/compiler/rustc_codegen_gcc/CONTRIBUTING.md +++ b/compiler/rustc_codegen_gcc/CONTRIBUTING.md @@ -112,7 +112,7 @@ Full list of debugging options can be found in the [README](Readme.md#env-vars). ### Code Style Guidelines - Follow Rust standard coding conventions -- Ensure your code passes `rustfmt` and `clippy` +- Ensure your code passes `rustfmt` and `clippy` (you can run them with `y.sh fmt` and `y.sh clippy`) - Add comments explaining complex logic, especially in GCC interface code ## Additional Resources diff --git a/compiler/rustc_codegen_gcc/Cargo.lock b/compiler/rustc_codegen_gcc/Cargo.lock index 7ce94b58c0516..6467f1a8c3763 100644 --- a/compiler/rustc_codegen_gcc/Cargo.lock +++ b/compiler/rustc_codegen_gcc/Cargo.lock @@ -31,9 +31,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "errno" -version = "0.3.10" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", "windows-sys", @@ -56,18 +56,18 @@ dependencies = [ [[package]] name = "gccjit" -version = "3.3.0" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b73d18b642ce16378af78f89664841d7eeafa113682ff5d14573424eb0232a" +checksum = "dfdb84ba432f6679794790de652bfbed86201893a30c03dc669c2bfe7424f472" dependencies = [ "gccjit_sys", ] [[package]] name = "gccjit_sys" -version = "1.3.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee689456c013616942d5aef9a84d613cefcc3b335340d036f3650fc1a7459e15" +checksum = "e081669728b490723537f9def7eb674b7c9acd8de0b92ad4f4abf5f5cc75ea4b" dependencies = [ "libc", ] @@ -117,15 +117,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.168" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "memchr" @@ -194,9 +194,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.7" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ "bitflags", "errno", @@ -216,9 +216,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.20.0" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", "getrandom", @@ -311,78 +311,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows-sys" -version = "0.59.0" +name = "windows-link" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets", -] +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] -name = "windows-targets" -version = "0.52.6" +name = "windows-sys" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows-link", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - [[package]] name = "wit-bindgen-rt" version = "0.39.0" diff --git a/compiler/rustc_codegen_gcc/Cargo.toml b/compiler/rustc_codegen_gcc/Cargo.toml index ac5e94b9454e2..35046980f0695 100644 --- a/compiler/rustc_codegen_gcc/Cargo.toml +++ b/compiler/rustc_codegen_gcc/Cargo.toml @@ -20,11 +20,11 @@ default = ["master"] [dependencies] object = { version = "0.39.0", default-features = false, features = ["std", "read"] } tempfile = "3.20" -gccjit = { version = "3.3.0", features = ["dlopen"] } +gccjit = { version = "7.0.0", features = ["dlopen"] } #gccjit = { git = "https://github.com/rust-lang/gccjit.rs", branch = "error-dlopen", features = ["dlopen"] } # Local copy. -#gccjit = { path = "../gccjit.rs", features = ["dlopen"] } +# gccjit = { path = "../gccjit.rs", features = ["dlopen"] } [dev-dependencies] boml = "0.3.1" diff --git a/compiler/rustc_codegen_gcc/Readme.md b/compiler/rustc_codegen_gcc/Readme.md index ce5ee1e4adee6..14c0e3ad0f21c 100644 --- a/compiler/rustc_codegen_gcc/Readme.md +++ b/compiler/rustc_codegen_gcc/Readme.md @@ -136,23 +136,25 @@ $ ./y.sh cargo build --manifest-path tests/hello-world/Cargo.toml ### Cargo ```bash -$ CHANNEL="release" $CG_GCCJIT_DIR/y.sh cargo run +$ CHANNEL=release $CG_GCCJIT_DIR/y.sh cargo run ``` -If you compiled cg_gccjit in debug mode (aka you didn't pass `--release` to `./y.sh test`) you should use `CHANNEL="debug"` instead or omit `CHANNEL="release"` completely. +If you compiled `cg_gcc` in debug mode (aka you didn't pass `--release` to `./y.sh build`) you should use `CHANNEL=debug` instead or omit `CHANNEL=release` completely. ### Rustc If you want to run `rustc` directly, you can do so with: ```bash -$ ./y.sh rustc my_crate.rs +$ CHANNEL=release ./y.sh rustc my_crate.rs ``` +If you compiled `cg_gcc` in debug mode (aka you didn't pass `--release` to `./y.sh build`) you should use `CHANNEL=debug` instead or omit `CHANNEL=release` completely. + You can do the same manually (although we don't recommend it): ```bash -$ LIBRARY_PATH="[gcc-path value]" LD_LIBRARY_PATH="[gcc-path value]" rustc +$(cat $CG_GCCJIT_DIR/rust-toolchain | grep 'channel' | cut -d '=' -f 2 | sed 's/"//g' | sed 's/ //g') -Cpanic=abort -Zcodegen-backend=$CG_GCCJIT_DIR/target/release/librustc_codegen_gcc.so --sysroot $CG_GCCJIT_DIR/build_sysroot/sysroot my_crate.rs +$ LIBRARY_PATH="[gcc-path value]" LD_LIBRARY_PATH="[gcc-path value]" rustc +$(cat $CG_GCCJIT_DIR/rust-toolchain.toml | grep 'channel' | cut -d '=' -f 2 | sed 's/"//g' | sed 's/ //g') -Cpanic=abort -Zcodegen-backend=$CG_GCCJIT_DIR/target/release/librustc_codegen_gcc.so --sysroot $CG_GCCJIT_DIR/build_sysroot/sysroot my_crate.rs ``` ## Environment variables @@ -176,7 +178,7 @@ $ LIBRARY_PATH="[gcc-path value]" LD_LIBRARY_PATH="[gcc-path value]" rustc +$(ca More specific documentation is available in the [`doc`](./doc) folder: * [Common errors](./doc/errors.md) - * [Debugging GCC LTO](./doc/debugging-gcc-lto.md) + * [Debugging](./doc/debugging.md) * [Debugging libgccjit](./doc/debugging-libgccjit.md) * [Git subtree sync](./doc/subtree.md) * [List of useful commands](./doc/tips.md) diff --git a/compiler/rustc_codegen_gcc/build_system/Cargo.lock b/compiler/rustc_codegen_gcc/build_system/Cargo.lock index e727561a2bfba..5e761149eb3bc 100644 --- a/compiler/rustc_codegen_gcc/build_system/Cargo.lock +++ b/compiler/rustc_codegen_gcc/build_system/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "boml" diff --git a/compiler/rustc_codegen_gcc/build_system/asm-tester/Cargo.lock b/compiler/rustc_codegen_gcc/build_system/asm-tester/Cargo.lock new file mode 100644 index 0000000000000..9ad96acfda407 --- /dev/null +++ b/compiler/rustc_codegen_gcc/build_system/asm-tester/Cargo.lock @@ -0,0 +1,507 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "asm-tester" +version = "0.1.0" +dependencies = [ + "compiletest_rs", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "compiletest_rs" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f150fe9105fcd2a57cad53f0c079a24de65195903ef670990f5909f695eac04c" +dependencies = [ + "diff", + "filetime", + "getopts", + "lazy_static", + "libc", + "log", + "miow", + "regex", + "rustfix", + "serde", + "serde_derive", + "serde_json", + "tester", + "windows-sys 0.59.0", +] + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "filetime" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759" +dependencies = [ + "cfg-if", + "libc", +] + +[[package]] +name = "getopts" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libredox" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652" +dependencies = [ + "libc", +] + +[[package]] +name = "log" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" + +[[package]] +name = "memchr" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" + +[[package]] +name = "miow" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "536bfad37a309d62069485248eeaba1e8d9853aaf951caaeaed0585a95346f08" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + +[[package]] +name = "rustfix" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82fa69b198d894d84e23afde8e9ab2af4400b2cba20d6bf2b428a8b01c222c5a" +dependencies = [ + "serde", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "syn" +version = "2.0.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + +[[package]] +name = "tester" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e8bf7e0eb2dd7b4228cc1b6821fc5114cd6841ae59f652a85488c016091e5f" +dependencies = [ + "cfg-if", + "getopts", + "libc", + "num_cpus", + "term", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/compiler/rustc_codegen_gcc/build_system/asm-tester/Cargo.toml b/compiler/rustc_codegen_gcc/build_system/asm-tester/Cargo.toml new file mode 100644 index 0000000000000..eeefe61bdc75b --- /dev/null +++ b/compiler/rustc_codegen_gcc/build_system/asm-tester/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "asm-tester" +version = "0.1.0" +edition = "2024" + +[dependencies] +compiletest_rs = "0.11.2" + +[[bin]] +name = "asm-tester" +path = "src/main.rs" + +[workspace] diff --git a/compiler/rustc_codegen_gcc/build_system/asm-tester/src/main.rs b/compiler/rustc_codegen_gcc/build_system/asm-tester/src/main.rs new file mode 100644 index 0000000000000..00ee4ac936520 --- /dev/null +++ b/compiler/rustc_codegen_gcc/build_system/asm-tester/src/main.rs @@ -0,0 +1,66 @@ +use std::path::PathBuf; + +#[derive(Default)] +struct Config { + llvm_filecheck: Option, + filters: Vec, + rustc_flags: Vec, +} + +impl Config { + fn new() -> Result { + // We skip the program's name. + let mut args = std::env::args().skip(1); + let mut config = Self::default(); + + while let Some(arg) = args.next() { + match arg.as_str() { + "--llvm-filecheck" => { + config.llvm_filecheck = args.next().map(PathBuf::from); + } + "--filter" => { + if let Some(arg) = args.next() { + config.filters.push(arg); + } + } + "--" => { + config.rustc_flags.extend(&mut args); + // Nothing else to be read but the `break` makes it more clear. + break; + } + arg => return Err(format!("Unknown argument {arg:?}")), + } + } + if config.llvm_filecheck.is_none() { + Err("Missing `--llvm-filecheck` option".to_owned()) + } else if config.rustc_flags.is_empty() { + Err("Missing rustc flags (passed after `--`)".to_owned()) + } else { + Ok(config) + } + } +} + +fn main() { + let Config { llvm_filecheck, filters, rustc_flags } = match Config::new() { + Ok(c) => c, + Err(error) => { + eprintln!("{error}"); + std::process::exit(1); + } + }; + + let mut test_config = compiletest_rs::Config::default(); + + test_config.mode = compiletest_rs::common::Mode::Assembly; + test_config.src_base = PathBuf::from("tests/asm"); + test_config.llvm_filecheck = llvm_filecheck; + test_config.filters = filters; + test_config.strict_headers = true; + test_config.build_base = PathBuf::from("build/tests/asm"); + test_config.target_rustcflags = Some(rustc_flags.join(" ")); + test_config.link_deps(); + test_config.clean_rmeta(); + + compiletest_rs::run_tests(&test_config) +} diff --git a/compiler/rustc_codegen_gcc/build_system/src/abi_test.rs b/compiler/rustc_codegen_gcc/build_system/src/abi_test.rs index a85886d87f365..9fc06dc8dbc04 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/abi_test.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/abi_test.rs @@ -34,7 +34,7 @@ pub fn run() -> Result<(), String> { .map_err(|err| format!("Git clone failed with message: {err:?}!"))?; // Configure abi-cafe to use the exact same rustc version we use - this is crucial. // Otherwise, the concept of ABI compatibility becomes meanignless. - std::fs::copy("rust-toolchain", "clones/abi-cafe/rust-toolchain") + std::fs::copy("rust-toolchain.toml", "clones/abi-cafe/rust-toolchain.toml") .expect("Could not copy toolchain configs!"); // Get the backend path. // We will use the *debug* build of the backend - it has more checks enabled. diff --git a/compiler/rustc_codegen_gcc/build_system/src/build.rs b/compiler/rustc_codegen_gcc/build_system/src/build.rs index 839c762fed742..bcd386bfebdae 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/build.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/build.rs @@ -132,6 +132,24 @@ pub fn build_sysroot(env: &HashMap, config: &ConfigInfo) -> Resu // Builds libs let mut rustflags = env.get("RUSTFLAGS").cloned().unwrap_or_default(); + + // Record the sysroot sources under the path the `rust-src` component uses, which is where + // rustc looks for them to turn a sysroot span into `/rustc/$hash`. Without this, ui tests + // print the build path where they expect `$SRC_DIR`. + let sysroot_source_dir = lib_path.join("rustlib/src/rust/library"); + rustflags.push_str(&format!( + " --remap-path-prefix={library_dir}={sysroot_source_dir}", + library_dir = std::path::absolute(&library_dir) + .map_err(|error| format!( + "Failed to get the absolute path of the sysroot sources: {error:?}" + ))? + .display(), + sysroot_source_dir = std::path::absolute(&sysroot_source_dir) + .map_err(|error| format!( + "Failed to get the absolute path of the sysroot sources: {error:?}" + ))? + .display(), + )); if config.sysroot_panic_abort { rustflags.push_str(" -Cpanic=abort -Zpanic-abort-tests"); } @@ -188,12 +206,33 @@ pub fn build_sysroot(env: &HashMap, config: &ConfigInfo) -> Resu // FIXME: should not use shell command! run_command(&[&"cp", &"-r", &dir_to_copy, &sysroot_path], None).map(|_| ()) }; - walk_dir( - library_dir.join(format!("target/{}/{}/deps", config.target_triple, channel)), - &mut copier.clone(), - &mut copier, - false, - )?; + let target_dir = library_dir.join(format!("target/{}/{}", config.target_triple, channel)); + let deps_dir = target_dir.join("deps"); + if deps_dir.is_dir() { + // Keep copying in the old directory just in case. + walk_dir(&deps_dir, &mut copier.clone(), &mut copier, false)?; + } else { + let build_dir = target_dir.join("build"); + walk_dir( + &build_dir, + &mut |package_dir: &Path| { + walk_dir( + package_dir, + &mut |unit_dir: &Path| { + let out_dir = unit_dir.join("out"); + if out_dir.is_dir() { + walk_dir(&out_dir, &mut copier.clone(), &mut copier.clone(), false)?; + } + Ok(()) + }, + &mut |_| Ok(()), + false, + ) + }, + &mut |_| Ok(()), + false, + )?; + } // Copy the source files to the sysroot (Rust for Linux needs this). let sysroot_src_path = start_dir.join("sysroot/lib/rustlib/src/rust"); @@ -227,7 +266,7 @@ fn build_codegen(args: &mut BuildArg) -> Result<(), String> { } run_command_with_output_and_env(&command, None, Some(&env))?; - args.config_info.setup(&mut env, false)?; + args.config_info.setup(&mut env, false, true)?; // We voluntarily ignore the error. let _ = fs::remove_dir_all("target/out"); diff --git a/compiler/rustc_codegen_gcc/build_system/src/clean.rs b/compiler/rustc_codegen_gcc/build_system/src/clean.rs index 43f01fdf35ecb..ec2092ee92ef5 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/clean.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/clean.rs @@ -74,7 +74,8 @@ fn clean_ui_tests() -> Result<(), String> { let path = Path::new(crate::BUILD_DIR) .join("rust/build/x86_64-unknown-linux-gnu/test/") .join(directory); - run_command(&[&"find", &path, &"-name", &"stamp", &"-delete"], None)?; + // The directory might not exist, so ignore the error. + let _ = run_command(&[&"find", &path, &"-name", &"stamp", &"-delete"], None); } Ok(()) } diff --git a/compiler/rustc_codegen_gcc/build_system/src/clippy.rs b/compiler/rustc_codegen_gcc/build_system/src/clippy.rs new file mode 100644 index 0000000000000..813d4b9141e1c --- /dev/null +++ b/compiler/rustc_codegen_gcc/build_system/src/clippy.rs @@ -0,0 +1,62 @@ +use std::path::Path; + +use crate::utils::{run_command_with_output, run_tool_and_install_it_if_not_present}; + +fn show_usage() { + println!( + r#" +`clippy` command help: + + --help : Show this help"# + ); +} + +pub fn run() -> Result<(), String> { + // We skip binary name and the `info` command. + let args = std::env::args().skip(2); + #[allow(clippy::never_loop)] + for arg in args { + match arg.as_str() { + "--help" => { + show_usage(); + return Ok(()); + } + _ => return Err(format!("Unknown option {arg}")), + } + } + + run_tool_and_install_it_if_not_present(&[ + &"cargo", + &"clippy", + &"--all-targets", + &"--", + &"-D", + &"warnings", + ])?; + run_command_with_output( + &[ + &"cargo", + &"clippy", + &"--all-targets", + &"--no-default-features", + &"--", + &"-D", + &"warnings", + ], + Some(Path::new(".")), + )?; + run_command_with_output( + &[ + &"cargo", + &"clippy", + &"--all-targets", + &"--manifest-path", + &"build_system/Cargo.toml", + &"--", + &"-D", + &"warnings", + ], + Some(Path::new(".")), + )?; + Ok(()) +} diff --git a/compiler/rustc_codegen_gcc/build_system/src/config.rs b/compiler/rustc_codegen_gcc/build_system/src/config.rs index 8eb6d8f019e1c..fd78f691d1657 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/config.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/config.rs @@ -314,6 +314,7 @@ impl ConfigInfo { &mut self, env: &mut HashMap, use_system_gcc: bool, + generate_out_dir: bool, ) -> Result<(), String> { env.insert("CARGO_INCREMENTAL".to_string(), "0".to_string()); @@ -444,12 +445,12 @@ impl ConfigInfo { self.rustc_command = vec![rustc]; self.rustc_command.extend_from_slice(&rustflags); - self.rustc_command.extend_from_slice(&[ - "-L".to_string(), - format!("crate={}", self.cargo_target_dir), - "--out-dir".to_string(), - self.cargo_target_dir.clone(), - ]); + self.rustc_command + .extend_from_slice(&["-L".to_string(), format!("crate={}", self.cargo_target_dir)]); + if generate_out_dir { + self.rustc_command + .extend_from_slice(&["--out-dir".to_string(), self.cargo_target_dir.clone()]); + } if !env.contains_key("RUSTC_LOG") { env.insert("RUSTC_LOG".to_string(), "warn".to_string()); diff --git a/compiler/rustc_codegen_gcc/build_system/src/fmt.rs b/compiler/rustc_codegen_gcc/build_system/src/fmt.rs index 91535f217e351..dc1ca1d3e82ae 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/fmt.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/fmt.rs @@ -1,7 +1,7 @@ use std::ffi::OsStr; use std::path::Path; -use crate::utils::{run_command_with_output, walk_dir}; +use crate::utils::{run_command_with_output, run_tool_and_install_it_if_not_present, walk_dir}; fn show_usage() { println!( @@ -31,8 +31,9 @@ pub fn run() -> Result<(), String> { let cmd: &[&dyn AsRef] = if check { &[&"cargo", &"fmt", &"--check"] } else { &[&"cargo", &"fmt"] }; - run_command_with_output(cmd, Some(Path::new(".")))?; + run_tool_and_install_it_if_not_present(cmd)?; run_command_with_output(cmd, Some(Path::new("build_system")))?; + run_command_with_output(cmd, Some(Path::new("build_system/asm-tester")))?; run_rustfmt_recursively("tests/run", check) } diff --git a/compiler/rustc_codegen_gcc/build_system/src/fuzz/reduce.rs b/compiler/rustc_codegen_gcc/build_system/src/fuzz/reduce.rs index 20715ab0e7c48..d1ed8fabbaf11 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/fuzz/reduce.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/fuzz/reduce.rs @@ -203,7 +203,7 @@ fn block_abort(file: &mut Vec, path: &PathBuf, cache: &mut ResultCache) // ..and insert an unconditional call to abort. file_copy.insert( block_starts, - "Call(tmp = core::intrinsics::abort(), ReturnTo(bb1), UnwindUnreachable())\n" + "Call(tmp = core::intrinsics::abort_immediate(), ReturnTo(bb1), UnwindUnreachable())\n" .to_string(), ); file_copy.insert(block_starts, "let tmp = ();\n".to_string()); diff --git a/compiler/rustc_codegen_gcc/build_system/src/main.rs b/compiler/rustc_codegen_gcc/build_system/src/main.rs index ae975c94fff25..37b1f306817fd 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/main.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/main.rs @@ -3,6 +3,7 @@ use std::{env, process}; mod abi_test; mod build; mod clean; +mod clippy; mod clone_gcc; mod config; mod fmt; @@ -12,6 +13,7 @@ mod prepare; mod rust_tools; mod rustc_info; mod test; +mod todo; mod utils; const BUILD_DIR: &str = "build"; @@ -24,43 +26,67 @@ macro_rules! arg_error { }}; } -fn usage() { - println!( - "\ +macro_rules! commands_decl { + ($($variant:ident: $doc_name:literal => $doc:literal ,)+) => { + enum Command { + $($variant),+ + } + + impl<'a> From> for Command { + fn from(arg: Option<&'a str>) -> Self { + match arg { + $(Some($doc_name) => Self::$variant,)+ + Some("--help") => { + usage(); + process::exit(0); + } + Some(flag) if flag.starts_with('-') => arg_error!("Expected command found flag {}", flag), + Some(command) => arg_error!("Unknown command {}", command), + None => { + usage(); + process::exit(0); + } + } + } + } + + fn usage() { + println!("\ rustc_codegen_gcc build system Usage: build_system [command] [options] Options: - --help : Displays this help message. + --help : Displays this help message. + +Commands:", + ); + let mut commands = vec![$(($doc_name, $doc),)+]; + let longest = commands.iter().map(|(name, _)| name.len()).max().unwrap(); -Commands: - cargo : Executes a cargo command. - rustc : Compiles the program using the GCC compiler. - clean : Cleans the build directory, removing all compiled files and artifacts. - prepare : Prepares the environment for building, including fetching dependencies and setting up configurations. - build : Compiles the project. - test : Runs tests for the project. - info : Displays information about the build environment and project configuration. - clone-gcc : Clones the GCC compiler from a specified source. - fmt : Runs rustfmt - fuzz : Fuzzes `cg_gcc` using rustlantis - abi-test : Runs the abi-cafe test suite on the codegen, checking for ABI compatibility with LLVM" - ); + commands.sort_unstable_by(|a, b| a.0.cmp(b.0)); + for (name, doc) in commands { + let spacing = std::iter::repeat(' ').take(longest - name.len() + 1).collect::(); + eprintln!(" {name}{spacing}: {doc}."); + } + } + } } -pub enum Command { - Cargo, - Clean, - CloneGcc, - Prepare, - Build, - Rustc, - Test, - Info, - Fmt, - Fuzz, - AbiTest, +commands_decl! { + Cargo: "cargo" => "Executes a cargo command", + Clean: "clean" => "Cleans the build directory, removing all compiled files and artifacts", + Clippy: "clippy" => "Runs clippy", + CloneGcc: "clone-gcc" => "Clones the GCC compiler from a specified source", + Prepare: "prepare" => "Prepares the environment for building, including fetching dependencies and setting up configurations", + Build: "build" => "Compiles the project", + Rustc: "rustc" => "Compiles the program using the GCC compiler", + Test: "test" => "Runs tests for the project", + Info: "info" => "Displays information about the build environment and project configuration", + Fmt: "fmt" => "Runs rustfmt", + Fuzz: "fuzz" => "Fuzzes `cg_gcc` using `rustlantis`", + AbiTest: "abi-test" => "Runs the abi-cafe test suite on the codegen, checking for ABI compatibility with LLVM", + CheckTodo: "check-todo" => "Checks todo in the project", } fn main() { @@ -70,31 +96,7 @@ fn main() { } } - let command = match env::args().nth(1).as_deref() { - Some("cargo") => Command::Cargo, - Some("rustc") => Command::Rustc, - Some("clean") => Command::Clean, - Some("prepare") => Command::Prepare, - Some("build") => Command::Build, - Some("test") => Command::Test, - Some("info") => Command::Info, - Some("clone-gcc") => Command::CloneGcc, - Some("abi-test") => Command::AbiTest, - Some("fmt") => Command::Fmt, - Some("fuzz") => Command::Fuzz, - Some("--help") => { - usage(); - process::exit(0); - } - Some(flag) if flag.starts_with('-') => arg_error!("Expected command found flag {}", flag), - Some(command) => arg_error!("Unknown command {}", command), - None => { - usage(); - process::exit(0); - } - }; - - if let Err(e) = match command { + if let Err(e) = match Command::from(env::args().nth(1).as_deref()) { Command::Cargo => rust_tools::run_cargo(), Command::Rustc => rust_tools::run_rustc(), Command::Clean => clean::run(), @@ -106,8 +108,13 @@ fn main() { Command::Fmt => fmt::run(), Command::Fuzz => fuzz::run(), Command::AbiTest => abi_test::run(), + Command::Clippy => clippy::run(), + Command::CheckTodo => todo::run(), } { eprintln!("Command failed to run: {e}"); - process::exit(1); + // CI needs to tell a build system error apart from the test failures some suites expect. + let exit_code = + if e == test::TESTS_FAILED_ERROR { test::TESTS_FAILED_EXIT_CODE } else { 1 }; + process::exit(exit_code); } } diff --git a/compiler/rustc_codegen_gcc/build_system/src/rust_tools.rs b/compiler/rustc_codegen_gcc/build_system/src/rust_tools.rs index b1faa27acc4a2..1b50f11c3d324 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/rust_tools.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/rust_tools.rs @@ -72,7 +72,7 @@ impl RustcTools { let mut env: HashMap = std::env::vars().collect(); let mut config = ConfigInfo::default(); - config.setup(&mut env, false)?; + config.setup(&mut env, false, false)?; let toolchain = get_toolchain()?; let toolchain_version = rustc_toolchain_version_info(&toolchain)?; diff --git a/compiler/rustc_codegen_gcc/build_system/src/test.rs b/compiler/rustc_codegen_gcc/build_system/src/test.rs index 2475a3a6a7155..98111bb44b4f6 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/test.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/test.rs @@ -1,18 +1,29 @@ -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::ffi::OsStr; -use std::fs::{File, remove_dir_all}; +use std::fs::{File, read_to_string, remove_dir_all}; use std::io::{BufRead, BufReader}; use std::path::{Path, PathBuf}; use std::str::FromStr; +use boml::Toml; + use crate::build; use crate::config::{Channel, ConfigInfo}; use crate::utils::{ create_dir, get_sysroot_dir, get_toolchain, git_clone, git_clone_root_dir, remove_file, - run_command, run_command_with_env, run_command_with_output_and_env, rustc_version_info, - split_args, walk_dir, + run_command, run_command_with_env, run_command_with_output_and_env, + run_command_with_output_and_env_no_err, rustc_version_info, split_args, walk_dir, }; +/// Exit code of `y.sh test` when the tests ran and reported failures, as opposed to the build +/// system failing to run them at all. CI relies on the distinction: the suites of known-failing +/// tests are expected to report failures, but a broken build system must never pass silently. +pub const TESTS_FAILED_EXIT_CODE: i32 = 2; + +/// The error returned for that case. `main` compares against it to pick the exit code, so no other +/// error may use this message. +pub const TESTS_FAILED_ERROR: &str = "the test suite reported failures"; + type Env = HashMap; type Runner = fn(&Env, &TestArg) -> Result<(), String>; type Runners = HashMap<&'static str, (&'static str, Runner)>; @@ -28,8 +39,12 @@ fn get_runners() -> Runners { ("Run failing ui pattern tests", test_failing_ui_pattern_tests), ); runners.insert("--test-failing-rustc", ("Run failing rustc tests", test_failing_rustc)); + runners.insert("--run-ui-tests", ("Run specified rustc UI tests", run_ui_tests)); runners.insert("--projects", ("Run the tests of popular crates", test_projects)); runners.insert("--test-libcore", ("Run libcore tests", test_libcore)); + runners.insert("--test-release-libcore", ("Run libcore tests", test_release_libcore)); + runners.insert("--test-libcore-doctests", ("Run libcore doc-tests", test_libcore_doctests)); + runners.insert("--alloc-tests", ("Run alloc tests", test_alloc)); runners.insert("--clean", ("Empty cargo target directory", clean)); runners.insert("--build-sysroot", ("Build sysroot", build_sysroot)); runners.insert("--std-tests", ("Run std tests", std_tests)); @@ -42,8 +57,10 @@ fn get_runners() -> Runners { ); runners.insert("--extended-regex-tests", ("Run extended regex tests", extended_regex_tests)); runners.insert("--mini-tests", ("Run mini tests", mini_tests)); + runners.insert("--gcc-asm-tests", ("Run cg_gcc asm tests", test_asm)); runners.insert("--cargo-tests", ("Run cargo tests", cargo_tests)); runners.insert("--no-builtins-tests", ("Test #![no_builtins] attribute", no_builtins_tests)); + runners.insert("--stdarch-tests", ("Run stdarch tests", test_stdarch as Runner)); runners } @@ -230,6 +247,8 @@ fn mini_tests(env: &Env, args: &TestArg) -> Result<(), String> { } .to_string(); let mut command = args.config_info.rustc_command_vec(); + // Implicitly passed by rustc's build system + command.push(&"-Zforce-unstable-if-unmarked"); command.extend_from_slice(&[ &"example/mini_core.rs", &"--crate-name", @@ -244,6 +263,8 @@ fn mini_tests(env: &Env, args: &TestArg) -> Result<(), String> { // FIXME: create a function "display_if_not_quiet" or something along the line. println!("[BUILD] example"); let mut command = args.config_info.rustc_command_vec(); + // Implicitly passed by rustc's build system + command.push(&"-Zforce-unstable-if-unmarked"); command.extend_from_slice(&[ &"example/example.rs", &"--crate-type", @@ -256,6 +277,8 @@ fn mini_tests(env: &Env, args: &TestArg) -> Result<(), String> { // FIXME: create a function "display_if_not_quiet" or something along the line. println!("[AOT] mini_core_hello_world"); let mut command = args.config_info.rustc_command_vec(); + // Implicitly passed by rustc's build system + command.push(&"-Zforce-unstable-if-unmarked"); command.extend_from_slice(&[ &"example/mini_core_hello_world.rs", &"--crate-name", @@ -505,6 +528,26 @@ fn std_tests(env: &Env, args: &TestArg) -> Result<(), String> { Ok(()) } +fn get_llvm_filecheck(env: &Env) -> Result { + match run_command_with_env( + &[ + &"bash", + &"-c", + &"which FileCheck-10 || \ + which FileCheck-11 || \ + which FileCheck-12 || \ + which FileCheck-13 || \ + which FileCheck-14 || \ + which FileCheck", + ], + None, + Some(env), + ) { + Ok(cmd) => Ok(String::from_utf8_lossy(&cmd.stdout).trim().to_string()), + Err(_) => Err("Failed to retrieve LLVM FileCheck, ignoring...".to_owned()), + } +} + fn setup_rustc(env: &mut Env, args: &TestArg) -> Result { let toolchain = format!( "+{channel}-{host}", @@ -548,23 +591,10 @@ fn setup_rustc(env: &mut Env, args: &TestArg) -> Result { let rustc = rustc.trim().to_owned(); if rustc.is_empty() { Err("`rustc` path is empty".to_string()) } else { Ok(rustc) } })?; - let llvm_filecheck = match run_command_with_env( - &[ - &"bash", - &"-c", - &"which FileCheck-10 || \ - which FileCheck-11 || \ - which FileCheck-12 || \ - which FileCheck-13 || \ - which FileCheck-14 || \ - which FileCheck", - ], - rust_dir, - Some(env), - ) { - Ok(cmd) => String::from_utf8_lossy(&cmd.stdout).to_string(), - Err(_) => { - eprintln!("Failed to retrieve LLVM FileCheck, ignoring..."); + let llvm_filecheck = match get_llvm_filecheck(env) { + Ok(l) => l, + Err(error) => { + eprintln!("{error}"); // FIXME: the test tests/run-make/no-builtins-attribute will fail if we cannot find // FileCheck. String::new() @@ -634,7 +664,7 @@ fn asm_tests(env: &Env, args: &TestArg) -> Result<(), String> { &"0", &"--set", &"build.compiletest-allow-stage0=true", - &"tests/assembly-llvm/asm", + &"tests/assembly-gcc/asm", &"--compiletest-rustc-args", &rustc_args, ], @@ -700,40 +730,109 @@ where // echo "[BUILD] sysroot in release mode" // ./build_sysroot/build_sysroot.sh --release +struct Project { + url: &'static str, + /// Arguments added to both the `cargo build` and the `cargo test` invocations. + cargo_arguments: &'static [&'static str], + /// Arguments forwarded to the test harness by `cargo test`. + test_harness_arguments: &'static [&'static str], + /// Variables added to the environment of both invocations. + environment_variables: &'static [(&'static str, &'static str)], +} + +impl Project { + const fn new(url: &'static str) -> Self { + Self { url, cargo_arguments: &[], test_harness_arguments: &[], environment_variables: &[] } + } + + const fn cargo_arguments(mut self, arguments: &'static [&'static str]) -> Self { + self.cargo_arguments = arguments; + self + } + + const fn test_harness_arguments(mut self, arguments: &'static [&'static str]) -> Self { + self.test_harness_arguments = arguments; + self + } + + const fn environment_variables( + mut self, + variables: &'static [(&'static str, &'static str)], + ) -> Self { + self.environment_variables = variables; + self + } +} + fn test_projects(env: &Env, args: &TestArg) -> Result<(), String> { let projects = [ - //"https://gitlab.gnome.org/GNOME/librsvg", // FIXME: doesn't compile in the CI since the - // version of cairo and other libraries is too old. - "https://github.com/rust-random/getrandom", - "https://github.com/BurntSushi/memchr", - "https://github.com/dtolnay/itoa", - "https://github.com/rust-lang/cfg-if", - //"https://github.com/rust-lang-nursery/lazy-static.rs", // FIXME: re-enable when the - //failing test is fixed upstream. - //"https://github.com/marshallpierce/rust-base64", // FIXME: one test is OOM-killed. - // FIXME: ignore the base64 test that is OOM-killed. - //"https://github.com/time-rs/time", // FIXME: one test fails (https://github.com/time-rs/time/issues/719). - "https://github.com/rust-lang/log", - "https://github.com/bitflags/bitflags", - //"https://github.com/serde-rs/serde", // FIXME: one test fails. - //"https://github.com/rayon-rs/rayon", // FIXME: very slow, only run on master? - //"https://github.com/rust-lang/cargo", // FIXME: very slow, only run on master? + // The reference images assume the exact cairo, pango and freetype that librsvg pins in its + // own CI; this one renders text decorations a pixel off with the versions Ubuntu ships. + Project::new("https://gitlab.gnome.org/GNOME/librsvg") + .test_harness_arguments(&["--skip", "tests::svg1_1_text_text_03_b_svg", "--exact"]) + // A debug build of librsvg needs about 5 MB of stack per `cargo test` thread to reach + // its maximum layer nesting depth; librsvg's own CI sets the same value. + .environment_variables(&[("RUST_MIN_STACK", "8388608")]), + Project::new("https://github.com/rust-random/getrandom"), + Project::new("https://github.com/BurntSushi/memchr"), + Project::new("https://github.com/dtolnay/itoa"), + Project::new("https://github.com/rust-lang/cfg-if"), + // The `ui` test compares against the diagnostics of the compiler it was blessed with, so it + // fails on the nightly we use no matter which backend produces the code. + Project::new("https://github.com/rust-lang-nursery/lazy-static.rs") + .test_harness_arguments(&["--skip", "ui", "--exact"]), + Project::new("https://github.com/marshallpierce/rust-base64"), + // The test suite refuses to build unless every feature is enabled; it otherwise spawns a + // nested `cargo test --all-features` which would not use this backend. + Project::new("https://github.com/time-rs/time").cargo_arguments(&["--all-features"]), + Project::new("https://github.com/rust-lang/log"), + Project::new("https://github.com/bitflags/bitflags"), + Project::new("https://github.com/serde-rs/serde"), + Project::new("https://github.com/rayon-rs/rayon"), + // FIXME: too slow to run in the CI: the release build alone takes 46 minutes and the + // `cargo` crate itself needs 5.4 GB of memory in a single rustc process. + //Project::new("https://github.com/rust-lang/cargo"), ]; let mut env = env.clone(); let rustflags = format!("{} --cap-lints allow", env.get("RUSTFLAGS").cloned().unwrap_or_default()); env.insert("RUSTFLAGS".to_string(), rustflags); - let run_tests = |projects_path, iter: &mut dyn Iterator| -> Result<(), String> { - for project in iter { - let clone_result = git_clone_root_dir(project, projects_path, true)?; - let repo_path = Path::new(&clone_result.repo_dir); - run_cargo_command(&[&"build", &"--release"], Some(repo_path), &env, args)?; - run_cargo_command(&[&"test"], Some(repo_path), &env, args)?; - } + let run_tests = + |projects_path, iter: &mut dyn Iterator| -> Result<(), String> { + for project in iter { + let clone_result = git_clone_root_dir(project.url, projects_path, true)?; + let repo_path = Path::new(&clone_result.repo_dir); + + let mut project_environment = env.clone(); + for (name, value) in project.environment_variables { + project_environment.insert(name.to_string(), value.to_string()); + } - Ok(()) - }; + let mut build_command: Vec<&dyn AsRef> = vec![&"build", &"--release"]; + build_command.extend( + project.cargo_arguments.iter().map(|argument| argument as &dyn AsRef), + ); + run_cargo_command(&build_command, Some(repo_path), &project_environment, args)?; + + let mut test_command: Vec<&dyn AsRef> = vec![&"test"]; + test_command.extend( + project.cargo_arguments.iter().map(|argument| argument as &dyn AsRef), + ); + if !project.test_harness_arguments.is_empty() { + test_command.push(&"--"); + test_command.extend( + project + .test_harness_arguments + .iter() + .map(|argument| argument as &dyn AsRef), + ); + } + run_cargo_command(&test_command, Some(repo_path), &project_environment, args)?; + } + + Ok(()) + }; let projects_path = Path::new("projects"); create_dir(projects_path)?; @@ -755,10 +854,117 @@ fn test_projects(env: &Env, args: &TestArg) -> Result<(), String> { } fn test_libcore(env: &Env, args: &TestArg) -> Result<(), String> { + test_libcore_inner(env, args, false) +} + +fn test_release_libcore(env: &Env, args: &TestArg) -> Result<(), String> { + test_libcore_inner(env, args, true) +} + +fn test_libcore_inner(env: &Env, args: &TestArg, release: bool) -> Result<(), String> { // FIXME: create a function "display_if_not_quiet" or something along the line. println!("[TEST] libcore"); let path = get_sysroot_dir().join("sysroot_src/library/coretests"); let _ = remove_dir_all(path.join("target")); + let mut command: Vec<&dyn AsRef> = vec![&"test"]; + if release { + command.push(&"--release"); + } + run_cargo_command(&command, Some(&path), env, args)?; + Ok(()) +} + +/// Returns the edition declared in the manifest of the given library crate, so that the doctests +/// are run with the same edition as the crate they are extracted from. +fn get_crate_edition(crate_dir: &Path) -> Result { + let manifest_path = crate_dir.join("Cargo.toml"); + let content = read_to_string(&manifest_path) + .map_err(|error| format!("Failed to read `{}`: {error:?}", manifest_path.display()))?; + let manifest = Toml::parse(&content) + .map_err(|error| format!("Failed to parse `{}`: {error:?}", manifest_path.display()))?; + manifest + .get_table("package") + .and_then(|package| package.get_string("edition")) + .map(|edition| edition.to_string()) + .map_err(|error| { + format!("Failed to get `package.edition` from `{}`: {error:?}", manifest_path.display()) + }) +} + +fn test_libcore_doctests(env: &Env, args: &TestArg) -> Result<(), String> { + // FIXME: create a function "display_if_not_quiet" or something along the line. + println!("[TEST] libcore doctests"); + + let library_dir = get_sysroot_dir().join("sysroot_src/library"); + let edition = get_crate_edition(&library_dir.join("core"))?; + // `rustdoc` is called directly instead of through `cargo test --doc` because `cargo` builds its + // own `core` and passes it with `--extern`, which then conflicts with the `core` of the sysroot + // the doctests are linked against ("duplicate lang item" errors). + let toolchain = get_toolchain()?; + let toolchain_arg = format!("+{toolchain}"); + let rustflags = split_args(&env.get("RUSTFLAGS").cloned().unwrap_or_default())?; + // `-Zunstable-options` is needed for `--test-args`. + let mut command: Vec<&dyn AsRef> = vec![ + &"rustdoc", + &toolchain_arg, + &"--test", + &"core/src/lib.rs", + &"--crate-name", + &"core", + &"--crate-type", + &"lib", + &"--edition", + &edition, + &"-Zunstable-options", + // FIXME: remove `-Zforce-unstable-if-unmarked` once the doctest of + // `core::io::ErrorKind`'s `Display` impl declares `#![feature(core_io)]` upstream: without + // it, that doctest fails to compile with `E0658` on any backend. + &"-Zforce-unstable-if-unmarked", + // FIXME: one test cannot compile due to an upstream bug in the new trait solver. + &"-Znext-solver=coherence", + ]; + for flag in &rustflags { + command.push(flag); + } + // Additional arguments are forwarded to the test harness, so that a subset of the doctests can + // be run. + let test_args = + args.test_args.iter().map(|test_arg| format!("--test-args={test_arg}")).collect::>(); + for test_arg in &test_args { + command.push(test_arg); + } + run_command_with_output_and_env(&command, Some(&library_dir), Some(env))?; + Ok(()) +} + +fn test_stdarch(env: &Env, args: &TestArg) -> Result<(), String> { + println!("[TEST] stdarch"); + let manifest_path = get_sysroot_dir().join("sysroot_src/library/stdarch/Cargo.toml"); + let mut env = env.clone(); + + // `config.setup` already baked `CG_RUSTFLAGS` into `RUSTFLAGS`, so append the lint-allow to + // `RUSTFLAGS` directly (which `run_cargo_command` also propagates to `RUSTDOCFLAGS`). + let rustflags = env.get("RUSTFLAGS").cloned().unwrap_or_default(); + env.insert( + "RUSTFLAGS".to_string(), + format!("{rustflags} -Ainternal_features").trim().to_owned(), + ); + env.insert("TARGET".to_string(), args.config_info.target_triple.clone()); + + let mut command: Vec<&dyn AsRef> = + vec![&"test", &"--manifest-path", &manifest_path, &"--"]; + for test_name in &args.test_args { + command.push(test_name); + } + run_cargo_command(&command, None, &env, args)?; + Ok(()) +} + +fn test_alloc(env: &Env, args: &TestArg) -> Result<(), String> { + // FIXME: create a function "display_if_not_quiet" or something along the line. + println!("[TEST] alloc"); + let path = get_sysroot_dir().join("sysroot_src/library/alloctests"); + let _ = remove_dir_all(path.join("target")); // FIXME(antoyo): run in release mode when we fix the failures. run_cargo_command(&[&"test"], Some(&path), env, args)?; Ok(()) @@ -908,7 +1114,6 @@ fn contains_ui_error_patterns(file_path: &Path, keep_lto_tests: bool) -> Result< "//@ known-bug", "-Cllvm-args", "//~", - "thread", ] .iter() .any(|check| line.contains(check)) @@ -934,10 +1139,7 @@ fn contains_ui_error_patterns(file_path: &Path, keep_lto_tests: bool) -> Result< eprintln!("nothing found for {file_path:?}"); } // The files in this directory contain errors. - if file_path.contains("/error-emitter/") { - return Ok(true); - } - Ok(false) + Ok(file_path.contains("/error-emitter/")) } // # Parameters @@ -947,6 +1149,8 @@ fn contains_ui_error_patterns(file_path: &Path, keep_lto_tests: bool) -> Result< // * `prepare_files_callback`: A callback function that prepares the files needed for the test. Its used to remove/retain tests giving Error to run various rust test suits. // * `run_error_pattern_test`: A boolean that determines whether to run only error pattern tests. // * `test_type`: A string that indicates the type of the test being run. +// * `retained_tests_list_path`: The list of tests that `prepare_files_callback` retained, if any. +// It is checked against the tests remaining after the filtering to report dead lines. // fn test_rustc_inner( env: &Env, @@ -954,7 +1158,7 @@ fn test_rustc_inner( prepare_files_callback: F, run_error_pattern_test: bool, test_type: &str, - run_ignored_tests: bool, + retained_tests_list_path: Option<&str>, ) -> Result<(), String> where F: Fn(&Path) -> Result, @@ -970,74 +1174,57 @@ where } if test_type == "ui" { - if run_error_pattern_test { - // After we removed the error tests that are known to panic with rustc_codegen_gcc, we now remove the passing tests since this runs the error tests. - walk_dir( - rust_path.join("tests/ui"), - &mut |_dir| Ok(()), - &mut |file_path| { - if contains_ui_error_patterns(file_path, args.keep_lto_tests)? { - Ok(()) - } else { - remove_file(file_path).map_err(|e| e.to_string()) - } - }, - true, - )?; - } else { - walk_dir( - rust_path.join("tests/ui"), - &mut |dir| { - let dir_name = dir.file_name().and_then(|name| name.to_str()).unwrap_or(""); - if ["abi", "extern", "proc-macro", "threads-sendsync"].contains(&dir_name) { - remove_dir_all(dir).map_err(|error| { - format!("Failed to remove folder `{}`: {:?}", dir.display(), error) - })?; - } - Ok(()) - }, - &mut |_| Ok(()), - false, - )?; - - // These two functions are used to remove files that are known to not be working currently - // with the GCC backend to reduce noise. - fn dir_handling(keep_lto_tests: bool) -> impl Fn(&Path) -> Result<(), String> { - move |dir| { - if dir.file_name().map(|name| name == "auxiliary").unwrap_or(true) { - return Ok(()); - } - - walk_dir( - dir, - &mut dir_handling(keep_lto_tests), - &mut file_handling(keep_lto_tests), - false, - ) + // Each mode runs one half of the ui tests and removes the other: `run_error_pattern_test` + // runs the tests expected to error, the other mode runs the rest. Only `.rs` files outside + // `auxiliary` are tests, so the expected output and the auxiliary crates are left alone. + fn dir_handling( + keep_lto_tests: bool, + remove_error_pattern_tests: bool, + ) -> impl Fn(&Path) -> Result<(), String> { + move |dir| { + if dir.file_name().map(|name| name == "auxiliary").unwrap_or(true) { + return Ok(()); } + + walk_dir( + dir, + &mut dir_handling(keep_lto_tests, remove_error_pattern_tests), + &mut file_handling(keep_lto_tests, remove_error_pattern_tests), + false, + ) } + } - fn file_handling(keep_lto_tests: bool) -> impl Fn(&Path) -> Result<(), String> { - move |file_path| { - if !file_path.extension().map(|extension| extension == "rs").unwrap_or(false) { - return Ok(()); - } - let path_str = file_path.display().to_string().replace("\\", "/"); - if valid_ui_error_pattern_test(&path_str) { - return Ok(()); - } else if contains_ui_error_patterns(file_path, keep_lto_tests)? { - return remove_file(&file_path); - } - Ok(()) + fn file_handling( + keep_lto_tests: bool, + remove_error_pattern_tests: bool, + ) -> impl Fn(&Path) -> Result<(), String> { + move |file_path| { + if !file_path.extension().map(|extension| extension == "rs").unwrap_or(false) { + return Ok(()); + } + let path_str = file_path.display().to_string().replace("\\", "/"); + if valid_ui_error_pattern_test(&path_str) { + return Ok(()); } + if contains_ui_error_patterns(file_path, keep_lto_tests)? + == remove_error_pattern_tests + { + return remove_file(file_path); + } + Ok(()) } + } - walk_dir( - rust_path.join("tests/ui"), - &mut dir_handling(args.keep_lto_tests), - &mut file_handling(args.keep_lto_tests), - false, - )?; + let remove_error_pattern_tests = !run_error_pattern_test; + walk_dir( + rust_path.join("tests/ui"), + &mut dir_handling(args.keep_lto_tests, remove_error_pattern_tests), + &mut file_handling(args.keep_lto_tests, remove_error_pattern_tests), + false, + )?; + if let Some(retained_tests_list_path) = retained_tests_list_path { + check_for_dead_listed_tests(&rust_path, retained_tests_list_path)?; } let nb_parts = args.nb_parts.unwrap_or(0); if nb_parts > 0 { @@ -1097,7 +1284,7 @@ where env.get_mut("RUSTFLAGS").unwrap().clear(); let test_dir = format!("tests/{test_type}"); - let mut command: Vec<&dyn AsRef> = vec![ + let command: Vec<&dyn AsRef> = vec![ &"./x.py", &"test", &"--run", @@ -1112,19 +1299,85 @@ where &"--bypass-ignore-backends", ]; - if run_ignored_tests { - command.push(&"--"); - command.push(&"--ignored"); + run_test_command(&command, &rust_path, &env) +} + +/// Reads the list of tests at `list_path`, checking that each of them still exists in the rust +/// checkout at `rust_path` and that none is listed twice. +/// +/// Both problems make a line a no-op: the test it names is neither kept nor removed, so the test +/// suite silently drifts away from what the list claims to describe. +fn read_test_list(rust_path: &Path, list_path: &str) -> Result, String> { + let content = std::fs::read_to_string(list_path) + .map_err(|error| format!("Failed to read `{list_path}`: {error:?}"))?; + + let mut tests = Vec::new(); + let mut seen = HashSet::new(); + let mut missing = Vec::new(); + let mut duplicated = Vec::new(); + + for line in content.lines().map(|line| line.trim()).filter(|line| !line.is_empty()) { + if !seen.insert(line) { + duplicated.push(line); + continue; + } + if !rust_path.join(line.trim_end_matches('/')).exists() { + missing.push(line); + } + tests.push(line.to_string()); + } + + if missing.is_empty() && duplicated.is_empty() { + return Ok(tests); } - run_command_with_output_and_env(&command, Some(&rust_path), Some(&env))?; - Ok(()) + let mut error = format!("`{list_path}` is out of date:\n"); + if !missing.is_empty() { + error.push_str(&format!( + "\nThese tests no longer exist in `{rust_path}`:\n{missing}\n", + rust_path = rust_path.display(), + missing = missing.join("\n"), + )); + } + if !duplicated.is_empty() { + error.push_str(&format!( + "\nThese tests are listed more than once:\n{}\n", + duplicated.join("\n") + )); + } + error.push_str( + "\nEvery line must name a test that exists, exactly once, otherwise the line filters \ + nothing. Delete the stale lines, or update them to the test's current path.", + ); + Err(error) +} + +/// Checks that every test listed in `list_path` survived the filtering done by +/// `contains_ui_error_patterns`. +fn check_for_dead_listed_tests(rust_path: &Path, list_path: &str) -> Result<(), String> { + let listed_tests = std::fs::read_to_string(list_path) + .map_err(|error| format!("Failed to read `{list_path}`: {error:?}"))?; + let dead_tests = listed_tests + .lines() + .map(|line| line.trim()) + .filter(|line| !line.is_empty() && !rust_path.join(line).exists()) + .collect::>(); + if dead_tests.is_empty() { + return Ok(()); + } + Err(format!( + "The following tests listed in `{list_path}` are filtered out before the tests are run, \ + so listing them has no effect:\n{}\n\nThis happens when a test contains an error pattern \ + (like `//~` or `//@ known-bug`), in which case it should be removed from `{list_path}`, \ + or when it uses LTO, in which case it should be moved to `tests/failing-lto-tests.txt`.", + dead_tests.join("\n") + )) } fn test_rustc(env: &Env, args: &TestArg) -> Result<(), String> { - test_rustc_inner(env, args, |_| Ok(false), false, "run-make", false)?; - test_rustc_inner(env, args, |_| Ok(false), false, "run-make-cargo", false)?; - test_rustc_inner(env, args, |_| Ok(false), false, "ui", false) + test_rustc_inner(env, args, |_| Ok(false), false, "run-make", None)?; + test_rustc_inner(env, args, |_| Ok(false), false, "run-make-cargo", None)?; + test_rustc_inner(env, args, |_| Ok(false), false, "ui", None) } fn test_failing_rustc(env: &Env, args: &TestArg) -> Result<(), String> { @@ -1134,7 +1387,7 @@ fn test_failing_rustc(env: &Env, args: &TestArg) -> Result<(), String> { retain_files_callback("tests/failing-run-make-tests.txt", "run-make"), false, "run-make", - true, + None, ); let run_make_cargo_result = test_rustc_inner( @@ -1142,8 +1395,8 @@ fn test_failing_rustc(env: &Env, args: &TestArg) -> Result<(), String> { args, retain_files_callback("tests/failing-run-make-tests.txt", "run-make-cargo"), false, - "run-make", - true, + "run-make-cargo", + None, ); let ui_result = test_rustc_inner( @@ -1152,36 +1405,51 @@ fn test_failing_rustc(env: &Env, args: &TestArg) -> Result<(), String> { retain_files_callback("tests/failing-ui-tests.txt", "ui"), false, "ui", - true, + Some("tests/failing-ui-tests.txt"), ); - run_make_result.and(run_make_cargo_result).and(ui_result) + combine_test_results([run_make_result, run_make_cargo_result, ui_result]) +} + +/// Combines the results of several test suites, letting a build system error win over a test +/// failure so that a broken build system is never reported to CI as the failures those suites +/// expect. +fn combine_test_results(results: [Result<(), String>; N]) -> Result<(), String> { + let mut tests_failed = false; + for result in results { + match result { + Ok(()) => {} + Err(error) if error == TESTS_FAILED_ERROR => tests_failed = true, + Err(error) => return Err(error), + } + } + if tests_failed { Err(TESTS_FAILED_ERROR.to_string()) } else { Ok(()) } } fn test_successful_rustc(env: &Env, args: &TestArg) -> Result<(), String> { test_rustc_inner( env, args, - remove_files_callback("tests/failing-ui-tests.txt", "ui"), + remove_files_callback("tests/failing-ui-tests.txt"), false, "ui", - false, + None, )?; test_rustc_inner( env, args, - remove_files_callback("tests/failing-run-make-tests.txt", "run-make"), + remove_files_callback("tests/failing-run-make-tests.txt"), false, "run-make", - false, + None, )?; test_rustc_inner( env, args, - remove_files_callback("tests/failing-run-make-tests.txt", "run-make-cargo"), + remove_files_callback("tests/failing-run-make-tests.txt"), false, "run-make-cargo", - false, + None, ) } @@ -1189,20 +1457,73 @@ fn test_failing_ui_pattern_tests(env: &Env, args: &TestArg) -> Result<(), String test_rustc_inner( env, args, - remove_files_callback("tests/failing-ice-tests.txt", "ui"), + remove_files_callback("tests/failing-ice-tests.txt"), true, "ui", - false, + None, ) } +fn run_ui_tests(env: &Env, args: &TestArg) -> Result<(), String> { + let mut env = env.clone(); + let rust_path = setup_rustc(&mut env, args)?; + + let extra = + if args.is_using_gcc_master_branch() { "" } else { " -Csymbol-mangling-version=v0" }; + + let rustc_args = format!( + "{test_flags} -Zcodegen-backend={backend} --sysroot {sysroot}{extra}", + test_flags = env.get("TEST_FLAGS").unwrap_or(&String::new()), + backend = args.config_info.cg_backend_path, + sysroot = args.config_info.sysroot_path, + extra = extra, + ); + + env.get_mut("RUSTFLAGS").unwrap().clear(); + + let mut command: Vec<&dyn AsRef> = vec![ + &"./x.py", + &"test", + &"--run", + &"always", + &"--stage", + &"0", + &"--set", + &"build.compiletest-allow-stage0=true", + &"--compiletest-rustc-args", + &rustc_args, + &"--bypass-ignore-backends", + &"--force-rerun", + ]; + + for test_name in &args.test_args { + command.push(test_name); + } + + run_test_command(&command, &rust_path, &env) +} + +/// Runs the command that actually runs a test suite, mapping its failure to `TESTS_FAILED_ERROR`. +fn run_test_command( + command: &[&dyn AsRef], + rust_path: &Path, + env: &Env, +) -> Result<(), String> { + if let Err(error) = run_command_with_output_and_env(command, Some(rust_path), Some(env)) { + // The failures themselves were already streamed to the console. + eprintln!("{error}"); + return Err(TESTS_FAILED_ERROR.to_string()); + } + Ok(()) +} + fn retain_files_callback<'a>( file_path: &'a str, test_type: &'a str, ) -> impl Fn(&Path) -> Result + 'a { move |rust_path| { - let files = std::fs::read_to_string(file_path).unwrap_or_default(); - let first_file_name = files.lines().next().unwrap_or(""); + let tests = read_test_list(rust_path, file_path)?; + let first_file_name = tests.first().map(String::as_str).unwrap_or(""); // If the first line ends with a `/`, we treat all lines in the file as a directory. if first_file_name.ends_with('/') { // Treat as directory @@ -1244,59 +1565,91 @@ fn retain_files_callback<'a>( } // Putting back only the failing ones. - if let Ok(files) = std::fs::read_to_string(file_path) { - for file in files.split('\n').map(|line| line.trim()).filter(|line| !line.is_empty()) { - run_command(&[&"git", &"checkout", &"--", &file], Some(rust_path))?; - } - } else { - println!("Failed to read `{file_path}`, not putting back failing {test_type} tests"); + for test in &tests { + run_command(&[&"git", &"checkout", &"--", test], Some(rust_path))?; } Ok(true) } } -fn remove_files_callback<'a>( - file_path: &'a str, - test_type: &'a str, -) -> impl Fn(&Path) -> Result + 'a { +fn remove_files_callback(file_path: &str) -> impl Fn(&Path) -> Result + '_ { move |rust_path| { - let files = std::fs::read_to_string(file_path).unwrap_or_default(); - let first_file_name = files.lines().next().unwrap_or(""); + let tests = read_test_list(rust_path, file_path)?; + let first_file_name = tests.first().map(String::as_str).unwrap_or(""); // If the first line ends with a `/`, we treat all lines in the file as a directory. if first_file_name.ends_with('/') { // Removing the failing tests. - if let Ok(files) = std::fs::read_to_string(file_path) { - for file in - files.split('\n').map(|line| line.trim()).filter(|line| !line.is_empty()) - { - let path = rust_path.join(file); - if let Err(e) = remove_dir_all(&path) { - println!("Failed to remove directory `{}`: {}", path.display(), e); - } - } - } else { - println!( - "Failed to read `{file_path}`, not putting back failing {test_type} tests" - ); + for test in &tests { + let path = rust_path.join(test); + remove_dir_all(&path).map_err(|error| { + format!("Failed to remove directory `{}`: {error}", path.display()) + })?; } } else { // Removing the failing tests. - if let Ok(files) = std::fs::read_to_string(file_path) { - for file in - files.split('\n').map(|line| line.trim()).filter(|line| !line.is_empty()) - { - let path = rust_path.join(file); - remove_file(&path)?; - } - } else { - println!("Failed to read `{file_path}`, not putting back failing ui tests"); + for test in &tests { + remove_file(&rust_path.join(test))?; } } Ok(true) } } +fn test_asm(env: &Env, args: &TestArg) -> Result<(), String> { + fn is_path_time_more_recent(ref_time: std::time::SystemTime, path: &str) -> bool { + std::fs::metadata(path) + .and_then(|metadata| metadata.modified()) + .is_ok_and(|time| ref_time < time) + } + + // FIXME: create a function "display_if_not_quiet" or something along the line. + println!("[TEST] cg_gcc assembly"); + let llvm_filecheck = get_llvm_filecheck(env)?; + + let target_dir = std::env::current_dir().unwrap().join("build_system/asm-tester/target"); + + // All this code is because `cargo` keeps recompiling this file, and we can't figure out why. + let binary_file_path = "build_system/asm-tester/target/debug/asm-tester"; + let mut need_recompilation = true; + if let Ok(metadata) = std::fs::metadata(binary_file_path) + && let Ok(ref_time) = metadata.modified() + && !is_path_time_more_recent(ref_time, "build_system/asm-tester/Cargo.toml") + && !is_path_time_more_recent(ref_time, "build_system/asm-tester/Cargo.lock") + && !is_path_time_more_recent(ref_time, "build_system/asm-tester/src/main.rs") + { + need_recompilation = false; + } + + if need_recompilation { + let build_asm_args: Vec<&dyn AsRef> = vec![ + &"cargo", + &"build", + &"--manifest-path", + &"build_system/asm-tester/Cargo.toml", + &"--target-dir", + &target_dir, + &"--", + ]; + run_command_with_output_and_env_no_err(&build_asm_args, Some(Path::new(".")), Some(env))?; + } + + let mut test_asm_args: Vec<&dyn AsRef> = vec![ + &"build_system/asm-tester/target/debug/asm-tester", + &"--llvm-filecheck", + &llvm_filecheck, + ]; + for test_arg in &args.test_args { + test_asm_args.push(&"--filter"); + test_asm_args.push(test_arg); + } + test_asm_args.push(&"--"); + for arg in args.config_info.rustc_command_vec().into_iter().skip(1) { + test_asm_args.push(arg); + } + run_command_with_output_and_env_no_err(&test_asm_args, Some(Path::new(".")), Some(env)) +} + fn run_all(env: &Env, args: &TestArg) -> Result<(), String> { clean(env, args)?; mini_tests(env, args)?; @@ -1308,6 +1661,7 @@ fn run_all(env: &Env, args: &TestArg) -> Result<(), String> { cargo_tests(env, args)?; no_builtins_tests(env, args)?; test_rustc(env, args)?; + test_asm(env, args)?; Ok(()) } @@ -1329,7 +1683,7 @@ pub fn run() -> Result<(), String> { return Ok(()); } - args.config_info.setup(&mut env, args.use_system_gcc)?; + args.config_info.setup(&mut env, args.use_system_gcc, true)?; if args.runners.is_empty() { run_all(&env, &args)?; @@ -1342,3 +1696,58 @@ pub fn run() -> Result<(), String> { Ok(()) } + +#[cfg(test)] +mod tests { + use super::*; + + fn write_test_list(directory: &Path, content: &str) -> PathBuf { + let list_path = directory.join("failing-tests.txt"); + std::fs::write(&list_path, content).unwrap(); + list_path + } + + #[test] + fn test_combine_test_results() { + let tests_failed = || Err(TESTS_FAILED_ERROR.to_string()); + let build_error = || Err("could not clone rust".to_string()); + + assert_eq!(combine_test_results([Ok(()), Ok(())]), Ok(())); + assert_eq!(combine_test_results([Ok(()), tests_failed()]), tests_failed()); + assert_eq!(combine_test_results([Ok(()), build_error()]), build_error()); + // A build system error wins, whichever suite reported it. + assert_eq!(combine_test_results([tests_failed(), build_error()]), build_error()); + assert_eq!(combine_test_results([build_error(), tests_failed()]), build_error()); + } + + #[test] + fn test_read_test_list() { + let rust_path = std::env::temp_dir().join("cg_gcc_read_test_list"); + let _ = remove_dir_all(&rust_path); + create_dir(rust_path.join("tests/ui")).unwrap(); + std::fs::write(rust_path.join("tests/ui/alive.rs"), "").unwrap(); + + let list_path = write_test_list(&rust_path, "\ntests/ui/alive.rs\n \n"); + let list_path = list_path.display().to_string(); + assert_eq!( + read_test_list(&rust_path, &list_path), + Ok(vec!["tests/ui/alive.rs".to_string()]) + ); + + write_test_list(&rust_path, "tests/ui/alive.rs\ntests/ui/gone.rs\n"); + let error = read_test_list(&rust_path, &list_path).unwrap_err(); + assert!(error.contains("no longer exist"), "{error}"); + assert!(error.contains("tests/ui/gone.rs"), "{error}"); + + write_test_list(&rust_path, "tests/ui/alive.rs\ntests/ui/alive.rs\n"); + let error = read_test_list(&rust_path, &list_path).unwrap_err(); + assert!(error.contains("listed more than once"), "{error}"); + assert!(error.contains("tests/ui/alive.rs"), "{error}"); + + // Directories are listed with a trailing `/`. + write_test_list(&rust_path, "tests/ui/\n"); + assert_eq!(read_test_list(&rust_path, &list_path), Ok(vec!["tests/ui/".to_string()])); + + remove_dir_all(&rust_path).unwrap(); + } +} diff --git a/compiler/rustc_codegen_gcc/build_system/src/todo.rs b/compiler/rustc_codegen_gcc/build_system/src/todo.rs new file mode 100644 index 0000000000000..5b89410844788 --- /dev/null +++ b/compiler/rustc_codegen_gcc/build_system/src/todo.rs @@ -0,0 +1,72 @@ +use std::ffi::OsStr; +use std::fs::File; +use std::io::{BufRead, BufReader}; +use std::path::{Path, PathBuf}; +use std::process::Command; + +const EXTENSIONS: &[&str] = + &["rs", "py", "js", "sh", "c", "cpp", "h", "md", "css", "ftl", "toml", "yml", "yaml"]; + +fn has_supported_extension(path: &Path) -> bool { + path.extension().is_some_and(|ext| EXTENSIONS.iter().any(|e| ext == OsStr::new(e))) +} + +fn list_tracked_files() -> Result, String> { + let output = Command::new("git") + .args(["ls-files", "-z"]) + .output() + .map_err(|e| format!("Failed to run `git ls-files`: {e}"))?; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + return Err(format!("`git ls-files` failed: {stderr}")); + } + + let mut files = Vec::new(); + for entry in output.stdout.split(|b| *b == 0) { + if entry.is_empty() { + continue; + } + let path = std::str::from_utf8(entry).unwrap(); + files.push(PathBuf::from(path)); + } + + Ok(files) +} + +pub(crate) fn run() -> Result<(), String> { + let files = list_tracked_files()?; + let mut error_count = 0; + // Avoid embedding the task marker in source so greps only find real occurrences. + let todo_marker = "todo".to_ascii_uppercase(); + + for file in files { + if !has_supported_extension(&file) { + continue; + } + + let file_handle = + File::open(&file).map_err(|e| format!("Failed to open {}: {e}", file.display()))?; + let reader = BufReader::new(file_handle); + + for (i, line) in reader.lines().enumerate() { + let line = line.map_err(|e| format!("Failed to read {}: {e}", file.display()))?; + let trimmed = line.trim(); + if trimmed.contains(&todo_marker) { + eprintln!( + "{}:{}: {} is used for tasks that should be done before merging a PR; if you want to leave a message in the codebase use FIXME", + file.display(), + i + 1, + todo_marker + ); + error_count += 1; + } + } + } + + if error_count == 0 { + return Ok(()); + } + + Err(format!("found {} {}(s)", error_count, todo_marker)) +} diff --git a/compiler/rustc_codegen_gcc/build_system/src/utils.rs b/compiler/rustc_codegen_gcc/build_system/src/utils.rs index 112322f8688c1..dda44e4d16d41 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/utils.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/utils.rs @@ -2,10 +2,11 @@ use std::collections::HashMap; use std::ffi::OsStr; use std::fmt::Debug; use std::fs; +use std::io::{BufReader, Read}; #[cfg(unix)] use std::os::unix::process::ExitStatusExt; use std::path::{Path, PathBuf}; -use std::process::{Command, ExitStatus, Output}; +use std::process::{Command, ExitStatus, Output, Stdio}; fn exec_command( input: &[&dyn AsRef], @@ -47,7 +48,7 @@ pub(crate) fn get_command_inner( command } -fn check_exit_status( +pub(crate) fn check_exit_status( input: &[&dyn AsRef], cwd: Option<&Path>, exit_status: ExitStatus, @@ -115,6 +116,30 @@ pub fn run_command_with_output( check_exit_status(input, cwd, exit_status, None, true) } +pub fn run_command_with_output_and_get_it( + input: &[&dyn AsRef], + cwd: Option<&Path>, +) -> Result<(ExitStatus, String), String> { + let mut child = get_command_inner(input, cwd, None) + .stderr(Stdio::piped()) + .spawn() + .map_err(|e| command_error(input, &cwd, e))?; + + let stderr = child.stderr.take().expect("Failed to capture stderr"); + let mut captured = String::new(); + BufReader::new(stderr).read_to_string(&mut captured).expect("failed to read stderr"); + + let status = child.wait().map_err(|e| command_error(input, &cwd, e))?; + #[cfg(unix)] + { + if let Some(signal) = status.signal() { + // In case the signal didn't kill the current process. + return Err(command_error(input, &cwd, format!("Process received signal {signal}"))); + } + } + Ok((status, captured)) +} + pub fn run_command_with_output_and_env( input: &[&dyn AsRef], cwd: Option<&Path>, @@ -124,7 +149,6 @@ pub fn run_command_with_output_and_env( check_exit_status(input, cwd, exit_status, None, true) } -#[cfg(not(unix))] pub fn run_command_with_output_and_env_no_err( input: &[&dyn AsRef], cwd: Option<&Path>, @@ -218,9 +242,9 @@ fn rustc_version_info_inner( } pub fn get_toolchain() -> Result { - let content = match fs::read_to_string("rust-toolchain") { + let content = match fs::read_to_string("rust-toolchain.toml") { Ok(content) => content, - Err(_) => return Err("No `rust-toolchain` file found".to_string()), + Err(_) => return Err("No `rust-toolchain.toml` file found".to_string()), }; match content .split('\n') @@ -235,7 +259,7 @@ pub fn get_toolchain() -> Result { .next() { Some(toolchain) => Ok(toolchain.to_string()), - None => Err("Couldn't find `channel` in `rust-toolchain` file".to_string()), + None => Err("Couldn't find `channel` in `rust-toolchain.toml` file".to_string()), } } @@ -419,6 +443,34 @@ pub fn get_sysroot_dir() -> PathBuf { Path::new(crate::BUILD_DIR).join("build_sysroot") } +pub fn run_tool_and_install_it_if_not_present(cmd: &[&dyn AsRef]) -> Result<(), String> { + let (exit_status, stderr) = run_command_with_output_and_get_it(cmd, Some(Path::new(".")))?; + if exit_status.success() { + return Ok(()); + } + let mut iter = stderr.split('\n'); + if let Some(line) = iter.next() + && line.contains("is not installed for the toolchain") + && let Some(line) = iter.next() + && line.contains("run `rustup component add") + && let Some(cmd) = line.split('`').nth(1) + && let Some(tool_name) = cmd.rsplit(' ').next() + { + println!("`{tool_name}` is not installed for this toolchain, installing it..."); + // A weird round-about way to get a `&&str` so I can get a `&dyn AsRef` but + // as long as it works... + let cmd = cmd.split(' ').collect::>(); + let cmd = cmd.iter().map(|s: &&str| s as &dyn AsRef).collect::>(); + run_command_with_output(cmd.as_slice(), Some(Path::new(".")))?; + } else { + // If the component is installed, then it's something else. In this case we fail like we + // should have and let the user handles the error. + return check_exit_status(cmd, Some(Path::new(".")), exit_status, None, true); + } + // We retry the command... + run_command_with_output(cmd, Some(Path::new("."))) +} + #[cfg(test)] mod tests { use super::*; diff --git a/compiler/rustc_codegen_gcc/clippy.toml b/compiler/rustc_codegen_gcc/clippy.toml new file mode 100644 index 0000000000000..4ba3642756d4c --- /dev/null +++ b/compiler/rustc_codegen_gcc/clippy.toml @@ -0,0 +1,3 @@ +disallowed-methods = [ + { path = "gccjit::types::Type::add_attribute", reason = "go through `type_::apply_struct_attributes` instead: an attribute set directly on a type would not be part of the `CodegenCx::struct_types` cache key, so it would silently change every other use of that type", allow-invalid = true }, +] diff --git a/compiler/rustc_codegen_gcc/doc/subtree.md b/compiler/rustc_codegen_gcc/doc/subtree.md index a81b6c9c74bdd..fcac399e46542 100644 --- a/compiler/rustc_codegen_gcc/doc/subtree.md +++ b/compiler/rustc_codegen_gcc/doc/subtree.md @@ -1,7 +1,7 @@ # git subtree sync `rustc_codegen_gcc` is a subtree of the rust compiler. As such, it needs to be -sync from time to time to ensure changes that happened on their side are also +synced from time to time to ensure changes that happened on their side are also included on our side. ### How to install a forked git-subtree @@ -41,6 +41,8 @@ cd ../rust git pull origin master git checkout -b subtree-update_cg_gcc_YYYY-MM-DD PATH="$HOME/bin:$PATH" ~/bin/git-subtree pull --prefix=compiler/rustc_codegen_gcc/ https://github.com/rust-lang/rustc_codegen_gcc.git master +# Don't forget to update the `gcc` submodule to the same version as the +# one in `rustc_codegen_gcc/libgccjit.version`. git push # Immediately merge the merge commit into cg_gcc to prevent merge conflicts when syncing from rust-lang/rust later. diff --git a/compiler/rustc_codegen_gcc/doc/tips.md b/compiler/rustc_codegen_gcc/doc/tips.md index ff92566d4a1ab..28964f66403bf 100644 --- a/compiler/rustc_codegen_gcc/doc/tips.md +++ b/compiler/rustc_codegen_gcc/doc/tips.md @@ -41,7 +41,7 @@ COLLECT_NO_DEMANGLE=1 ### How to use a custom-build rustc * Build the stage2 compiler (`rustup toolchain link debug-current build/x86_64-unknown-linux-gnu/stage2`). - * Clean and rebuild the codegen with `debug-current` in the file `rust-toolchain`. + * Clean and rebuild the codegen with `debug-current` in the file `rust-toolchain.toml`. ### How to use a custom sysroot source path @@ -58,7 +58,7 @@ If you wish to build a custom sysroot, pass the path of your sysroot source to ` ### How to generate GIMPLE If you need to check what gccjit is generating (GIMPLE), then take a look at how to -generate it in [gimple.md](./doc/gimple.md). +generate it in [gimple.md](./gimple.md). ### How to build a cross-compiling libgccjit diff --git a/compiler/rustc_codegen_gcc/example/alloc_example.rs b/compiler/rustc_codegen_gcc/example/alloc_example.rs index 9a0b46d5b221a..338057e1fcb87 100644 --- a/compiler/rustc_codegen_gcc/example/alloc_example.rs +++ b/compiler/rustc_codegen_gcc/example/alloc_example.rs @@ -20,12 +20,12 @@ extern "C" { #[panic_handler] fn panic_handler(_: &core::panic::PanicInfo<'_>) -> ! { - core::intrinsics::abort(); + core::intrinsics::abort_immediate(); } #[alloc_error_handler] fn alloc_error_handler(_: alloc::alloc::Layout) -> ! { - core::intrinsics::abort(); + core::intrinsics::abort_immediate(); } #[lang = "eh_personality"] diff --git a/compiler/rustc_codegen_gcc/example/mini_core.rs b/compiler/rustc_codegen_gcc/example/mini_core.rs index 2d5a29ceb8191..383fbfc678922 100644 --- a/compiler/rustc_codegen_gcc/example/mini_core.rs +++ b/compiler/rustc_codegen_gcc/example/mini_core.rs @@ -10,7 +10,8 @@ auto_traits, freeze_impls, pattern_types, - thread_local + thread_local, + rustc_private )] #![no_core] #![allow(dead_code, internal_features, ambiguous_wide_pointer_comparisons)] @@ -509,7 +510,7 @@ pub trait FnMut: FnOnce { pub fn panic(_msg: &'static str) -> ! { unsafe { libc::puts("Panicking\n\0" as *const str as *const u8); - intrinsics::abort(); + intrinsics::abort_immediate(); } } @@ -546,7 +547,7 @@ panic_const! { fn panic_cannot_unwind() -> ! { unsafe { libc::puts("Panicking\n\0" as *const str as *const u8); - intrinsics::abort(); + intrinsics::abort_immediate(); } } @@ -555,7 +556,7 @@ fn panic_cannot_unwind() -> ! { fn panic_in_cleanup() -> ! { unsafe { libc::printf("panic in a destructor during cleanup\n\0" as *const str as *const i8); - intrinsics::abort(); + intrinsics::abort_immediate(); } } @@ -568,7 +569,7 @@ fn panic_bounds_check(index: usize, len: usize) -> ! { len, index, ); - intrinsics::abort(); + intrinsics::abort_immediate(); } } @@ -698,7 +699,7 @@ pub mod intrinsics { #[rustc_intrinsic] pub const fn black_box(_dummy: T) -> T; #[rustc_intrinsic] - pub fn abort() -> !; + pub fn abort_immediate() -> !; #[rustc_intrinsic] pub const fn size_of() -> usize; #[rustc_intrinsic] diff --git a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs index 6e155f89ee5cc..6fa815ff5fe40 100644 --- a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs @@ -1,12 +1,12 @@ // Adapted from https://github.com/sunfishcode/mir2cranelift/blob/master/rust-examples/nocore-hello-world.rs #![feature( - no_core, unboxed_closures, lang_items, never_type, linkage, - extern_types, thread_local + no_core, unboxed_closures, lang_items, linkage, + extern_types, thread_local, rustc_private )] #![no_core] #![allow(dead_code, internal_features, non_camel_case_types)] -#![rustfmt_skip] +#![cfg_attr(rustfmt, rustfmt_skip)] extern crate mini_core; @@ -438,10 +438,10 @@ pub enum E2 { #[allow(unreachable_patterns)] fn check_niche_behavior () { if let E1::V2 { .. } = (E1::V1 { f: true }) { - intrinsics::abort(); + intrinsics::abort_immediate(); } if let E2::V1 { .. } = E2::V3:: { - intrinsics::abort(); + intrinsics::abort_immediate(); } } diff --git a/compiler/rustc_codegen_gcc/libgccjit.version b/compiler/rustc_codegen_gcc/libgccjit.version index 5eef70260466f..62417a80f827e 100644 --- a/compiler/rustc_codegen_gcc/libgccjit.version +++ b/compiler/rustc_codegen_gcc/libgccjit.version @@ -1 +1 @@ -6f155cc3f5a2dff33afe6cc3ed6c2e0e605ae6a3 +badf78d09d16e66f4ca07971c51aa6a227558d4f diff --git a/compiler/rustc_codegen_gcc/patches/0001-Add-stdarch-Cargo.toml-for-testing.patch b/compiler/rustc_codegen_gcc/patches/0001-Add-stdarch-Cargo.toml-for-testing.patch deleted file mode 100644 index 3a8c37a8b8d9a..0000000000000 --- a/compiler/rustc_codegen_gcc/patches/0001-Add-stdarch-Cargo.toml-for-testing.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 190e26c9274b3c93a9ee3516b395590e6bd9213b Mon Sep 17 00:00:00 2001 -From: None -Date: Sun, 3 Aug 2025 19:54:56 -0400 -Subject: [PATCH] Patch 0001-Add-stdarch-Cargo.toml-for-testing.patch - ---- - library/stdarch/Cargo.toml | 20 ++++++++++++++++++++ - 1 file changed, 20 insertions(+) - create mode 100644 library/stdarch/Cargo.toml - -diff --git a/library/stdarch/Cargo.toml b/library/stdarch/Cargo.toml -new file mode 100644 -index 0000000..bd6725c ---- /dev/null -+++ b/library/stdarch/Cargo.toml -@@ -0,0 +1,20 @@ -+[workspace] -+resolver = "1" -+members = [ -+ "crates/*", -+ #"examples/" -+] -+exclude = [ -+ "crates/wasm-assert-instr-tests", -+ "rust_programs", -+] -+ -+[profile.release] -+debug = true -+opt-level = 3 -+incremental = true -+ -+[profile.bench] -+debug = 1 -+opt-level = 3 -+incremental = true --- -2.50.1 - diff --git a/compiler/rustc_codegen_gcc/rust-toolchain b/compiler/rustc_codegen_gcc/rust-toolchain.toml similarity index 70% rename from compiler/rustc_codegen_gcc/rust-toolchain rename to compiler/rustc_codegen_gcc/rust-toolchain.toml index 56fcfdff1c719..63af0047e6e00 100644 --- a/compiler/rustc_codegen_gcc/rust-toolchain +++ b/compiler/rustc_codegen_gcc/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2026-04-29" +channel = "nightly-2026-09-29" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs index 6a05f1cbbeef1..5b88cebb4f174 100644 --- a/compiler/rustc_codegen_gcc/src/abi.rs +++ b/compiler/rustc_codegen_gcc/src/abi.rs @@ -11,7 +11,7 @@ use rustc_middle::ty::layout::LayoutOf; #[cfg(feature = "master")] use rustc_session::{Session, config}; use rustc_span::bug; -use rustc_target::callconv::{ArgAttributes, CastTarget, FnAbi, PassMode}; +use rustc_target::callconv::{ArgAttributes, CastTarget, FnAbi, IndirectMode, PassMode}; #[cfg(feature = "master")] use rustc_target::spec::Arch; @@ -73,7 +73,7 @@ impl GccType for CastTarget { args.push(cx.type_ix(rem_bytes * 8)); } - cx.type_struct(&args, false) + cx.type_struct(&args, &[]) } } @@ -146,12 +146,23 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { if attrs.regular.contains(rustc_target::callconv::ArgAttribute::NonNull) { non_null_args.push(arg_index as i32 + 1); } + // There are a few others `ArgAttribute` variants" + // + // * ArgAttribute::ReadOnly: `access(read_only())`, but it's only used for emitting + // warning, not for optimization. + // * ArgAttribute::NoUndef: No equivalent in GCC + // * ArgAttribute::Writable: `access(read_write())` or `access(write_only())`, but it's + // only used for emitting warning, not for optimization. + // * ArgAttribute::NoFree: No equivalent in GCC ty }; #[cfg(not(feature = "master"))] let apply_attrs = |ty: Type<'gcc>, _attrs: &ArgAttributes, _arg_index: usize| ty; - for arg in self.args.iter() { + for (source_arg_index, arg) in self.args.iter().enumerate() { + #[cfg(not(feature = "master"))] + let _ = source_arg_index; + let arg_ty = match arg.mode { PassMode::Ignore => continue, PassMode::Pair(a, b) => { @@ -178,19 +189,64 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { let ty = cast.gcc_type(cx); apply_attrs(ty, &cast.attrs, argument_tys.len()) } - PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: true } => { - // This is a "byval" argument, so we don't apply the `restrict` attribute on it. - on_stack_param_indices.insert(argument_tys.len()); - arg.layout.gcc_type(cx) + PassMode::Indirect { + attrs: _, + meta_attrs: None, + address_space: _, + mode: IndirectMode::OnStack, + } => { + let x86_interrupt_first_arg = { + #[cfg(feature = "master")] + { + source_arg_index == 0 + && matches!(self.conv, CanonAbi::Interrupt(InterruptKind::X86)) + } + #[cfg(not(feature = "master"))] + { + false + } + }; + + if x86_interrupt_first_arg { + // Rust lowers the first `x86-interrupt` argument as a byval stack slot. + // LLVM represents that as a pointer parameter with `byval`; GCC's + // interrupt attribute likewise requires a pointer-shaped first parameter. + // Do not add this parameter to `on_stack_param_indices`: that set is only + // needed when GCC represents a byval argument as a value parameter, while + // this parameter is already pointer-shaped. + cx.type_ptr_to(arg.layout.gcc_type(cx)) + } else { + // This is a "byval" argument, so we don't apply the `restrict` attribute on it. + on_stack_param_indices.insert(argument_tys.len()); + arg.layout.gcc_type(cx) + } + } + PassMode::Indirect { + attrs: _, + meta_attrs: None, + address_space: _, + mode: IndirectMode::AmdgpuKernelArg, + } => { + unimplemented!("unsupported amdgpu kernel argument") } PassMode::Direct(attrs) => { apply_attrs(arg.layout.immediate_gcc_type(cx), &attrs, argument_tys.len()) } - PassMode::Indirect { attrs, meta_attrs: None, on_stack: false } => { + PassMode::Indirect { + attrs, + meta_attrs: None, + address_space: _, + mode: IndirectMode::Pointer, + } => { apply_attrs(cx.type_ptr_to(arg.layout.gcc_type(cx)), &attrs, argument_tys.len()) } - PassMode::Indirect { attrs, meta_attrs: Some(meta_attrs), on_stack } => { - assert!(!on_stack); + PassMode::Indirect { + attrs, + meta_attrs: Some(meta_attrs), + address_space: _, + mode, + } => { + assert!(mode == IndirectMode::Pointer); // Construct the type of a (wide) pointer to `ty`, and pass its two fields. // Any two ABI-compatible unsized types have the same metadata type and // moreover the same metadata value leads to the same dynamic size and diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index 420bf1e7a31c1..75a48c217c49b 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -297,7 +297,9 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { out_place, }); - if !readwrite { + if readwrite { + self.llbb().add_assignment(None, tmp_var, in_value.immediate()); + } else { let out_gcc_idx = outputs.len() - 1; let constraint = Cow::Owned(out_gcc_idx.to_string()); @@ -363,7 +365,14 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { let ty = value.layout.gcc_type(self.cx); let reg_var = self.current_func().new_local(None, ty, "input_register"); reg_var.set_register_name(reg_name); - self.llbb().add_assignment(None, reg_var, value.immediate()); + // FIXME: We should remove this when switching to "untyped" pointers + let value = value.immediate(); + let value = if value.get_type() != ty { + self.context.new_cast(None, value, ty) + } else { + value + }; + self.llbb().add_assignment(None, reg_var, value); inputs.push(AsmInOperand { constraint: "r".into(), @@ -602,6 +611,12 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { self.llbb().add_eval(None, self.context.new_call(None, builtin_unreachable, &[])); } + if !options.contains(InlineAsmOptions::NORETURN) + && let Some(dest) = dest + { + self.switch_to_block(dest); + } + // Write results to outputs. // // We need to do this because: @@ -681,6 +696,7 @@ fn explicit_reg_to_gcc(reg: InlineAsmReg) -> &'static str { } InlineAsmReg::Arm(reg) => reg.name(), InlineAsmReg::AArch64(reg) => reg.name(), + InlineAsmReg::M68k(reg) => reg.name(), _ => unimplemented!(), } } diff --git a/compiler/rustc_codegen_gcc/src/attributes.rs b/compiler/rustc_codegen_gcc/src/attributes.rs index a5cc44a46e154..2e9bf42874148 100644 --- a/compiler/rustc_codegen_gcc/src/attributes.rs +++ b/compiler/rustc_codegen_gcc/src/attributes.rs @@ -2,16 +2,21 @@ use gccjit::FnAttribute; use gccjit::Function; #[cfg(feature = "master")] -use rustc_hir::attrs::InlineAttr; -use rustc_hir::attrs::InstructionSetAttr; +use rustc_abi::{CanonAbi, InterruptKind}; +#[cfg(feature = "master")] +use rustc_attr_ir::InlineAttr; +use rustc_attr_ir::InstructionSetAttr; #[cfg(feature = "master")] use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; #[cfg(feature = "master")] use rustc_middle::mir::TerminatorKind; use rustc_middle::ty; +use rustc_target::callconv::FnAbi; #[cfg(feature = "master")] use rustc_target::spec::Arch; +#[cfg(feature = "master")] +use crate::base; use crate::context::CodegenCx; use crate::gcc_util::to_gcc_features; @@ -82,12 +87,23 @@ fn inline_attr<'gcc, 'tcx>( } } +#[cfg(feature = "master")] +fn is_x86_interrupt<'tcx>(fn_abi: Option<&FnAbi<'tcx, ty::Ty<'tcx>>>) -> bool { + matches!( + fn_abi, + Some(fn_abi) if matches!(fn_abi.conv, CanonAbi::Interrupt(InterruptKind::X86)) + ) +} + /// Composite function which sets GCC attributes for function depending on its AST (`#[attribute]`) /// attributes. pub fn from_fn_attrs<'gcc, 'tcx>( cx: &CodegenCx<'gcc, 'tcx>, #[cfg_attr(not(feature = "master"), expect(unused_variables))] func: Function<'gcc>, instance: ty::Instance<'tcx>, + #[cfg_attr(not(feature = "master"), expect(unused_variables))] fn_abi: Option< + &FnAbi<'tcx, ty::Ty<'tcx>>, + >, ) { let codegen_fn_attrs = cx.tcx.codegen_instance_attrs(instance.def); @@ -102,6 +118,16 @@ pub fn from_fn_attrs<'gcc, 'tcx>( } else { codegen_fn_attrs.inline }; + // GCC drops `weak` from a function that is also `inline`, leaving the symbol strong, and + // the linkage is what has to survive. `inline(never)` does not conflict. + let inline = match inline { + InlineAttr::Always | InlineAttr::Hint | InlineAttr::Force { .. } + if codegen_fn_attrs.linkage.is_some_and(base::linkage_needs_weak_attribute) => + { + InlineAttr::None + } + inline => inline, + }; if let Some(attr) = inline_attr(cx, inline, instance) { if let FnAttribute::AlwaysInline = attr { func.add_attribute(FnAttribute::Inline); @@ -120,6 +146,11 @@ pub fn from_fn_attrs<'gcc, 'tcx>( } } + #[cfg(feature = "master")] + let x86_interrupt = is_x86_interrupt(fn_abi); + #[cfg(not(feature = "master"))] + let x86_interrupt = false; + let mut function_features = codegen_fn_attrs .target_features .iter() @@ -135,6 +166,13 @@ pub fn from_fn_attrs<'gcc, 'tcx>( // Check if GCC requires the same. let mut global_features = cx.tcx.sess.global_backend_features.iter().map(|s| s.as_str()); function_features.extend(&mut global_features); + if x86_interrupt { + // GCC does not preserve SSE, MMX, or x87 state in interrupt handlers and rejects + // them whenever those instruction sets are enabled, even if the handler does not + // emit such instructions. Restrict the function to general registers so the + // interrupt attribute works with the default x86_64 target features. + function_features.push("general-regs-only"); + } let target_features = function_features .iter() .filter_map(|feature| { diff --git a/compiler/rustc_codegen_gcc/src/back/lto.rs b/compiler/rustc_codegen_gcc/src/back/lto.rs index 98f9abdb05c4c..baf1fda02e258 100644 --- a/compiler/rustc_codegen_gcc/src/back/lto.rs +++ b/compiler/rustc_codegen_gcc/src/back/lto.rs @@ -20,6 +20,7 @@ use std::ffi::CString; use std::fs::{self, File}; use std::path::{Path, PathBuf}; +use std::sync::Arc; use gccjit::OutputKind; use object::read::archive::ArchiveFile; @@ -29,14 +30,15 @@ use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput, SharedEmitter} use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{CompiledModule, ModuleCodegen, ModuleKind}; use rustc_data_structures::memmap::Mmap; -use rustc_data_structures::profiling::SelfProfilerRef; use rustc_errors::{DiagCtxt, DiagCtxtHandle}; use rustc_log::tracing::info; +use rustc_session::Session; use tempfile::{TempDir, tempdir}; use crate::back::write::{codegen, save_temp_bitcode}; use crate::diagnostics::LtoBitcodeFromRlib; -use crate::{GccCodegenBackend, GccContext, LtoMode, to_gcc_opt_level}; +use crate::gcc_util::new_context; +use crate::{GccCodegenBackend, GccContext, LtoMode, SyncContext, to_gcc_opt_level}; struct LtoData { // FIXME(antoyo): use symbols_below_threshold. @@ -102,8 +104,8 @@ fn save_as_file(obj: &[u8], path: &Path) -> Result<(), LtoBitcodeFromRlib> { /// Performs fat LTO by merging all modules into a single one and returning it /// for further optimization. pub(crate) fn run_fat( + sess: &Session, cgcx: &CodegenContext, - prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, each_linked_rlib_for_lto: &[PathBuf], modules: Vec>, @@ -114,8 +116,8 @@ pub(crate) fn run_fat( /*let symbols_below_threshold = lto_data.symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::>();*/ fat_lto( + sess, cgcx, - prof, dcx, modules, lto_data.upstream_modules, @@ -125,15 +127,15 @@ pub(crate) fn run_fat( } fn fat_lto( + sess: &Session, cgcx: &CodegenContext, - prof: &SelfProfilerRef, dcx: DiagCtxtHandle<'_>, modules: Vec>, mut serialized_modules: Vec<(SerializedModule, CString)>, tmp_path: TempDir, //symbols_below_threshold: &[String], ) -> CompiledModule { - let _timer = prof.generic_activity("GCC_fat_lto_build_monolithic_module"); + let _timer = sess.prof.generic_activity("GCC_fat_lto_build_monolithic_module"); info!("going for a fat lto"); // Sort out all our lists of incoming modules into two lists. @@ -183,17 +185,16 @@ fn fat_lto( // module and create a linker with it. let mut module: ModuleCodegen = match costliest_module { Some((_cost, i)) => in_memory.remove(i), - None => { - unimplemented!("Incremental"); - /*assert!(!serialized_modules.is_empty(), "must have at least one serialized module"); - let (buffer, name) = serialized_modules.remove(0); - info!("no in-memory regular modules to choose from, parsing {:?}", name); - ModuleCodegen { - module_llvm: GccContext::parse(cgcx, &name, buffer.data(), dcx)?, - name: name.into_string().unwrap(), - kind: ModuleKind::Regular, - }*/ - } + None => ModuleCodegen::new_regular( + "lto_module".to_string(), + GccContext { + context: Arc::new(SyncContext::new(new_context(sess))), + relocation_model: sess.relocation_model(), + lto_supported: true, + lto_mode: LtoMode::None, + temp_dir: None, + }, + ), }; { info!("using {:?} as a base module", module.name); @@ -220,7 +221,8 @@ fn fat_lto( // We add the object files and save in should_combine_object_files that we should combine // them into a single object file when compiling later. for (bc_decoded, name) in serialized_modules { - let _timer = prof + let _timer = sess + .prof .generic_activity_with_arg_recorder("GCC_fat_lto_link_module", |recorder| { recorder.record_arg(format!("{:?}", name)) }); @@ -258,7 +260,7 @@ fn fat_lto( // of now. module.module_llvm.temp_dir = Some(tmp_path); - codegen(cgcx, prof, dcx, module, &cgcx.module_config) + codegen(cgcx, &sess.prof, dcx, module, &cgcx.module_config) } pub struct ModuleBuffer(PathBuf); diff --git a/compiler/rustc_codegen_gcc/src/back/write.rs b/compiler/rustc_codegen_gcc/src/back/write.rs index cf5514412f745..1f4fd8a314ad2 100644 --- a/compiler/rustc_codegen_gcc/src/back/write.rs +++ b/compiler/rustc_codegen_gcc/src/back/write.rs @@ -11,8 +11,8 @@ use rustc_log::tracing::debug; use rustc_session::config::OutputType; use rustc_target::spec::SplitDebuginfo; -use crate::base::add_pic_option; use crate::diagnostics::CopyBitcode; +use crate::gcc_util::add_pic_option; use crate::{GccContext, LtoMode}; pub(crate) fn codegen( @@ -60,9 +60,6 @@ pub(crate) fn codegen( let _timer = prof .generic_activity_with_arg("GCC_module_codegen_embed_bitcode", &*module.name); if lto_supported { - // FIXME(antoyo): maybe we should call embed_bitcode to have the proper iOS fixes? - //embed_bitcode(cgcx, llcx, llmod, &config.bc_cmdline, data); - context.add_command_line_option("-flto=auto"); context.add_command_line_option("-flto-partition=one"); context.add_command_line_option("-ffat-lto-objects"); diff --git a/compiler/rustc_codegen_gcc/src/base.rs b/compiler/rustc_codegen_gcc/src/base.rs index 101af0bb0bff1..acf5c75e4d8cd 100644 --- a/compiler/rustc_codegen_gcc/src/base.rs +++ b/compiler/rustc_codegen_gcc/src/base.rs @@ -1,14 +1,13 @@ -use std::collections::HashSet; -use std::env; use std::sync::Arc; use std::time::Instant; -use gccjit::{CType, Context, FunctionType, GlobalKind}; +#[cfg(feature = "master")] +use gccjit::VarAttribute; +use gccjit::{CType, FunctionType, GlobalKind}; +use rustc_attr_ir::{AttributeKind, Linkage, find_attr}; use rustc_codegen_ssa::ModuleCodegen; use rustc_codegen_ssa::base::maybe_create_entry_wrapper; use rustc_codegen_ssa::mono_item::MonoItemExt; -use rustc_hir::attrs::{AttributeKind, Linkage}; -use rustc_hir::find_attr; use rustc_middle::dep_graph; #[cfg(feature = "master")] use rustc_middle::mono::Visibility; @@ -17,11 +16,11 @@ use rustc_session::config::DebugInfo; use rustc_span::Symbol; #[cfg(feature = "master")] use rustc_target::spec::SymbolVisibility; -use rustc_target::spec::{Arch, RelocModel}; use crate::builder::Builder; use crate::context::CodegenCx; -use crate::{GccContext, LtoMode, SharedTargetInfo, SyncContext, gcc_util, new_context}; +use crate::gcc_util::new_context; +use crate::{GccContext, LtoMode, SharedTargetInfo, SyncContext}; #[cfg(feature = "master")] pub fn visibility_to_gcc(visibility: Visibility) -> gccjit::Visibility { @@ -41,32 +40,61 @@ pub fn symbol_visibility_to_gcc(visibility: SymbolVisibility) -> gccjit::Visibil } } +/// The kind of a global *definition* with an explicit `#[linkage]`. +/// +/// The flavours that another object file is allowed to override also need +/// `global_linkage_attribute` from the caller: `GlobalKind` alone cannot express weakness. pub fn global_linkage_to_gcc(linkage: Linkage) -> GlobalKind { match linkage { - Linkage::External => GlobalKind::Imported, - Linkage::AvailableExternally => GlobalKind::Imported, - Linkage::LinkOnceAny => unimplemented!(), - Linkage::LinkOnceODR => unimplemented!(), - Linkage::WeakAny => unimplemented!(), - Linkage::WeakODR => unimplemented!(), - Linkage::Internal => GlobalKind::Internal, - Linkage::ExternalWeak => GlobalKind::Imported, // FIXME(antoyo): should be weak linkage. - Linkage::Common => unimplemented!(), + Linkage::External => GlobalKind::Exported, + // libgccjit cannot emit a definition that the linker discards in favour of the one in + // another object file, so emit a private copy of it instead. + Linkage::AvailableExternally | Linkage::Internal => GlobalKind::Internal, + // libgccjit exposes no comdat, so `weak` stands in for the linkonce flavours. + Linkage::LinkOnceAny + | Linkage::LinkOnceODR + | Linkage::WeakAny + | Linkage::WeakODR + | Linkage::ExternalWeak => GlobalKind::Exported, } } +/// The attribute a global *definition* needs on top of its [`GlobalKind`] to get this linkage. +#[cfg(feature = "master")] +pub fn global_linkage_attribute<'gcc>(linkage: Linkage) -> Option> { + if linkage_needs_weak_attribute(linkage) { Some(VarAttribute::Weak) } else { None } +} + +/// The type of a function *definition* with an explicit `#[linkage]`. +/// +/// The flavours that another object file is allowed to override also need +/// `linkage_needs_weak_attribute` from the caller: `FunctionType` alone cannot express weakness. pub fn linkage_to_gcc(linkage: Linkage) -> FunctionType { match linkage { Linkage::External => FunctionType::Exported, - // FIXME(antoyo): set the attribute externally_visible. - Linkage::AvailableExternally => FunctionType::Extern, - Linkage::LinkOnceAny => unimplemented!(), - Linkage::LinkOnceODR => unimplemented!(), - Linkage::WeakAny => FunctionType::Exported, // FIXME(antoyo): should be similar to linkonce. - Linkage::WeakODR => unimplemented!(), - Linkage::Internal => FunctionType::Internal, - Linkage::ExternalWeak => unimplemented!(), - Linkage::Common => unimplemented!(), + // libgccjit cannot emit a definition that the linker discards in favour of the one in + // another object file, so emit a private copy of it instead. + Linkage::AvailableExternally | Linkage::Internal => FunctionType::Internal, + // libgccjit exposes no comdat, so `weak` stands in for every overridable flavour. + Linkage::LinkOnceAny + | Linkage::LinkOnceODR + | Linkage::WeakAny + | Linkage::WeakODR + | Linkage::ExternalWeak => FunctionType::Exported, + } +} + +/// Whether a definition with this linkage must carry the `weak` attribute, so that a strong +/// definition in another object file wins over it instead of clashing with it. +#[cfg(feature = "master")] +pub fn linkage_needs_weak_attribute(linkage: Linkage) -> bool { + match linkage { + Linkage::LinkOnceAny + | Linkage::LinkOnceODR + | Linkage::WeakAny + | Linkage::WeakODR + | Linkage::ExternalWeak => true, + Linkage::External | Linkage::AvailableExternally | Linkage::Internal => false, } } @@ -101,41 +129,7 @@ pub fn compile_codegen_unit( ) -> ModuleCodegen { let cgu = tcx.codegen_unit(cgu_name); // Instantiate monomorphizations without filling out definitions yet... - let context = new_context(tcx); - - if tcx.sess.panic_strategy().unwinds() { - context.add_command_line_option("-fexceptions"); - context.add_driver_option("-fexceptions"); - } - - let disabled_features: HashSet<_> = tcx - .sess - .opts - .cg - .target_feature - .split(',') - .filter(|feature| feature.starts_with('-')) - .map(|string| &string[1..]) - .collect(); - - if !disabled_features.contains("avx") && tcx.sess.target.arch == Arch::X86_64 { - // NOTE: we always enable AVX because the equivalent of llvm.x86.sse2.cmp.pd in GCC for - // SSE2 is multiple builtins, so we use the AVX __builtin_ia32_cmppd instead. - // FIXME(antoyo): use the proper builtins for llvm.x86.sse2.cmp.pd and similar. - context.add_command_line_option("-mavx"); - } - - for arg in &tcx.sess.opts.cg.llvm_args { - context.add_command_line_option(arg); - } - // NOTE: This is needed to compile the file src/intrinsic/archs.rs during a bootstrap of rustc. - context.add_command_line_option("-fno-var-tracking-assignments"); - // NOTE: an optimization (https://github.com/rust-lang/rustc_codegen_gcc/issues/53). - context.add_command_line_option("-fno-semantic-interposition"); - // NOTE: Rust relies on LLVM not doing TBAA (https://github.com/rust-lang/unsafe-code-guidelines/issues/292). - context.add_command_line_option("-fno-strict-aliasing"); - // NOTE: Rust relies on LLVM doing wrapping on overflow. - context.add_command_line_option("-fwrapv"); + let context = new_context(tcx.sess); // NOTE: We need to honor the `#![no_builtins]` attribute to prevent GCC from // replacing code patterns (like loops) with calls to builtins (like memset). @@ -148,64 +142,6 @@ pub fn compile_codegen_unit( context.add_command_line_option("-fno-tree-loop-distribute-patterns"); } - if let Some(model) = tcx.sess.code_model() { - use rustc_target::spec::CodeModel; - - context.add_command_line_option(match model { - CodeModel::Tiny => "-mcmodel=tiny", - CodeModel::Small => "-mcmodel=small", - CodeModel::Kernel => "-mcmodel=kernel", - CodeModel::Medium => "-mcmodel=medium", - CodeModel::Large => "-mcmodel=large", - }); - } - - add_pic_option(&context, tcx.sess.relocation_model()); - - let target_cpu = gcc_util::target_cpu(tcx.sess); - if target_cpu != "generic" { - context.add_command_line_option(format!("-march={}", target_cpu)); - } - - if tcx - .sess - .opts - .unstable_opts - .function_sections - .unwrap_or(tcx.sess.target.function_sections) - { - context.add_command_line_option("-ffunction-sections"); - context.add_command_line_option("-fdata-sections"); - } - - if env::var("CG_GCCJIT_DUMP_RTL").as_deref() == Ok("1") { - context.add_command_line_option("-fdump-rtl-vregs"); - } - if env::var("CG_GCCJIT_DUMP_RTL_ALL").as_deref() == Ok("1") { - context.add_command_line_option("-fdump-rtl-all"); - } - if env::var("CG_GCCJIT_DUMP_TREE_ALL").as_deref() == Ok("1") { - context.add_command_line_option("-fdump-tree-all-eh"); - } - if env::var("CG_GCCJIT_DUMP_IPA_ALL").as_deref() == Ok("1") { - context.add_command_line_option("-fdump-ipa-all-eh"); - } - if env::var("CG_GCCJIT_DUMP_CODE").as_deref() == Ok("1") { - context.set_dump_code_on_compile(true); - } - if env::var("CG_GCCJIT_DUMP_GIMPLE").as_deref() == Ok("1") { - context.set_dump_initial_gimple(true); - } - if env::var("CG_GCCJIT_DUMP_EVERYTHING").as_deref() == Ok("1") { - context.set_dump_everything(true); - } - if env::var("CG_GCCJIT_KEEP_INTERMEDIATES").as_deref() == Ok("1") { - context.set_keep_intermediates(true); - } - if env::var("CG_GCCJIT_VERBOSE").as_deref() == Ok("1") { - context.add_driver_option("-v"); - } - // NOTE: The codegen generates unreachable blocks. context.set_allow_unreachable_blocks(true); @@ -214,6 +150,7 @@ pub fn compile_codegen_unit( // -fsyntax-only), forbid the compilation when get_target_info() is called on a // context. let f16_type_supported = target_info.supports_target_dependent_type(CType::Float16); + let f16b_type_supported = target_info.supports_target_dependent_type(CType::BFloat16); let f32_type_supported = target_info.supports_target_dependent_type(CType::Float32); let f64_type_supported = target_info.supports_target_dependent_type(CType::Float64); let f128_type_supported = target_info.supports_target_dependent_type(CType::Float128); @@ -225,6 +162,7 @@ pub fn compile_codegen_unit( tcx, u128_type_supported, f16_type_supported, + f16b_type_supported, f32_type_supported, f64_type_supported, f128_type_supported, @@ -243,6 +181,11 @@ pub fn compile_codegen_unit( // ... and now that we have everything pre-defined, fill out those definitions. for &(mono_item, item_data) in &mono_items { mono_item.define::>(&mut cx, cgu_name.as_str(), item_data); + + // Now that this function's blocks all exist, fill in the cleanup + // regions reconstructed from MIR while lowering its `invoke`s. + #[cfg(feature = "master")] + cx.populate_cleanup_regions(); } // If this codegen unit contains the main function, also create the @@ -269,24 +212,3 @@ pub fn compile_codegen_unit( (module, cost) } - -pub fn add_pic_option<'gcc>(context: &Context<'gcc>, relocation_model: RelocModel) { - match relocation_model { - rustc_target::spec::RelocModel::Static => { - context.add_command_line_option("-fno-pie"); - context.add_driver_option("-fno-pie"); - } - rustc_target::spec::RelocModel::Pic => { - context.add_command_line_option("-fPIC"); - // NOTE: we use both add_command_line_option and add_driver_option because the usage in - // this module (compile_codegen_unit) requires add_command_line_option while the usage - // in the back::write module (codegen) requires add_driver_option. - context.add_driver_option("-fPIC"); - } - rustc_target::spec::RelocModel::Pie => { - context.add_command_line_option("-fPIE"); - context.add_driver_option("-fPIE"); - } - model => eprintln!("Unsupported relocation model: {:?}", model), - } -} diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 0a88085960e89..a1eab8f448990 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -4,8 +4,8 @@ use std::convert::TryFrom; use std::ops::Deref; use gccjit::{ - BinaryOp, Block, ComparisonOp, Context, Function, LValue, Location, RValue, ToRValue, Type, - UnaryOp, + BinaryOp, Block, CType, ComparisonOp, Context, Function, LValue, Location, RValue, ToRValue, + Type, UnaryOp, }; use rustc_abi as abi; use rustc_abi::{Align, HasDataLayout, Size, TargetDataLayout, WrappingRange}; @@ -33,9 +33,11 @@ use rustc_target::callconv::FnAbi; use rustc_target::spec::{HasTargetSpec, HasX86AbiOpt, Target, X86Abi}; use crate::abi::FnAbiGccExt; +use crate::builder; use crate::common::{SignType, TypeReflection, type_is_pointer}; use crate::context::CodegenCx; -use crate::diagnostics; +#[cfg(feature = "master")] +use crate::context::PendingCleanup; use crate::intrinsic::llvm; use crate::type_of::LayoutGccExt; @@ -64,6 +66,33 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { self.value_counter.get() } + /// Tell GCC that `pointer` is `align`-aligned, so that the bulk memory builtins can widen their + /// accesses: a pointer cast to an aligned type would be dropped as a useless conversion. + fn assume_aligned(&mut self, pointer: RValue<'gcc>, align: Align) -> RValue<'gcc> { + if align.bytes() <= 1 { + return pointer; + } + let assume_aligned = self.context.get_builtin_function("__builtin_assume_aligned"); + let alignment = self.context.new_rvalue_from_long(self.type_size_t(), align.bytes() as i64); + let pointer_type = pointer.get_type(); + let const_void_ptr_type = self.context.new_type::<()>().make_const().make_pointer(); + let pointer = self.context.new_cast(self.location, pointer, const_void_ptr_type); + let aligned = self.context.new_call(self.location, assume_aligned, &[pointer, alignment]); + self.context.new_cast(self.location, aligned, pointer_type) + } + + /// GCC ignores a volatile qualifier on the pointers given to `memcpy`/`memmove`/`memset` and + /// happily deletes the call, so a barrier is what keeps the operation observable. The pointers + /// are fed to it because a clobber alone does not reach memory GCC believes never escapes. + fn volatile_barrier(&mut self, pointers: &[RValue<'gcc>]) { + let barrier = self.block.add_extended_asm(self.location, ""); + for pointer in pointers { + barrier.add_input_operand(None, "r", *pointer); + } + barrier.add_clobber("memory"); + barrier.set_volatile_flag(true); + } + fn atomic_extremum( &mut self, operation: ExtremumOperation, @@ -89,7 +118,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { ); let previous_var = func.new_local(self.location, previous_value.get_type(), "previous_value"); - let return_value = func.new_local(self.location, previous_value.get_type(), "return_value"); + let return_value = self.new_temp(func, self.location, previous_value.get_type()); self.llbb().add_assignment(self.location, previous_var, previous_value); self.llbb().add_assignment(self.location, return_value, previous_var.to_rvalue()); @@ -316,34 +345,71 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { self.block.get_function() } + /// Shared implementation of `call` and `tail_call`. For tail call it is important that this + /// returns a bare call, and not the result assigned to a local, or the result of `add_eval`. + #[allow(clippy::too_many_arguments)] + fn build_call( + &mut self, + typ: Type<'gcc>, + fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, + func: RValue<'gcc>, + return_slot: ReturnSlot< as BackendTypes>::Value>, + args: &[RValue<'gcc>], + funclet: Option<&Funclet>, + must_tail: bool, + ) -> RValue<'gcc> { + // FIXME: change this in the `rustc_codegen_gcc` repo after the sync, to use the `libgccjit` indirect return suppport. + let args = match return_slot { + ReturnSlot::Direct => Cow::Borrowed(args), + ReturnSlot::Indirect(sret_ptr) => { + let mut args = args.to_vec(); + // Prepend the indirect return pointer + args.insert(0, sret_ptr); + Cow::Owned(args) + } + }; + // FIXME(antoyo): remove when having a proper API. + let gcc_func = unsafe { std::mem::transmute::, Function<'gcc>>(func) }; + let call = if self.functions.borrow().values().any(|value| *value == gcc_func) { + // FIXME(antoyo): remove when the API supports a different type for functions. + let func: Function<'gcc> = self.cx.rvalue_as_function(func); + self.function_call(func, &args, funclet, must_tail) + } else { + // If it's a not function that was defined, it's a function pointer. + self.function_ptr_call(typ, fn_abi, func, &args, funclet, must_tail) + }; + if let Some(_fn_abi) = fn_abi { + // FIXME(bjorn3): Apply function attributes + } + call + } + pub fn function_call( &mut self, func: Function<'gcc>, args: &[RValue<'gcc>], _funclet: Option<&Funclet>, + must_tail: bool, ) -> RValue<'gcc> { let args = self.check_call("call", func, args); + let call = self.cx.context.new_call(self.location, func, &args); + if must_tail { + // Return the bare tail call, don't assign or `add_eval` it yet. + return call; + } + // gccjit requires to use the result of functions, even when it's not used. // That's why we assign the result to a local or call add_eval(). let return_type = func.get_return_type(); let void_type = self.context.new_type::<()>(); let current_func = self.block.get_function(); if return_type != void_type { - let result = current_func.new_local( - self.location, - return_type, - format!("returnValue{}", self.next_value_counter()), - ); - self.block.add_assignment( - self.location, - result, - self.cx.context.new_call(self.location, func, &args), - ); + let result = self.new_temp(current_func, self.location, return_type); + self.block.add_assignment(self.location, result, call); result.to_rvalue() } else { - self.block - .add_eval(self.location, self.cx.context.new_call(self.location, func, &args)); + self.block.add_eval(self.location, call); // Return dummy value when not having return value. self.context.new_rvalue_zero(self.isize_type) } @@ -356,6 +422,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { mut func_ptr: RValue<'gcc>, args: &[RValue<'gcc>], _funclet: Option<&Funclet>, + must_tail: bool, ) -> RValue<'gcc> { let func_ptr_type = { let func_ptr_type = func_ptr.get_type(); @@ -380,6 +447,12 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { let args_adjusted = args.len() != previous_arg_count; let args = self.check_ptr_call("call", func_ptr, &args, &on_stack_param_indices); + if must_tail { + // Return the bare tail call, don't assign or `add_eval` it yet. + let call = self.cx.context.new_call_through_ptr(self.location, func_ptr, &args); + return call; + } + // gccjit requires to use the result of functions, even when it's not used. // That's why we assign the result to a local or call add_eval(). let return_type = gcc_func.get_return_type(); @@ -396,11 +469,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { args_adjusted, orig_args, ); - let result = current_func.new_local( - self.location, - return_value.get_type(), - format!("ptrReturnValue{}", self.next_value_counter()), - ); + let result = self.new_temp(current_func, self.location, return_value.get_type()); self.block.add_assignment(self.location, result, return_value); result.to_rvalue() } else { @@ -422,8 +491,16 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { self.location, self.cx.context.new_call_through_ptr(self.location, func_ptr, &args), ); - // Return dummy value when not having return value. - self.context.new_rvalue_zero(self.isize_type) + // Return dummy value when not having return value, unless the intrinsic adapter + // needs to synthesize a non-void LLVM-level result from out-parameters. + llvm::adjust_intrinsic_return_value( + self, + self.context.new_rvalue_zero(self.isize_type), + &func_name, + &args, + args_adjusted, + orig_args, + ) } } @@ -438,11 +515,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { let return_type = self.context.new_type::(); let current_func = self.block.get_function(); // FIXME(antoyo): return the new_call() directly? Since the overflow function has no side-effects. - let result = current_func.new_local( - self.location, - return_type, - format!("overflowReturnValue{}", self.next_value_counter()), - ); + let result = self.new_temp(current_func, self.location, return_type); self.block.add_assignment( self.location, result, @@ -574,6 +647,18 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { default_block: Block<'gcc>, cases: impl ExactSizeIterator)>, ) { + // A switch with no cases is equivalent to an unconditional jump to the + // default block. Such a `SwitchInt` (one with only an `otherwise` target) + // is normally simplified into a `goto`, but `-Z mir-preserve-ub` keeps it, + // so it can reach here with e.g. the `bool` discriminant produced by a + // range-pattern comparison. `gcc_jit_block_end_with_switch` rejects a + // discriminant that is not of integer type, so emit a plain jump instead + // of a (pointless) switch. + if cases.len() == 0 { + self.block.end_with_jump(self.location, default_block); + return; + } + let mut gcc_cases = vec![]; let typ = self.val_ty(value); // FIXME(FractalFir): This is a workaround for a libgccjit limitation. @@ -614,7 +699,9 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { _funclet: Option<&Funclet>, instance: Option>, ) -> RValue<'gcc> { - let try_block = self.current_func().new_block("try"); + let current_func = self.current_func(); + let try_region = current_func.new_region(self.location); + let try_block = try_region.new_block("try"); let current_block = self.block; self.block = try_block; @@ -622,17 +709,25 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { let call = self.call(typ, fn_attrs, fn_abi, func, return_slot, args, None, instance); self.block = current_block; - let return_value = - self.current_func().new_local(self.location, call.get_type(), "invokeResult"); + let return_value = self.new_temp(current_func, self.location, call.get_type()); try_block.add_assignment(self.location, return_value, call); try_block.end_with_jump(self.location, then); - if self.cleanup_blocks.borrow().contains(&catch) { - self.block.add_try_finally(self.location, try_block, catch); + if self.cx.landing_pads.borrow().contains(&catch) { + let cleanup_region = current_func.new_region(self.location); + self.block.add_cleanup(self.location, try_region, cleanup_region); + self.cx + .pending_cleanups + .borrow_mut() + .push(PendingCleanup { region: cleanup_region, landing_pad: catch }); } else { - self.block.add_try_catch(self.location, try_block, catch); + let catch_region = current_func.new_region(self.location); + for clone in gccjit::clone_blocks(&[catch]) { + catch_region.add_block(clone); + } + self.block.add_try_catch(self.location, try_region, catch_region); } self.block.end_with_jump(self.location, then); @@ -671,8 +766,9 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { if return_type == void_type { self.block.end_with_void_return(self.location) } else { - let return_value = - self.current_func().new_local(self.location, return_type, "unreachableReturn"); + let trap = self.context.get_builtin_function("__builtin_trap"); + self.block.add_eval(self.location, self.context.new_call(self.location, trap, &[])); + let return_value = self.new_temp(self.current_func(), self.location, return_type); self.block.end_with_return(self.location, return_value) } } @@ -991,11 +1087,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { // the current basic block. Otherwise, it could be used in another basic block, causing a // dereference after a drop, for instance. let deref = ptr.dereference(self.location).to_rvalue(); - let loaded_value = function.new_local( - self.location, - aligned_type, - format!("loadedValue{}", self.next_value_counter()), - ); + let loaded_value = self.new_temp(function, self.location, aligned_type); block.add_assignment(self.location, loaded_value, deref); loaded_value.to_rvalue() } @@ -1114,7 +1206,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { let next_bb = self.append_sibling_block("repeat_loop_next"); let ptr_type = start.get_type(); - let current = self.llbb().get_function().new_local(self.location, ptr_type, "loop_var"); + let current = self.new_temp(self.llbb().get_function(), self.location, ptr_type); let current_val = current.to_rvalue(); self.assign(current, start); @@ -1405,47 +1497,53 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { fn memcpy( &mut self, dst: RValue<'gcc>, - _dst_align: Align, + dst_align: Align, src: RValue<'gcc>, - _src_align: Align, + src_align: Align, size: RValue<'gcc>, flags: MemFlags, _tt: Option, // Autodiff TypeTrees are LLVM-only, ignored in GCC backend ) { assert!(!flags.contains(MemFlags::NONTEMPORAL), "non-temporal memcpy not supported"); let size = self.intcast(size, self.type_size_t(), false); - let _is_volatile = flags.contains(MemFlags::VOLATILE); let dst = self.pointercast(dst, self.type_i8p()); + let dst = self.assume_aligned(dst, dst_align); let src = self.pointercast(src, self.type_ptr_to(self.type_void())); + let src = self.assume_aligned(src, src_align); let memcpy = self.context.get_builtin_function("memcpy"); - // FIXME(antoyo): handle aligns and is_volatile. self.block.add_eval( self.location, self.context.new_call(self.location, memcpy, &[dst, src, size]), ); + if flags.contains(MemFlags::VOLATILE) { + self.volatile_barrier(&[dst, src]); + } } fn memmove( &mut self, dst: RValue<'gcc>, - _dst_align: Align, + dst_align: Align, src: RValue<'gcc>, - _src_align: Align, + src_align: Align, size: RValue<'gcc>, flags: MemFlags, ) { assert!(!flags.contains(MemFlags::NONTEMPORAL), "non-temporal memmove not supported"); let size = self.intcast(size, self.type_size_t(), false); - let _is_volatile = flags.contains(MemFlags::VOLATILE); let dst = self.pointercast(dst, self.type_i8p()); + let dst = self.assume_aligned(dst, dst_align); let src = self.pointercast(src, self.type_ptr_to(self.type_void())); + let src = self.assume_aligned(src, src_align); let memmove = self.context.get_builtin_function("memmove"); - // FIXME(antoyo): handle is_volatile. self.block.add_eval( self.location, self.context.new_call(self.location, memmove, &[dst, src, size]), ); + if flags.contains(MemFlags::VOLATILE) { + self.volatile_barrier(&[dst, src]); + } } fn memset( @@ -1453,20 +1551,22 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { ptr: RValue<'gcc>, fill_byte: RValue<'gcc>, size: RValue<'gcc>, - _align: Align, + align: Align, flags: MemFlags, ) { assert!(!flags.contains(MemFlags::NONTEMPORAL), "non-temporal memset not supported"); - let _is_volatile = flags.contains(MemFlags::VOLATILE); let ptr = self.pointercast(ptr, self.type_i8p()); + let ptr = self.assume_aligned(ptr, align); let memset = self.context.get_builtin_function("memset"); - // FIXME(antoyo): handle align and is_volatile. let fill_byte = self.context.new_cast(self.location, fill_byte, self.i32_type); let size = self.intcast(size, self.type_size_t(), false); self.block.add_eval( self.location, self.context.new_call(self.location, memset, &[ptr, fill_byte, size]), ); + if flags.contains(MemFlags::VOLATILE) { + self.volatile_barrier(&[ptr]); + } } fn vscale(&mut self, _: Self::Type) -> Self::Value { @@ -1480,7 +1580,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { mut else_val: RValue<'gcc>, ) -> RValue<'gcc> { let func = self.current_func(); - let variable = func.new_local(self.location, then_val.get_type(), "selectVar"); + let variable = self.new_temp(func, self.location, then_val.get_type()); let then_block = func.new_block("then"); let else_block = func.new_block("else"); let after_block = func.new_block("after"); @@ -1502,8 +1602,10 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { variable.to_rvalue() } - fn va_arg(&mut self, _list: RValue<'gcc>, _ty: Type<'gcc>) -> RValue<'gcc> { - unimplemented!(); + fn va_arg(&mut self, list: RValue<'gcc>, ty: Type<'gcc>) -> RValue<'gcc> { + let va_list_type = self.context.new_c_type(CType::VaList); + let list = self.context.new_cast(self.location, list, va_list_type.make_pointer()); + self.context.new_va_arg(self.location, list, ty) } #[cfg(feature = "master")] @@ -1606,45 +1708,32 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { // NOTE: insert the current block in a variable so that a later call to invoke knows to // generate a try/finally instead of a try/catch for this block. - self.cleanup_blocks.borrow_mut().insert(self.block); - - let eh_pointer_builtin = - self.cx.context.get_target_builtin_function("__builtin_eh_pointer"); - let zero = self.cx.context.new_rvalue_zero(self.int_type); - let ptr = self.cx.context.new_call(self.location, eh_pointer_builtin, &[zero]); - - let value1_type = self.u8_type.make_pointer(); - let ptr = self.cx.context.new_cast(self.location, ptr, value1_type); - let value1 = ptr; - let value2 = zero; // FIXME(antoyo): set the proper value here (the type of exception?). + self.cx.landing_pads.borrow_mut().insert(self.block); + // A cleanup resumes by falling through: it never inspects the exception + // object. + let value1 = self.context.new_null(self.u8_type.make_pointer()); + let value2 = self.context.new_rvalue_zero(self.i32_type); (value1, value2) } #[cfg(not(feature = "master"))] fn cleanup_landing_pad(&mut self, _pers_fn: Function<'gcc>) -> (RValue<'gcc>, RValue<'gcc>) { let value1 = self - .current_func() - .new_local(self.location, self.u8_type.make_pointer(), "landing_pad0") + .new_temp(self.current_func(), self.location, self.u8_type.make_pointer()) .to_rvalue(); - let value2 = - self.current_func().new_local(self.location, self.i32_type, "landing_pad1").to_rvalue(); + let value2 = self.new_temp(self.current_func(), self.location, self.i32_type).to_rvalue(); (value1, value2) } fn filter_landing_pad(&mut self, pers_fn: Function<'gcc>) { - // FIXME(antoyo): generate the correct landing pad - self.cleanup_landing_pad(pers_fn); + self.set_personality_fn(pers_fn); } #[cfg(feature = "master")] - fn resume(&mut self, exn0: RValue<'gcc>, _exn1: RValue<'gcc>) { - let exn_type = exn0.get_type(); - let exn = self.context.new_cast(self.location, exn0, exn_type); - let unwind_resume = self.context.get_target_builtin_function("__builtin_unwind_resume"); - self.llbb() - .add_eval(self.location, self.context.new_call(self.location, unwind_resume, &[exn])); - self.unreachable(); + fn resume(&mut self, _exn0: RValue<'gcc>, _exn1: RValue<'gcc>) { + // End the cleanup by falling off the end of its region body. + self.block.end_with_fallthrough(self.location); } #[cfg(not(feature = "master"))] @@ -1696,7 +1785,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { // NOTE: since success contains the call to the intrinsic, it must be added to the basic block before // expected so that we store expected after the call. - let success_var = self.current_func().new_local(self.location, self.bool_type, "success"); + let success_var = self.new_temp(self.current_func(), self.location, self.bool_type); self.llbb().add_assignment(self.location, success_var, success); (expected.to_rvalue(), success_var.to_rvalue()) @@ -1786,45 +1875,35 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { funclet: Option<&Funclet>, _instance: Option>, ) -> RValue<'gcc> { - // FIXME: change this in the `rustc_codegen_gcc` repo after the sync, to use the `libgccjit` indirect return suppport. - let args = match return_slot { - ReturnSlot::Direct => args.to_vec(), - ReturnSlot::Indirect(sret_ptr) => { - let mut args = args.to_vec(); - // Prepend the indirect return pointer - args.insert(0, sret_ptr); - args - } - }; - // FIXME(antoyo): remove when having a proper API. - let gcc_func = unsafe { std::mem::transmute::, Function<'gcc>>(func) }; - let call = if self.functions.borrow().values().any(|value| *value == gcc_func) { - // FIXME(antoyo): remove when the API supports a different type for functions. - let func: Function<'gcc> = self.cx.rvalue_as_function(func); - self.function_call(func, &args, funclet) - } else { - // If it's a not function that was defined, it's a function pointer. - self.function_ptr_call(typ, fn_abi, func, &args, funclet) - }; - if let Some(_fn_abi) = fn_abi { - // FIXME(bjorn3): Apply function attributes - } - call + self.build_call(typ, fn_abi, func, return_slot, args, funclet, false) } fn tail_call( &mut self, - _llty: Self::Type, + llty: Self::Type, _fn_attrs: Option<&CodegenFnAttrs>, - _fn_abi: &FnAbi<'tcx, Ty<'tcx>>, - _llfn: Self::Value, - _return_slot: ReturnSlot, - _args: &[Self::Value], - _funclet: Option<&Self::Funclet>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + llfn: Self::Value, + return_slot: ReturnSlot, + args: &[Self::Value], + funclet: Option<&Self::Funclet>, _instance: Option>, ) { - // FIXME: implement support for explicit tail calls like rustc_codegen_llvm. - self.tcx.dcx().emit_fatal(diagnostics::ExplicitTailCallsUnsupported); + // `emit_call` returns a bare call for here, it has not been assigned or passed to add_eval. + let call = self.build_call(llty, Some(fn_abi), llfn, return_slot, args, funclet, true); + call.set_require_tail_call(true); + + let return_type = self.current_func().get_return_type(); + let void_type = self.context.new_type::<()>(); + + if return_type == void_type { + // For a void return the call is emitted as its own statement, immediately + // followed by a void return, so the tail call sits in tail position. + self.llbb().add_eval(self.location, call); + self.ret_void(); + } else { + self.ret(call) + } } fn zext(&mut self, value: RValue<'gcc>, dest_typ: Type<'gcc>) -> RValue<'gcc> { @@ -2409,11 +2488,31 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { self.bitcast_if_needed(res, result_type) } + /// Create a temporary variable. + /// + /// GCC will use more stack space with a local variable than with a temporary variable in debug mode, + /// so in order to avoid having the stack probe test fail in CI, we avoid creating local variables for temporaries. + pub fn new_temp( + &self, + function: Function<'gcc>, + location: Option>, + typ: Type<'gcc>, + ) -> LValue<'gcc> { + #[cfg(feature = "master")] + { + function.new_temp(location, typ) + } + #[cfg(not(feature = "master"))] + { + function.new_local(location, typ, format!("temp{}", self.next_value_counter())) + } + } + // GCC doesn't like deeply nested expressions. // By assigning intermediate expressions to a variable, this allow us to avoid deeply nested // expressions and GCC will use much less RAM. fn assign_to_var(&self, value: RValue<'gcc>) -> RValue<'gcc> { - let var = self.current_func().new_local(self.location, value.get_type(), "opResult"); + let var = self.new_temp(self.current_func(), self.location, value.get_type()); self.llbb().add_assignment(self.location, var, value); var.to_rvalue() } diff --git a/compiler/rustc_codegen_gcc/src/callee.rs b/compiler/rustc_codegen_gcc/src/callee.rs index 00f095ed54371..21a0bbb4030bb 100644 --- a/compiler/rustc_codegen_gcc/src/callee.rs +++ b/compiler/rustc_codegen_gcc/src/callee.rs @@ -70,7 +70,7 @@ pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>) cx.linkage.set(FunctionType::Extern); let func = cx.declare_fn(sym, fn_abi); - attributes::from_fn_attrs(cx, func, instance); + attributes::from_fn_attrs(cx, func, instance, Some(fn_abi)); #[cfg(feature = "master")] { @@ -106,7 +106,7 @@ pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>) // This is a monomorphization of a generic function. if !(cx.tcx.sess.opts.share_generics() || tcx.codegen_instance_attrs(instance.def).inline - == rustc_hir::attrs::InlineAttr::Never) + == rustc_attr_ir::InlineAttr::Never) { // When not sharing generics, all instances are in the same // crate and have hidden visibility. diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index 6bd186f1121fc..21d92c6cc2936 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -12,6 +12,7 @@ use rustc_session::PointerAuthSchema; use crate::consts::const_alloc_to_gcc; use crate::context::{CodegenCx, new_array_type}; +use crate::type_::struct_attributes; use crate::type_of::LayoutGccExt; impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { @@ -125,78 +126,86 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { } } +/// The element type and element count of the array used to represent a run of `len` constant bytes. +/// +/// Larger integers are used where possible: this reduces the number of rvalues, which is a +/// significant memory saving on constant-heavy crates. +fn byte_run_shape<'gcc>(cx: &CodegenCx<'gcc, '_>, len: usize) -> (Type<'gcc>, u64) { + match len % 8 { + 0 => (cx.context.new_type::(), len as u64 / 8), + 4 => (cx.context.new_type::(), len as u64 / 4), + _ => (cx.context.new_type::(), len as u64), + } +} + +/// The type [`bytes_in_context`] gives a run of `len` constant bytes. +/// +/// Exposed separately so that the type of a constant allocation can be computed before any of its +/// rvalues exist; see [`crate::consts::const_alloc_type`]. +/// +/// The result is cached because `gcc_jit_context_new_array_type` mints a fresh type every call. +/// Two equal-but-distinct array types would key [`CodegenCx::type_struct`] differently and so +/// produce two distinct anonymous structs, and libgccjit compares struct types by identity. +pub fn bytes_type_in_context<'gcc>(cx: &CodegenCx<'gcc, '_>, len: usize) -> Type<'gcc> { + let (element_type, count) = byte_run_shape(cx, len); + if let Some(&typ) = cx.byte_array_types.borrow().get(&(element_type, count)) { + return typ; + } + let typ = new_array_type(cx.context, None, element_type, count); + cx.byte_array_types.borrow_mut().insert((element_type, count), typ); + typ +} + +// FIXME(FractalFir): Consider using `global_set_initializer` instead. Before this is done, we need to confirm that +// `global_set_initializer` is more memory efficient than the current solution. +// `global_set_initializer` calls `global_set_initializer_rvalue` under the hood - does it generate an array of rvalues, +// or is it using a more efficient representation? pub fn bytes_in_context<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, bytes: &[u8]) -> RValue<'gcc> { - // Instead of always using an array of bytes, use an array of larger integers of target endianness - // if possible. This reduces the amount of `rvalues` we use, which reduces memory usage significantly. - // - // FIXME(FractalFir): Consider using `global_set_initializer` instead. Before this is done, we need to confirm that - // `global_set_initializer` is more memory efficient than the current solution. - // `global_set_initializer` calls `global_set_initializer_rvalue` under the hood - does it generate an array of rvalues, - // or is it using a more efficient representation? - match bytes.len() % 8 { + let typ = bytes_type_in_context(cx, bytes.len()); + let (element_type, _) = byte_run_shape(cx, bytes.len()); + let context = &cx.context; + // Since we are representing arbitrary byte runs as integers, we need to follow the target + // endianness. + let endian = cx.sess().target.options.endian; + let elements: Vec<_> = match bytes.len() % 8 { 0 => { - debug_assert_eq!( - bytes.len() % 8, - 0, - "bytes length is not a multiple of 8, so bytes.as_chunks will have a remainder" - ); - let context = &cx.context; - let byte_type = context.new_type::(); - let typ = new_array_type(context, None, byte_type, bytes.len() as u64 / 8); - let elements: Vec<_> = bytes - .as_chunks::<8>() - .0 + let (arrays, remainder) = bytes.as_chunks::<8>(); + debug_assert!(remainder.is_empty()); + arrays .iter() .map(|&arr| { context.new_rvalue_from_long( - byte_type, - // Since we are representing arbitrary byte runs as integers, we need to follow the target - // endianness. - match cx.sess().target.options.endian { + element_type, + match endian { rustc_abi::Endian::Little => u64::from_le_bytes(arr) as i64, rustc_abi::Endian::Big => u64::from_be_bytes(arr) as i64, }, ) }) - .collect(); - context.new_array_constructor(None, typ, &elements) + .collect() } 4 => { - debug_assert_eq!( - bytes.len() % 4, - 0, - "bytes length is not a multiple of 4, so bytes.as_chunks will have a remainder" - ); - let context = &cx.context; - let byte_type = context.new_type::(); - let typ = new_array_type(context, None, byte_type, bytes.len() as u64 / 4); - let elements: Vec<_> = bytes - .as_chunks::<4>() - .0 + let (arrays, remainder) = bytes.as_chunks::<4>(); + debug_assert!(remainder.is_empty()); + arrays .iter() .map(|&arr| { context.new_rvalue_from_int( - byte_type, - match cx.sess().target.options.endian { + element_type, + match endian { rustc_abi::Endian::Little => u32::from_le_bytes(arr) as i32, rustc_abi::Endian::Big => u32::from_be_bytes(arr) as i32, }, ) }) - .collect(); - context.new_array_constructor(None, typ, &elements) - } - _ => { - let context = cx.context; - let byte_type = context.new_type::(); - let typ = new_array_type(context, None, byte_type, bytes.len() as u64); - let elements: Vec<_> = bytes - .iter() - .map(|&byte| context.new_rvalue_from_int(byte_type, byte as i32)) - .collect(); - context.new_array_constructor(None, typ, &elements) + .collect() } - } + _ => bytes + .iter() + .map(|&byte| context.new_rvalue_from_int(element_type, byte as i32)) + .collect(), + }; + context.new_array_constructor(None, typ, &elements) } pub fn type_is_pointer(typ: Type<'_>) -> bool { @@ -298,7 +307,7 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> { fn const_struct(&self, values: &[RValue<'gcc>], packed: bool) -> RValue<'gcc> { let fields: Vec<_> = values.iter().map(|value| value.get_type()).collect(); // FIXME(antoyo): cache the type? It's anonymous, so probably not. - let typ = self.type_struct(&fields, packed); + let typ = self.type_struct(&fields, &struct_attributes(packed, None)); let struct_type = typ.is_struct().expect("struct type"); self.context.new_struct_constructor(None, struct_type.as_type(), None, values) } diff --git a/compiler/rustc_codegen_gcc/src/consts.rs b/compiler/rustc_codegen_gcc/src/consts.rs index 6c3b404547cfc..0784064547e0f 100644 --- a/compiler/rustc_codegen_gcc/src/consts.rs +++ b/compiler/rustc_codegen_gcc/src/consts.rs @@ -1,25 +1,29 @@ +use std::ops::Range; + #[cfg(feature = "master")] use gccjit::{FnAttribute, VarAttribute, Visibility}; use gccjit::{Function, GlobalKind, LValue, RValue, ToRValue, Type}; use rustc_abi::{self as abi, Align, HasDataLayout, Primitive, Size, WrappingRange}; +use rustc_attr_ir::Linkage; use rustc_codegen_ssa::traits::{ BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods, }; -use rustc_hir::attrs::Linkage; use rustc_hir::def::DefKind; use rustc_hir::def_id::LOCAL_CRATE; use rustc_log::tracing::trace; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::mir::interpret::{ - self, ConstAllocation, ErrorHandled, Scalar as InterpScalar, read_target_uint, + self, ConstAllocation, CtfeProvenance, ErrorHandled, Scalar as InterpScalar, read_target_uint, }; +use rustc_middle::mono::MonoItem; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Instance}; use rustc_span::def_id::DefId; use rustc_span::{bug, span_bug}; -use crate::base; +use crate::common::bytes_type_in_context; use crate::context::CodegenCx; +use crate::type_::struct_attributes; use crate::type_of::LayoutGccExt; pub(crate) fn const_alloc_to_gcc<'gcc, 'tcx>( @@ -99,10 +103,11 @@ impl<'gcc, 'tcx> StaticCodegenMethods for CodegenCx<'gcc, 'tcx> { let is_thread_local = attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL); let global = self.get_static_inner(def_id, val_llty); - #[cfg(feature = "master")] - if global.to_rvalue().get_type() != val_llty { - global.to_rvalue().set_type(val_llty); - } + debug_assert_eq!( + global.to_rvalue().get_type(), + val_llty, + "`predefine_static` declared this global with a type its initializer does not have" + ); // NOTE: Alignment from attributes has already been applied to the allocation. set_global_alignment(self, global, alloc.align); @@ -160,29 +165,52 @@ impl<'gcc, 'tcx> StaticCodegenMethods for CodegenCx<'gcc, 'tcx> { } // Wasm statics with custom link sections get special treatment as they - // go into custom sections of the wasm executable. - if self.tcx.sess.target.is_like_wasm { + // go into custom sections of the wasm executable. The exception to this + // is the `.init_array` section which are treated specially by the wasm linker. + if self.tcx.sess.target.is_like_wasm + && attrs + .link_section + .map(|link_section| !link_section.as_str().starts_with(".init_array")) + .unwrap_or(true) + { if let Some(_section) = attrs.link_section { unimplemented!(); } - } else { - // FIXME(antoyo): set link section. + } else if let Some(_section) = attrs.link_section { + #[cfg(feature = "master")] + global.add_attribute(VarAttribute::Section(_section.as_str())); } - if attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER) - || attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) - { - self.add_used_global(global.to_rvalue()); + if attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER) { + // To copy the conditions from the LLVM backend... + assert!(!attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)); + self.add_used_global(global); + } + if attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) { + // To copy the conditions from the LLVM backend... + assert!(!attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER)); + self.add_retained_global(global); } } } impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { - /// Add a global value to a list to be stored in the `llvm.used` variable, an array of i8*. - pub fn add_used_global(&mut self, _global: RValue<'gcc>) { - // FIXME(antoyo) + /// Need to have the `SHF_GNU_RETAIN` flag, so needs to use the `retain` attribute instead of + /// `used`. This is used by `#[used(linker)]`. + pub fn add_retained_global(&mut self, global: LValue<'gcc>) { + // We need to add the `used` C attribute in any case. + self.add_used_global(global); + #[cfg(feature = "master")] + global.add_attribute(VarAttribute::Retain); + } + + /// This is used by `#[used(compiler)]` and `#[used]`. + pub fn add_used_global(&mut self, _global: LValue<'gcc>) { + #[cfg(feature = "master")] + _global.add_attribute(VarAttribute::Used); } + // No need to have the `SHF_GNU_RETAIN` flag, so `used` attribute is ok. #[cfg_attr(not(feature = "master"), expect(unused_variables))] pub fn add_used_function(&self, function: Function<'gcc>) { #[cfg(feature = "master")] @@ -237,15 +265,14 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { return global; } - // FIXME: Once we stop removing globals in `codegen_static`, we can uncomment this code. - // let defined_in_current_codegen_unit = - // self.codegen_unit.items().contains_key(&MonoItem::Static(def_id)); - // assert!( - // !defined_in_current_codegen_unit, - // "consts::get_static() should always hit the cache for \ - // statics defined in the same CGU, but did not for `{:?}`", - // def_id - // ); + let defined_in_current_codegen_unit = + self.codegen_unit.items().contains_key(&MonoItem::Static(def_id)); + assert!( + !defined_in_current_codegen_unit, + "consts::get_static() should always hit the cache for \ + statics defined in the same CGU, but did not for `{:?}`", + def_id + ); let sym = self.tcx.symbol_name(instance).name; let fn_attrs = self.tcx.codegen_fn_attrs(def_id); @@ -309,71 +336,118 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { global } } -/// Converts a given const alloc to a gcc Rvalue, without any caching or deduplication. -/// YOU SHOULD NOT call this function directly - that may break the semantics of Rust. -/// Use `const_data_from_alloc` instead. -pub(crate) fn const_alloc_to_gcc_uncached<'gcc>( - cx: &CodegenCx<'gcc, '_>, - alloc: ConstAllocation<'_>, -) -> RValue<'gcc> { - let alloc = alloc.inner(); - let mut llvals = Vec::with_capacity(alloc.provenance().ptrs().len() + 1); - let dl = cx.data_layout(); - let pointer_size = dl.pointer_size().bytes() as usize; +/// One field of the packed struct that a constant allocation is lowered to. +enum AllocField { + /// A run of bytes carrying no provenance. + Bytes { range: Range }, + /// A pointer with provenance, occupying one target pointer worth of bytes. + Pointer { offset: usize, prov: CtfeProvenance }, +} + +/// The field-by-field shape of `alloc`. +/// +/// [`const_alloc_to_gcc_uncached`] and [`const_alloc_type`] have to agree exactly on this, down to +/// the empty trailing run an allocation ending on a pointer produces, so both derive the shape here +/// instead of each walking the allocation on its own. +fn alloc_fields(cx: &CodegenCx<'_, '_>, alloc: &interpret::Allocation) -> Vec { + let pointer_size = cx.data_layout().pointer_size().bytes() as usize; + let mut fields = Vec::with_capacity(alloc.provenance().ptrs().len() + 1); let mut next_offset = 0; for &(offset, prov) in alloc.provenance().ptrs().iter() { - let alloc_id = prov.alloc_id(); let offset = offset.bytes(); assert_eq!(offset as usize as u64, offset); let offset = offset as usize; if offset > next_offset { - // This `inspect` is okay since we have checked that it is not within a pointer with provenance, it - // is within the bounds of the allocation, and it doesn't affect interpreter execution - // (we inspect the result after interpreter execution). Any undef byte is replaced with - // some arbitrary byte value. - // - // FIXME: relay undef bytes to codegen as undef const bytes - let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(next_offset..offset); - llvals.push(cx.const_bytes(bytes)); + fields.push(AllocField::Bytes { range: next_offset..offset }); } - let ptr_offset = read_target_uint( - dl.endian, - // This `inspect` is okay since it is within the bounds of the allocation, it doesn't - // affect interpreter execution (we inspect the result after interpreter execution), - // and we properly interpret the provenance as a relocation pointer offset. - alloc.inspect_with_uninit_and_ptr_outside_interpreter(offset..(offset + pointer_size)), - ) - .expect("const_alloc_to_gcc_uncached: could not read relocation pointer") - as u64; - - let address_space = cx.tcx.global_alloc(alloc_id).address_space(cx); - - llvals.push(cx.scalar_to_backend( - InterpScalar::from_pointer( - interpret::Pointer::new(prov, Size::from_bytes(ptr_offset)), - &cx.tcx, - ), - abi::Scalar::Initialized { - value: Primitive::Pointer(address_space), - valid_range: WrappingRange::full(dl.pointer_size()), - }, - cx.type_i8p_ext(address_space), - )); + fields.push(AllocField::Pointer { offset, prov }); next_offset = offset + pointer_size; } if alloc.len() >= next_offset { - let range = next_offset..alloc.len(); - // This `inspect` is okay since we have check that it is after all provenance, it is - // within the bounds of the allocation, and it doesn't affect interpreter execution (we - // inspect the result after interpreter execution). Any undef byte is replaced with some - // arbitrary byte value. - // - // FIXME: relay undef bytes to codegen as undef const bytes - let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(range); - llvals.push(cx.const_bytes(bytes)); + fields.push(AllocField::Bytes { range: next_offset..alloc.len() }); } + fields +} + +/// The type [`const_alloc_to_gcc`] gives `alloc`, computed without building any rvalue. +/// +/// This lets `predefine_static` declare a static's global with the type its initializer will have, +/// so that the two never disagree. It must not reach for the rvalue of anything it points at: +/// during the predefine pass the pointee may not be declared yet, and `alloc_to_backend` would +/// declare it with the wrong type behind our back. +pub(crate) fn const_alloc_type<'gcc>( + cx: &CodegenCx<'gcc, '_>, + alloc: ConstAllocation<'_>, +) -> Type<'gcc> { + let fields: Vec<_> = alloc_fields(cx, alloc.inner()) + .into_iter() + .map(|field| match field { + AllocField::Bytes { range } => bytes_type_in_context(cx, range.len()), + AllocField::Pointer { prov, .. } => { + let address_space = cx.tcx.global_alloc(prov.alloc_id()).address_space(cx); + cx.type_i8p_ext(address_space) + } + }) + .collect(); + cx.type_struct(&fields, &struct_attributes(true, None)) +} + +/// Converts a given const alloc to a gcc Rvalue, without any caching or deduplication. +/// YOU SHOULD NOT call this function directly - that may break the semantics of Rust. +/// Use `const_data_from_alloc` instead. +pub(crate) fn const_alloc_to_gcc_uncached<'gcc>( + cx: &CodegenCx<'gcc, '_>, + alloc: ConstAllocation<'_>, +) -> RValue<'gcc> { + let alloc = alloc.inner(); + let dl = cx.data_layout(); + let pointer_size = dl.pointer_size(); + + let llvals: Vec<_> = alloc_fields(cx, alloc) + .into_iter() + .map(|field| match field { + AllocField::Bytes { range } => { + // This `inspect` is okay since we have checked that it is not within a pointer with + // provenance, it is within the bounds of the allocation, and it doesn't affect + // interpreter execution (we inspect the result after interpreter execution). Any + // undef byte is replaced with some arbitrary byte value. + // + // FIXME: relay undef bytes to codegen as undef const bytes + cx.const_bytes(alloc.inspect_with_uninit_and_ptr_outside_interpreter(range)) + } + AllocField::Pointer { offset, prov } => { + let ptr_offset = read_target_uint( + dl.endian, + // This `inspect` is okay since it is within the bounds of the allocation, it + // doesn't affect interpreter execution (we inspect the result after interpreter + // execution), and we properly interpret the provenance as a relocation pointer + // offset. + alloc.inspect_with_uninit_and_ptr_outside_interpreter( + offset..(offset + pointer_size.bytes() as usize), + ), + ) + .expect("const_alloc_to_gcc_uncached: could not read relocation pointer") + as u64; + + let address_space = cx.tcx.global_alloc(prov.alloc_id()).address_space(cx); + + cx.scalar_to_backend( + InterpScalar::from_pointer( + interpret::Pointer::new(prov, Size::from_bytes(ptr_offset)), + &cx.tcx, + ), + abi::Scalar::Initialized { + value: Primitive::Pointer(address_space), + valid_range: WrappingRange::full(pointer_size), + }, + cx.type_i8p_ext(address_space), + ) + } + }) + .collect(); + // FIXME(bjorn3) avoid wrapping in a struct when there is only a single element. cx.const_struct(&llvals, true) } @@ -394,10 +468,10 @@ fn check_and_apply_linkage<'gcc, 'tcx>( ) -> LValue<'gcc> { let is_tls = attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL); if let Some(linkage) = attrs.import_linkage { - // Declare a symbol `foo` with the desired linkage. - let global1 = - cx.declare_global_with_linkage(sym, cx.type_i8(), base::global_linkage_to_gcc(linkage)); + // Whatever the flavour, an import is an undefined reference to a symbol defined elsewhere. + let global1 = cx.declare_global_with_linkage(sym, cx.type_i8(), GlobalKind::Imported); + // Only `extern_weak` lets the symbol stay unresolved, in which case it reads as null. if linkage == Linkage::ExternalWeak { #[cfg(feature = "master")] global1.add_attribute(VarAttribute::Weak); @@ -411,8 +485,13 @@ fn check_and_apply_linkage<'gcc, 'tcx>( // zero. let real_name = format!("_rust_extern_with_linkage_{:016x}_{sym}", cx.tcx.stable_crate_id(LOCAL_CRATE)); - let global2 = cx.define_global(&real_name, gcc_type, is_tls, attrs.link_section); - // FIXME(antoyo): set linkage. + let global2 = cx.define_global( + &real_name, + gcc_type, + GlobalKind::Internal, + is_tls, + attrs.link_section, + ); let value = cx.const_ptrcast(global1.get_address(None), gcc_type); global2.global_set_initializer_rvalue(value); global2 diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 38e0e5f329f76..5f342e8b2dc33 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -1,6 +1,8 @@ use std::cell::{Cell, RefCell}; use std::collections::HashMap; +#[cfg(feature = "master")] +use gccjit::Region; use gccjit::{Block, CType, Context, Function, FunctionType, LValue, Location, RValue, Type}; use rustc_abi::{Align, HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx}; use rustc_codegen_ssa::base::wants_msvc_seh; @@ -25,6 +27,13 @@ use rustc_target::spec::{HasTargetSpec, HasX86AbiOpt, Target, TlsModel, X86Abi}; use crate::abi::conv_to_fn_attribute; use crate::callee::get_fn; use crate::common::SignType; +use crate::type_::StructTypeKey; + +#[cfg(feature = "master")] +pub struct PendingCleanup<'gcc> { + pub region: Region<'gcc>, + pub landing_pad: Block<'gcc>, +} #[cfg_attr(not(feature = "master"), expect(dead_code))] pub struct CodegenCx<'gcc, 'tcx> { @@ -72,6 +81,7 @@ pub struct CodegenCx<'gcc, 'tcx> { pub supports_128bit_integers: bool, pub supports_f16_type: bool, + pub supports_f16b_type: bool, pub supports_f32_type: bool, pub supports_f64_type: bool, pub supports_f128_type: bool, @@ -84,7 +94,14 @@ pub struct CodegenCx<'gcc, 'tcx> { pub types: RefCell, Option), Type<'gcc>>>, pub tcx: TyCtxt<'tcx>, - pub struct_types: RefCell>, Type<'gcc>>>, + /// Cache of the anonymous struct types. + pub struct_types: RefCell, Type<'gcc>>>, + + /// Cache of the array types used for runs of constant bytes, keyed by element type and count. + /// + /// libgccjit mints a fresh type on every `new_array_type`, and struct types are keyed on their + /// field types, so without this two equal byte runs would yield two distinct anonymous structs. + pub byte_array_types: RefCell, u64), Type<'gcc>>>, /// Cache instances of monomorphic and polymorphic items pub instances: RefCell, LValue<'gcc>>>, @@ -125,8 +142,14 @@ pub struct CodegenCx<'gcc, 'tcx> { pub pointee_infos: RefCell, Size), Option>>, + /// Blocks that are cleanup landing pads, so `invoke` can tell an unwind + /// edge into a cleanup from a catch/terminate. #[cfg(feature = "master")] - pub cleanup_blocks: RefCell>>, + pub landing_pads: RefCell>>, + /// Cleanup regions to be filled in once the function is fully codegened + /// (done in `populate_cleanup_regions`). + #[cfg(feature = "master")] + pub pending_cleanups: RefCell>>, /// The alignment of a u128/i128 type. // We cache this, since it is needed for alignment checks during loads. pub int128_align: Align, @@ -140,6 +163,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { tcx: TyCtxt<'tcx>, supports_128bit_integers: bool, supports_f16_type: bool, + supports_f16b_type: bool, supports_f32_type: bool, supports_f64_type: bool, supports_f128_type: bool, @@ -225,12 +249,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { let isize_type = usize_type; let bool_type = context.new_type::(); - let mut functions = FxHashMap::default(); - let builtins = ["abort"]; - - for builtin in builtins.iter() { - functions.insert(builtin.to_string(), context.get_builtin_function(builtin)); - } + let functions = FxHashMap::default(); let mut cx = Self { int128_align: tcx @@ -276,6 +295,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { supports_128bit_integers, supports_f16_type, + supports_f16b_type, supports_f32_type, supports_f64_type, supports_f128_type, @@ -297,6 +317,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { types: Default::default(), tcx, struct_types: Default::default(), + byte_array_types: Default::default(), local_gen_sym_counter: Cell::new(0), global_gen_sym_counter: Cell::new(0), eh_personality: Cell::new(None), @@ -304,13 +325,43 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { rust_try_fn: Cell::new(None), pointee_infos: Default::default(), #[cfg(feature = "master")] - cleanup_blocks: Default::default(), + landing_pads: Default::default(), + #[cfg(feature = "master")] + pending_cleanups: Default::default(), }; // FIXME(antoyo): instead of doing this, add SsizeT to libgccjit. cx.isize_type = usize_type.to_signed(&cx); cx } + /// Fill in the member blocks of every pending cleanup region. + /// + /// Clone all blocks reachable from a cleanup block into the cleanup region. + #[cfg(feature = "master")] + pub fn populate_cleanup_regions(&self) { + let pending = std::mem::take(&mut *self.pending_cleanups.borrow_mut()); + + for cleanup in pending { + // The landing pad is the region's entry, so it must come first. + let mut blocks = vec![]; + let mut visited = FxHashSet::default(); + let mut stack = vec![cleanup.landing_pad]; + while let Some(block) = stack.pop() { + if !visited.insert(block) { + continue; + } + blocks.push(block); + stack.extend(block.get_successors()); + } + + for clone in gccjit::clone_blocks(&blocks) { + cleanup.region.add_block(clone); + } + } + + self.landing_pads.borrow_mut().clear(); + } + pub fn rvalue_as_function(&self, value: RValue<'gcc>) -> Function<'gcc> { let function: Function<'gcc> = unsafe { std::mem::transmute(value) }; debug_assert!( diff --git a/compiler/rustc_codegen_gcc/src/declare.rs b/compiler/rustc_codegen_gcc/src/declare.rs index 4174eebcf7b02..32bb7c3aa349e 100644 --- a/compiler/rustc_codegen_gcc/src/declare.rs +++ b/compiler/rustc_codegen_gcc/src/declare.rs @@ -1,12 +1,12 @@ #[cfg(feature = "master")] -use gccjit::{FnAttribute, ToRValue}; +use gccjit::{FnAttribute, ToRValue, VarAttribute}; use gccjit::{Function, FunctionType, GlobalKind, LValue, RValue, Type}; use rustc_codegen_ssa::traits::BaseTypeCodegenMethods; use rustc_middle::ty::Ty; use rustc_span::Symbol; use rustc_target::callconv::FnAbi; -use crate::abi::{FnAbiGcc, FnAbiGccExt}; +use crate::abi::FnAbiGccExt; use crate::context::CodegenCx; impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { @@ -14,6 +14,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { &self, name: &str, ty: Type<'gcc>, + global_kind: GlobalKind, is_tls: bool, link_section: Option, ) -> LValue<'gcc> { @@ -24,11 +25,14 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { global.set_tls_model(self.tls_model); } if let Some(link_section) = link_section { + #[cfg(feature = "master")] + global.add_attribute(VarAttribute::Section(link_section.as_str())); + #[cfg(not(feature = "master"))] global.set_link_section(link_section.as_str()); } global } else { - self.declare_global(name, ty, GlobalKind::Exported, is_tls, link_section) + self.declare_global(name, ty, global_kind, is_tls, link_section) } } @@ -73,6 +77,9 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { global.set_tls_model(self.tls_model); } if let Some(link_section) = link_section { + #[cfg(feature = "master")] + global.add_attribute(VarAttribute::Section(link_section.as_str())); + #[cfg(not(feature = "master"))] global.set_link_section(link_section.as_str()); } let global_address = global.get_address(None); @@ -110,22 +117,22 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { } pub fn declare_fn(&self, name: &str, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Function<'gcc> { - let FnAbiGcc { - return_type, - arguments_type, - is_c_variadic, - on_stack_param_indices, - #[cfg(feature = "master")] - fn_attributes, - } = fn_abi.gcc_type(self); + let fn_abi_gcc = fn_abi.gcc_type(self); #[cfg(feature = "master")] let conv = fn_abi.gcc_cconv(self); #[cfg(not(feature = "master"))] let conv = None; - let func = declare_raw_fn(self, name, conv, return_type, &arguments_type, is_c_variadic); - self.on_stack_function_params.borrow_mut().insert(func, on_stack_param_indices); + let func = declare_raw_fn( + self, + name, + conv, + fn_abi_gcc.return_type, + &fn_abi_gcc.arguments_type, + fn_abi_gcc.is_c_variadic, + ); + self.on_stack_function_params.borrow_mut().insert(func, fn_abi_gcc.on_stack_param_indices); #[cfg(feature = "master")] - for fn_attr in fn_attributes { + for fn_attr in fn_abi_gcc.fn_attributes { func.add_attribute(fn_attr); } func @@ -135,10 +142,11 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { &self, name: &str, ty: Type<'gcc>, + global_kind: GlobalKind, is_tls: bool, link_section: Option, ) -> LValue<'gcc> { - self.get_or_insert_global(name, ty, is_tls, link_section) + self.get_or_insert_global(name, ty, global_kind, is_tls, link_section) } pub fn get_declared_value(&self, name: &str) -> Option> { diff --git a/compiler/rustc_codegen_gcc/src/diagnostics.rs b/compiler/rustc_codegen_gcc/src/diagnostics.rs index de633d3bdde79..67723ebd2f30b 100644 --- a/compiler/rustc_codegen_gcc/src/diagnostics.rs +++ b/compiler/rustc_codegen_gcc/src/diagnostics.rs @@ -20,10 +20,6 @@ pub(crate) struct LtoBitcodeFromRlib { pub gcc_err: String, } -#[derive(Diagnostic)] -#[diag("explicit tail calls with the 'become' keyword are not implemented in the GCC backend")] -pub(crate) struct ExplicitTailCallsUnsupported; - #[derive(Diagnostic)] #[diag("asm contains a NUL byte")] pub(crate) struct NulBytesInAsm { diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs index 24f552fed32c6..90d08d13a080a 100644 --- a/compiler/rustc_codegen_gcc/src/gcc_util.rs +++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs @@ -1,10 +1,17 @@ -#[cfg(feature = "master")] +use std::borrow::Cow; +use std::collections::HashSet; +use std::env; + use gccjit::Context; +#[cfg(feature = "master")] +use gccjit::Version; use rustc_codegen_ssa::target_features; use rustc_data_structures::smallvec::{SmallVec, smallvec}; -use rustc_session::EarlySession; use rustc_session::config::NATIVE_CPU; -use rustc_target::spec::{Arch, Target}; +use rustc_session::{EarlySession, Session}; +use rustc_target::spec::{ + Arch, MergeFunctions, RelocModel, StackProbeType, StackProtector, Target, +}; fn gcc_features_by_flags(sess: &EarlySession, features: &mut Vec) { target_features::retpoline_features_by_flags(sess, features); @@ -110,29 +117,190 @@ pub fn to_gcc_features<'a>(target: &Target, s: &'a str) -> SmallVec<[&'a str; 2] fn arch_to_gcc(name: &str) -> &str { match name { "M68000" => "68000", + "M68010" => "68010", "M68020" => "68020", + "M68030" => "68030", + "M68040" => "68040", + "M68060" => "68060", _ => name, } } -fn handle_native(name: &str) -> &str { +fn handle_native(name: &str) -> Cow<'_, str> { if name != NATIVE_CPU { - return arch_to_gcc(name); + return arch_to_gcc(name).into(); } #[cfg(feature = "master")] { // Get the native arch. let context = Context::default(); - context.get_target_info().arch().unwrap().to_str().unwrap() + Cow::Owned(context.get_target_info().arch().to_str().unwrap().to_string()) } #[cfg(not(feature = "master"))] unimplemented!(); } -pub fn target_cpu(sess: &EarlySession) -> &str { +pub fn target_cpu(sess: &EarlySession) -> Cow<'_, str> { match sess.opts.cg.target_cpu { Some(ref name) => handle_native(name), None => handle_native(sess.target.cpu.as_ref()), } } + +pub fn new_context<'gcc>(sess: &Session) -> Context<'gcc> { + let context = Context::default(); + if matches!(sess.target.arch, Arch::X86 | Arch::X86_64) { + context.add_command_line_option("-masm=intel"); + } + #[cfg(feature = "master")] + { + context.set_special_chars_allowed_in_func_names("$.*"); + let version = Version::get(); + let version = format!("{}.{}.{}", version.major, version.minor, version.patch); + context.set_output_ident(&format!( + "rustc version {} with libgccjit {}", + rustc_interface::util::rustc_version_str().unwrap_or("unknown version"), + version, + )); + } + if !sess.must_emit_unwind_tables() { + context.add_command_line_option("-fno-asynchronous-unwind-tables"); + } + + if sess.panic_strategy().unwinds() { + context.add_command_line_option("-fexceptions"); + context.add_driver_option("-fexceptions"); + } + + let disabled_features: HashSet<_> = sess + .opts + .cg + .target_feature + .split(',') + .filter(|feature| feature.starts_with('-')) + .map(|string| &string[1..]) + .collect(); + + if !disabled_features.contains("avx") && sess.target.arch == Arch::X86_64 { + // NOTE: we always enable AVX because the equivalent of llvm.x86.sse2.cmp.pd in GCC for + // SSE2 is multiple builtins, so we use the AVX __builtin_ia32_cmppd instead. + // FIXME(antoyo): use the proper builtins for llvm.x86.sse2.cmp.pd and similar. + context.add_command_line_option("-mavx"); + } + + for arg in &sess.opts.cg.llvm_args { + context.add_command_line_option(arg); + } + // NOTE: This is needed to compile the file src/intrinsic/archs.rs during a bootstrap of rustc. + context.add_command_line_option("-fno-var-tracking-assignments"); + // NOTE: an optimization (https://github.com/rust-lang/rustc_codegen_gcc/issues/53). + context.add_command_line_option("-fno-semantic-interposition"); + // NOTE: Rust relies on LLVM not doing TBAA (https://github.com/rust-lang/unsafe-code-guidelines/issues/292). + context.add_command_line_option("-fno-strict-aliasing"); + // NOTE: Rust relies on LLVM doing wrapping on overflow. + context.add_command_line_option("-fwrapv"); + // NOTE: This is needed to hide a warning caused by the alignment fix on byval arguments. + context.add_command_line_option("-Wno-psabi"); + + if let Some(model) = sess.code_model() { + use rustc_target::spec::CodeModel; + + context.add_command_line_option(match model { + CodeModel::Tiny => "-mcmodel=tiny", + CodeModel::Small => "-mcmodel=small", + CodeModel::Kernel => "-mcmodel=kernel", + CodeModel::Medium => "-mcmodel=medium", + CodeModel::Large => "-mcmodel=large", + }); + } + + match sess.stack_protector() { + StackProtector::All => context.add_command_line_option("-fstack-protector-all"), + StackProtector::Strong => context.add_command_line_option("-fstack-protector-strong"), + StackProtector::Basic => context.add_command_line_option("-fstack-protector"), + StackProtector::None => (), + } + + match sess.merge_functions() { + MergeFunctions::Disabled => { + context.add_command_line_option("-fno-ipa-icf-functions"); + } + // GCC always merges functions with trampolines rather than aliases, so we don't + // need to differentiate MergeFunctions::Trampolines and MergeFunctions::Aliases + // as trampolines are allowed either way. + MergeFunctions::Trampolines | MergeFunctions::Aliases => {} + } + + match sess.target.stack_probes { + StackProbeType::None => (), + StackProbeType::Inline | StackProbeType::InlineOrCall { .. } => { + context.add_command_line_option("-fstack-clash-protection") + } + // FIXME(antoyo): We should define the stack probe symbol to be __rust_probestack, but it seems GCC cannot do that. + StackProbeType::Call => (), + }; + + add_pic_option(&context, sess.relocation_model()); + + let target_cpu = target_cpu(sess); + if target_cpu != "generic" { + context.add_command_line_option(format!("-march={}", target_cpu)); + } + + if sess.opts.unstable_opts.function_sections.unwrap_or(sess.target.function_sections) { + context.add_command_line_option("-ffunction-sections"); + context.add_command_line_option("-fdata-sections"); + } + + if env::var("CG_GCCJIT_DUMP_RTL").as_deref() == Ok("1") { + context.add_command_line_option("-fdump-rtl-vregs"); + } + if env::var("CG_GCCJIT_DUMP_RTL_ALL").as_deref() == Ok("1") { + context.add_command_line_option("-fdump-rtl-all"); + } + if env::var("CG_GCCJIT_DUMP_TREE_ALL").as_deref() == Ok("1") { + context.add_command_line_option("-fdump-tree-all-eh"); + } + if env::var("CG_GCCJIT_DUMP_IPA_ALL").as_deref() == Ok("1") { + context.add_command_line_option("-fdump-ipa-all-eh"); + } + if env::var("CG_GCCJIT_DUMP_CODE").as_deref() == Ok("1") { + context.set_dump_code_on_compile(true); + } + if env::var("CG_GCCJIT_DUMP_GIMPLE").as_deref() == Ok("1") { + context.set_dump_initial_gimple(true); + } + if env::var("CG_GCCJIT_DUMP_EVERYTHING").as_deref() == Ok("1") { + context.set_dump_everything(true); + } + if env::var("CG_GCCJIT_KEEP_INTERMEDIATES").as_deref() == Ok("1") { + context.set_keep_intermediates(true); + } + if env::var("CG_GCCJIT_VERBOSE").as_deref() == Ok("1") { + context.add_driver_option("-v"); + } + + context +} + +pub fn add_pic_option<'gcc>(context: &Context<'gcc>, relocation_model: RelocModel) { + match relocation_model { + rustc_target::spec::RelocModel::Static => { + context.add_command_line_option("-fno-pie"); + context.add_driver_option("-fno-pie"); + } + rustc_target::spec::RelocModel::Pic => { + context.add_command_line_option("-fPIC"); + // NOTE: we use both add_command_line_option and add_driver_option because the usage in + // base (compile_codegen_unit) requires add_command_line_option while the usage + // in the back::write module (codegen) requires add_driver_option. + context.add_driver_option("-fPIC"); + } + rustc_target::spec::RelocModel::Pie => { + context.add_command_line_option("-fPIE"); + context.add_driver_option("-fPIE"); + } + model => eprintln!("Unsupported relocation model: {:?}", model), + } +} diff --git a/compiler/rustc_codegen_gcc/src/int.rs b/compiler/rustc_codegen_gcc/src/int.rs index dfae4eceebe44..de8762d1c06b4 100644 --- a/compiler/rustc_codegen_gcc/src/int.rs +++ b/compiler/rustc_codegen_gcc/src/int.rs @@ -21,12 +21,12 @@ use crate::context::CodegenCx; impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { pub fn gcc_urem(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { // 128-bit unsigned %: __umodti3 - self.multiplicative_operation(BinaryOp::Modulo, "mod", false, a, b) + self.division_operation(BinaryOp::Modulo, "mod", false, a, b) } pub fn gcc_srem(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { // 128-bit signed %: __modti3 - self.multiplicative_operation(BinaryOp::Modulo, "mod", true, a, b) + self.division_operation(BinaryOp::Modulo, "mod", true, a, b) } pub fn gcc_not(&self, a: RValue<'gcc>) -> RValue<'gcc> { @@ -178,6 +178,9 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { } else { debug_assert!(a_type.dyncast_array().is_some()); debug_assert!(b_type.dyncast_array().is_some()); + if a_type != b_type { + b = self.gcc_int_cast(b, a_type); + } let signed = a_type.is_compatible_with(self.i128_type); let func_name = match (operation, signed) { (BinaryOp::Plus, true) => "__rust_i128_add", @@ -187,7 +190,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { _ => unreachable!("unexpected additive operation {:?}", operation), }; let param_a = self.context.new_parameter(self.location, a_type, "a"); - let param_b = self.context.new_parameter(self.location, b_type, "b"); + let param_b = self.context.new_parameter(self.location, a_type, "b"); let func = self.context.new_function( self.location, FunctionType::Extern, @@ -212,6 +215,27 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { self.additive_operation(BinaryOp::Minus, a, b) } + fn division_operation( + &self, + operation: BinaryOp, + operation_name: &str, + signed: bool, + mut a: RValue<'gcc>, + mut b: RValue<'gcc>, + ) -> RValue<'gcc> { + let a_type = a.get_type(); + if self.is_native_int_type(a_type) && self.is_native_int_type(b.get_type()) { + let typ = if signed { a_type.to_signed(self.cx) } else { a_type.to_unsigned(self.cx) }; + if !typ.is_compatible_with(a_type) { + a = self.context.new_cast(self.location, a, typ); + } + if !typ.is_compatible_with(b.get_type()) { + b = self.context.new_cast(self.location, b, typ); + } + } + self.multiplicative_operation(operation, operation_name, signed, a, b) + } + fn multiplicative_operation( &self, operation: BinaryOp, @@ -238,10 +262,13 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { } else { debug_assert!(a_type.dyncast_array().is_some()); debug_assert!(b_type.dyncast_array().is_some()); + if a_type != b_type { + b = self.gcc_int_cast(b, a_type); + } let sign = if signed { "" } else { "u" }; let func_name = format!("__{}{}ti3", sign, operation_name); let param_a = self.context.new_parameter(self.location, a_type, "a"); - let param_b = self.context.new_parameter(self.location, b_type, "b"); + let param_b = self.context.new_parameter(self.location, a_type, "b"); let func = self.context.new_function( self.location, FunctionType::Extern, @@ -255,15 +282,13 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { } pub fn gcc_sdiv(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { - // FIXME(antoyo): check if the types are signed? // 128-bit, signed: __divti3 - // FIXME(antoyo): convert the arguments to signed? - self.multiplicative_operation(BinaryOp::Divide, "div", true, a, b) + self.division_operation(BinaryOp::Divide, "div", true, a, b) } pub fn gcc_udiv(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { // 128-bit, unsigned: __udivti3 - self.multiplicative_operation(BinaryOp::Divide, "div", false, a, b) + self.division_operation(BinaryOp::Divide, "div", false, a, b) } pub fn gcc_checked_binop( @@ -432,7 +457,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { if self.is_non_native_int_type(a_type) || self.is_non_native_int_type(b_type) { // This algorithm is based on compiler-rt's __cmpti2: // https://github.com/llvm-mirror/compiler-rt/blob/f0745e8476f069296a7c71accedd061dce4cdf79/lib/builtins/cmpti2.c#L21 - let result = self.current_func().new_local(self.location, self.int_type, "icmp_result"); + let result = self.new_temp(self.current_func(), self.location, self.int_type); let block1 = self.current_func().new_block("block1"); let block2 = self.current_func().new_block("block2"); let block3 = self.current_func().new_block("block3"); @@ -462,9 +487,18 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { lhs_high = self.context.new_cast(self.location, lhs_high, unsigned_type); rhs_high = self.context.new_cast(self.location, rhs_high, unsigned_type); } - // FIXME(antoyo): we probably need to handle signed comparison for unsigned - // integers. - _ => (), + IntPredicate::IntSGT + | IntPredicate::IntSGE + | IntPredicate::IntSLT + | IntPredicate::IntSLE => { + let signed_type = native_int_type.to_signed(self.cx); + lhs_high = self.context.new_cast(self.location, lhs_high, signed_type); + rhs_high = self.context.new_cast(self.location, rhs_high, signed_type); + } + IntPredicate::IntEQ | IntPredicate::IntNE => { + lhs_high = self.context.new_cast(self.location, lhs_high, unsigned_type); + rhs_high = self.context.new_cast(self.location, rhs_high, unsigned_type); + } } let condition = self.context.new_comparison( @@ -602,9 +636,17 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { rhs = self.context.new_cast(self.location, rhs, unsigned_type); } } - // FIXME(antoyo): we probably need to handle signed comparison for unsigned - // integers. - _ => (), + IntPredicate::IntSGT + | IntPredicate::IntSGE + | IntPredicate::IntSLT + | IntPredicate::IntSLE => { + if !a_type.is_vector() { + let signed_type = a_type.to_signed(self.cx); + lhs = self.context.new_cast(self.location, lhs, signed_type); + rhs = self.context.new_cast(self.location, rhs, signed_type); + } + } + IntPredicate::IntEQ | IntPredicate::IntNE => (), } self.context.new_comparison(self.location, op.to_gcc_comparison(), lhs, rhs) } @@ -623,6 +665,9 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { } a ^ b } else { + if a_type != b_type { + b = self.gcc_int_cast(b, a_type); + } self.concat_low_high_rvalues( a_type, self.low(a) ^ self.low(b), @@ -832,6 +877,9 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { !a_native && !b_native, "both types should either be native or non-native for or operation" ); + if a_type != b_type { + b = self.gcc_int_cast(b, a_type); + } let native_int_type = a_type.dyncast_array().expect("get element type"); self.concat_low_high_rvalues( a_type, @@ -862,7 +910,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { self.bitwise_operation(BinaryOp::BitwiseOr, a, b, loc) } - // FIXME(antoyo): can we use https://github.com/rust-lang/compiler-builtins/blob/master/src/int/mod.rs#L379 instead? + // FIXME(antoyo): can we use https://github.com/rust-lang/compiler-builtins/blob/1a99c2aa295bb2d507fa0e67a3b5eef64fba92a0/libm/src/math/support/int_traits.rs#L485 instead? pub fn gcc_int_cast(&self, value: RValue<'gcc>, dest_typ: Type<'gcc>) -> RValue<'gcc> { let value_type = value.get_type(); if self.is_native_int_type_or_bool(dest_typ) && self.is_native_int_type_or_bool(value_type) @@ -906,6 +954,10 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { debug_assert!(value_type.dyncast_array().is_some()); let name_suffix = match self.type_kind(dest_typ) { + TypeKind::Half if dest_typ.is_compatible_with(self.type_f16()) => { + let value = self.int_to_float_cast(signed, value, self.float_type); + return self.context.new_cast(None, value, dest_typ); + } // cSpell:disable TypeKind::Float => "tisf", TypeKind::Double => "tidf", diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs b/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs index 3c1698df6dec2..1856c2468616d 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs @@ -24,6 +24,7 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "gcsss" => "__builtin_arm_gcsss", "isb" => "__builtin_arm_isb", "prefetch" => "__builtin_arm_prefetch", + "prefetch.ir" => "__builtin_arm_prefetch_ir", "range.prefetch" => "__builtin_arm_range_prefetch", "sme.in.streaming.mode" => "__builtin_arm_in_streaming_mode", "sve.aesd" => "__builtin_sve_svaesd_u8", @@ -53,6 +54,7 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "alignbyte" => "__builtin_amdgcn_alignbyte", "ashr.pk.i8.i32" => "__builtin_amdgcn_ashr_pk_i8_i32", "ashr.pk.u8.i32" => "__builtin_amdgcn_ashr_pk_u8_i32", + "asyncmark" => "__builtin_amdgcn_asyncmark", "buffer.wbinvl1" => "__builtin_amdgcn_buffer_wbinvl1", "buffer.wbinvl1.sc" => "__builtin_amdgcn_buffer_wbinvl1_sc", "buffer.wbinvl1.vol" => "__builtin_amdgcn_buffer_wbinvl1_vol", @@ -270,6 +272,7 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "fdot2c.f32.bf16" => "__builtin_amdgcn_fdot2c_f32_bf16", "flat.prefetch" => "__builtin_amdgcn_flat_prefetch", "fmul.legacy" => "__builtin_amdgcn_fmul_legacy", + "global.load.async.lds" => "__builtin_amdgcn_global_load_async_lds", "global.load.async.to.lds.b128" => { "__builtin_amdgcn_global_load_async_to_lds_b128" } @@ -361,11 +364,7 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "perm.pk16.b4.u4" => "__builtin_amdgcn_perm_pk16_b4_u4", "perm.pk16.b6.u4" => "__builtin_amdgcn_perm_pk16_b6_u4", "perm.pk16.b8.u4" => "__builtin_amdgcn_perm_pk16_b8_u4", - "permlane.bcast" => "__builtin_amdgcn_permlane_bcast", - "permlane.down" => "__builtin_amdgcn_permlane_down", "permlane.idx.gen" => "__builtin_amdgcn_permlane_idx_gen", - "permlane.up" => "__builtin_amdgcn_permlane_up", - "permlane.xor" => "__builtin_amdgcn_permlane_xor", "permlane16.var" => "__builtin_amdgcn_permlane16_var", "permlanex16.var" => "__builtin_amdgcn_permlanex16_var", "pk.add.max.i16" => "__builtin_amdgcn_pk_add_max_i16", @@ -375,6 +374,9 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "prng.b32" => "__builtin_amdgcn_prng_b32", "qsad.pk.u16.u8" => "__builtin_amdgcn_qsad_pk_u16_u8", "queue.ptr" => "__builtin_amdgcn_queue_ptr", + "raw.ptr.buffer.load.async.lds" => { + "__builtin_amdgcn_raw_ptr_buffer_load_async_lds" + } "raw.ptr.buffer.load.lds" => "__builtin_amdgcn_raw_ptr_buffer_load_lds", "rcp.legacy" => "__builtin_amdgcn_rcp_legacy", "rsq.legacy" => "__builtin_amdgcn_rsq_legacy", @@ -386,6 +388,7 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "s.barrier.signal.isfirst" => "__builtin_amdgcn_s_barrier_signal_isfirst", "s.barrier.signal.var" => "__builtin_amdgcn_s_barrier_signal_var", "s.barrier.wait" => "__builtin_amdgcn_s_barrier_wait", + "s.bitreplicate" => "__builtin_amdgcn_s_bitreplicate", "s.buffer.prefetch.data" => "__builtin_amdgcn_s_buffer_prefetch_data", "s.cluster.barrier" => "__builtin_amdgcn_s_cluster_barrier", "s.dcache.inv" => "__builtin_amdgcn_s_dcache_inv", @@ -412,6 +415,7 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "s.ttracedata" => "__builtin_amdgcn_s_ttracedata", "s.ttracedata.imm" => "__builtin_amdgcn_s_ttracedata_imm", "s.wait.asynccnt" => "__builtin_amdgcn_s_wait_asynccnt", + "s.wait.event" => "__builtin_amdgcn_s_wait_event", "s.wait.event.export.ready" => "__builtin_amdgcn_s_wait_event_export_ready", "s.wait.tensorcnt" => "__builtin_amdgcn_s_wait_tensorcnt", "s.waitcnt" => "__builtin_amdgcn_s_waitcnt", @@ -462,16 +466,18 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "smfmac.i32.16x16x64.i8" => "__builtin_amdgcn_smfmac_i32_16x16x64_i8", "smfmac.i32.32x32x32.i8" => "__builtin_amdgcn_smfmac_i32_32x32x32_i8", "smfmac.i32.32x32x64.i8" => "__builtin_amdgcn_smfmac_i32_32x32x64_i8", + "struct.ptr.buffer.load.async.lds" => { + "__builtin_amdgcn_struct_ptr_buffer_load_async_lds" + } "struct.ptr.buffer.load.lds" => "__builtin_amdgcn_struct_ptr_buffer_load_lds", "sudot4" => "__builtin_amdgcn_sudot4", "sudot8" => "__builtin_amdgcn_sudot8", "tensor.load.to.lds" => "__builtin_amdgcn_tensor_load_to_lds", - "tensor.load.to.lds.d2" => "__builtin_amdgcn_tensor_load_to_lds_d2", "tensor.store.from.lds" => "__builtin_amdgcn_tensor_store_from_lds", - "tensor.store.from.lds.d2" => "__builtin_amdgcn_tensor_store_from_lds_d2", "udot2" => "__builtin_amdgcn_udot2", "udot4" => "__builtin_amdgcn_udot4", "udot8" => "__builtin_amdgcn_udot8", + "wait.asyncmark" => "__builtin_amdgcn_wait_asyncmark", "wave.barrier" => "__builtin_amdgcn_wave_barrier", "wavefrontsize" => "__builtin_amdgcn_wavefrontsize", "workgroup.id.x" => "__builtin_amdgcn_workgroup_id_x", @@ -4844,7 +4850,11 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "add.rn.f" => "__nvvm_add_rn_f", "add.rn.ftz.f" => "__nvvm_add_rn_ftz_f", "add.rn.ftz.sat.f" => "__nvvm_add_rn_ftz_sat_f", + "add.rn.ftz.sat.f16" => "__nvvm_add_rn_ftz_sat_f16", + "add.rn.ftz.sat.v2f16" => "__nvvm_add_rn_ftz_sat_v2f16", "add.rn.sat.f" => "__nvvm_add_rn_sat_f", + "add.rn.sat.f16" => "__nvvm_add_rn_sat_f16", + "add.rn.sat.v2f16" => "__nvvm_add_rn_sat_v2f16", "add.rp.d" => "__nvvm_add_rp_d", "add.rp.f" => "__nvvm_add_rp_f", "add.rp.ftz.f" => "__nvvm_add_rp_ftz_f", @@ -5063,18 +5073,10 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "fma.rn.bf16x2" => "__nvvm_fma_rn_bf16x2", "fma.rn.d" => "__nvvm_fma_rn_d", "fma.rn.f" => "__nvvm_fma_rn_f", - "fma.rn.ftz.bf16" => "__nvvm_fma_rn_ftz_bf16", - "fma.rn.ftz.bf16x2" => "__nvvm_fma_rn_ftz_bf16x2", "fma.rn.ftz.f" => "__nvvm_fma_rn_ftz_f", - "fma.rn.ftz.relu.bf16" => "__nvvm_fma_rn_ftz_relu_bf16", - "fma.rn.ftz.relu.bf16x2" => "__nvvm_fma_rn_ftz_relu_bf16x2", - "fma.rn.ftz.sat.bf16" => "__nvvm_fma_rn_ftz_sat_bf16", - "fma.rn.ftz.sat.bf16x2" => "__nvvm_fma_rn_ftz_sat_bf16x2", "fma.rn.ftz.sat.f" => "__nvvm_fma_rn_ftz_sat_f", "fma.rn.relu.bf16" => "__nvvm_fma_rn_relu_bf16", "fma.rn.relu.bf16x2" => "__nvvm_fma_rn_relu_bf16x2", - "fma.rn.sat.bf16" => "__nvvm_fma_rn_sat_bf16", - "fma.rn.sat.bf16x2" => "__nvvm_fma_rn_sat_bf16x2", "fma.rn.sat.f" => "__nvvm_fma_rn_sat_f", "fma.rp.d" => "__nvvm_fma_rp_d", "fma.rp.f" => "__nvvm_fma_rp_f", @@ -5195,6 +5197,10 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "mul.rn.d" => "__nvvm_mul_rn_d", "mul.rn.f" => "__nvvm_mul_rn_f", "mul.rn.ftz.f" => "__nvvm_mul_rn_ftz_f", + "mul.rn.ftz.sat.f16" => "__nvvm_mul_rn_ftz_sat_f16", + "mul.rn.ftz.sat.v2f16" => "__nvvm_mul_rn_ftz_sat_v2f16", + "mul.rn.sat.f16" => "__nvvm_mul_rn_sat_f16", + "mul.rn.sat.v2f16" => "__nvvm_mul_rn_sat_v2f16", "mul.rp.d" => "__nvvm_mul_rp_d", "mul.rp.f" => "__nvvm_mul_rp_f", "mul.rp.ftz.f" => "__nvvm_mul_rp_ftz_f", @@ -5827,8 +5833,10 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "altivec.vmuleuh" => "__builtin_altivec_vmuleuh", "altivec.vmuleuw" => "__builtin_altivec_vmuleuw", "altivec.vmulhsd" => "__builtin_altivec_vmulhsd", + "altivec.vmulhsh" => "__builtin_altivec_vmulhsh", "altivec.vmulhsw" => "__builtin_altivec_vmulhsw", "altivec.vmulhud" => "__builtin_altivec_vmulhud", + "altivec.vmulhuh" => "__builtin_altivec_vmulhuh", "altivec.vmulhuw" => "__builtin_altivec_vmulhuw", "altivec.vmulosb" => "__builtin_altivec_vmulosb", "altivec.vmulosd" => "__builtin_altivec_vmulosd", @@ -5912,22 +5920,45 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "altivec.vsum4shs" => "__builtin_altivec_vsum4shs", "altivec.vsum4ubs" => "__builtin_altivec_vsum4ubs", "altivec.vsumsws" => "__builtin_altivec_vsumsws", + "altivec.vucmprhb" => "__builtin_altivec_vucmprhb", + "altivec.vucmprhh" => "__builtin_altivec_vucmprhh", + "altivec.vucmprhn" => "__builtin_altivec_vucmprhn", + "altivec.vucmprlb" => "__builtin_altivec_vucmprlb", + "altivec.vucmprlh" => "__builtin_altivec_vucmprlh", + "altivec.vucmprln" => "__builtin_altivec_vucmprln", "altivec.vupkhpx" => "__builtin_altivec_vupkhpx", "altivec.vupkhsb" => "__builtin_altivec_vupkhsb", "altivec.vupkhsh" => "__builtin_altivec_vupkhsh", + "altivec.vupkhsntob" => "__builtin_altivec_vupkhsntob", "altivec.vupkhsw" => "__builtin_altivec_vupkhsw", + "altivec.vupkint4tobf16" => "__builtin_altivec_vupkint4tobf16", + "altivec.vupkint4tofp32" => "__builtin_altivec_vupkint4tofp32", + "altivec.vupkint8tobf16" => "__builtin_altivec_vupkint8tobf16", + "altivec.vupkint8tofp32" => "__builtin_altivec_vupkint8tofp32", "altivec.vupklpx" => "__builtin_altivec_vupklpx", "altivec.vupklsb" => "__builtin_altivec_vupklsb", "altivec.vupklsh" => "__builtin_altivec_vupklsh", + "altivec.vupklsntob" => "__builtin_altivec_vupklsntob", "altivec.vupklsw" => "__builtin_altivec_vupklsw", "amo.ldat" => "__builtin_amo_ldat", + "amo.ldat.cond" => "__builtin_amo_ldat_cond", + "amo.ldat.csne" => "__builtin_amo_ldat_csne", "amo.lwat" => "__builtin_amo_lwat", + "amo.lwat.cond" => "__builtin_amo_lwat_cond", + "amo.lwat.csne" => "__builtin_amo_lwat_csne", + "amo.stdat" => "__builtin_amo_stdat", + "amo.stwat" => "__builtin_amo_stwat", "bcdadd" => "__builtin_ppc_bcdadd", "bcdadd.p" => "__builtin_ppc_bcdadd_p", "bcdcopysign" => "__builtin_ppc_bcdcopysign", "bcdsetsign" => "__builtin_ppc_bcdsetsign", + "bcdshift" => "__builtin_ppc_bcdshift", + "bcdshiftround" => "__builtin_ppc_bcdshiftround", "bcdsub" => "__builtin_ppc_bcdsub", "bcdsub.p" => "__builtin_ppc_bcdsub_p", + "bcdtruncate" => "__builtin_ppc_bcdtruncate", + "bcdunsignedshift" => "__builtin_ppc_bcdunsignedshift", + "bcdunsignedtruncate" => "__builtin_ppc_bcdunsignedtruncate", "bpermd" => "__builtin_bpermd", "cbcdtd" => "__builtin_cbcdtd", "cbcdtdd" => "__builtin_ppc_cbcdtd", @@ -6126,6 +6157,27 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "vsx.xxinsertw" => "__builtin_vsx_xxinsertw", "vsx.xxleqv" => "__builtin_vsx_xxleqv", "vsx.xxpermx" => "__builtin_vsx_xxpermx", + "xsaddaddsuqm" => "__builtin_xsaddaddsuqm", + "xsaddadduqm" => "__builtin_xsaddadduqm", + "xsaddsubsuqm" => "__builtin_xsaddsubsuqm", + "xsaddsubuqm" => "__builtin_xsaddsubuqm", + "xsmerge2t1uqm" => "__builtin_xsmerge2t1uqm", + "xsmerge2t2uqm" => "__builtin_xsmerge2t2uqm", + "xsmerge2t3uqm" => "__builtin_xsmerge2t3uqm", + "xsmerge3t1uqm" => "__builtin_xsmerge3t1uqm", + "xsrebase2t1uqm" => "__builtin_xsrebase2t1uqm", + "xsrebase2t2uqm" => "__builtin_xsrebase2t2uqm", + "xsrebase2t3uqm" => "__builtin_xsrebase2t3uqm", + "xsrebase2t4uqm" => "__builtin_xsrebase2t4uqm", + "xsrebase3t1uqm" => "__builtin_xsrebase3t1uqm", + "xsrebase3t2uqm" => "__builtin_xsrebase3t2uqm", + "xsrebase3t3uqm" => "__builtin_xsrebase3t3uqm", + "xxmulmul" => "__builtin_xxmulmul", + "xxmulmulhiadd" => "__builtin_xxmulmulhiadd", + "xxmulmulloadd" => "__builtin_xxmulmulloadd", + "xxssumudm" => "__builtin_xxssumudm", + "xxssumudmc" => "__builtin_xxssumudmc", + "xxssumudmcext" => "__builtin_xxssumudmcext", "zoned2packed" => "__builtin_ppc_zoned2packed", _ => unimplemented!("***** unsupported LLVM intrinsic {full_name}"), } @@ -6388,13 +6440,13 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { // spv "group.memory.barrier.with.group.sync" => "__builtin_spirv_group_barrier", "num.subgroups" => "__builtin_spirv_num_subgroups", + "subgroup.ballot" => "__builtin_spirv_subgroup_ballot", "subgroup.id" => "__builtin_spirv_subgroup_id", "subgroup.local.invocation.id" => { "__builtin_spirv_subgroup_local_invocation_id" } "subgroup.max.size" => "__builtin_spirv_subgroup_max_size", "subgroup.size" => "__builtin_spirv_subgroup_size", - "wave.ballot" => "__builtin_spirv_subgroup_ballot", _ => unimplemented!("***** unsupported LLVM intrinsic {full_name}"), } } @@ -8661,10 +8713,6 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "bmi.bextr.64" => "__builtin_ia32_bextr_u64", "bmi.bzhi.32" => "__builtin_ia32_bzhi_si", "bmi.bzhi.64" => "__builtin_ia32_bzhi_di", - "bmi.pdep.32" => "__builtin_ia32_pdep_si", - "bmi.pdep.64" => "__builtin_ia32_pdep_di", - "bmi.pext.32" => "__builtin_ia32_pext_si", - "bmi.pext.64" => "__builtin_ia32_pext_di", "cldemote" => "__builtin_ia32_cldemote", "clflushopt" => "__builtin_ia32_clflushopt", "clrssbsy" => "__builtin_ia32_clrssbsy", diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs b/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs index 41efe3e8209bf..ef381715c1ea2 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs @@ -5,6 +5,7 @@ use rustc_codegen_ssa::traits::BuilderMethods; use crate::builder::Builder; use crate::context::{CodegenCx, new_array_type}; +use crate::type_::{StructAttribute, apply_struct_attributes}; fn encode_key_128_type<'a, 'gcc, 'tcx>( builder: &Builder<'a, 'gcc, 'tcx>, @@ -22,8 +23,7 @@ fn encode_key_128_type<'a, 'gcc, 'tcx>( "EncodeKey128Output", &[field1, field2, field3, field4, field5, field6, field7], ); - #[cfg(feature = "master")] - encode_type.as_type().set_packed(); + apply_struct_attributes(encode_type.as_type(), &[StructAttribute::Packed]); (encode_type.as_type(), field1, field2) } @@ -44,8 +44,7 @@ fn encode_key_256_type<'a, 'gcc, 'tcx>( "EncodeKey256Output", &[field1, field2, field3, field4, field5, field6, field7, field8], ); - #[cfg(feature = "master")] - encode_type.as_type().set_packed(); + apply_struct_attributes(encode_type.as_type(), &[StructAttribute::Packed]); (encode_type.as_type(), field1, field2) } @@ -57,8 +56,7 @@ fn aes_output_type<'a, 'gcc, 'tcx>( let field2 = builder.context.new_field(None, m128i, "field2"); let aes_output_type = builder.context.new_struct_type(None, "AesOutput", &[field1, field2]); let typ = aes_output_type.as_type(); - #[cfg(feature = "master")] - typ.set_packed(); + apply_struct_attributes(typ, &[StructAttribute::Packed]); (typ, field1, field2) } @@ -80,8 +78,7 @@ fn wide_aes_output_type<'a, 'gcc, 'tcx>( "WideAesOutput", &[field1, field2, field3, field4, field5, field6, field7, field8, field9], ); - #[cfg(feature = "master")] - aes_output_type.as_type().set_packed(); + apply_struct_attributes(aes_output_type.as_type(), &[StructAttribute::Packed]); (aes_output_type.as_type(), field1, field2) } @@ -478,6 +475,26 @@ pub fn adjust_intrinsic_arguments<'a, 'b, 'gcc, 'tcx>( new_args.push(variable.get_address(None)); args = new_args.into(); } + "__builtin_ia32_2intersectd128" + | "__builtin_ia32_2intersectq128" + | "__builtin_ia32_2intersectd256" + | "__builtin_ia32_2intersectq256" + | "__builtin_ia32_2intersectd512" + | "__builtin_ia32_2intersectq512" => { + let old_args = args.to_vec(); + let mut new_args = vec![]; + let arg1_type = gcc_func.get_param_type(0); + let first_mask = + builder.current_func().new_local(None, arg1_type, "return_2intersect_arg1"); + let arg2_type = gcc_func.get_param_type(1); + let second_mask = + builder.current_func().new_local(None, arg2_type, "return_2intersect_arg2"); + new_args.push(first_mask.get_address(None)); + new_args.push(second_mask.get_address(None)); + new_args.push(old_args[0]); + new_args.push(old_args[1]); + args = new_args.into(); + } "__builtin_ia32_vpermt2varqi512_mask" | "__builtin_ia32_vpermt2varqi256_mask" | "__builtin_ia32_vpermt2varqi128_mask" @@ -489,6 +506,23 @@ pub fn adjust_intrinsic_arguments<'a, 'b, 'gcc, 'tcx>( let minus_one = builder.context.new_rvalue_from_int(arg4_type, -1); args = vec![new_args[1], new_args[0], new_args[2], minus_one].into(); } + "__builtin_ia32_fpclassph128_mask" + | "__builtin_ia32_fpclassph256_mask" + | "__builtin_ia32_fpclassph512_mask" + | "__builtin_ia32_fpclasspd128_mask" + | "__builtin_ia32_fpclassps128_mask" + | "__builtin_ia32_fpclasspd256_mask" + | "__builtin_ia32_fpclassps256_mask" + | "__builtin_ia32_fpclasspd512_mask" + | "__builtin_ia32_fpclassps512_mask" + | "__builtin_ia32_vpshufbitqmb128_mask" + | "__builtin_ia32_vpshufbitqmb256_mask" + | "__builtin_ia32_vpshufbitqmb512_mask" => { + let new_args = args.to_vec(); + let arg3_type = gcc_func.get_param_type(2); + let minus_one = builder.context.new_rvalue_from_int(arg3_type, -1); + args = vec![new_args[0], new_args[1], minus_one].into(); + } "__builtin_ia32_xrstor" | "__builtin_ia32_xrstor64" | "__builtin_ia32_xsavec" @@ -840,7 +874,7 @@ pub fn adjust_intrinsic_return_value<'a, 'gcc, 'tcx>( "__builtin_ia32_rdrand64_step" => { let random_number = args[0].dereference(None).to_rvalue(); let success_variable = - builder.current_func().new_local(None, return_value.get_type(), "success"); + builder.new_temp(builder.current_func(), None, return_value.get_type()); builder.llbb().add_assignment(None, success_variable, return_value); let field1 = builder.context.new_field(None, random_number.get_type(), "random_number"); @@ -854,6 +888,25 @@ pub fn adjust_intrinsic_return_value<'a, 'gcc, 'tcx>( &[random_number, success_variable.to_rvalue()], ); } + "__builtin_ia32_2intersectd128" + | "__builtin_ia32_2intersectq128" + | "__builtin_ia32_2intersectd256" + | "__builtin_ia32_2intersectq256" + | "__builtin_ia32_2intersectd512" + | "__builtin_ia32_2intersectq512" => { + let first_mask = args[0].dereference(None).to_rvalue(); + let second_mask = args[1].dereference(None).to_rvalue(); + let field1 = builder.context.new_field(None, first_mask.get_type(), "first_mask"); + let field2 = builder.context.new_field(None, second_mask.get_type(), "second_mask"); + let struct_type = + builder.context.new_struct_type(None, "vp2intersect_result", &[field1, field2]); + return_value = builder.context.new_struct_constructor( + None, + struct_type.as_type(), + None, + &[first_mask, second_mask], + ); + } "fma" => { let f16_type = builder.context.new_c_type(CType::Float16); return_value = builder.context.new_cast(None, return_value, f16_type); @@ -1182,6 +1235,9 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function "llvm.x86.avx512.mask.vpshufbitqmb.512" => "__builtin_ia32_vpshufbitqmb512_mask", "llvm.x86.avx512.mask.vpshufbitqmb.256" => "__builtin_ia32_vpshufbitqmb256_mask", "llvm.x86.avx512.mask.vpshufbitqmb.128" => "__builtin_ia32_vpshufbitqmb128_mask", + "llvm.x86.avx512.vpshufbitqmb.512" => "__builtin_ia32_vpshufbitqmb512_mask", + "llvm.x86.avx512.vpshufbitqmb.256" => "__builtin_ia32_vpshufbitqmb256_mask", + "llvm.x86.avx512.vpshufbitqmb.128" => "__builtin_ia32_vpshufbitqmb128_mask", "llvm.x86.avx512.mask.ucmp.w.512" => "__builtin_ia32_ucmpw512_mask", "llvm.x86.avx512.mask.ucmp.w.256" => "__builtin_ia32_ucmpw256_mask", "llvm.x86.avx512.mask.ucmp.w.128" => "__builtin_ia32_ucmpw128_mask", @@ -1339,11 +1395,20 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function "llvm.x86.avx512bf16.cvtne2ps2bf16.128" => "__builtin_ia32_cvtne2ps2bf16_v8bf", "llvm.x86.avx512bf16.cvtne2ps2bf16.256" => "__builtin_ia32_cvtne2ps2bf16_v16bf", "llvm.x86.avx512bf16.cvtne2ps2bf16.512" => "__builtin_ia32_cvtne2ps2bf16_v32bf", + "llvm.x86.vcvtneps2bf16128" => "__builtin_ia32_cvtneps2bf16_v4sf", + "llvm.x86.vcvtneps2bf16256" => "__builtin_ia32_cvtneps2bf16_v8sf", + "llvm.x86.avx512bf16.mask.cvtneps2bf16.128" => "__builtin_ia32_cvtneps2bf16_v4sf_mask", "llvm.x86.avx512bf16.cvtneps2bf16.256" => "__builtin_ia32_cvtneps2bf16_v8sf", "llvm.x86.avx512bf16.cvtneps2bf16.512" => "__builtin_ia32_cvtneps2bf16_v16sf", "llvm.x86.avx512bf16.dpbf16ps.128" => "__builtin_ia32_dpbf16ps_v4sf", "llvm.x86.avx512bf16.dpbf16ps.256" => "__builtin_ia32_dpbf16ps_v8sf", "llvm.x86.avx512bf16.dpbf16ps.512" => "__builtin_ia32_dpbf16ps_v16sf", + "llvm.x86.avx512.vp2intersect.d.128" => "__builtin_ia32_2intersectd128", + "llvm.x86.avx512.vp2intersect.q.128" => "__builtin_ia32_2intersectq128", + "llvm.x86.avx512.vp2intersect.d.256" => "__builtin_ia32_2intersectd256", + "llvm.x86.avx512.vp2intersect.q.256" => "__builtin_ia32_2intersectq256", + "llvm.x86.avx512.vp2intersect.d.512" => "__builtin_ia32_2intersectd512", + "llvm.x86.avx512.vp2intersect.q.512" => "__builtin_ia32_2intersectq512", "llvm.x86.pclmulqdq.512" => "__builtin_ia32_vpclmulqdq_v8di", "llvm.x86.pclmulqdq.256" => "__builtin_ia32_vpclmulqdq_v4di", "llvm.x86.avx512.pmulhu.w.512" => "__builtin_ia32_pmulhuw512_mask", @@ -1577,38 +1642,79 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function "llvm.x86.avx512.uitofp.round.v4f64.v4i64" => "__builtin_ia32_cvtuqq2pd256_mask", "llvm.x86.avx512.uitofp.round.v8f32.v8i64" => "__builtin_ia32_cvtuqq2ps512_mask", "llvm.x86.avx512.uitofp.round.v4f32.v4i64" => "__builtin_ia32_cvtuqq2ps256_mask", + "llvm.x86.avx512fp16.fpclass.ph.128" => "__builtin_ia32_fpclassph128_mask", + "llvm.x86.avx512fp16.mask.cmp.ph.128" => "__builtin_ia32_cmpph128_mask", + "llvm.x86.avx512fp16.fpclass.ph.256" => "__builtin_ia32_fpclassph256_mask", + "llvm.x86.avx512fp16.fpclass.ph.512" => "__builtin_ia32_fpclassph512_mask", + "llvm.x86.avx512fp16.mask.cmp.ph.256" => "__builtin_ia32_cmpph256_mask", + "llvm.x86.avx512fp16.mask.cmp.ph.512" => "__builtin_ia32_cmpph512_mask_round", + "llvm.x86.avx512.fpclass.pd.128" => "__builtin_ia32_fpclasspd128_mask", + "llvm.x86.avx512.fpclass.ps.128" => "__builtin_ia32_fpclassps128_mask", + "llvm.x86.avx512.fpclass.pd.256" => "__builtin_ia32_fpclasspd256_mask", + "llvm.x86.avx512.fpclass.ps.256" => "__builtin_ia32_fpclassps256_mask", + "llvm.x86.avx512.fpclass.pd.512" => "__builtin_ia32_fpclasspd512_mask", + "llvm.x86.avx512.fpclass.ps.512" => "__builtin_ia32_fpclassps512_mask", // FIXME: support the tile builtins: "llvm.x86.ldtilecfg" => "__builtin_trap", "llvm.x86.sttilecfg" => "__builtin_trap", "llvm.x86.tileloadd64" => "__builtin_trap", + "llvm.x86.tileloadd64.internal" => "__builtin_trap", "llvm.x86.tilerelease" => "__builtin_trap", "llvm.x86.tilestored64" => "__builtin_trap", + "llvm.x86.tilestored64.internal" => "__builtin_trap", "llvm.x86.tileloaddrs64" => "__builtin_trap", + "llvm.x86.tileloaddrs64.internal" => "__builtin_trap", "llvm.x86.tileloaddt164" => "__builtin_trap", + "llvm.x86.tileloaddt164.internal" => "__builtin_trap", "llvm.x86.tileloaddrst164" => "__builtin_trap", + "llvm.x86.tileloaddrst164.internal" => "__builtin_trap", "llvm.x86.tilezero" => "__builtin_trap", + "llvm.x86.tilezero.internal" => "__builtin_trap", "llvm.x86.tilemovrow" => "__builtin_trap", + "llvm.x86.tilemovrow.internal" => "__builtin_trap", "llvm.x86.tilemovrowi" => "__builtin_trap", "llvm.x86.tdpbhf8ps" => "__builtin_trap", + "llvm.x86.tdpbhf8ps.internal" => "__builtin_trap", "llvm.x86.tdphbf8ps" => "__builtin_trap", + "llvm.x86.tdphbf8ps.internal" => "__builtin_trap", "llvm.x86.tdpbf8ps" => "__builtin_trap", + "llvm.x86.tdpbf8ps.internal" => "__builtin_trap", "llvm.x86.tdphf8ps" => "__builtin_trap", + "llvm.x86.tdphf8ps.internal" => "__builtin_trap", "llvm.x86.tdpbf16ps" => "__builtin_trap", + "llvm.x86.tdpbf16ps.internal" => "__builtin_trap", "llvm.x86.tdpbssd" => "__builtin_trap", + "llvm.x86.tdpbssd.internal" => "__builtin_trap", "llvm.x86.tdpbsud" => "__builtin_trap", + "llvm.x86.tdpbsud.internal" => "__builtin_trap", "llvm.x86.tdpbusd" => "__builtin_trap", + "llvm.x86.tdpbusd.internal" => "__builtin_trap", "llvm.x86.tdpbuud" => "__builtin_trap", + "llvm.x86.tdpbuud.internal" => "__builtin_trap", "llvm.x86.tdpfp16ps" => "__builtin_trap", + "llvm.x86.tdpfp16ps.internal" => "__builtin_trap", "llvm.x86.tmmultf32ps" => "__builtin_trap", + "llvm.x86.tmmultf32ps.internal" => "__builtin_trap", "llvm.x86.tcvtrowps2phh" => "__builtin_trap", + "llvm.x86.tcvtrowps2phh.internal" => "__builtin_trap", "llvm.x86.tcvtrowps2phl" => "__builtin_trap", + "llvm.x86.tcvtrowps2phl.internal" => "__builtin_trap", "llvm.x86.tcvtrowd2ps" => "__builtin_trap", + "llvm.x86.tcvtrowd2ps.internal" => "__builtin_trap", "llvm.x86.tcvtrowd2psi" => "__builtin_trap", "llvm.x86.tcvtrowps2phhi" => "__builtin_trap", "llvm.x86.tcvtrowps2phli" => "__builtin_trap", + "llvm.x86.tcvtrowps2bf16h" => "__builtin_trap", + "llvm.x86.tcvtrowps2bf16h.internal" => "__builtin_trap", + "llvm.x86.tcvtrowps2bf16hi" => "__builtin_trap", + "llvm.x86.tcvtrowps2bf16l" => "__builtin_trap", + "llvm.x86.tcvtrowps2bf16l.internal" => "__builtin_trap", + "llvm.x86.tcvtrowps2bf16li" => "__builtin_trap", "llvm.x86.tcmmimfp16ps" => "__builtin_trap", + "llvm.x86.tcmmimfp16ps.internal" => "__builtin_trap", "llvm.x86.tcmmrlfp16ps" => "__builtin_trap", + "llvm.x86.tcmmrlfp16ps.internal" => "__builtin_trap", // NOTE: this file is generated by https://github.com/GuillaumeGomez/llvmint/blob/master/generate_list.py _ => map_arch_intrinsic(name), diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 41fa7b3f9f162..0f735172667ab 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -4,7 +4,7 @@ mod simd; #[cfg(feature = "master")] use std::iter; -use gccjit::{ComparisonOp, Function, FunctionType, RValue, ToRValue, Type, UnaryOp}; +use gccjit::{CType, ComparisonOp, Function, FunctionType, RValue, ToRValue, Type, UnaryOp}; use rustc_abi::{Align, BackendRepr, HasDataLayout, WrappingRange}; use rustc_codegen_ssa::base::wants_msvc_seh; use rustc_codegen_ssa::common::IntPredicate; @@ -24,6 +24,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_middle::ty::layout::FnAbiOf; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Instance, Ty}; +use rustc_session::config::OptLevel; use rustc_span::{Span, Symbol, bug, span_bug, sym}; use rustc_target::callconv::{ArgAbi, PassMode}; @@ -80,7 +81,6 @@ fn get_simple_intrinsic<'gcc, 'tcx>( sym::floorf64 => "floor", sym::ceilf32 => "ceilf", sym::ceilf64 => "ceil", - sym::powf128 => return float_intrinsic(cx, cx.type_f128(), "powf128"), sym::truncf32 => "truncf", sym::truncf64 => "trunc", // We match the LLVM backend and lower this to `rint`. @@ -88,7 +88,6 @@ fn get_simple_intrinsic<'gcc, 'tcx>( sym::round_ties_even_f64 => "rint", sym::roundf32 => "roundf", sym::roundf64 => "round", - sym::abort => "abort", _ => return None, }; Some(cx.context.get_builtin_function(gcc_name)) @@ -180,14 +179,11 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc let simple = get_simple_intrinsic(self, name); let value = match name { - _ if simple.is_some() => { - let func = simple.expect("simple intrinsic function"); - self.cx.context.new_call( - self.location, - func, - &args.iter().map(|arg| arg.immediate()).collect::>(), - ) - } + _ if let Some(func) = simple => self.cx.context.new_call( + self.location, + func, + &args.iter().map(|arg| arg.immediate()).collect::>(), + ), // FIXME(antoyo): We can probably remove these and use the fallback intrinsic implementation. sym::minimumf32 | sym::minimumf64 | sym::maximumf32 | sym::maximumf64 => { let (ty, func_name) = match name { @@ -322,7 +318,9 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc unimplemented!(); } sym::va_arg => { - unimplemented!(); + let va_list = args[0].immediate(); + let gcc_type = self.immediate_backend_type(result.layout); + self.va_arg(va_list, gcc_type) } sym::volatile_load | sym::unaligned_volatile_load => { @@ -611,7 +609,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc self.on_stack_function_params.borrow_mut().insert(func, FxHashSet::default()); - crate::attributes::from_fn_attrs(self, func, instance); + crate::attributes::from_fn_attrs(self, func, instance, None); func }; @@ -663,17 +661,27 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc llret } - fn abort(&mut self) { - let func = self.context.get_builtin_function("abort"); - let func: RValue<'gcc> = unsafe { std::mem::transmute(func) }; - self.call(self.type_void(), None, None, func, ReturnSlot::Direct, &[], None, None); + fn abort_immediate(&mut self) { + let func = self.context.get_builtin_function("__builtin_trap"); + self.block.add_eval(self.location, self.context.new_call(self.location, func, &[])); } fn assume(&mut self, value: Self::Value) { - // FIXME(antoyo): switch to assume when it exists. - // Or use something like this: - // #define __assume(cond) do { if (!(cond)) __builtin_unreachable(); } while (0) - self.expect(value, true); + // libgccjit currently has no direct equivalent of LLVM's `llvm.assume`, + // so use the idiom `if (!cond) __builtin_unreachable()`. + // FIXME: this should use IFN_ASSUME when we have internal functions in + // libgccjit. + if self.sess().opts.optimize == OptLevel::No { + return; + } + let then_block = self.append_sibling_block("assume_holds"); + let unreachable_block = self.append_sibling_block("assume_violated"); + self.block.end_with_conditional(self.location, value, then_block, unreachable_block); + + self.switch_to_block(unreachable_block); + self.unreachable(); + + self.switch_to_block(then_block); } fn expect(&mut self, cond: Self::Value, _expected: bool) -> Self::Value { @@ -691,8 +699,18 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc self.context.new_rvalue_from_int(self.int_type, 0) } - fn va_start(&mut self, _va_list: RValue<'gcc>) { - unimplemented!(); + fn va_start(&mut self, va_list: RValue<'gcc>) { + let func = self.context.get_builtin_function("__builtin_va_start"); + + let va_list_type = self.context.new_c_type(CType::VaList); + let va_list = self.context.new_cast(self.location, va_list, va_list_type.make_pointer()); + + // Pre-C23 requires that the last "normal" argument was passed to va_start. + // Just pass 0, this appears to be handled correctly. + let last_normal_arg = self.context.new_rvalue_from_int(self.int_type, 0); + + let call = self.context.new_call(self.location, func, &[va_list, last_normal_arg]); + self.block.add_eval(self.location, call); } fn retag_reg(&mut self, _ptr: Self::Value, _info: &RetagInfo) -> Self::Value { @@ -950,7 +968,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { let else_block = func.new_block("else"); let after_block = func.new_block("after"); - let result = func.new_local(None, self.u32_type, "zeros"); + let result = self.new_temp(func, None, self.u32_type); let zero = self.cx.gcc_zero(arg.get_type()); let cond = self.gcc_icmp(IntPredicate::IntEQ, arg, zero); self.llbb().end_with_conditional(None, cond, then_block, else_block); @@ -1031,7 +1049,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { // else call it on the 64 high bits and add 64. In the else case, 64 high bits can't be 0 // because arg is not 0. - let result = self.current_func().new_local(None, result_type, "count_zeroes_results"); + let result = self.new_temp(self.current_func(), None, result_type); let cz_then_block = self.current_func().new_block("cz_then"); let cz_else_block = self.current_func().new_block("cz_else"); @@ -1146,8 +1164,8 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { let loop_tail = func.new_block("tail"); let counter_type = self.int_type; - let counter = self.current_func().new_local(None, counter_type, "popcount_counter"); - let val = self.current_func().new_local(None, value_type, "popcount_value"); + let counter = self.new_temp(self.current_func(), None, counter_type); + let val = self.new_temp(self.current_func(), None, value_type); let zero = self.gcc_zero(counter_type); self.llbb().add_assignment(self.location, counter, zero); self.llbb().add_assignment(self.location, val, value); diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/old_archs.rs b/compiler/rustc_codegen_gcc/src/intrinsic/old_archs.rs index 8d3e3487b5cb4..1aac52c28d220 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/old_archs.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/old_archs.rs @@ -1240,6 +1240,10 @@ pub(crate) fn old_archs(arch: &str, name: &str) -> ArchCheckResult { "avx512.vbroadcast.sd.pd.512" => "__builtin_ia32_vbroadcastsd_pd512", "avx512.vbroadcast.ss.512" => "__builtin_ia32_vbroadcastss512", "avx512.vbroadcast.ss.ps.512" => "__builtin_ia32_vbroadcastss_ps512", + "bmi.pdep.32" => "__builtin_ia32_pdep_si", + "bmi.pdep.64" => "__builtin_ia32_pdep_di", + "bmi.pext.32" => "__builtin_ia32_pext_si", + "bmi.pext.64" => "__builtin_ia32_pext_di", "fma.mask.vfmadd.pd.512" => "__builtin_ia32_vfmaddpd512_mask", "fma.mask.vfmadd.ps.512" => "__builtin_ia32_vfmaddps512_mask", "fma.mask.vfmaddsub.pd.512" => "__builtin_ia32_vfmaddsubpd512_mask", diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs index 1416f4eec9c4a..54013b6be6173 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs @@ -11,13 +11,14 @@ use rustc_codegen_ssa::diagnostics::ExpectedPointerMutability; use rustc_codegen_ssa::diagnostics::InvalidMonomorphization; use rustc_codegen_ssa::mir::operand::OperandRef; use rustc_codegen_ssa::mir::place::PlaceRef; -use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, BuilderMethods}; +use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, BuilderMethods, LayoutTypeCodegenMethods}; #[cfg(feature = "master")] use rustc_hir as hir; use rustc_middle::mir::BinOp; -use rustc_middle::ty::layout::HasTyCtxt; +use rustc_middle::ty::consts::ConstExt; +use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; use rustc_middle::ty::{self, Ty}; -use rustc_span::{ErrorGuaranteed, Span, Symbol, sym}; +use rustc_span::{ErrorGuaranteed, Span, Symbol, span_bug, sym}; use crate::builder::Builder; #[cfg(not(feature = "master"))] @@ -655,6 +656,39 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( return Ok(bx.context.new_rvalue_from_vector(bx.location, llret_ty, &values)); } + if name == sym::simd_arith_offset { + // This also checks that the first operand is a ptr type. + let pointee = in_elem.builtin_deref(true).unwrap_or_else(|| { + span_bug!(span, "must be called with a vector of pointer types as first argument") + }); + let layout = bx.layout_of(pointee); + // The second argument must be a ptr-sized integer. + // (We don't care about the signedness, this is wrapping anyway.) + let (_, offsets_elem) = args[1].layout.ty.simd_size_and_type(bx.tcx()); + if !matches!(offsets_elem.kind(), ty::Int(ty::IntTy::Isize) | ty::Uint(ty::UintTy::Usize)) { + span_bug!( + span, + "must be called with a vector of pointer-sized integers as second argument" + ); + } + + let pointee_type = bx.backend_type(layout); + let pointers = args[0].immediate(); + let offsets = args[1].immediate(); + let elem_type = llret_ty.dyncast_vector().expect("vector return type").get_element_type(); + let values: Vec<_> = (0..in_len) + .map(|i| { + let index = bx.context.new_rvalue_from_long(bx.usize_type, i as _); + let pointer = bx.extract_element(pointers, index); + let offset = bx.extract_element(offsets, index); + let pointer = bx.gep(pointee_type, pointer, &[offset]); + // GCC has no pointer vectors, so the lanes are `usize`. + bx.ptrtoint(pointer, elem_type) + }) + .collect(); + return Ok(bx.context.new_rvalue_from_vector(bx.location, llret_ty, &values)); + } + #[cfg(feature = "master")] if name == sym::simd_cast || name == sym::simd_as { require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty }); @@ -675,20 +709,28 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( return Ok(args[0].immediate()); } + #[derive(Copy, Clone)] + enum Sign { + Unsigned, + Signed, + } + use Sign::*; + enum Style { Float, - Int, + Int(Sign), Unsupported, } let in_style = match *in_elem.kind() { - ty::Int(_) | ty::Uint(_) => Style::Int, + ty::Int(_) => Style::Int(Signed), + ty::Uint(_) => Style::Int(Unsigned), ty::Float(_) => Style::Float, _ => Style::Unsupported, }; - let out_style = match *out_elem.kind() { - ty::Int(_) | ty::Uint(_) => Style::Int, + ty::Int(_) => Style::Int(Signed), + ty::Uint(_) => Style::Int(Unsigned), ty::Float(_) => Style::Float, _ => Style::Unsupported, }; @@ -707,6 +749,19 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( } ); } + (Style::Float, Style::Int(sign)) if name == sym::simd_as => { + let vector = args[0].immediate(); + let elem_type = + llret_ty.dyncast_vector().expect("vector return type").get_element_type(); + let values: Vec<_> = (0..in_len) + .map(|i| { + let index = bx.context.new_rvalue_from_long(bx.usize_type, i as _); + let value = bx.extract_element(vector, index); + bx.cast_float_to_int(matches!(sign, Sign::Signed), value, elem_type) + }) + .collect(); + return Ok(bx.context.new_rvalue_from_vector(bx.location, llret_ty, &values)); + } _ => return Ok(bx.context.convert_vector(None, args[0].immediate(), llret_ty)), } } @@ -1310,32 +1365,28 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( (true, false) => { // FIXME(antoyo): dyncast_vector should not require a call to unqualified. let arg_type = lhs.get_type().unqualified(); - // FIXME(antoyo): this uses the same algorithm from saturating add, but add the - // negative of the right operand. Find a proper subtraction algorithm. - let rhs = bx.context.new_unary_op(None, UnaryOp::Minus, arg_type, rhs); - // FIXME(antoyo): convert lhs and rhs to unsigned. - let sum = lhs + rhs; + let difference = lhs - rhs; let vector_type = arg_type.dyncast_vector().expect("vector type"); let unit = vector_type.get_num_units(); let a = bx.context.new_rvalue_from_int(elem_ty, ((elem_width as i32) << 3) - 1); let width = bx.context.new_rvalue_from_vector(None, lhs.get_type(), &vec![a; unit]); + // The subtraction overflows when the operands have different signs and the result + // has a different sign than the left operand. let xor1 = lhs ^ rhs; - let xor2 = lhs ^ sum; - let and = - bx.context.new_unary_op(None, UnaryOp::BitwiseNegate, arg_type, xor1) & xor2; - let mask = and >> width; + let xor2 = lhs ^ difference; + let mask = (xor1 & xor2) >> width; let one = bx.context.new_rvalue_one(elem_ty); let ones = bx.context.new_rvalue_from_vector(None, lhs.get_type(), &vec![one; unit]); let shift1 = ones << width; - let shift2 = sum >> width; + let shift2 = difference >> width; let mask_min = shift1 ^ shift2; - let and1 = - bx.context.new_unary_op(None, UnaryOp::BitwiseNegate, arg_type, mask) & sum; + let and1 = bx.context.new_unary_op(None, UnaryOp::BitwiseNegate, arg_type, mask) + & difference; let and2 = mask & mask_min; and1 + and2 diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index d7a3ef3b4a6c5..b82d05dc211aa 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -25,6 +25,7 @@ extern crate rustc_abi; extern crate rustc_apfloat; extern crate rustc_ast; +extern crate rustc_attr_ir; extern crate rustc_codegen_ssa; extern crate rustc_data_structures; extern crate rustc_errors; @@ -74,9 +75,9 @@ use std::ops::Deref; use std::path::{Path, PathBuf}; use std::sync::Arc; -use gccjit::{CType, Context, OptimizationLevel}; #[cfg(feature = "master")] -use gccjit::{TargetInfo, Version}; +use gccjit::TargetInfo; +use gccjit::{CType, Context, OptimizationLevel}; use rustc_ast::expand::allocator::AllocatorMethod; use rustc_codegen_ssa::back::lto::ThinModule; use rustc_codegen_ssa::back::write::{ @@ -94,7 +95,7 @@ use rustc_middle::ty::TyCtxt; use rustc_session::config::{OptLevel, OutputFilenames}; use rustc_session::{CodegenBackendInit, EarlySession, IncrCompSession, Session}; use rustc_span::{Symbol, sym}; -use rustc_target::spec::{Arch, RelocModel}; +use rustc_target::spec::{RelocModel, TargetTuple}; use tempfile::TempDir; use crate::back::lto::ModuleBuffer; @@ -176,15 +177,21 @@ impl CodegenBackend for GccCodegenBackend { } fn init(&mut self, sess: &EarlySession) -> CodegenBackendInit { - fn file_path(sysroot_path: &Path, sess: &EarlySession) -> PathBuf { - let rustlib_path = - rustc_target::relative_target_rustlib_path(sysroot_path, &sess.host.llvm_target); - sysroot_path - .join(rustlib_path) - .join("codegen-backends") - .join("lib") - .join(sess.target.llvm_target.as_ref()) - .join("libgccjit.so") + fn file_paths(sysroot_path: &Path, sess: &EarlySession) -> Vec { + let rustlib_path = rustc_target::relative_target_rustlib_path( + sysroot_path, + rustc_session::config::host_tuple(), + ); + let lib_path = sysroot_path.join(rustlib_path).join("codegen-backends").join("lib"); + let rust_target_path = + lib_path.join(sess.opts.target_triple.tuple()).join("libgccjit.so"); + let mut paths = vec![rust_target_path]; + if matches!(sess.opts.target_triple, TargetTuple::TargetJson { .. }) { + let llvm_target_path = + lib_path.join(sess.target.llvm_target.as_ref()).join("libgccjit.so"); + paths.push(llvm_target_path); + } + paths } let global_backend_features = gcc_util::global_gcc_features(sess); @@ -192,21 +199,24 @@ impl CodegenBackend for GccCodegenBackend { // We use all_paths() instead of only path() in case the path specified by --sysroot is // invalid. // This is the case for instance in Rust for Linux where they specify --sysroot=/dev/null. - for path in sess.opts.sysroot.all_paths() { - let libgccjit_target_lib_file = file_path(path, sess); - if let Ok(true) = fs::exists(&libgccjit_target_lib_file) { - load_libgccjit_if_needed(&libgccjit_target_lib_file); - break; + 'sysroot: for path in sess.opts.sysroot.all_paths() { + for libgccjit_target_lib_file in file_paths(path, sess) { + if let Ok(true) = fs::exists(&libgccjit_target_lib_file) { + load_libgccjit_if_needed(&libgccjit_target_lib_file); + break 'sysroot; + } } } if !gccjit::is_loaded() { let mut paths = vec![]; for path in sess.opts.sysroot.all_paths() { - let libgccjit_target_lib_file = file_path(path, sess); - paths.push(libgccjit_target_lib_file); + for libgccjit_target_lib_file in file_paths(path, sess) { + paths.push(libgccjit_target_lib_file); + } } + paths.dedup(); panic!("Could not load libgccjit.so. Attempted paths: {:#?}", paths); } @@ -227,7 +237,7 @@ impl CodegenBackend for GccCodegenBackend { lto_supported: gccjit::is_lto_supported(), }); - gccjit::set_global_personality_function_name(b"rust_eh_personality\0"); + gccjit::set_global_personality_function_name(c"rust_eh_personality"); } #[cfg(not(feature = "master"))] @@ -259,7 +269,7 @@ impl CodegenBackend for GccCodegenBackend { } fn target_cpu(&self, sess: &Session) -> String { - target_cpu(sess).to_owned() + target_cpu(sess).into_owned() } fn codegen_crate(&self, tcx: TyCtxt<'_>) -> Box { @@ -285,27 +295,6 @@ impl CodegenBackend for GccCodegenBackend { } } -fn new_context<'gcc, 'tcx>(tcx: TyCtxt<'tcx>) -> Context<'gcc> { - let context = Context::default(); - if matches!(tcx.sess.target.arch, Arch::X86 | Arch::X86_64) { - context.add_command_line_option("-masm=intel"); - } - #[cfg(feature = "master")] - { - context.set_special_chars_allowed_in_func_names("$.*"); - let version = Version::get(); - let version = format!("{}.{}.{}", version.major, version.minor, version.patch); - context.set_output_ident(&format!( - "rustc version {} with libgccjit {}", - rustc_interface::util::rustc_version_str().unwrap_or("unknown version"), - version, - )); - } - // FIXME(antoyo): check if this should only be added when using -Cforce-unwind-tables=n. - context.add_command_line_option("-fno-asynchronous-unwind-tables"); - context -} - impl ExtraBackendMethods for GccCodegenBackend { type Module = GccContext; @@ -316,7 +305,7 @@ impl ExtraBackendMethods for GccCodegenBackend { methods: &[AllocatorMethod], ) -> Self::Module { let mut mods = GccContext { - context: Arc::new(SyncContext::new(new_context(tcx))), + context: Arc::new(SyncContext::new(gcc_util::new_context(tcx.sess))), relocation_model: tcx.sess.relocation_model(), lto_mode: LtoMode::None, lto_supported: self.config().lto_supported, @@ -409,7 +398,7 @@ impl WriteBackendMethods for GccCodegenBackend { each_linked_rlib_for_lto: &[PathBuf], modules: Vec>, ) -> CompiledModule { - back::lto::run_fat(cgcx, &sess.prof, shared_emitter, each_linked_rlib_for_lto, modules) + back::lto::run_fat(sess, cgcx, shared_emitter, each_linked_rlib_for_lto, modules) } fn run_thin_lto( @@ -503,6 +492,7 @@ fn target_config(sess: &EarlySession, target_info: &SharedTargetInfo) -> TargetC ); let has_reliable_f16 = target_info.supports_target_dependent_type(CType::Float16); + let has_reliable_f16b = target_info.supports_target_dependent_type(CType::BFloat16); let has_reliable_f128 = target_info.supports_target_dependent_type(CType::Float128); TargetConfig { @@ -510,6 +500,7 @@ fn target_config(sess: &EarlySession, target_info: &SharedTargetInfo) -> TargetC // There are no known bugs with GCC support for f16 or f128 has_reliable_f16, has_reliable_f16_math: has_reliable_f16, + has_reliable_f16b, has_reliable_f128, has_reliable_f128_math: has_reliable_f128, } diff --git a/compiler/rustc_codegen_gcc/src/mono_item.rs b/compiler/rustc_codegen_gcc/src/mono_item.rs index fc92cc3d5c7a9..9aa015658fe42 100644 --- a/compiler/rustc_codegen_gcc/src/mono_item.rs +++ b/compiler/rustc_codegen_gcc/src/mono_item.rs @@ -1,15 +1,17 @@ +use gccjit::Function; #[cfg(feature = "master")] -use gccjit::{FnAttribute, VarAttribute}; +use gccjit::{FnAttribute, GlobalKind, ToRValue, Type, VarAttribute}; +use rustc_attr_ir::Linkage; use rustc_codegen_ssa::traits::PreDefineCodegenMethods; -use rustc_hir::attrs::Linkage; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::mono::Visibility; use rustc_middle::ty::layout::{FnAbiOf, HasTypingEnv, LayoutOf}; use rustc_middle::ty::{self, Instance, TypeVisitableExt}; use rustc_span::bug; +use crate::consts::const_alloc_type; use crate::context::CodegenCx; use crate::type_of::LayoutGccExt; use crate::{attributes, base}; @@ -19,25 +21,59 @@ impl<'gcc, 'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn predefine_static( &mut self, def_id: DefId, - _linkage: Linkage, + linkage: Linkage, visibility: Visibility, - symbol_name: &str, + global_name: &str, ) { let attrs = self.tcx.codegen_fn_attrs(def_id); let instance = Instance::mono(self.tcx, def_id); - let DefKind::Static { nested, .. } = self.tcx.def_kind(def_id) else { bug!() }; - // Nested statics do not have a type, so pick a dummy type and let `codegen_static` figure out - // the gcc type from the actual evaluated initializer. - let ty = - if nested { self.tcx.types.unit } else { instance.ty(self.tcx, self.typing_env()) }; - let gcc_type = self.layout_of(ty).gcc_type(self); + // Declare the global with the type its initializer will have, so that `codegen_static` + // never has to retype it afterwards. The initializer is lowered as a packed struct of byte + // runs and relocations, which almost never matches the layout type. + let gcc_type = match self.tcx.eval_static_initializer(def_id) { + Ok(alloc) => const_alloc_type(self, alloc), + // The initializer failed to evaluate; `codegen_static` bails out on it too, so this + // type is never used to hold one. + Err(_) => { + let DefKind::Static { nested, .. } = self.tcx.def_kind(def_id) else { bug!() }; + // Nested statics do not have a type, so pick a dummy one. + let ty = if nested { + self.tcx.types.unit + } else { + instance.ty(self.tcx, self.typing_env()) + }; + self.layout_of(ty).gcc_type(self) + } + }; let is_tls = attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL); - let global = self.define_global(symbol_name, gcc_type, is_tls, attrs.link_section); + let global_kind = base::global_linkage_to_gcc(linkage); + let global = + self.define_global(global_name, gcc_type, global_kind, is_tls, attrs.link_section); + #[cfg(feature = "master")] + { + // Visibility is meaningless on an internal global: GCC ignores the attribute and + // warns about it. + if !matches!(global_kind, GlobalKind::Internal) { + // If we're compiling the compiler-builtins crate, e.g., the equivalent of + // compiler-rt, then we want to implicitly compile everything with hidden + // visibility as we're going to link this object all over the place but + // don't want the symbols to get exported. + let visibility = if self.tcx.is_compiler_builtins(LOCAL_CRATE) { + gccjit::Visibility::Hidden + } else { + base::visibility_to_gcc(visibility) + }; + global.add_attribute(VarAttribute::Visibility(visibility)); + } + if let Some(attribute) = base::global_linkage_attribute(linkage) { + global.add_attribute(attribute); + } + } + #[cfg(feature = "master")] - global.add_attribute(VarAttribute::Visibility(base::visibility_to_gcc(visibility))); + self.add_static_aliases(gcc_type, global_name, attrs, &attrs.foreign_item_symbol_aliases); - // FIXME(antoyo): set linkage. self.instances.borrow_mut().insert(instance, global); } @@ -50,12 +86,114 @@ impl<'gcc, 'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { ) { assert!(!instance.args.has_infer()); + let attrs = self.tcx.codegen_instance_attrs(instance.def); + + let decl = + self.predefine_without_aliases(instance, &attrs, linkage, visibility, symbol_name); + + #[cfg(feature = "master")] + self.add_function_aliases(instance, decl, &attrs, &attrs.foreign_item_symbol_aliases); + + self.functions.borrow_mut().insert(symbol_name.to_string(), decl); + self.function_instances.borrow_mut().insert(instance, decl); + } +} + +impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { + #[cfg(feature = "master")] + fn add_static_aliases( + &self, + gcc_type: Type<'gcc>, + aliased: &str, + attrs: &CodegenFnAttrs, + aliases: &[(DefId, Linkage, Visibility)], + ) { + let is_tls = attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL); + + for &(alias, linkage, visibility) in aliases { + let instance = Instance::mono(self.tcx, alias); + let symbol_name = self.tcx.symbol_name(instance); + + let alias = self.declare_global( + symbol_name.name, + gcc_type, + GlobalKind::Imported, + is_tls, + attrs.link_section, + ); + alias.add_attribute(VarAttribute::Visibility(base::visibility_to_gcc(visibility))); + alias.add_attribute(VarAttribute::Alias(aliased)); + if linkage == Linkage::WeakAny { + alias.add_attribute(VarAttribute::Weak); + } + + // Add the alias name to the set of cached items, so there is no duplicate + // instance added to it during the normal `external static` codegen + let prev_entry = self.instances.borrow_mut().insert(instance, alias); + + // If there already was a previous entry, then `add_static_aliases` was called multiple times for the same `alias` + // which would result in incorrect codegen + assert!(prev_entry.is_none(), "An instance was already present for {instance:?}"); + } + } + + #[cfg(feature = "master")] + fn add_function_aliases( + &self, + aliased_instance: Instance<'tcx>, + aliased: Function<'gcc>, + attrs: &CodegenFnAttrs, + aliases: &[(DefId, Linkage, Visibility)], + ) { + for &(alias, linkage, visibility) in aliases { + let symbol_name = self.tcx.symbol_name(Instance::mono(self.tcx, alias)); + + // predefine another copy of the original instance + // with a new symbol name + let alias_fn_decl = self.predefine_without_aliases( + aliased_instance, + attrs, + linkage, + visibility, + symbol_name.name, + ); + + let block = alias_fn_decl.new_block("start"); + let nb_params = alias_fn_decl.get_param_count(); + let mut args = Vec::with_capacity(nb_params); + for idx in 0..nb_params { + args.push(alias_fn_decl.get_param(idx as _).to_rvalue()); + } + + let void_type = self.context.new_type::<()>(); + let call = self.context.new_call(None, aliased, &args); + if alias_fn_decl.get_return_type() == void_type { + block.add_eval(None, call); + block.end_with_void_return(None); + } else { + block.end_with_return(None, call); + } + } + } + + fn predefine_without_aliases( + &self, + instance: Instance<'tcx>, + _attrs: &CodegenFnAttrs, + linkage: Linkage, + visibility: Visibility, + symbol_name: &str, + ) -> Function<'gcc> { let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty()); self.linkage.set(base::linkage_to_gcc(linkage)); - let decl = self.declare_fn(symbol_name, fn_abi); - //let attrs = self.tcx.codegen_instance_attrs(instance.def); + let fn_decl = self.declare_fn(symbol_name, fn_abi); + + attributes::from_fn_attrs(self, fn_decl, instance, Some(fn_abi)); - attributes::from_fn_attrs(self, decl, instance); + #[cfg(feature = "master")] + if base::linkage_needs_weak_attribute(linkage) { + fn_decl.add_attribute(FnAttribute::Weak); + } // If we're compiling the compiler-builtins crate, e.g., the equivalent of // compiler-rt, then we want to implicitly compile everything with hidden @@ -63,17 +201,21 @@ impl<'gcc, 'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { // don't want the symbols to get exported. if linkage != Linkage::Internal && self.tcx.is_compiler_builtins(LOCAL_CRATE) { #[cfg(feature = "master")] - decl.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden)); + fn_decl.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden)); } else if visibility != Visibility::Default { #[cfg(feature = "master")] - decl.add_attribute(FnAttribute::Visibility(base::visibility_to_gcc(visibility))); + fn_decl.add_attribute(FnAttribute::Visibility(base::visibility_to_gcc(visibility))); + } + + #[cfg(feature = "master")] + if let Some(section) = _attrs.link_section { + fn_decl.add_attribute(FnAttribute::Section(section.as_str())); } - // FIXME(antoyo): call set_link_section() to allow initializing argc/argv. // FIXME(antoyo): set unique comdat. // FIXME(antoyo): use inline attribute from there in linkage.set() above. + // FIXME: Should we handle dso? - self.functions.borrow_mut().insert(symbol_name.to_string(), decl); - self.function_instances.borrow_mut().insert(instance, decl); + fn_decl } } diff --git a/compiler/rustc_codegen_gcc/src/type_.rs b/compiler/rustc_codegen_gcc/src/type_.rs index 27b0d2079e63e..05f712f1064c7 100644 --- a/compiler/rustc_codegen_gcc/src/type_.rs +++ b/compiler/rustc_codegen_gcc/src/type_.rs @@ -1,8 +1,9 @@ #[cfg(feature = "master")] use std::convert::TryInto; +use std::mem::discriminant; #[cfg(feature = "master")] -use gccjit::CType; +use gccjit::{CType, TypeAttribute}; use gccjit::{RValue, Struct, Type}; use rustc_abi::{AddressSpace, Align, Integer, Size}; use rustc_codegen_ssa::common::TypeKind; @@ -102,9 +103,10 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { self.bool_type } - pub fn type_struct(&self, fields: &[Type<'gcc>], packed: bool) -> Type<'gcc> { - let types = fields.to_vec(); - if let Some(typ) = self.struct_types.borrow().get(fields) { + pub fn type_struct(&self, fields: &[Type<'gcc>], attributes: &[StructAttribute]) -> Type<'gcc> { + let key = + StructTypeKey { fields: fields.to_vec(), attributes: canonical_attributes(attributes) }; + if let Some(typ) = self.struct_types.borrow().get(&key) { return *typ; } let fields: Vec<_> = fields @@ -115,15 +117,91 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { }) .collect(); let typ = self.context.new_struct_type(None, "struct", &fields).as_type(); - if packed { - #[cfg(feature = "master")] - typ.set_packed(); - } - self.struct_types.borrow_mut().insert(types, typ); + apply_struct_attributes(typ, &key.attributes); + self.struct_types.borrow_mut().insert(key, typ); typ } } +/// An attribute that can be set on a GCC struct type. +/// +/// This mirrors the subset of `gccjit::TypeAttribute` that cg_gcc needs, rather than using it +/// directly, because it must exist without the `master` feature and because it is what +/// `StructTypeKey` is keyed on. Adding a variant here is therefore all it takes to make a new +/// attribute part of the cache key: there is no second place to remember to update. +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub enum StructAttribute { + /// Alignment, in bytes. + Aligned(u32), + /// Lay the fields out without inserting padding between them. + Packed, +} + +/// Identifies an anonymous struct type in `CodegenCx::struct_types`. +/// +/// Two Rust types with the same field list can still need distinct GCC types — `struct { a: u64, +/// b: u64 }` with and without `repr(align(16))` produces the same fields — so every attribute has +/// to be part of the key. Holding them as one [`StructAttribute`] list rather than as separate +/// fields is what keeps that true when a new attribute is added. +#[derive(Clone, Eq, Hash, PartialEq)] +pub struct StructTypeKey<'gcc> { + pub fields: Vec>, + pub attributes: Vec, +} + +/// The attributes a GCC struct needs in order to match the Rust layout it is built from. +pub fn struct_attributes(packed: bool, align: Option) -> Vec { + let mut attributes = Vec::new(); + if packed { + attributes.push(StructAttribute::Packed); + } + if let Some(align) = align + && align.bytes() > 1 + && align.bytes() <= MAX_STRUCT_ALIGNMENT + { + attributes.push(StructAttribute::Aligned(align.bytes() as u32)); + } + attributes +} + +/// The largest alignment GCC accepts on a type, in bytes. +const MAX_STRUCT_ALIGNMENT: u64 = 1 << 28; + +/// Put an attribute list into a canonical form so that it can be used as a cache key. +/// +/// Without this, `[Packed, Aligned(8)]` and `[Aligned(8), Packed]` would hash differently and mint +/// two GCC types for what is one Rust type. +fn canonical_attributes(attributes: &[StructAttribute]) -> Vec { + let mut attributes = attributes.to_vec(); + attributes.sort_unstable(); + attributes.dedup(); + debug_assert!( + attributes.windows(2).all(|pair| discriminant(&pair[0]) != discriminant(&pair[1])), + "contradictory struct attributes: {attributes:?}" + ); + attributes +} + +/// Set `attributes` on the struct type `typ`. +/// +/// This is the only place allowed to call `Type::add_attribute`; `clippy.toml` forbids it +/// everywhere else. An attribute set on a type that `CodegenCx::struct_types` handed out would +/// change every other use of that type, so attributes have to be decided when the type is created +/// and be part of its cache key. Going through [`StructAttribute`] is what enforces that. +#[cfg(feature = "master")] +#[allow(clippy::disallowed_methods)] +pub fn apply_struct_attributes(typ: Type<'_>, attributes: &[StructAttribute]) { + for attribute in attributes { + typ.add_attribute(match *attribute { + StructAttribute::Aligned(align) => TypeAttribute::Aligned(align), + StructAttribute::Packed => TypeAttribute::Packed, + }); + } +} + +#[cfg(not(feature = "master"))] +pub fn apply_struct_attributes(_typ: Type<'_>, _attributes: &[StructAttribute]) {} + impl<'gcc, 'tcx> BaseTypeCodegenMethods for CodegenCx<'gcc, 'tcx> { fn type_i8(&self) -> Type<'gcc> { self.i8_type @@ -154,7 +232,15 @@ impl<'gcc, 'tcx> BaseTypeCodegenMethods for CodegenCx<'gcc, 'tcx> { if self.supports_f16_type { return self.context.new_c_type(CType::Float16); } - bug!("unsupported float width 16") + self.u16_type + } + + fn type_f16b(&self) -> Type<'gcc> { + #[cfg(feature = "master")] + if self.supports_f16b_type { + return self.context.new_c_type(CType::BFloat16); + } + bug!("unsupported type bfloat16") } fn type_f32(&self) -> Type<'gcc> { @@ -325,17 +411,19 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { self.type_array(self.type_from_integer(unit), size / unit_size) } - pub fn set_struct_body(&self, typ: Struct<'gcc>, fields: &[Type<'gcc>], packed: bool) { + pub fn set_struct_body( + &self, + typ: Struct<'gcc>, + fields: &[Type<'gcc>], + attributes: &[StructAttribute], + ) { let fields: Vec<_> = fields .iter() .enumerate() .map(|(index, field)| self.context.new_field(None, *field, format!("field_{}", index))) .collect(); typ.set_fields(None, &fields); - if packed { - #[cfg(feature = "master")] - typ.as_type().set_packed(); - } + apply_struct_attributes(typ.as_type(), &canonical_attributes(attributes)); } pub fn type_named_struct(&self, name: &str) -> Struct<'gcc> { diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs index 31654d0e6be81..833ba9efe4efb 100644 --- a/compiler/rustc_codegen_gcc/src/type_of.rs +++ b/compiler/rustc_codegen_gcc/src/type_of.rs @@ -17,7 +17,7 @@ use rustc_target::callconv::{CastTarget, FnAbi}; use crate::abi::{FnAbiGcc, FnAbiGccExt, GccType}; use crate::context::CodegenCx; -use crate::type_::struct_fields; +use crate::type_::{struct_attributes, struct_fields}; impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { fn type_from_unsigned_integer(&self, i: Integer) -> Type<'gcc> { @@ -81,7 +81,7 @@ fn uncached_gcc_type<'gcc, 'tcx>( layout.scalar_pair_element_gcc_type(cx, 0), layout.scalar_pair_element_gcc_type(cx, 1), ], - false, + &struct_attributes(false, Some(layout.align.abi)), ); } BackendRepr::Memory { .. } => {} @@ -129,11 +129,12 @@ fn uncached_gcc_type<'gcc, 'tcx>( FieldsShape::Primitive | FieldsShape::Union(_) => { let fill = cx.type_padding_filler(layout.size, layout.align.abi); let packed = false; + let attributes = struct_attributes(packed, Some(layout.align.abi)); match name { - None => cx.type_struct(&[fill], packed), + None => cx.type_struct(&[fill], &attributes), Some(ref name) => { let gcc_type = cx.type_named_struct(name); - cx.set_struct_body(gcc_type, &[fill], packed); + cx.set_struct_body(gcc_type, &[fill], &attributes); gcc_type.as_type() } } @@ -142,7 +143,7 @@ fn uncached_gcc_type<'gcc, 'tcx>( FieldsShape::Arbitrary { .. } => match name { None => { let (gcc_fields, packed) = struct_fields(cx, layout); - cx.type_struct(&gcc_fields, packed) + cx.type_struct(&gcc_fields, &struct_attributes(packed, Some(layout.align.abi))) } Some(ref name) => { let gcc_type = cx.type_named_struct(name); @@ -240,7 +241,11 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> { if let Some((deferred_ty, layout)) = defer { let (fields, packed) = struct_fields(cx, layout); - cx.set_struct_body(deferred_ty, &fields, packed); + cx.set_struct_body( + deferred_ty, + &fields, + &struct_attributes(packed, Some(layout.align.abi)), + ); } ty diff --git a/compiler/rustc_codegen_gcc/tests/asm/asm/comments.rs b/compiler/rustc_codegen_gcc/tests/asm/asm/comments.rs new file mode 100644 index 0000000000000..603bb014930c4 --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/asm/asm/comments.rs @@ -0,0 +1,12 @@ +//@ assembly-output: emit-asm +//@ only-x86_64 +// Check that comments in assembly get passed + +#![crate_type = "lib"] + +// CHECK-LABEL: "test_comments": +#[no_mangle] +pub fn test_comments() { + // CHECK: example comment + unsafe { core::arch::asm!("nop // example comment") }; +} diff --git a/compiler/rustc_codegen_gcc/tests/asm/bulk_memory_alignment.rs b/compiler/rustc_codegen_gcc/tests/asm/bulk_memory_alignment.rs new file mode 100644 index 0000000000000..6115467447104 --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/asm/bulk_memory_alignment.rs @@ -0,0 +1,45 @@ +//@ assembly-output: emit-asm +//@ only-x86_64-unknown-linux-gnu +//@ compile-flags: -Copt-level=3 + +#![crate_type = "lib"] + +// The alignment reaches GCC's `memcpy`/`memset` expansion only through +// `__builtin_assume_aligned`; a pointer cast to an aligned type is stripped as a useless +// conversion. An over-aligned type therefore has to expand to aligned moves and a packed one +// to unaligned moves. The alignment is 64 so that the contrast holds whatever vector width +// the host picks. + +#[repr(align(64))] +pub struct Aligned([u8; 64]); + +#[repr(C, packed)] +pub struct Packed([u8; 64]); + +// CHECK-LABEL: "copy_aligned": +// CHECK: {{(v)?mov(dqa|aps)}} +#[no_mangle] +pub unsafe fn copy_aligned(destination: *mut Aligned, source: *const Aligned) { + core::ptr::copy_nonoverlapping(source, destination, 1); +} + +// CHECK-LABEL: "copy_packed": +// CHECK: {{(v)?mov(dqu|ups)}} +#[no_mangle] +pub unsafe fn copy_packed(destination: *mut Packed, source: *const Packed) { + core::ptr::copy_nonoverlapping(source, destination, 1); +} + +// CHECK-LABEL: "set_aligned": +// CHECK: {{(v)?mov(dqa|aps)}} +#[no_mangle] +pub unsafe fn set_aligned(destination: *mut Aligned) { + core::ptr::write_bytes(destination, 0, 1); +} + +// CHECK-LABEL: "set_packed": +// CHECK: {{(v)?mov(dqu|ups)}} +#[no_mangle] +pub unsafe fn set_packed(destination: *mut Packed) { + core::ptr::write_bytes(destination, 0, 1); +} diff --git a/compiler/rustc_codegen_gcc/tests/asm/naked-functions/x86_64-naked-fn-no-cet-prolog.rs b/compiler/rustc_codegen_gcc/tests/asm/naked-functions/x86_64-naked-fn-no-cet-prolog.rs new file mode 100644 index 0000000000000..81ee9b13b4eca --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/asm/naked-functions/x86_64-naked-fn-no-cet-prolog.rs @@ -0,0 +1,24 @@ +//@ compile-flags: -C no-prepopulate-passes -Zcf-protection=full +//@ assembly-output: emit-asm +//@ needs-asm-support +//@ only-x86_64 + +#![crate_type = "lib"] + +use std::arch::naked_asm; + +// The problem at hand: Rust has adopted a fairly strict meaning for "naked functions", +// meaning "no prologue whatsoever, no, really, not one instruction." +// Unfortunately, x86's control-flow enforcement, specifically indirect branch protection, +// works by using an instruction for each possible landing site, +// and LLVM implements this via making sure of that. +#[no_mangle] +#[unsafe(naked)] +pub extern "sysv64" fn will_halt() -> ! { + // CHECK-NOT: endbr{{32|64}} + // CHECK: hlt + naked_asm!("hlt") +} + +// what about aarch64? +// "branch-protection"=false diff --git a/compiler/rustc_codegen_gcc/tests/asm/panic-no-unwind-no-uwtable.rs b/compiler/rustc_codegen_gcc/tests/asm/panic-no-unwind-no-uwtable.rs new file mode 100644 index 0000000000000..b51b173e9616e --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/asm/panic-no-unwind-no-uwtable.rs @@ -0,0 +1,8 @@ +//@ assembly-output: emit-asm +//@ only-x86_64-unknown-linux-gnu +//@ compile-flags: -C panic=unwind -C force-unwind-tables=n -Copt-level=3 + +#![crate_type = "lib"] + +// CHECK-NOT: .cfi_startproc +pub fn foo() {} diff --git a/compiler/rustc_codegen_gcc/tests/asm/used.rs b/compiler/rustc_codegen_gcc/tests/asm/used.rs new file mode 100644 index 0000000000000..deb0c69dc48fa --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/asm/used.rs @@ -0,0 +1,14 @@ +//@ assembly-output: emit-asm +//@ only-x86_64-unknown-linux-gnu + +#![feature(used_with_arg)] +#![crate_type = "lib"] + +// CHECK: .section .rodata.X,"a" +#[used(compiler)] +#[no_mangle] +pub static X: u32 = 12; +// CHECK: .section .rodata.Y,"aR" +#[used(linker)] +#[no_mangle] +pub static Y: u32 = 12; diff --git a/compiler/rustc_codegen_gcc/tests/asm/volatile_bulk_memory.rs b/compiler/rustc_codegen_gcc/tests/asm/volatile_bulk_memory.rs new file mode 100644 index 0000000000000..6233b8ac74c0e --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/asm/volatile_bulk_memory.rs @@ -0,0 +1,37 @@ +//@ assembly-output: emit-asm +//@ only-x86_64-unknown-linux-gnu +//@ compile-flags: -Copt-level=3 + +#![feature(core_intrinsics)] +#![crate_type = "lib"] + +use std::intrinsics::{ + volatile_copy_memory, volatile_copy_nonoverlapping_memory, volatile_set_memory, +}; + +// The buffers below are never read back, so the writes only survive because they are volatile. +// The functions are ordered alphabetically because that is the order they are emitted in. + +// CHECK-LABEL: "volatile_copy": +// CHECK: mov +#[no_mangle] +pub unsafe fn volatile_copy(source: *const u8) { + let mut buffer = [1u8; 64]; + volatile_copy_memory(buffer.as_mut_ptr(), source, 64); +} + +// CHECK-LABEL: "volatile_copy_nonoverlapping": +// CHECK: mov +#[no_mangle] +pub unsafe fn volatile_copy_nonoverlapping(source: *const u8) { + let mut buffer = [1u8; 64]; + volatile_copy_nonoverlapping_memory(buffer.as_mut_ptr(), source, 64); +} + +// CHECK-LABEL: "volatile_set": +// CHECK: mov +#[no_mangle] +pub unsafe fn volatile_set() { + let mut buffer = [1u8; 64]; + volatile_set_memory(buffer.as_mut_ptr(), 0, 64); +} diff --git a/compiler/rustc_codegen_gcc/tests/asm/x86_64-sse_crc.rs b/compiler/rustc_codegen_gcc/tests/asm/x86_64-sse_crc.rs new file mode 100644 index 0000000000000..bde58955a2146 --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/asm/x86_64-sse_crc.rs @@ -0,0 +1,12 @@ +//@ only-x86_64 +//@ assembly-output: emit-asm +//@ compile-flags: --crate-type staticlib -Ctarget-feature=+sse4.2 + +// CHECK-LABEL: banana +// CHECK: crc32 +#[no_mangle] +pub unsafe fn banana(v: u8) -> u32 { + use std::arch::x86_64::*; + let out = !0u32; + _mm_crc32_u8(out, v) +} diff --git a/compiler/rustc_codegen_gcc/tests/c/import_linkage.c b/compiler/rustc_codegen_gcc/tests/c/import_linkage.c new file mode 100644 index 0000000000000..227fa1c848ddf --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/c/import_linkage.c @@ -0,0 +1,14 @@ +/* The symbols that `tests/run/import_linkage.rs` imports with an explicit `#[linkage]`. + * + * Such an import is a pointer whose value is the address of the symbol, so what the Rust side + * reads back is `&value_*`, not the pointer stored in it. The distinct values make a mix-up + * visible. */ + +#include + +int32_t available_externally_value = 2; +int32_t linkonce_value = 3; +int32_t linkonce_odr_value = 4; +int32_t weak_value = 5; +int32_t weak_odr_value = 6; +int32_t extern_weak_value = 8; diff --git a/compiler/rustc_codegen_gcc/tests/c/overaligned_byval_abi.c b/compiler/rustc_codegen_gcc/tests/c/overaligned_byval_abi.c new file mode 100644 index 0000000000000..826a104f185ea --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/c/overaligned_byval_abi.c @@ -0,0 +1,54 @@ +/* Reference side of `tests/run/overaligned_byval_abi.rs`, compiled by the real GCC. + * + * `Aligned` is an over-aligned aggregate passed by value ("byval"): the ABI places it in a stack + * slot aligned to its own alignment, not packed right after the preceding argument. cg_gcc used + * to build the GCC struct type from the field list alone, which dropped Rust's `repr(align(64))`, + * so it placed the argument at an offset nobody else agreed on. + * + * The two functions here check both directions: `c_take_both` is a GCC-built callee for a cg_gcc + * caller, and `c_call_rust` is a GCC-built caller for a cg_gcc callee. + * + * The checks are on the *values* received rather than on the address of the argument: which + * alignment the ABI gives a stack slot is target-specific, but caller and callee agreeing on it + * is not. A disagreement makes the arguments arrive as garbage. */ + +#include + +struct Big { + int64_t a, b, c; +}; + +struct __attribute__((aligned(64))) Aligned { + int32_t x; +}; + +/* Defined on the Rust side. */ +extern int32_t rust_take_both(struct Big first, struct Aligned second, struct Big third, + struct Aligned fourth); + +/* Called from Rust: checks what a cg_gcc caller passed. */ +int32_t c_take_both(struct Big first, struct Aligned second, struct Big third, + struct Aligned fourth) +{ + if (first.a != 1 || first.b != 2 || first.c != 3) + return 1; + if (second.x != 42) + return 2; + if (third.a != 4 || third.b != 5 || third.c != 6) + return 3; + if (fourth.x != 43) + return 4; + return 0; +} + +/* Called from Rust: passes the arguments the way the ABI says, for a cg_gcc callee to read. */ +int32_t c_call_rust(void) +{ + struct Big first = {1, 2, 3}; + struct Big third = {4, 5, 6}; + struct Aligned second, fourth; + + second.x = 42; + fourth.x = 43; + return rust_take_both(first, second, third, fourth); +} diff --git a/compiler/rustc_codegen_gcc/tests/c/static_linkage.c b/compiler/rustc_codegen_gcc/tests/c/static_linkage.c new file mode 100644 index 0000000000000..00360d8633e68 --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/c/static_linkage.c @@ -0,0 +1,28 @@ +/* Strong definitions of the statics that `tests/run/static_linkage.rs` also defines, but weakly. + * The linker has to keep these and drop the Rust ones; a backend that emits the Rust definitions + * as ordinary global symbols fails the link with a duplicate definition instead. */ + +#include + +int32_t weak_static = 1; +int32_t weak_odr_static = 2; +int32_t linkonce_static = 3; +int32_t linkonce_odr_static = 4; + +/* `available_externally` promises the real definition lives elsewhere: a backend may read this one + * or emit an equivalent copy of the Rust initializer, so the two have to hold the same value. */ +int32_t available_externally_static = 7; + +/* Called from Rust, so that the reads also happen in a translation unit GCC compiled. */ +int32_t c_read_all(void) +{ + if (weak_static != 1) + return 11; + if (weak_odr_static != 2) + return 12; + if (linkonce_static != 3) + return 13; + if (linkonce_odr_static != 4) + return 14; + return 0; +} diff --git a/compiler/rustc_codegen_gcc/tests/c/weak_function_linkage.c b/compiler/rustc_codegen_gcc/tests/c/weak_function_linkage.c new file mode 100644 index 0000000000000..25dcdedbcd950 --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/c/weak_function_linkage.c @@ -0,0 +1,54 @@ +/* Strong definitions of the functions that `tests/run/weak_function_linkage.rs` also defines, but + * weakly. The linker has to keep these and drop the Rust ones. + * + * A backend that emits the Rust definitions as ordinary global symbols does not merely pick the + * wrong one: the link fails outright with a duplicate definition. */ + +#include + +int32_t weak_function(void) +{ + return 1; +} + +int32_t weak_odr_function(void) +{ + return 2; +} + +int32_t linkonce_function(void) +{ + return 3; +} + +int32_t linkonce_odr_function(void) +{ + return 4; +} + +int32_t weak_inline_function(void) +{ + return 8; +} + +/* `available_externally` promises the real definition lives elsewhere: a backend may call this one + * or emit an equivalent copy of the Rust body, so the two have to return the same value. */ +int32_t available_externally_function(void) +{ + return 7; +} + +/* Called from Rust, so that the calls also go through a caller that GCC compiled: a cg_gcc caller + * could inline the weak body it can see instead of calling the symbol. */ +int32_t c_call_all(void) +{ + if (weak_function() != 1) + return 11; + if (weak_odr_function() != 2) + return 12; + if (linkonce_function() != 3) + return 13; + if (linkonce_odr_function() != 4) + return 14; + return 0; +} diff --git a/compiler/rustc_codegen_gcc/tests/compile/asm_noreturn_call.rs b/compiler/rustc_codegen_gcc/tests/compile/asm_noreturn_call.rs new file mode 100644 index 0000000000000..c9238697d8097 --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/compile/asm_noreturn_call.rs @@ -0,0 +1,15 @@ +// Compiler: + +// Regression test for https://github.com/rust-lang/rustc_codegen_gcc/issues/827 + +#![crate_type = "lib"] + +#[cfg(target_arch = "x86_64")] +pub type NoReturn = extern "sysv64" fn(&'static u8) -> !; + +#[cfg(target_arch = "x86_64")] +pub fn call_no_return(function: *const NoReturn) -> ! { + unsafe { + std::arch::asm!("call {}", in(reg) function, options(noreturn)); + } +} diff --git a/compiler/rustc_codegen_gcc/tests/compile/recursive_types.rs b/compiler/rustc_codegen_gcc/tests/compile/recursive_types.rs new file mode 100644 index 0000000000000..3c69074887f3e --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/compile/recursive_types.rs @@ -0,0 +1,22 @@ +// Compiler: + +// `Set` reaches itself by value through `*mut Root`, so a backend that emits `Root`'s +// fields with the still-incomplete `Set` type fails to compile this. + +#![crate_type = "lib"] + +#[repr(C)] +pub struct Set { + pub root: *mut Root, + pub first: usize, + pub second: usize, +} + +#[repr(C)] +pub struct Root { + pub default_set: Set, +} + +pub fn identity(set: Set) -> Set { + set +} diff --git a/compiler/rustc_codegen_gcc/tests/compile/x86_interrupt_first_arg_byval.rs b/compiler/rustc_codegen_gcc/tests/compile/x86_interrupt_first_arg_byval.rs new file mode 100644 index 0000000000000..4b6bbd48f7ad5 --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/compile/x86_interrupt_first_arg_byval.rs @@ -0,0 +1,16 @@ +// Compiler: + +// Test that `x86-interrupt` functions whose first argument is passed by value +// emit pointer-shaped GCC parameters and compile with interrupt-safe target features. + +#![feature(abi_x86_interrupt)] +#![crate_type = "lib"] + +#[repr(C)] +pub struct Frame { + ip: u64, +} + +pub extern "x86-interrupt" fn scalar(_a: i64) {} + +pub extern "x86-interrupt" fn aggregate(_frame: Frame) {} diff --git a/compiler/rustc_codegen_gcc/tests/cpuid.def b/compiler/rustc_codegen_gcc/tests/cpuid.def new file mode 100644 index 0000000000000..05fe8e94a8282 --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/cpuid.def @@ -0,0 +1,27 @@ +# Input => Output +# EAX ECX => EAX EBX ECX EDX +00000000 ******** => 00000024 756e6547 6c65746e 49656e69 #Processor ID and Manufacturer +00000001 ******** => 00400f10 00100800 7ffaf3ff bfebfbff +00000007 00000000 => 00000002 f3bfbfbf bac05ffe 03d54130 #Extended Features +00000007 00000001 => 98ee00bf 00000002 00000020 1d29cd3e +0000000d 00000000 => 000e02e7 00002b00 00002b00 00000000 #xcr0 +0000000d 00000001 => 0000001f 00000240 00000100 00000000 #Supervisor State +0000000d 00000002 => 00000100 00000240 00000000 00000000 +0000000d 00000005 => 00000040 00000440 00000000 00000000 #zmasks +0000000d 00000006 => 00000200 00000480 00000000 00000000 #zmmh +0000000d 00000007 => 00000400 00000680 00000000 00000000 #zmm +0000000d 00000011 => 00000040 00000ac0 00000002 00000000 #tileconfig +0000000d 00000012 => 00002000 00000b00 00000006 00000000 #tiles +0000000d 00000013 => 00000080 000003c0 00000000 00000000 #APX +00000019 ******** => 00000000 00000005 00000000 00000000 #Key Locker +0000001d 00000000 => 00000001 00000000 00000000 00000000 #AMX Tile +0000001d 00000001 => 04002000 00080040 00000010 00000000 #AMX Palette1 +0000001e 00000000 => 00000001 00004010 00000000 00000000 #AMX Tmul +0000001e 00000001 => 000001ff 00000000 00000000 00000000 +00000024 00000000 => 00000001 00070002 00000000 00000000 #AVX10 +00000024 00000001 => 00000000 00000000 00000004 00000000 +80000000 ******** => 80000004 00000000 00000000 00000000 +80000001 ******** => 00000000 00000000 00000121 2c100000 +80000002 ******** => 00000000 00000000 00000000 00000000 +80000003 ******** => 00000000 00000000 00000000 00000000 +80000004 ******** => 00000000 00000000 00000000 00000000 diff --git a/compiler/rustc_codegen_gcc/tests/failing-ice-tests.txt b/compiler/rustc_codegen_gcc/tests/failing-ice-tests.txt index ff1b6f1489468..f9d6f99e7b42b 100644 --- a/compiler/rustc_codegen_gcc/tests/failing-ice-tests.txt +++ b/compiler/rustc_codegen_gcc/tests/failing-ice-tests.txt @@ -10,7 +10,6 @@ tests/ui/simd/intrinsic/generic-arithmetic-saturating-2.rs tests/ui/simd/intrinsic/generic-arithmetic-2.rs tests/ui/panics/default-backtrace-ice.rs tests/ui/mir/lint/storage-live.rs -tests/ui/layout/valid_range_oob.rs tests/ui/higher-ranked/trait-bounds/future.rs tests/ui/consts/const-eval/const-eval-query-stack.rs tests/ui/simd/masked-load-store.rs @@ -28,13 +27,21 @@ tests/ui/lto/thin-lto-global-allocator.rs tests/ui/lto/msvc-imp-present.rs tests/ui/lto/dylib-works.rs tests/ui/lto/all-crates.rs -tests/ui/issues/issue-47364.rs +tests/ui/codegen/no-segfault-with-multiple-codegen-units.rs tests/ui/functions-closures/parallel-codegen-closures.rs tests/ui/sepcomp/sepcomp-unwind.rs tests/ui/extern/issue-64655-extern-rust-must-allow-unwind.rs tests/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs -tests/ui/unwind-no-uwtable.rs +tests/ui/panics/unwind-force-no-unwind-tables.rs tests/ui/delegation/fn-header.rs tests/ui/simd/intrinsic/generic-arithmetic-pass.rs -tests/ui/simd/masked-load-store.rs -tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.rs +tests/ui/codegen/unknown-llvm-intrinsic.rs +tests/ui/codegen/incorrect-llvm-intrinsic-signature.rs +tests/ui/codegen/incorrect-arch-intrinsic.rs +tests/ui/codegen/custom-target-invalid-llvm-target.rs +tests/ui/asm/x86_64/naked_asm_escape.rs +tests/ui/lto/debuginfo-lto-alloc.rs +tests/ui/codegen/normalization-overflow/recursion-issue-118590.rs +tests/ui/codegen/normalization-overflow/recursion-issue-122823.rs +tests/ui/codegen/normalization-overflow/recursion-issue-131342.rs +tests/ui/codegen/normalization-overflow/recursion-issue-92004.rs diff --git a/compiler/rustc_codegen_gcc/tests/failing-lto-tests.txt b/compiler/rustc_codegen_gcc/tests/failing-lto-tests.txt index 4c62c35a512c1..7527005321991 100644 --- a/compiler/rustc_codegen_gcc/tests/failing-lto-tests.txt +++ b/compiler/rustc_codegen_gcc/tests/failing-lto-tests.txt @@ -1,6 +1,5 @@ tests/ui/lto/debuginfo-lto-alloc.rs -tests/ui/panic-runtime/lto-unwind.rs -tests/ui/uninhabited/uninhabited-transparent-return-abi.rs -tests/ui/coroutine/panic-drops-resume.rs -tests/ui/coroutine/panic-drops.rs -tests/ui/coroutine/panic-safe.rs +tests/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs +tests/ui/extern/issue-64655-extern-rust-must-allow-unwind.rs +tests/ui/lto/thin-lto-inlines2.rs +tests/ui/lto/lto-thin-rustc-loads-linker-plugin.rs diff --git a/compiler/rustc_codegen_gcc/tests/failing-run-make-tests.txt b/compiler/rustc_codegen_gcc/tests/failing-run-make-tests.txt index 528ee1df9f583..d5297e069f72f 100644 --- a/compiler/rustc_codegen_gcc/tests/failing-run-make-tests.txt +++ b/compiler/rustc_codegen_gcc/tests/failing-run-make-tests.txt @@ -11,4 +11,5 @@ tests/run-make/foreign-exceptions/ tests/run-make/glibc-staticlib-args/ tests/run-make/lto-smoke-c/ tests/run-make/return-non-c-like-enum/ -tests/run-make/short-ice +tests/run-make/short-ice/ +tests/run-make/embed-source-dwarf/ diff --git a/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt b/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt index e8a26a90890c1..7f96bfabedbea 100644 --- a/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt +++ b/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt @@ -1,113 +1,21 @@ tests/ui/asm/may_unwind.rs tests/ui/asm/x86_64/may_unwind.rs -tests/ui/drop/dynamic-drop-async.rs -tests/ui/cfg/cfg-panic-abort.rs tests/ui/intrinsics/panic-uninitialized-zeroed.rs -tests/ui/iterators/iter-sum-overflow-debug.rs -tests/ui/iterators/iter-sum-overflow-overflow-checks.rs -tests/ui/mir/mir_drop_order.rs -tests/ui/mir/mir_let_chains_drop_order.rs -tests/ui/mir/mir_match_guard_let_chains_drop_order.rs -tests/ui/panic-runtime/abort-link-to-unwinding-crates.rs -tests/ui/panic-runtime/abort.rs -tests/ui/panic-runtime/link-to-abort.rs -tests/ui/parser/unclosed-delimiter-in-dep.rs -tests/ui/consts/missing_span_in_backtrace.rs -tests/ui/drop/dynamic-drop.rs tests/ui/simd/issue-17170.rs tests/ui/simd/issue-39720.rs -tests/ui/drop/panic-during-drop-14875.rs -tests/ui/issues/issue-29948.rs tests/ui/process/println-with-broken-pipe.rs -tests/ui/lto/thin-lto-inlines2.rs -tests/ui/panic-runtime/lto-abort.rs -tests/ui/lto/lto-thin-rustc-loads-linker-plugin.rs -tests/ui/async-await/deep-futures-are-freeze.rs -tests/ui/coroutine/resume-after-return.rs -tests/ui/simd/masked-load-store.rs tests/ui/simd/repr_packed.rs -tests/ui/async-await/in-trait/dont-project-to-specializable-projection.rs -tests/ui/coroutine/unwind-abort-mix.rs -tests/ui/consts/issue-miri-1910.rs -tests/ui/consts/const_cmp_type_id.rs -tests/ui/consts/issue-94675.rs -tests/ui/traits/const-traits/const-drop-fail.rs -tests/ui/runtime/on-broken-pipe/child-processes.rs -tests/ui/sanitizer/cfi/assoc-ty-lifetime-issue-123053.rs -tests/ui/sanitizer/cfi/async-closures.rs -tests/ui/sanitizer/cfi/closures.rs -tests/ui/sanitizer/cfi/complex-receiver.rs -tests/ui/sanitizer/cfi/coroutine.rs -tests/ui/sanitizer/cfi/drop-in-place.rs -tests/ui/sanitizer/cfi/drop-no-principal.rs -tests/ui/sanitizer/cfi/fn-ptr.rs -tests/ui/sanitizer/cfi/self-ref.rs -tests/ui/sanitizer/cfi/supertraits.rs -tests/ui/sanitizer/cfi/virtual-auto.rs -tests/ui/sanitizer/cfi/sized-associated-ty.rs -tests/ui/sanitizer/cfi/can-reveal-opaques.rs -tests/ui/sanitizer/kcfi-mangling.rs -tests/ui/delegation/fn-header.rs -tests/ui/consts/const-eval/parse_ints.rs -tests/ui/simd/intrinsic/generic-as.rs -tests/ui/runtime/rt-explody-panic-payloads.rs -tests/ui/codegen/equal-pointers-unequal/as-cast/inline1.rs -tests/ui/codegen/equal-pointers-unequal/as-cast/inline2.rs -tests/ui/codegen/equal-pointers-unequal/as-cast/segfault.rs -tests/ui/codegen/equal-pointers-unequal/as-cast/zero.rs -tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline1.rs -tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline2.rs -tests/ui/codegen/equal-pointers-unequal/exposed-provenance/segfault.rs -tests/ui/codegen/equal-pointers-unequal/exposed-provenance/zero.rs -tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline1.rs -tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline2.rs -tests/ui/codegen/equal-pointers-unequal/strict-provenance/segfault.rs -tests/ui/codegen/equal-pointers-unequal/strict-provenance/zero.rs tests/ui/simd/simd-bitmask-notpow2.rs tests/ui/codegen/StackColoring-not-blowup-stack-issue-40883.rs tests/ui/numbers-arithmetic/u128-as-f32.rs tests/ui/process/nofile-limit.rs -tests/ui/linking/no-gc-encapsulation-symbols.rs -tests/ui/panics/unwind-force-no-unwind-tables.rs tests/ui/attributes/fn-align-dyn.rs tests/ui/linkage-attr/raw-dylib/elf/glibc-x86_64.rs -tests/ui/explicit-tail-calls/recursion-etc.rs -tests/ui/explicit-tail-calls/indexer.rs -tests/ui/explicit-tail-calls/drop-order.rs -tests/ui/c-variadic/valid.rs -tests/ui/c-variadic/inherent-method.rs -tests/ui/c-variadic/trait-method.rs -tests/ui/explicit-tail-calls/become-cast-return.rs -tests/ui/explicit-tail-calls/become-indirect-return.rs -tests/ui/panics/panic-abort-backtrace-without-debuginfo.rs -tests/ui/sanitizer/kcfi-c-variadic.rs -tests/ui/sanitizer/kcfi/fn-trait-objects.rs tests/ui/statics/const_generics.rs -tests/ui/test-attrs/test-panic-while-printing.rs tests/ui/thir-print/offset_of.rs -tests/ui/iterators/rangefrom-overflow-debug.rs -tests/ui/iterators/rangefrom-overflow-overflow-checks.rs -tests/ui/iterators/iter-filter-count-debug-check.rs -tests/ui/eii/linking/codegen_single_crate.rs -tests/ui/eii/linking/codegen_cross_crate.rs -tests/ui/eii/default/local_crate.rs -tests/ui/eii/duplicate/multiple_impls.rs -tests/ui/eii/default/call_default.rs -tests/ui/eii/linking/same-symbol.rs -tests/ui/eii/privacy1.rs -tests/ui/eii/default/call_impl.rs -tests/ui/c-variadic/copy.rs tests/ui/asm/x86_64/global_asm_escape.rs tests/ui/lto/all-crates.rs -tests/ui/consts/const-eval/c-variadic.rs -tests/ui/eii/default/call_default_panics.rs -tests/ui/explicit-tail-calls/indirect.rs -tests/ui/traits/inheritance/self-in-supertype.rs -tests/ui/fmt/fmt_debug/shallow.rs -tests/ui/c-variadic/roundtrip.rs -tests/ui/eii/eii_impl_with_contract.rs -tests/ui/eii/static/cross_crate_decl.rs -tests/ui/eii/static/cross_crate_def.rs -tests/ui/eii/static/same_address.rs -tests/ui/eii/static/simple.rs -tests/ui/explicit-tail-calls/default-trait-method.rs +tests/ui/explicit-tail-calls/tailcc-no-signature-restriction.rs +tests/ui/abi/rust-tail-cc.rs +tests/ui/abi/rust-preserve-none-cc.rs +tests/ui/extern/extern-types-field-offset.rs diff --git a/compiler/rustc_codegen_gcc/tests/lang_tests.rs b/compiler/rustc_codegen_gcc/tests/lang_tests.rs index 6afd54e1c3fe0..7ec0ab877b025 100644 --- a/compiler/rustc_codegen_gcc/tests/lang_tests.rs +++ b/compiler/rustc_codegen_gcc/tests/lang_tests.rs @@ -7,6 +7,68 @@ use std::process::Command; use lang_tester::LangTester; use tempfile::TempDir; +/// Directory holding the C files that the `tests/run` tests can link against. +/// +/// A `tests/c/.c` is compiled by the real GCC and linked into `tests/run/.rs`. +const C_TESTS_DIR: &str = "tests/c"; + +/// The m68k cross toolchain is not on the default `PATH` in CI. +// FIXME(antoyo): find a better way to add the PATH necessary locally. +const M68K_TOOLCHAIN_DIR: &str = "/opt/m68k-unknown-linux-gnu/bin"; + +fn target_path(test_target: &Option) -> Option { + test_target.as_ref().map(|_| { + let env_path = std::env::var("PATH").unwrap_or_default(); + format!("{}:{}", M68K_TOOLCHAIN_DIR, env_path) + }) +} + +/// Compile every C file in `tests/c` to an object file in `objects_dir`. +fn compile_c_files(objects_dir: &Path, test_target: &Option) { + let c_tests_dir = Path::new(C_TESTS_DIR); + if !c_tests_dir.is_dir() { + return; + } + std::fs::create_dir_all(objects_dir).expect("create the directory for the C object files"); + + let compiler = match test_target { + Some(target) => format!("{}-gcc", target), + None => "gcc".to_string(), + }; + + for entry in std::fs::read_dir(c_tests_dir).expect("read the C tests directory") { + let source = entry.expect("directory entry").path(); + if source.extension().and_then(|extension| extension.to_str()) != Some("c") { + continue; + } + let object = c_object_path(objects_dir, &source); + + let mut command = Command::new(&compiler); + command.arg("-c"); + // Optimize: an unoptimized C caller can happen to agree with a wrong callee. + command.arg("-O1"); + // GCC notes that the ABI of over-aligned arguments changed in GCC 4.6. That is the ABI + // being tested in overaligned_byval_abi, so the note is expected rather than a problem. + command.arg("-Wno-psabi"); + command.arg("-o"); + command.arg(&object); + command.arg(&source); + if let Some(env_path) = target_path(test_target) { + command.env("PATH", env_path); + } + + let status = command + .status() + .unwrap_or_else(|error| panic!("failed to run `{}`: {}", compiler, error)); + assert!(status.success(), "failed to compile `{}`", source.display()); + } +} + +/// The object file that a test source links against, if any: `tests/c/x.c` for `tests/run/x.rs`. +fn c_object_path(objects_dir: &Path, source: &Path) -> PathBuf { + objects_dir.join(source.file_stem().expect("file_stem")).with_extension("o") +} + fn compile_and_run_cmds( compiler_args: Vec, test_target: &Option, @@ -18,10 +80,7 @@ fn compile_and_run_cmds( // Test command 2: run `tempdir/x`. if test_target.is_some() { - let mut env_path = std::env::var("PATH").unwrap_or_default(); - // FIXME(antoyo): find a better way to add the PATH necessary locally. - env_path = format!("/opt/m68k-unknown-linux-gnu/bin:{}", env_path); - compiler.env("PATH", env_path); + compiler.env("PATH", target_path(test_target).expect("target PATH")); let mut commands = vec![("Compiler", compiler)]; if test_mode.should_run() { @@ -82,8 +141,10 @@ impl TestMode { } } +#[allow(clippy::too_many_arguments)] fn build_test_runner( tempdir: PathBuf, + c_objects_dir: PathBuf, current_dir: String, build_mode: BuildMode, test_kind: &str, @@ -159,6 +220,13 @@ fn build_test_runner( path.to_str().expect("to_str").into(), ]; + // Link against `tests/c/.c`, when the test has one. + let c_object = c_object_path(&c_objects_dir, path); + if c_object.exists() { + compiler_args.push("-C".into()); + compiler_args.push(format!("link-arg={}", c_object.display())); + } + if let Some(ref target) = test_target { compiler_args.extend_from_slice(&["--target".into(), target.into()]); @@ -172,6 +240,16 @@ fn build_test_runner( } } + // Extra flags passed at run time (as opposed to the compile-time + // `TEST_FLAGS`). This lets a single test opt into flags like + // `-Zmir-preserve-ub` via an `ignore-if` directive that checks + // whether `CARGO_TEST_FLAGS` is set. + if let Ok(flags) = std::env::var("CARGO_TEST_FLAGS") { + for flag in flags.split_whitespace() { + compiler_args.push(flag.into()); + } + } + if build_mode.is_debug() { compiler_args .extend_from_slice(&["-C".to_string(), "llvm-args=sanitize-undefined".into()]); @@ -193,36 +271,51 @@ fn build_test_runner( .run(); } -fn compile_tests(tempdir: PathBuf, current_dir: String) { +fn compile_tests(tempdir: PathBuf, c_objects_dir: PathBuf, current_dir: String) { build_test_runner( tempdir, + c_objects_dir, current_dir, BuildMode::Debug, "lang compile", "tests/compile", TestMode::Compile, - &["simd-ffi.rs", "asm_nul_byte.rs", "global_asm_nul_byte.rs", "naked_asm_nul_byte.rs"], + &[ + "simd-ffi.rs", + "asm_nul_byte.rs", + "global_asm_nul_byte.rs", + "naked_asm_nul_byte.rs", + "x86_interrupt_first_arg_byval.rs", + ], ); } -fn run_tests(tempdir: PathBuf, current_dir: String) { +fn run_tests(tempdir: PathBuf, c_objects_dir: PathBuf, current_dir: String) { build_test_runner( tempdir.clone(), + c_objects_dir.clone(), current_dir.clone(), BuildMode::Debug, "[DEBUG] lang run", "tests/run", TestMode::CompileAndRun, - &[], + &[ + // FIXME: remove this when the unwind issue is fixed in GCC m68k upstream. + "catch_unwind.rs", + ], ); build_test_runner( tempdir, + c_objects_dir, current_dir.to_string(), BuildMode::Release, "[RELEASE] lang run", "tests/run", TestMode::CompileAndRun, - &[], + &[ + // FIXME: remove this when the unwind issue is fixed in GCC m68k upstream. + "catch_unwind.rs", + ], ); } @@ -232,6 +325,11 @@ fn main() { let current_dir = current_dir.to_str().expect("current dir").to_string(); let tempdir_path: PathBuf = tempdir.as_ref().into(); - compile_tests(tempdir_path.clone(), current_dir.clone()); - run_tests(tempdir_path, current_dir); + let c_objects_dir = tempdir_path.join("c-objects"); + // FIXME(antoyo): find a way to send this via a cli argument. + let test_target = std::env::var("CG_GCC_TEST_TARGET").ok(); + compile_c_files(&c_objects_dir, &test_target); + + compile_tests(tempdir_path.clone(), c_objects_dir.clone(), current_dir.clone()); + run_tests(tempdir_path, c_objects_dir, current_dir); } diff --git a/compiler/rustc_codegen_gcc/tests/run/abort1.rs b/compiler/rustc_codegen_gcc/tests/run/abort1.rs index ff2bb75ece22a..c9e3c27a0791d 100644 --- a/compiler/rustc_codegen_gcc/tests/run/abort1.rs +++ b/compiler/rustc_codegen_gcc/tests/run/abort1.rs @@ -12,7 +12,7 @@ extern crate mini_core; use mini_core::*; fn test_fail() -> ! { - unsafe { intrinsics::abort() }; + unsafe { intrinsics::abort_immediate() }; } #[no_mangle] diff --git a/compiler/rustc_codegen_gcc/tests/run/abort2.rs b/compiler/rustc_codegen_gcc/tests/run/abort2.rs index 781f518e0b222..51f55e9404c1f 100644 --- a/compiler/rustc_codegen_gcc/tests/run/abort2.rs +++ b/compiler/rustc_codegen_gcc/tests/run/abort2.rs @@ -12,7 +12,7 @@ extern crate mini_core; use mini_core::*; fn fail() -> i32 { - unsafe { intrinsics::abort() }; + unsafe { intrinsics::abort_immediate() }; 0 } diff --git a/compiler/rustc_codegen_gcc/tests/run/always_inline.rs b/compiler/rustc_codegen_gcc/tests/run/always_inline.rs index ebd741ee090c8..2a580fe79acaf 100644 --- a/compiler/rustc_codegen_gcc/tests/run/always_inline.rs +++ b/compiler/rustc_codegen_gcc/tests/run/always_inline.rs @@ -47,7 +47,7 @@ fn fib_a(n: u8) -> u8 { #[no_mangle] extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { if fib(2) != fib_a(2) { - intrinsics::abort(); + intrinsics::abort_immediate(); } 0 } diff --git a/compiler/rustc_codegen_gcc/tests/run/asm.rs b/compiler/rustc_codegen_gcc/tests/run/asm.rs index 01775c92ffc8a..42141c671b596 100644 --- a/compiler/rustc_codegen_gcc/tests/run/asm.rs +++ b/compiler/rustc_codegen_gcc/tests/run/asm.rs @@ -3,6 +3,8 @@ // Run-time: // status: 0 +#![feature(asm_goto_with_outputs)] + #[cfg(target_arch = "x86_64")] use std::arch::{asm, global_asm}; @@ -32,6 +34,20 @@ pub unsafe fn mem_cpy(dst: *mut u8, src: *const u8, len: usize) { ); } +#[cfg(target_arch = "x86_64")] +#[unsafe(no_mangle)] +pub fn asm_goto_test(mut a: i16) -> i16 { + unsafe { + std::arch::asm!( + "jmp {op}", + inout("eax") a, + op = label { a = 7; }, + options(nostack,nomem) + ); + a + } +} + #[cfg(target_arch = "x86_64")] fn asm() { unsafe { @@ -190,6 +206,14 @@ fn asm() { } assert_eq!((x, y), (8, 8)); + // Regression test for + // typed pointer inputs to explicit registers need a cast. + let mut x = 123_i32; + unsafe { + asm!("", in("rdi") &mut x, options(nostack, preserves_flags)); + } + assert_eq!(x, 123); + // sysv64 is the default calling convention on unix systems. The rdi register is // used to pass arguments in the sysv64 calling convention, so this register will be clobbered #[cfg(unix)] @@ -227,6 +251,24 @@ fn asm() { out("r15b") _, ); } + + // Make sure the input value from inout is assigned to the input value + unsafe { + // Use a very distinctive value unlikely to live in any register. + let input: u64 = 0x1234567890ABCDEF; + let mut output: u64; + + asm!( + "push {1}", + "pop {0}", + out(reg) output, + inout(reg) input => _, + ); + + assert_eq!(output, 0x1234567890ABCDEF); + } + + asm_goto_test(0); } #[cfg(not(target_arch = "x86_64"))] diff --git a/compiler/rustc_codegen_gcc/tests/run/catch_unwind.rs b/compiler/rustc_codegen_gcc/tests/run/catch_unwind.rs new file mode 100644 index 0000000000000..919213db5a4a5 --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/run/catch_unwind.rs @@ -0,0 +1,25 @@ +// Compiler: +// +// Run-time: +// status: 0 +// stdout: Caught + +#![feature(fn_traits, unboxed_closures)] + +struct Wrapper(A); + +impl R> FnOnce<()> for Wrapper { + type Output = R; + + #[inline] + extern "rust-call" fn call_once(self, _args: ()) -> R { + (self.0)() + } +} + +fn main() { + std::panic::set_hook(Box::new(|_| {})); + let result = std::panic::catch_unwind(Wrapper(|| panic!())); + assert!(result.is_err()); + println!("Caught"); +} diff --git a/compiler/rustc_codegen_gcc/tests/run/custom_abort.rs b/compiler/rustc_codegen_gcc/tests/run/custom_abort.rs new file mode 100644 index 0000000000000..eafa4321a4324 --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/run/custom_abort.rs @@ -0,0 +1,27 @@ +// Compiler: +// +// Run-time: +// status: 42 + +// Check that a program can define its own `abort`. + +#![feature(no_core)] +#![no_std] +#![no_core] +#![no_main] + +extern crate mini_core; +use mini_core::*; + +#[no_mangle] +extern "C" fn abort() { + unsafe { + libc::exit(42); + } +} + +#[no_mangle] +extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 { + abort(); + 0 +} diff --git a/compiler/rustc_codegen_gcc/tests/run/import_linkage.rs b/compiler/rustc_codegen_gcc/tests/run/import_linkage.rs new file mode 100644 index 0000000000000..58008ecbc9746 --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/run/import_linkage.rs @@ -0,0 +1,66 @@ +// Compiler: +// +// Run-time: +// status: 0 + +// Checks the `#[linkage]` flavours an `extern` static can be imported with, against the symbols +// `tests/c/import_linkage.c` defines. +// +// The value of such an import is the address of the symbol rather than its contents, which is why +// the types are pointers: an `extern_weak` import of a symbol nobody defines reads as null instead +// of failing the link. + +#![feature(linkage, no_core)] +#![no_std] +#![no_core] +#![no_main] + +extern crate mini_core; +use mini_core::*; + +extern "C" { + #[linkage = "available_externally"] + static available_externally_value: *const i32; + #[linkage = "linkonce"] + static linkonce_value: *const i32; + #[linkage = "linkonce_odr"] + static linkonce_odr_value: *const i32; + #[linkage = "weak"] + static weak_value: *const i32; + #[linkage = "weak_odr"] + static weak_odr_value: *const i32; + #[linkage = "extern_weak"] + static extern_weak_value: *const i32; + + // Nothing defines this one, so it stays null instead of breaking the link. + #[linkage = "extern_weak"] + static undefined_value: *const i32; +} + +#[no_mangle] +extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 { + unsafe { + if *available_externally_value != 2 { + return 2; + } + if *linkonce_value != 3 { + return 3; + } + if *linkonce_odr_value != 4 { + return 4; + } + if *weak_value != 5 { + return 5; + } + if *weak_odr_value != 6 { + return 6; + } + if *extern_weak_value != 8 { + return 8; + } + if undefined_value as usize != 0 { + return 10; + } + } + 0 +} diff --git a/compiler/rustc_codegen_gcc/tests/run/int.rs b/compiler/rustc_codegen_gcc/tests/run/int.rs index 78675acb5447b..ef825b4d80185 100644 --- a/compiler/rustc_codegen_gcc/tests/run/int.rs +++ b/compiler/rustc_codegen_gcc/tests/run/int.rs @@ -319,4 +319,29 @@ fn main() { const VAL5: T = 73236519889708027473620326106273939584_i128; check_ops128!(); } + + { + #[allow(dead_code)] + #[repr(u8)] + enum Inner { + L0 = 0, + H255 = 255, + } + #[allow(dead_code)] + enum O { + A(Inner), + B, + C, + } + + #[inline(never)] + fn which(o: &O) -> &'static str { + match o { + O::A(_) => "a", + O::B => "b", + O::C => "c", + } + } + assert_eq!(which(black_box(&O::A(Inner::H255))), "a"); + } } diff --git a/compiler/rustc_codegen_gcc/tests/run/mir_preserve_ub_empty_switch.rs b/compiler/rustc_codegen_gcc/tests/run/mir_preserve_ub_empty_switch.rs new file mode 100644 index 0000000000000..26056360b9212 --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/run/mir_preserve_ub_empty_switch.rs @@ -0,0 +1,35 @@ +// ignore-if: test -z "$CARGO_TEST_FLAGS" +// Compiler: +// +// Run-time: +// status: 0 + +// Regression test for https://github.com/rust-lang/rustc_codegen_gcc/issues/881 +// +// This needs `-Zmir-preserve-ub`, so it is skipped unless that flag is passed +// through `CARGO_TEST_FLAGS` (see the `ignore-if` directive above). Run it with: +// CARGO_TEST_FLAGS="-Zmir-preserve-ub" ./y.sh test --cargo-tests -- mir_preserve_ub_empty_switch + +#![feature(no_core)] +#![no_std] +#![no_core] +#![no_main] + +extern crate mini_core; +use intrinsics::black_box; +use mini_core::*; + +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { + // With `-Zmir-preserve-ub`, the range pattern below is lowered to a pair of + // comparisons and the second one becomes a `SwitchInt` with no cases (only + // an `otherwise` target) whose discriminant is the `bool` comparison + // result. `gcc_jit_block_end_with_switch` rejects a non-integer + // discriminant, so the backend must emit a plain jump for it instead. + let value = black_box(argc); + match value { + 0..=9 => (), + _ => (), + } + 0 +} diff --git a/compiler/rustc_codegen_gcc/tests/run/nonzero_div_ceil.rs b/compiler/rustc_codegen_gcc/tests/run/nonzero_div_ceil.rs new file mode 100644 index 0000000000000..e6bd3c2ec9845 --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/run/nonzero_div_ceil.rs @@ -0,0 +1,17 @@ +// Compiler: +// +// Run-time: +// status: 0 + +use std::hint::black_box; +use std::num::NonZero; + +fn main() { + for (dividend, divisor, expected) in + [(10u8, 3u8, 4u8), (1, 254, 1), (1, 255, 1), (2, 254, 1), (2, 255, 1), (200, 100, 2)] + { + let dividend = NonZero::new(black_box(dividend)).unwrap(); + let divisor = NonZero::new(black_box(divisor)).unwrap(); + assert_eq!(dividend.div_ceil(divisor).get(), expected); + } +} diff --git a/compiler/rustc_codegen_gcc/tests/run/overaligned_byval_abi.rs b/compiler/rustc_codegen_gcc/tests/run/overaligned_byval_abi.rs new file mode 100644 index 0000000000000..78ee35f05ca58 --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/run/overaligned_byval_abi.rs @@ -0,0 +1,89 @@ +// Compiler: +// +// Run-time: +// status: 0 + +// Checks that cg_gcc passes an over-aligned by-value ("byval") argument where the platform ABI +// says it goes, by calling in both directions with `tests/c/overaligned_byval_abi.c`, which is +// compiled by the real GCC. +// +// `tests/run/overaligned_byval_arg.rs` covers the Rust-visible half of the same bug. It cannot +// cover this one: with cg_gcc on both sides of a call, caller and callee place the argument at +// the same wrong offset and agree with each other. +// +// Two over-aligned arguments are used rather than one so that the failure is deterministic. A +// backend that drops `align(64)` packs the arguments at offsets 0, 24, 88 and 112 of the argument +// area; 112 - 24 = 88 is not a multiple of 64, so the two of them cannot both land on a 64-byte +// boundary however the argument area itself is aligned. With a single over-aligned argument the +// frame often happens to be 64-aligned and the bug hides. +// +// Only the values received are checked, never the address an argument landed at: which alignment +// a target gives a by-value stack slot differs between targets, but the two sides of a call +// agreeing on it does not. `overaligned_byval_arg.rs` is where the alignment itself is asserted. + +#![feature(no_core)] +#![no_std] +#![no_core] +#![no_main] + +extern crate mini_core; +use mini_core::*; + +#[repr(C)] +struct Big { + a: i64, + b: i64, + c: i64, +} + +#[repr(C, align(64))] +struct Aligned { + x: i32, +} + +extern "C" { + fn c_take_both(first: Big, second: Aligned, third: Big, fourth: Aligned) -> i32; + fn c_call_rust() -> i32; +} + +// The callee for the GCC-built caller in `c_call_rust`. +// +// `#[no_mangle]` is not only about the symbol name: it makes the symbol externally visible, which +// pins the calling convention. Without it the function has internal linkage and GCC is free to +// clone it with a changed convention at `-O3` (the symbol comes out as `...constprop.0.isra.0`), +// so the arguments never travel through the stack slots and the release build passes spuriously. +#[no_mangle] +extern "C" fn rust_take_both(first: Big, second: Aligned, third: Big, fourth: Aligned) -> i32 { + if first.a as i32 != 1 || first.b as i32 != 2 || first.c as i32 != 3 { + return 5; + } + if second.x != 42 { + return 6; + } + if third.a as i32 != 4 || third.b as i32 != 5 || third.c as i32 != 6 { + return 7; + } + if fourth.x != 43 { + return 8; + } + 0 +} + +#[no_mangle] +extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 { + // cg_gcc as the caller, GCC as the callee. + let result = unsafe { + c_take_both( + Big { a: 1, b: 2, c: 3 }, + Aligned { x: 42 }, + Big { a: 4, b: 5, c: 6 }, + Aligned { x: 43 }, + ) + }; + if result != 0 { + return result; + } + + // GCC as the caller, cg_gcc as the callee. + unsafe { c_call_rust() } +} diff --git a/compiler/rustc_codegen_gcc/tests/run/overaligned_byval_arg.rs b/compiler/rustc_codegen_gcc/tests/run/overaligned_byval_arg.rs new file mode 100644 index 0000000000000..e20ec11732962 --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/run/overaligned_byval_arg.rs @@ -0,0 +1,41 @@ +// Compiler: +// +// Run-time: +// status: 0 + +#![feature(no_core)] +#![no_std] +#![no_core] +#![no_main] + +extern crate mini_core; +use mini_core::*; + +#[repr(C)] +struct Big { + a: i64, + b: i64, + c: i64, +} + +#[repr(C, align(64))] +struct Aligned { + x: i32, +} + +#[inline(never)] +#[no_mangle] +extern "C" fn check(_b1: Big, a1: Aligned, _b2: Big, a2: Aligned) -> i32 { + if (&a1 as *const Aligned as usize) % 64 != 0 { + return 1; + } + if (&a2 as *const Aligned as usize) % 64 != 0 { + return 2; + } + 0 +} + +#[no_mangle] +extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 { + check(Big { a: 1, b: 2, c: 3 }, Aligned { x: 42 }, Big { a: 4, b: 5, c: 6 }, Aligned { x: 43 }) +} diff --git a/compiler/rustc_codegen_gcc/tests/run/ptr_to_int_div.rs b/compiler/rustc_codegen_gcc/tests/run/ptr_to_int_div.rs new file mode 100644 index 0000000000000..afc563d6c977b --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/run/ptr_to_int_div.rs @@ -0,0 +1,19 @@ +// Compiler: +// +// Run-time: +// status: 0 + +use std::hint::black_box; +use std::mem::transmute; + +fn main() { + let pointer = black_box(usize::MAX) as *const (); + + let unsigned = unsafe { transmute::<*const (), usize>(pointer) }; + assert_eq!(unsigned / black_box(2), usize::MAX / 2); + assert_eq!(unsigned % black_box(2), usize::MAX % 2); + + let signed = unsafe { transmute::<*const (), isize>(pointer) }; + assert_eq!(signed / black_box(2), -1isize / 2); + assert_eq!(signed % black_box(2), -1isize % 2); +} diff --git a/compiler/rustc_codegen_gcc/tests/run/simd.rs b/compiler/rustc_codegen_gcc/tests/run/simd.rs new file mode 100644 index 0000000000000..e0a23fdccf8ce --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/run/simd.rs @@ -0,0 +1,81 @@ +// Compiler: +// +// Run-time: +// status: 0 + +#![feature(portable_simd)] + +use std::hint::black_box; +use std::simd::prelude::*; + +fn test_saturating_add() { + let values = i32x4::from_array([i32::MIN, -2, 3, i32::MAX]); + let ones = i32x4::splat(1); + assert_eq!( + black_box(values).saturating_add(black_box(ones)).to_array(), + [i32::MIN + 1, -1, 4, i32::MAX] + ); + + let values = u32x4::from_array([0, 2, 3, u32::MAX]); + let ones = u32x4::splat(1); + assert_eq!(black_box(values).saturating_add(black_box(ones)).to_array(), [1, 3, 4, u32::MAX]); +} + +fn test_saturating_sub() { + let values = i32x4::from_array([i32::MIN, -2, 3, i32::MAX]); + let zero = i32x4::splat(0); + assert_eq!( + black_box(zero).saturating_sub(black_box(values)).to_array(), + [i32::MAX, 2, -3, i32::MIN + 1] + ); + assert_eq!(black_box(values).saturating_neg().to_array(), [i32::MAX, 2, -3, i32::MIN + 1]); + assert_eq!(black_box(values).saturating_abs().to_array(), [i32::MAX, 2, 3, i32::MAX]); + + let values = i32x4::from_array([i32::MIN, -2, 3, i32::MAX]); + let ones = i32x4::splat(1); + assert_eq!( + black_box(values).saturating_sub(black_box(ones)).to_array(), + [i32::MIN, -3, 2, i32::MAX - 1] + ); + + let values = u32x4::from_array([0, 2, 3, u32::MAX]); + let ones = u32x4::splat(1); + assert_eq!( + black_box(values).saturating_sub(black_box(ones)).to_array(), + [0, 1, 2, u32::MAX - 1] + ); +} + +fn test_float_cast() { + let floats = f32x4::from_array([1.9, -4.5, f32::INFINITY, f32::NAN]); + assert_eq!(black_box(floats).cast::().to_array(), [1, -4, i32::MAX, 0]); + + let floats = f32x4::from_array([f32::NEG_INFINITY, 1e20, -1e20, -0.0]); + assert_eq!(black_box(floats).cast::().to_array(), [i32::MIN, i32::MAX, i32::MIN, 0]); + + let floats = f32x4::from_array([-1.0, 3.7, f32::NAN, 1e20]); + assert_eq!(black_box(floats).cast::().to_array(), [0, 3, 0, u32::MAX]); + + let floats = f64x4::from_array([-1.5, 2.5, f64::NAN, f64::INFINITY]); + assert_eq!(black_box(floats).cast::().to_array(), [-1, 2, 0, i64::MAX]); +} + +fn test_arith_offset() { + let values = [10i32, 11, 12, 13, 14, 15, 16, 17]; + let indices = usizex4::from_array([7, 5, 3, 1]); + assert_eq!( + i32x4::gather_or_default(black_box(&values), black_box(indices)).to_array(), + [17, 15, 13, 11] + ); + + let mut destination = [0i32; 8]; + i32x4::from_array([1, 2, 3, 4]).scatter(black_box(&mut destination), black_box(indices)); + assert_eq!(destination, [0, 4, 0, 3, 0, 2, 0, 1]); +} + +fn main() { + test_saturating_add(); + test_saturating_sub(); + test_float_cast(); + test_arith_offset(); +} diff --git a/compiler/rustc_codegen_gcc/tests/run/static_alloc_shapes.rs b/compiler/rustc_codegen_gcc/tests/run/static_alloc_shapes.rs new file mode 100644 index 0000000000000..39a4d07bdf638 --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/run/static_alloc_shapes.rs @@ -0,0 +1,50 @@ +// Compiler: +// +// Run-time: +// status: 0 +// stdout: 8 +// 12 +// 5 +// 7 +// 7 +// 9 + +#![feature(no_core)] +#![no_std] +#![no_core] +#![no_main] + +extern crate mini_core; +use mini_core::*; + +// One byte run of each length class that maps to a distinct array element type. +static mut BYTES8: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8]; +static mut BYTES12: [u8; 12] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; +static mut BYTES5: [u8; 5] = [1, 2, 3, 4, 5]; + +static mut VALUE: isize = 7; +static mut OTHER: isize = 9; + +// An allocation that is exactly one relocation, so it ends on a pointer with no trailing bytes. +static mut PTR: &isize = unsafe { &VALUE }; + +struct TwoRefs { + first: &'static isize, + second: &'static isize, +} + +// Two adjacent relocations, with no byte run between them. +static mut TWO_REFS: TwoRefs = TwoRefs { first: unsafe { &VALUE }, second: unsafe { &OTHER } }; + +#[no_mangle] +extern "C" fn main(_argc: isize, _argv: *const *const u8) -> i32 { + unsafe { + libc::printf(b"%ld\n\0" as *const u8 as *const i8, BYTES8[7] as isize); + libc::printf(b"%ld\n\0" as *const u8 as *const i8, BYTES12[11] as isize); + libc::printf(b"%ld\n\0" as *const u8 as *const i8, BYTES5[4] as isize); + libc::printf(b"%ld\n\0" as *const u8 as *const i8, *PTR); + libc::printf(b"%ld\n\0" as *const u8 as *const i8, *TWO_REFS.first); + libc::printf(b"%ld\n\0" as *const u8 as *const i8, *TWO_REFS.second); + } + 0 +} diff --git a/compiler/rustc_codegen_gcc/tests/run/static_linkage.rs b/compiler/rustc_codegen_gcc/tests/run/static_linkage.rs new file mode 100644 index 0000000000000..3c9303fa8cf16 --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/run/static_linkage.rs @@ -0,0 +1,65 @@ +// Compiler: +// +// Run-time: +// status: 0 + +// Checks that `#[linkage]` on a static that this crate defines reaches the symbol, against +// `tests/c/static_linkage.c`, which defines the overridable ones strongly. +// +// If `predefine_static` were to ignore its `linkage` argument outright, every static would come out as +// an ordinary global symbol: the overridable ones would clash with the C definitions at link time. + +#![feature(linkage, no_core)] +#![no_std] +#![no_core] +#![no_main] + +extern crate mini_core; +use mini_core::*; + +#[linkage = "weak"] +#[no_mangle] +pub static weak_static: i32 = 0; + +#[linkage = "weak_odr"] +#[no_mangle] +pub static weak_odr_static: i32 = 0; + +#[linkage = "linkonce"] +#[no_mangle] +pub static linkonce_static: i32 = 0; + +#[linkage = "linkonce_odr"] +#[no_mangle] +pub static linkonce_odr_static: i32 = 0; + +// Not overridden by the C side: the definition here is the one that survives. +#[linkage = "weak"] +#[no_mangle] +pub static only_weak_static: i32 = 6; + +// The real definition is the one in the C file; a backend may read it or emit an equivalent copy of +// this initializer, so both spell the same value. +#[linkage = "available_externally"] +#[no_mangle] +pub static available_externally_static: i32 = 7; + +extern "C" { + fn c_read_all() -> i32; +} + +#[no_mangle] +extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 { + let result = unsafe { c_read_all() }; + if result != 0 { + return result; + } + + if only_weak_static != 6 { + return 2; + } + if available_externally_static != 7 { + return 3; + } + 0 +} diff --git a/compiler/rustc_codegen_gcc/tests/run/weak_function_linkage.rs b/compiler/rustc_codegen_gcc/tests/run/weak_function_linkage.rs new file mode 100644 index 0000000000000..406b85df10b02 --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/run/weak_function_linkage.rs @@ -0,0 +1,103 @@ +// Compiler: +// +// Run-time: +// status: 0 + +// Checks that the `#[linkage]` flavours another object file is allowed to override are emitted as +// weak symbols, by linking against `tests/c/weak_function_linkage.c`, which defines the same +// symbols strongly. + +#![feature(linkage, no_core)] +#![no_std] +#![no_core] +#![no_main] + +extern crate mini_core; +use mini_core::*; + +#[linkage = "weak"] +#[no_mangle] +extern "C" fn weak_function() -> i32 { + 0 +} + +// `_odr` promises every definition of the symbol is equivalent, which lets a backend call this body +// instead of the one in the C file. They spell the same value for that reason. +#[linkage = "weak_odr"] +#[no_mangle] +extern "C" fn weak_odr_function() -> i32 { + 2 +} + +#[linkage = "linkonce"] +#[no_mangle] +extern "C" fn linkonce_function() -> i32 { + 0 +} + +#[linkage = "linkonce_odr"] +#[no_mangle] +extern "C" fn linkonce_odr_function() -> i32 { + 4 +} + +// Not overridden by the C side: the definition here is the one that runs. +#[linkage = "weak"] +#[no_mangle] +extern "C" fn only_weak_function() -> i32 { + 6 +} + +// The real definition is the one in the C file; a backend may call it or emit an equivalent copy of +// this body, so both spell the same value. +#[linkage = "available_externally"] +#[no_mangle] +extern "C" fn available_externally_function() -> i32 { + 7 +} + +// GCC drops `weak` from a function that is also `inline`: a backend that keeps the hint emits this +// as an ordinary global symbol and clashes with the C definition. rustc lints the hint as ignored +// on a function with an explicit `#[linkage]`, hence the `allow`. +#[linkage = "weak"] +#[inline] +#[no_mangle] +#[allow(unused_attributes)] +extern "C" fn weak_inline_function() -> i32 { + 0 +} + +extern "C" { + fn c_call_all() -> i32; +} + +#[no_mangle] +extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 { + let result = unsafe { c_call_all() }; + if result != 0 { + return result; + } + + if weak_function() != 1 { + return 1; + } + if weak_odr_function() != 2 { + return 2; + } + if linkonce_function() != 3 { + return 3; + } + if linkonce_odr_function() != 4 { + return 4; + } + if only_weak_function() != 6 { + return 6; + } + if available_externally_function() != 7 { + return 7; + } + if weak_inline_function() != 8 { + return 8; + } + 0 +} diff --git a/compiler/rustc_codegen_gcc/tools/cspell_dicts/rust.txt b/compiler/rustc_codegen_gcc/tools/cspell_dicts/rust.txt deleted file mode 100644 index 379cbd77eef01..0000000000000 --- a/compiler/rustc_codegen_gcc/tools/cspell_dicts/rust.txt +++ /dev/null @@ -1,2 +0,0 @@ -lateout -repr diff --git a/compiler/rustc_codegen_gcc/tools/cspell_dicts/rustc_codegen_gcc.txt b/compiler/rustc_codegen_gcc/tools/cspell_dicts/rustc_codegen_gcc.txt deleted file mode 100644 index 4fb018b3ecd87..0000000000000 --- a/compiler/rustc_codegen_gcc/tools/cspell_dicts/rustc_codegen_gcc.txt +++ /dev/null @@ -1,78 +0,0 @@ -aapcs -addo -archs -ashl -ashr -cgcx -clzll -cmse -codegened -csky -ctfe -ctlz -ctpop -cttz -ctzll -flto -fmaximumf -fmuladd -fmuladdf -fminimumf -fmul -fptosi -fptosui -fptoui -fwrapv -gimple -hrtb -immediates -interner -liblto -llbb -llcx -llextra -llfn -lgcc -llmod -llresult -llret -ltrans -llty -llval -llvals -loong -lshr -masm -maximumf -maxnumf -mavx -mcmodel -minimumf -minnumf -miri -monomorphization -monomorphizations -monomorphized -monomorphizing -movnt -mulo -nvptx -pointee -powitf -reassoc -riscv -rlib -roundevenf -rustc -sitofp -sizet -spir -subo -sysv -tbaa -uitofp -unord -uninlined -utrunc -xabort -zext diff --git a/compiler/rustc_codegen_gcc/tools/generate_intrinsics.py b/compiler/rustc_codegen_gcc/tools/generate_intrinsics.py index 5390323407779..06425f682a88b 100644 --- a/compiler/rustc_codegen_gcc/tools/generate_intrinsics.py +++ b/compiler/rustc_codegen_gcc/tools/generate_intrinsics.py @@ -84,6 +84,10 @@ def update_intrinsics(llvm_path): # This speeds up the comparison, and makes our code considerably smaller. # Since all intrinsic names start with "llvm.", we skip that prefix. print("Updating content of `{}`...".format(output_file)) + indent4 = " " + indent8 = indent4 + indent4 + indent12 = indent8 + indent4 + indent16 = indent12 + indent4 with open(output_file, "w", encoding="utf8") as out: out.write("""// File generated by `rustc_codegen_gcc/tools/generate_intrinsics.py` // DO NOT EDIT IT! @@ -95,33 +99,35 @@ def update_intrinsics(llvm_path): if let ArchCheckResult::Ok(res) = old_arch_res { return res; } -match arch {""") + match arch { +""") for arch in archs: if len(intrinsics[arch]) == 0: continue attribute = "#[expect(non_snake_case)]" if arch[0].isupper() else "" - out.write("\"{}\" => {{ {} fn {}(name: &str,full_name:&str) -> &'static str {{ match name {{".format(arch, attribute, arch)) + out.write(f"""{indent4}"{arch}" => {{ +{indent8}{attribute} fn {arch}(name: &str,full_name:&str) -> &'static str {{ +{indent12}match name {{""") intrinsics[arch].sort(key=lambda x: (x[0], x[1])) - out.write(' // {}\n'.format(arch)) + out.write(f'{indent16}// {arch}\n') for entry in intrinsics[arch]: llvm_name = entry[0].removeprefix("llvm."); llvm_name = llvm_name.removeprefix(arch); llvm_name = llvm_name.removeprefix("."); if "_round_mask" in entry[1]: - out.write(' // [INVALID CONVERSION]: "{}" => "{}",\n'.format(llvm_name, entry[1])) + out.write(f'{indent16}// [INVALID CONVERSION]: "{llvm_name}" => "{entry[1]}",\n') else: - out.write(' "{}" => "{}",\n'.format(llvm_name, entry[1])) - out.write(' _ => unimplemented!("***** unsupported LLVM intrinsic {full_name}"),\n') - out.write("}} }} {}(name,full_name) }}\n,".format(arch)) - out.write(""" _ => { - match old_arch_res { - ArchCheckResult::UnknownIntrinsic => unimplemented!("***** unsupported LLVM intrinsic {full_name}"), - ArchCheckResult::UnknownArch => unimplemented!("***** unsupported LLVM architecture {arch}, intrinsic: {full_name}"), - ArchCheckResult::Ok(_) => unreachable!(), - } - }""") + out.write(f'{indent16}"{llvm_name}" => "{entry[1]}",\n') + out.write(f'{indent16}_ => unimplemented!("***** unsupported LLVM intrinsic {{full_name}}"),\n') + out.write(f"{indent16}}}\n{indent12}}}\n{indent8}{arch}(name,full_name)\n{indent8}}}\n,") + out.write(f"""{indent4}_ => {{ +{indent8}match old_arch_res {{ +{indent8}ArchCheckResult::UnknownIntrinsic => unimplemented!("***** unsupported LLVM intrinsic {{full_name}}"), +{indent8}ArchCheckResult::UnknownArch => unimplemented!("***** unsupported LLVM architecture {{arch}}, intrinsic: {{full_name}}"), +{indent8}ArchCheckResult::Ok(_) => unreachable!(), +{indent4}}} +}}""") out.write("}\n}") - subprocess.call(["rustfmt", output_file]) print("Done!") diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index a45138849e4e0..19e6a3314f20a 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -15,7 +15,7 @@ use rustc_middle::ty::layout::LayoutOf; use rustc_session::{Session, config}; use rustc_span::bug; use rustc_target::callconv::{ - ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, CastTarget, FnAbi, PassMode, + ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, CastTarget, FnAbi, IndirectMode, PassMode, }; use rustc_target::spec::{Arch, SanitizerSet}; use smallvec::SmallVec; @@ -80,9 +80,11 @@ fn get_attrs<'ll>(this: &ArgAttributes, cx: &CodegenCx<'ll, '_>) -> SmallVec<[&' // Only apply remaining attributes when optimizing if cx.sess().opts.optimize != config::OptLevel::No { let deref = this.pointee_size.bytes(); - // dereferenceable in LLVM currently implies nofree, so only emit dereferenceable if nofree + // Prior to dereferenceable in LLVM + // implied nofree, so only emit dereferenceable on older versions of LLVM if nofree // is also set. - if deref != 0 && regular.contains(ArgAttribute::NoFree) { + let llvm_version = crate::llvm_util::get_version(); + if deref != 0 && (llvm_version >= (23, 0, 0) || regular.contains(ArgAttribute::NoFree)) { if regular.contains(ArgAttribute::NonNull) { attrs.push(llvm::CreateDereferenceableAttr(cx.llcx, deref)); } else { @@ -159,6 +161,7 @@ impl LlvmType for Reg { }, Primitive::Float(float) => match float { Float::F16 => cx.type_f16(), + Float::F16B => cx.type_f16b(), Float::F32 => cx.type_f32(), Float::F64 => cx.type_f64(), Float::F128 => cx.type_f128(), @@ -242,12 +245,12 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { match &self.mode { PassMode::Ignore => {} // Sized indirect arguments - PassMode::Indirect { attrs, meta_attrs: None, on_stack: _ } => { + PassMode::Indirect { attrs, meta_attrs: None, address_space: _, mode: _ } => { let align = attrs.pointee_align.unwrap_or(self.layout.align.abi); OperandValue::Ref(PlaceValue::new_sized(val, align)).store(bx, dst); } // Unsized indirect arguments cannot be stored - PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { + PassMode::Indirect { attrs: _, meta_attrs: Some(_), address_space: _, mode: _ } => { bug!("unsized `ArgAbi` cannot be stored"); } PassMode::Cast { cast, pad_i32_count: _ } => { @@ -303,11 +306,11 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { PassMode::Pair(..) => { OperandValue::Pair(next(), next()).store(bx, dst); } - PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { + PassMode::Indirect { attrs: _, meta_attrs: Some(_), address_space: _, mode: _ } => { bug!("unsized `ArgAbi` cannot be stored"); } PassMode::Direct(_) - | PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } + | PassMode::Indirect { attrs: _, meta_attrs: None, address_space: _, mode: _ } | PassMode::Cast { .. } => { let next_arg = next(); self.store(bx, next_arg, dst); @@ -368,8 +371,13 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { PassMode::Ignore => cx.type_void(), PassMode::Direct(_) | PassMode::Pair(..) => self.ret.layout.immediate_llvm_type(cx), PassMode::Cast { cast, pad_i32_count: _ } => cast.llvm_type(cx), - PassMode::Indirect { .. } => { - llargument_tys.push(cx.type_ptr()); + PassMode::Indirect { address_space, .. } => { + let ty = if let Some(address_space) = address_space { + cx.type_ptr_ext(*address_space) + } else { + cx.type_ptr() + }; + llargument_tys.push(ty); cx.type_void() } }; @@ -394,7 +402,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 1, true)); continue; } - PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { + PassMode::Indirect { attrs: _, meta_attrs: Some(_), address_space: _, mode: _ } => { // Construct the type of a (wide) pointer to `ty`, and pass its two fields. // Any two ABI-compatible unsized types have the same metadata type and // moreover the same metadata value leads to the same dynamic size and @@ -405,7 +413,13 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { llargument_tys.push(ptr_layout.scalar_pair_element_llvm_type(cx, 1, true)); continue; } - PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => cx.type_ptr(), + PassMode::Indirect { attrs: _, meta_attrs: None, address_space, mode: _ } => { + if let Some(address_space) = address_space { + cx.type_ptr_ext(*address_space) + } else { + cx.type_ptr() + } + } PassMode::Cast { cast, pad_i32_count } => { // Add padding. llargument_tys.extend(std::iter::repeat_n( @@ -495,8 +509,8 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { apply_range_attr(llvm::AttributePlace::ReturnValue, scalar); } } - PassMode::Indirect { attrs, meta_attrs: _, on_stack } => { - assert!(!on_stack); + PassMode::Indirect { attrs, meta_attrs: _, address_space: _, mode } => { + assert!(*mode == IndirectMode::Pointer); let i = apply(attrs); let sret = llvm::CreateStructRetAttr( cx.llcx, @@ -522,7 +536,12 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { for arg in self.args.iter() { match &arg.mode { PassMode::Ignore => {} - PassMode::Indirect { attrs, meta_attrs: None, on_stack: true } => { + PassMode::Indirect { + attrs, + meta_attrs: None, + address_space: _, + mode: IndirectMode::OnStack, + } => { let i = apply(attrs); let byval = llvm::CreateByValAttr( cx.llcx, @@ -530,13 +549,31 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { ); attributes::apply_to_llfn(llfn, llvm::AttributePlace::Argument(i), &[byval]); } + PassMode::Indirect { + attrs, + meta_attrs: None, + address_space: _, + mode: IndirectMode::AmdgpuKernelArg, + } => { + let i = apply(attrs); + let byref = llvm::CreateByRefAttr( + cx.llcx, + cx.type_array(cx.type_i8(), arg.layout.size.bytes()), + ); + attributes::apply_to_llfn(llfn, llvm::AttributePlace::Argument(i), &[byref]); + } PassMode::Direct(attrs) => { let i = apply(attrs); if let BackendRepr::Scalar(scalar) = arg.layout.backend_repr { apply_range_attr(llvm::AttributePlace::Argument(i), scalar); } } - PassMode::Indirect { attrs, meta_attrs: None, on_stack: false } => { + PassMode::Indirect { + attrs, + meta_attrs: None, + address_space: _, + mode: IndirectMode::Pointer, + } => { let i = apply(attrs); if cx.sess().opts.optimize != config::OptLevel::No { attributes::apply_to_llfn( @@ -546,8 +583,13 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { ); } } - PassMode::Indirect { attrs, meta_attrs: Some(meta_attrs), on_stack } => { - assert!(!on_stack); + PassMode::Indirect { + attrs, + meta_attrs: Some(meta_attrs), + address_space: _, + mode, + } => { + assert!(*mode == IndirectMode::Pointer); apply(attrs); apply(meta_attrs); } @@ -625,8 +667,8 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { PassMode::Direct(attrs) => { attrs.apply_attrs_to_callsite(llvm::AttributePlace::ReturnValue, bx.cx, callsite); } - PassMode::Indirect { attrs, meta_attrs: _, on_stack } => { - assert!(!on_stack); + PassMode::Indirect { attrs, meta_attrs: _, address_space: _, mode } => { + assert!(*mode == IndirectMode::Pointer); let i = apply(bx.cx, attrs); let sret = llvm::CreateStructRetAttr( bx.cx.llcx, @@ -646,7 +688,12 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { for arg in self.args.iter() { match &arg.mode { PassMode::Ignore => {} - PassMode::Indirect { attrs, meta_attrs: None, on_stack: true } => { + PassMode::Indirect { + attrs, + meta_attrs: None, + address_space: _, + mode: IndirectMode::OnStack, + } => { let i = apply(bx.cx, attrs); let byval = llvm::CreateByValAttr( bx.cx.llcx, @@ -658,11 +705,38 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { &[byval], ); } + PassMode::Indirect { + attrs, + meta_attrs: None, + address_space: _, + mode: IndirectMode::AmdgpuKernelArg, + } => { + let i = apply(bx.cx, attrs); + let byref = llvm::CreateByRefAttr( + bx.cx.llcx, + bx.cx.type_array(bx.cx.type_i8(), arg.layout.size.bytes()), + ); + attributes::apply_to_callsite( + callsite, + llvm::AttributePlace::Argument(i), + &[byref], + ); + } PassMode::Direct(attrs) - | PassMode::Indirect { attrs, meta_attrs: None, on_stack: false } => { + | PassMode::Indirect { + attrs, + meta_attrs: None, + address_space: _, + mode: IndirectMode::Pointer, + } => { apply(bx.cx, attrs); } - PassMode::Indirect { attrs, meta_attrs: Some(meta_attrs), on_stack: _ } => { + PassMode::Indirect { + attrs, + meta_attrs: Some(meta_attrs), + address_space: _, + mode: _, + } => { apply(bx.cx, attrs); apply(bx.cx, meta_attrs); } diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index f24f4fbacf2f7..ef77abd53527d 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -12,9 +12,10 @@ use rustc_symbol_mangling::mangle_internal_symbol; use crate::attributes::llfn_attrs_from_instance; use crate::builder::SBuilder; +use crate::context::SimpleCx; use crate::declare::declare_simple_fn; use crate::llvm::{self, FromGeneric, TRUE, Type}; -use crate::{SimpleCx, attributes, debuginfo}; +use crate::{attributes, debuginfo}; pub(crate) unsafe fn codegen( tcx: TyCtxt<'_>, diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 46f90aed0fd1d..21888720524a5 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -752,8 +752,13 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'_>>) -> // We use i32 as the type for discarded outputs 'w' }; - if class == 'x' && reg == InlineAsmReg::AArch64(AArch64InlineAsmReg::x30) { - // LLVM doesn't recognize x30. use lr instead. + + if class == 'x' + && reg == InlineAsmReg::AArch64(AArch64InlineAsmReg::x30) + && llvm_util::get_version() < (23, 0, 0) + { + // FIXME(llvm): LLVM <23 does not recognize `x30` as a register name. + // This workaround can be removed when support for LLVM 22 is dropped. "{lr}".to_string() } else { format!("{{{}{}}}", class, idx) @@ -787,7 +792,9 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'_>>) -> // LLVM uses `wN` for Hexagon HVX vector pair registers. format!("{{w{}}}", idx) } else if reg == InlineAsmReg::Arm(ArmInlineAsmReg::r14) { - // LLVM doesn't recognize r14 + // FIXME(llvm): LLVM <23 does not recognize `r14` as a register name + // in inline assembly. + // We can make this branch fallthrough again for LLVM 23+ when https://github.com/llvm/llvm-project/pull/225746 gets released. "{lr}".to_string() } else if let InlineAsmReg::Sparc(reg) = reg && let Some(num) = reg.dreg_number() diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 8a7e3ec6b5942..4073d521293a9 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -8,6 +8,7 @@ use rustc_middle::middle::codegen_fn_attrs::{ }; use rustc_middle::ty::{self, Instance, TyCtxt}; use rustc_sanitizers::ignorelist::SanitizerIgnoreList; +use rustc_session::Session; use rustc_session::config::{ BranchProtection, FunctionReturn, InstrumentMcount, InstrumentMcountOpts, OptLevel, PAuthKey, PacRet, @@ -23,7 +24,7 @@ use crate::llvm::AttributePlace::Function; use crate::llvm::{ self, AllocKindFlags, Attribute, AttributeKind, AttributePlace, MemoryEffects, Value, }; -use crate::{Session, attributes, llvm_util}; +use crate::{attributes, llvm_util}; pub(crate) fn apply_to_llfn(llfn: &Value, idx: AttributePlace, attrs: &[&Attribute]) { if !attrs.is_empty() { diff --git a/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs b/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs new file mode 100644 index 0000000000000..8780af2f6d595 --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs @@ -0,0 +1,491 @@ +//! Module containing [`LlvmCodegenBackend`], which implements [`CodegenBackend`] +//! and related traits for the LLVM codegen backend. + +use std::any::Any; +use std::ffi::CStr; +use std::mem::ManuallyDrop; +use std::path::PathBuf; + +use rustc_ast::expand::allocator::AllocatorMethod; +use rustc_codegen_ssa::back::lto::ThinModule; +use rustc_codegen_ssa::back::write::{ + CodegenContext, FatLtoInput, ModuleConfig, SharedEmitter, TargetMachineFactoryConfig, + TargetMachineFactoryFn, ThinLtoInput, +}; +use rustc_codegen_ssa::traits::{CodegenBackend, ExtraBackendMethods, WriteBackendMethods}; +use rustc_codegen_ssa::{CompiledModule, CompiledModules, CrateInfo, ModuleCodegen, TargetConfig}; +use rustc_data_structures::profiling::SelfProfilerRef; +use rustc_errors::{DiagCtxt, DiagCtxtHandle}; +use rustc_metadata::EncodedMetadata; +use rustc_middle::dep_graph::{WorkProduct, WorkProductMap}; +use rustc_middle::ty::TyCtxt; +use rustc_session::config::{OptLevel, OutputFilenames, PrintKind, PrintRequest}; +use rustc_session::{CodegenBackendInit, EarlySession, IncrCompSession, Session}; +use rustc_span::{Symbol, sym}; +use rustc_target::spec::{RelocModel, TlsModel}; + +use crate::back::owned_target_machine::OwnedTargetMachine; +use crate::back::write::{create_informational_target_machine, create_target_machine}; +use crate::context::{self, SimpleCx}; +use crate::llvm::{self, ToLlvmBool}; +use crate::llvm_util::{self, target_config}; +use crate::{allocator, back, base}; + +#[derive(Clone)] +pub struct LlvmCodegenBackend(()); + +struct TimeTraceProfiler {} + +impl TimeTraceProfiler { + fn new() -> Self { + unsafe { llvm::LLVMRustTimeTraceProfilerInitialize() } + TimeTraceProfiler {} + } +} + +impl Drop for TimeTraceProfiler { + fn drop(&mut self) { + unsafe { llvm::LLVMRustTimeTraceProfilerFinishThread() } + } +} + +impl ExtraBackendMethods for LlvmCodegenBackend { + type Module = ModuleLlvm; + + fn codegen_allocator<'tcx>( + &self, + tcx: TyCtxt<'tcx>, + module_name: &str, + methods: &[AllocatorMethod], + ) -> ModuleLlvm { + let module_llvm = ModuleLlvm::new_metadata(tcx, module_name); + let cx = + SimpleCx::new(module_llvm.llmod(), &module_llvm.llcx, tcx.data_layout.pointer_size()); + unsafe { + allocator::codegen(tcx, cx, module_name, methods); + } + module_llvm + } + fn compile_codegen_unit( + &self, + tcx: TyCtxt<'_>, + cgu_name: Symbol, + bitcode_needed: bool, + ) -> (ModuleCodegen, u64) { + base::compile_codegen_unit(tcx, cgu_name, bitcode_needed) + } +} + +impl WriteBackendMethods for LlvmCodegenBackend { + type Module = ModuleLlvm; + type ModuleBuffer = back::lto::ModuleBuffer; + type TargetMachine = OwnedTargetMachine; + type ThinData = back::lto::ThinData; + + fn thread_profiler() -> Box { + Box::new(TimeTraceProfiler::new()) + } + fn target_machine_factory( + &self, + sess: &Session, + optlvl: OptLevel, + ) -> TargetMachineFactoryFn { + back::write::target_machine_factory(sess, optlvl) + } + fn optimize_and_codegen_fat_lto( + sess: &Session, + cgcx: &CodegenContext, + shared_emitter: &SharedEmitter, + tm_factory: TargetMachineFactoryFn, + exported_symbols_for_lto: &[String], + each_linked_rlib_for_lto: &[PathBuf], + modules: Vec>, + ) -> CompiledModule { + let mut module = back::lto::run_fat( + cgcx, + &sess.prof, + shared_emitter, + tm_factory, + exported_symbols_for_lto, + each_linked_rlib_for_lto, + modules, + ); + + let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); + let dcx = dcx.handle(); + back::lto::run_pass_manager(cgcx, &sess.prof, dcx, &mut module, false); + + back::write::codegen(cgcx, &sess.prof, shared_emitter, module, &cgcx.module_config) + } + fn run_thin_lto( + cgcx: &CodegenContext, + prof: &SelfProfilerRef, + dcx: DiagCtxtHandle<'_>, + exported_symbols_for_lto: &[String], + each_linked_rlib_for_lto: &[PathBuf], + modules: Vec>, + ) -> (Vec>, Vec) { + back::lto::run_thin( + cgcx, + prof, + dcx, + exported_symbols_for_lto, + each_linked_rlib_for_lto, + modules, + ) + } + fn optimize( + cgcx: &CodegenContext, + prof: &SelfProfilerRef, + shared_emitter: &SharedEmitter, + module: &mut ModuleCodegen, + config: &ModuleConfig, + ) { + back::write::optimize(cgcx, prof, shared_emitter, module, config) + } + fn optimize_and_codegen_thin( + cgcx: &CodegenContext, + prof: &SelfProfilerRef, + shared_emitter: &SharedEmitter, + tm_factory: TargetMachineFactoryFn, + thin: ThinModule, + ) -> CompiledModule { + back::lto::optimize_and_codegen_thin_module(cgcx, prof, shared_emitter, tm_factory, thin) + } + fn codegen( + cgcx: &CodegenContext, + prof: &SelfProfilerRef, + shared_emitter: &SharedEmitter, + module: ModuleCodegen, + config: &ModuleConfig, + ) -> CompiledModule { + back::write::codegen(cgcx, prof, shared_emitter, module, config) + } + fn serialize_module(module: Self::Module, is_thin: bool) -> Self::ModuleBuffer { + back::lto::ModuleBuffer::new(module.llmod(), is_thin) + } +} + +impl LlvmCodegenBackend { + pub fn new() -> Box { + Box::new(LlvmCodegenBackend(())) + } +} + +impl CodegenBackend for LlvmCodegenBackend { + fn name(&self) -> &'static str { + "llvm" + } + + fn init(&mut self, sess: &EarlySession) -> CodegenBackendInit { + llvm_util::init(sess); // Make sure llvm is inited + + let global_backend_features = + llvm_util::global_llvm_features(sess, /* for_cfg */ false); + + // autodiff is based on Enzyme, a library which we might not have available, when it was + // neither build, nor downloaded via rustup. If autodiff is used, but not available we emit + // an early error here and abort compilation. + { + use rustc_session::config::AutoDiff; + + use crate::back::lto::enable_autodiff_settings; + if sess.opts.unstable_opts.autodiff.contains(&AutoDiff::Enable) { + match llvm::EnzymeWrapper::get_or_init(&sess.opts.sysroot) { + Ok(_) => {} + Err(llvm::EnzymeLibraryError::NotFound { err }) => { + sess.dcx().emit_fatal(crate::diagnostics::AutoDiffComponentMissing { err }); + } + Err(llvm::EnzymeLibraryError::LoadFailed { err }) => { + sess.dcx() + .emit_fatal(crate::diagnostics::AutoDiffComponentUnavailable { err }); + } + } + enable_autodiff_settings(&sess.opts.unstable_opts.autodiff); + } + } + + // Intrinsics whose fallback body will not be used by the LLVM backend. + let replaced_intrinsics = { + #[rustfmt::skip] + let will_not_use_fallback = vec![ + // These are mapped to LLVM intrinsics instead. + sym::unchecked_funnel_shl, + sym::unchecked_funnel_shr, + sym::carrying_mul_add, + sym::carryless_mul, + sym::integer_max, + sym::integer_min, + + // Fallback via libm, but the LLVM intrinsic is used instead. + sym::sin, + sym::cos, + sym::powf16, sym::powf32, sym::powf64, + sym::exp, + sym::exp2, + sym::log, + sym::log10, + sym::log2, + + // Fallback via f32 or f64, but the LLVM intrinsic is used instead. + sym::floorf16, sym::ceilf16, sym::truncf16, + sym::round_ties_even_f16, sym::roundf16, + sym::sqrtf16, sym::powif16, + sym::fmaf16, + + sym::copysignf16, sym::copysignf32, sym::copysignf64, sym::copysignf128, + ]; + + will_not_use_fallback + }; + + // `type_id_eq` is a safe choice since *all* backends use the fallback body for that. When + // adding more intrinsics, keep in mind that the distributed standard library is compiled + // with the LLVM backend but might later be included in a project built with cranelift or + // GCC. Adding an intrinsic here can therefore mean the fallback body is used with + // cranelift/GCC even if they have dedicated implementations. + let fallback_intrinsics = vec![sym::type_id_eq]; + + CodegenBackendInit { + global_backend_features, + replaced_intrinsics, + fallback_intrinsics, + thin_lto_supported: true, + } + } + + fn print(&self, req: &PrintRequest, out: &mut String, sess: &Session) { + use std::fmt::Write; + match req.kind { + PrintKind::RelocationModels => { + writeln!(out, "Available relocation models:").unwrap(); + for name in RelocModel::ALL.iter().map(RelocModel::desc).chain(["default"]) { + writeln!(out, " {name}").unwrap(); + } + writeln!(out).unwrap(); + } + PrintKind::CodeModels => { + writeln!(out, "Available code models:").unwrap(); + for name in &["tiny", "small", "kernel", "medium", "large"] { + writeln!(out, " {name}").unwrap(); + } + writeln!(out).unwrap(); + } + PrintKind::TlsModels => { + writeln!(out, "Available TLS models:").unwrap(); + for name in TlsModel::ALL.iter().map(TlsModel::desc) { + writeln!(out, " {name}").unwrap(); + } + writeln!(out).unwrap(); + } + PrintKind::StackProtectorStrategies => { + writeln!( + out, + r#"Available stack protector strategies: + all + Generate stack canaries in all functions. + + strong + Generate stack canaries in a function if it either: + - has a local variable of `[T; N]` type, regardless of `T` and `N` + - takes the address of a local variable. + + (Note that a local variable being borrowed is not equivalent to its + address being taken: e.g. some borrows may be removed by optimization, + while by-value argument passing may be implemented with reference to a + local stack variable in the ABI.) + + basic + Generate stack canaries in functions with local variables of `[T; N]` + type, where `T` is byte-sized and `N` >= 8. + + none + Do not generate stack canaries. +"# + ) + .unwrap(); + } + _other => llvm_util::print(req, out, sess), + } + } + + fn print_passes(&self) { + llvm_util::print_passes(); + } + + fn print_version(&self) { + llvm_util::print_version(); + } + + fn has_zstd(&self) -> bool { + llvm::LLVMRustLLVMHasZstdCompression() + } + + fn has_mnemonic(&self, sess: &Session, mnemonic: &str) -> bool { + llvm_util::target_has_mnemonic(sess, mnemonic) + } + + fn target_config(&self, sess: &EarlySession) -> TargetConfig { + target_config(sess) + } + + fn target_cpu(&self, sess: &Session) -> String { + crate::llvm_util::target_cpu(sess).to_string() + } + + fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Box { + use rustc_session::config::Offload; + + if tcx.sess.opts.unstable_opts.offload.iter().any(|o| matches!(o, Offload::Device(_))) + || tcx.sess.opts.unstable_opts.offload.iter().any(|o| matches!(o, Offload::Host(_))) + { + match llvm::RustOffloadWrapper::get_or_init(&tcx.sess.opts.sysroot) { + Ok(_) => {} + Err(llvm::RustOffloadLibraryError::NotFound { err }) => { + tcx.sess + .dcx() + .emit_fatal(crate::diagnostics::RustOffloadComponentMissing { err }); + } + Err(llvm::RustOffloadLibraryError::LoadFailed { err }) => { + tcx.sess + .dcx() + .emit_fatal(crate::diagnostics::RustOffloadComponentUnavailable { err }); + } + } + } + + Box::new(rustc_codegen_ssa::base::codegen_crate(LlvmCodegenBackend(()), tcx)) + } + + fn join_codegen( + &self, + ongoing_codegen: Box, + sess: &Session, + incr_comp_session: Option<&IncrCompSession>, + outputs: &OutputFilenames, + crate_info: &CrateInfo, + ) -> (CompiledModules, WorkProductMap) { + let (compiled_modules, work_products) = ongoing_codegen + .downcast::>() + .expect("Expected LlvmCodegenBackend's OngoingCodegen, found Box") + .join(sess, incr_comp_session, crate_info); + + if sess.opts.unstable_opts.llvm_time_trace { + sess.time("llvm_dump_timing_file", || { + let file_name = outputs.with_extension("llvm_timings.json"); + llvm_util::time_trace_profiler_finish(&file_name); + }); + } + + (compiled_modules, work_products) + } + + fn print_pass_timings(&self) { + let timings = llvm::build_string(|s| unsafe { llvm::LLVMRustPrintPassTimings(s) }).unwrap(); + print!("{timings}"); + } + + fn print_statistics(&self) { + let stats = llvm::build_string(|s| unsafe { llvm::LLVMRustPrintStatistics(s) }).unwrap(); + print!("{stats}"); + } + + fn print_statistics_json(&self) -> String { + llvm::build_string(|s| unsafe { llvm::LLVMRustPrintStatisticsJSON(s) }).unwrap() + } + + fn link( + &self, + sess: &Session, + compiled_modules: CompiledModules, + crate_info: CrateInfo, + metadata: EncodedMetadata, + outputs: &OutputFilenames, + ) { + use rustc_codegen_ssa::back::link::link_binary; + + use crate::back::archive::LlvmArchiveBuilderBuilder; + + // Run the linker on any artifacts that resulted from the LLVM run. + // This should produce either a finished executable or library. + link_binary( + sess, + &LlvmArchiveBuilderBuilder, + compiled_modules, + crate_info, + metadata, + outputs, + self.name(), + ); + } +} + +pub struct ModuleLlvm { + pub(crate) llcx: &'static mut llvm::Context, + llmod_raw: *const llvm::Module, + + // This field is `ManuallyDrop` because it is important that the `TargetMachine` + // is disposed prior to the `Context` being disposed otherwise UAFs can occur. + pub(crate) tm: ManuallyDrop, +} + +unsafe impl Send for ModuleLlvm {} +unsafe impl Sync for ModuleLlvm {} + +impl ModuleLlvm { + pub(crate) fn new(tcx: TyCtxt<'_>, mod_name: &str) -> Self { + unsafe { + let llcx = llvm::LLVMContextCreate(); + llvm::LLVMContextSetDiscardValueNames(llcx, tcx.sess.fewer_names().to_llvm_bool()); + let llmod_raw = context::create_module(tcx, llcx, mod_name) as *const _; + ModuleLlvm { + llmod_raw, + llcx, + tm: ManuallyDrop::new(create_target_machine(tcx, mod_name)), + } + } + } + + pub(crate) fn new_metadata(tcx: TyCtxt<'_>, mod_name: &str) -> Self { + unsafe { + let llcx = llvm::LLVMContextCreate(); + llvm::LLVMContextSetDiscardValueNames(llcx, tcx.sess.fewer_names().to_llvm_bool()); + let llmod_raw = context::create_module(tcx, llcx, mod_name) as *const _; + ModuleLlvm { + llmod_raw, + llcx, + tm: ManuallyDrop::new(create_informational_target_machine(tcx.sess)), + } + } + } + + pub(crate) fn parse( + cgcx: &CodegenContext, + tm_factory: TargetMachineFactoryFn, + name: &CStr, + buffer: &[u8], + dcx: DiagCtxtHandle<'_>, + ) -> Self { + unsafe { + let llcx = llvm::LLVMContextCreate(); + llvm::LLVMContextSetDiscardValueNames(llcx, cgcx.fewer_names.to_llvm_bool()); + let llmod_raw = back::lto::parse_module(llcx, name, buffer, dcx); + let tm = tm_factory(dcx, TargetMachineFactoryConfig::new(cgcx, name.to_str().unwrap())); + + ModuleLlvm { llmod_raw, llcx, tm: ManuallyDrop::new(tm) } + } + } + + pub(crate) fn llmod(&self) -> &llvm::Module { + unsafe { &*self.llmod_raw } + } +} + +impl Drop for ModuleLlvm { + fn drop(&mut self) { + unsafe { + ManuallyDrop::drop(&mut self.tm); + llvm::LLVMContextDispose(&mut *(self.llcx as *mut _)); + } + } +} diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index cb12c103834af..d52e34d30fb7d 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -463,23 +463,29 @@ fn thin_lto( info!("thin LTO data created"); - let (key_map_path, prev_key_map, curr_key_map) = if let Some(ref incr_comp_session_dir) = - cgcx.incr_comp_session_dir + let new_key_map_path = cgcx + .new_incr_comp_session_dir + .as_ref() + .map(|dir| dir.join(THIN_LTO_KEYS_INCR_COMP_FILE_NAME)); + + let prev_key_map = if let Some(ref old_incr_comp_session_dir) = + cgcx.old_incr_comp_session_dir { - let path = incr_comp_session_dir.join(THIN_LTO_KEYS_INCR_COMP_FILE_NAME); + let old_path = old_incr_comp_session_dir.join(THIN_LTO_KEYS_INCR_COMP_FILE_NAME); + // If the previous file was deleted, or we get an IO error // reading the file, then we'll just use `None` as the // prev_key_map, which will force the code to be recompiled. - let prev = - if path.exists() { ThinLTOKeysMap::load_from_file(&path).ok() } else { None }; - let curr = ThinLTOKeysMap::from_thin_lto_modules(&data, &thin_modules, &module_names); - (Some(path), prev, curr) + if old_path.exists() { ThinLTOKeysMap::load_from_file(&old_path).ok() } else { None } + } else { + assert!(green_modules.is_empty()); + None + }; + let curr_key_map = if cgcx.new_incr_comp_session_dir.is_some() { + ThinLTOKeysMap::from_thin_lto_modules(&data, &thin_modules, &module_names) } else { - // If we don't compile incrementally, we don't need to load the - // import data from LLVM. assert!(green_modules.is_empty()); - let curr = ThinLTOKeysMap::default(); - (None, None, curr) + ThinLTOKeysMap::default() }; info!("thin LTO cache key map loaded"); info!("prev_key_map: {:#?}", prev_key_map); @@ -500,7 +506,8 @@ fn thin_lto( if let (Some(prev_key_map), true) = (prev_key_map.as_ref(), green_modules.contains_key(module_name)) { - assert!(cgcx.incr_comp_session_dir.is_some()); + assert!(cgcx.old_incr_comp_session_dir.is_some()); + assert!(cgcx.new_incr_comp_session_dir.is_some()); // If a module exists in both the current and the previous session, // and has the same LTO cache key in both sessions, then we can re-use it @@ -508,7 +515,6 @@ fn thin_lto( let work_product = green_modules[module_name].clone(); copy_jobs.push(work_product); info!(" - {}: re-used", module_name); - assert!(cgcx.incr_comp_session_dir.is_some()); continue; } } @@ -518,8 +524,8 @@ fn thin_lto( } // Save the current ThinLTO import information for the next compilation - // session, overwriting the previous serialized data (if any). - if let Some(path) = key_map_path + // session. + if let Some(path) = new_key_map_path && let Err(err) = curr_key_map.save_to_file(&path) { write::llvm_err(dcx, LlvmError::WriteThinLtoKey { err }); diff --git a/compiler/rustc_codegen_llvm/src/back/mod.rs b/compiler/rustc_codegen_llvm/src/back/mod.rs index de6007c17bfff..3ecd7dd892d37 100644 --- a/compiler/rustc_codegen_llvm/src/back/mod.rs +++ b/compiler/rustc_codegen_llvm/src/back/mod.rs @@ -1,4 +1,5 @@ pub(crate) mod archive; +pub(crate) mod llvm_backend; pub(crate) mod lto; pub(crate) mod owned_mc_subtarget_info; pub(crate) mod owned_target_machine; diff --git a/compiler/rustc_codegen_llvm/src/back/owned_mc_subtarget_info.rs b/compiler/rustc_codegen_llvm/src/back/owned_mc_subtarget_info.rs index f57368e755add..1a85f1458d258 100644 --- a/compiler/rustc_codegen_llvm/src/back/owned_mc_subtarget_info.rs +++ b/compiler/rustc_codegen_llvm/src/back/owned_mc_subtarget_info.rs @@ -4,7 +4,7 @@ use std::ptr::NonNull; use rustc_data_structures::small_c_str::SmallCStr; use crate::diagnostics::LlvmError; -use crate::llvm; +use crate::{llvm, llvm_util}; /// Responsible for safely creating and disposing llvm::MCSubtargetInfo via ffi functions. /// Not cloneable as there is no clone function for llvm::MCSubtargetInfo. @@ -28,11 +28,20 @@ impl OwnedMCSubtargetInfo { .ok_or_else(|| LlvmError::CreateMCSubtargetInfo { triple: SmallCStr::from(triple) }) } - pub(crate) fn has_feature(&self, feature: &CStr) -> bool { - // SAFETY: `new` ensures we have a valid pointer created by + pub(crate) fn has_features(&self, features: llvm_util::LLVMFeature<'_>) -> bool { + // Convert to a feature string expected by LLVM's `SubtargetFeatures`. + // All of the required LLVM features must be enabled. + // (The trailing comma is OK, as `SubtargetFeatures::Split` discards empty substrings.) + let features = features.into_iter().flat_map(|feat| ["+", feat, ","]).collect::(); + + // SAFETY: `info_unique` is a valid pointer created by // `llvm::LLVMRustCreateMCSubtargetInfo`. unsafe { - llvm::LLVMRustMCSubtargetInfoHasFeature(self.info_unique.as_ref(), feature.as_ptr()) + llvm::LLVMRustMCSubtargetInfoCheckFeatures( + self.info_unique.as_ref(), + features.as_ptr(), + features.len(), + ) } } } diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 92989ba2dcf46..a95f259545982 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -22,7 +22,7 @@ use rustc_fs_util::{link_or_copy, path_to_c_string}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_session::config::{self, Lto, OutputType, Passes, SplitDwarfKind, SwitchWithOptPath}; -use rustc_span::{BytePos, InnerSpan, Pos, RemapPathScopeComponents, SpanData, SyntaxContext}; +use rustc_span::{BytePos, DUMMY_SP, InnerSpan, Pos, RemapPathScopeComponents}; use rustc_target::spec::{CodeModel, FloatAbi, RelocModel, SanitizerSet, SplitDebuginfo, TlsModel}; use tracing::{debug, trace}; @@ -34,6 +34,7 @@ use crate::back::profiling::{ use crate::builder::SBuilder; use crate::builder::gpu_offload::scalar_width; use crate::common::AsCCharPtr; +use crate::context::SimpleCx; use crate::diagnostics::{ CopyBitcode, FromLlvmDiag, FromLlvmOptimizationDiag, LlvmError, ParseTargetMachineConfig, UnsupportedCompression, WithLlvmError, WriteBytecode, @@ -41,7 +42,7 @@ use crate::diagnostics::{ use crate::llvm::diagnostic::OptimizationDiagnosticKind::*; use crate::llvm::{self, DiagnosticInfo}; use crate::type_::llvm_type_ptr; -use crate::{LlvmCodegenBackend, ModuleLlvm, SimpleCx, attributes, base, common, llvm_util}; +use crate::{LlvmCodegenBackend, ModuleLlvm, attributes, base, common, llvm_util}; pub(crate) fn llvm_err<'a>(dcx: DiagCtxtHandle<'_>, err: LlvmError<'a>) -> ! { match llvm::last_error() { @@ -71,17 +72,14 @@ fn write_output_file<'ll>( std::ptr::null() }; let result = unsafe { - let pm = llvm::LLVMCreatePassManager(); - llvm::LLVMAddAnalysisPasses(target, pm); - llvm::LLVMRustAddLibraryInfo(target, pm, m, no_builtins); llvm::LLVMRustWriteOutputFile( target, - pm, m, output_c.as_ptr(), dwo_output_ptr, file_type, verify_llvm_ir, + no_builtins, ) }; @@ -405,23 +403,20 @@ fn report_inline_asm( // In LTO build we may get srcloc values from other crates which are invalid // since they use a different source map. To be safe we just suppress these // in LTO builds. - let span = if cookie == 0 || matches!(cgcx.lto, Lto::Fat | Lto::Thin) { - SpanData::default() + let (lo, hi) = if cookie == 0 || matches!(cgcx.lto, Lto::Fat | Lto::Thin) { + (DUMMY_SP.lo(), DUMMY_SP.hi()) } else { - SpanData { - lo: BytePos::from_u32(cookie as u32), - hi: BytePos::from_u32((cookie >> 32) as u32), - ctxt: SyntaxContext::root(), - parent: None, - } + let lo = BytePos::from_u32(cookie as u32); + let hi = BytePos::from_u32((cookie >> 32) as u32); + (lo, hi) }; let level = match level { llvm::DiagnosticLevel::Error => Level::Error, - llvm::DiagnosticLevel::Warning => Level::Warning, + llvm::DiagnosticLevel::Warning => Level::Warning(None), llvm::DiagnosticLevel::Note | llvm::DiagnosticLevel::Remark => Level::Note, }; let msg = msg.trim_prefix("error: ").to_string(); - InlineAsmError { span, msg, level, source } + InlineAsmError { lo, hi, msg, level, source } } unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void) { @@ -824,9 +819,7 @@ pub(crate) unsafe fn llvm_optimize( // This assumes that we previously compiled our kernels for a gpu target, which created a // `device.bin` artifact. The user is supposed to provide us with a path to this artifact, we - // don't need any other artifacts from the previous run. We will embed this artifact into our - // LLVM-IR host module, to create a `host.o` ObjectFile, which we will write to disk. - // The last, not yet automated steps uses the `clang-linker-wrapper` to process `host.o`. + // don't need any other artifacts from the previous run. if !cgcx.target_is_like_gpu && is_final_stage { if let Some(device_path) = config .offload @@ -845,38 +838,7 @@ pub(crate) unsafe fn llvm_optimize( } else if !device_pathbuf.exists() { dcx.emit_err(crate::diagnostics::OffloadNonexistingPath); } - let host_path = cgcx.output_filenames.path(OutputType::Object); - let host_dir = host_path.parent().unwrap(); - let out_obj = host_dir.join("host.o"); let device_bin_c = path_to_c_string(device_pathbuf.as_path()); - - // 2) Finalize host: lib.bc + device.bin -> host.o (host TM) - // We create a full clone of our LLVM host module, since we will embed the device IR - // into it, and this might break caching or incremental compilation otherwise. - let ok = unsafe { - llvm::RustOffloadWrapper::get_instance().llvm_rust_offload_embed_buffer_in_module( - module.module_llvm.llmod(), - device_bin_c.as_c_str(), - ) - }; - if !ok { - dcx.emit_err(crate::diagnostics::OffloadEmbedFailed); - } - write_output_file( - dcx, - module.module_llvm.tm.raw(), - config.no_builtins, - module.module_llvm.llmod(), - &out_obj, - None, - llvm::FileType::ObjectFile, - prof, - true, - ); - // We ignore cgcx.save_temps here and unconditionally always keep our `device.bin` artifact. - // Otherwise, recompiling the host code would fail since we deleted that device artifact - // in the previous host compilation, which would be confusing at best. - let ok = unsafe { llvm::RustOffloadWrapper::get_instance().llvm_rust_offload_wrap_images( module.module_llvm.llmod(), diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 5316b7425be47..77cb37edec770 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -13,12 +13,12 @@ use std::time::Instant; +use rustc_attr_ir::Linkage; use rustc_codegen_ssa::ModuleCodegen; use rustc_codegen_ssa::base::maybe_create_entry_wrapper; use rustc_codegen_ssa::mono_item::MonoItemExt; use rustc_codegen_ssa::traits::*; use rustc_data_structures::small_c_str::SmallCStr; -use rustc_hir::attrs::Linkage; use rustc_middle::dep_graph; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrs, SanitizerFnAttrs}; use rustc_middle::mono::Visibility; @@ -27,12 +27,11 @@ use rustc_session::config::{DebugInfo, Offload}; use rustc_span::Symbol; use rustc_target::spec::SanitizerSet; -use super::ModuleLlvm; -use crate::attributes; use crate::builder::Builder; use crate::builder::gpu_offload::OffloadGlobals; use crate::context::CodegenCx; use crate::llvm::{self, Value}; +use crate::{ModuleLlvm, attributes}; pub(crate) struct ValueIter<'ll> { cur: Option<&'ll Value>, @@ -238,7 +237,6 @@ pub(crate) fn linkage_to_llvm(linkage: Linkage) -> llvm::Linkage { Linkage::WeakODR => llvm::Linkage::WeakODRLinkage, Linkage::Internal => llvm::Linkage::InternalLinkage, Linkage::ExternalWeak => llvm::Linkage::ExternalWeakLinkage, - Linkage::Common => llvm::Linkage::CommonLinkage, } } diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 9d4602e49968d..b7c930502978e 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -8,13 +8,13 @@ pub(crate) mod gpu_offload; use libc::{c_char, c_uint}; use rustc_abi::{self as abi, Align, CanonAbi, Size, WrappingRange}; +use rustc_attr_ir::{AttributeKind, UnrollAttr}; use rustc_codegen_ssa::MemFlags; use rustc_codegen_ssa::common::{IntPredicate, RealPredicate, SynchronizationScope, TypeKind}; use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; use rustc_codegen_ssa::mir::place::PlaceRef; use rustc_codegen_ssa::traits::*; use rustc_data_structures::small_c_str::SmallCStr; -use rustc_hir::attrs::{AttributeKind, UnrollAttr}; use rustc_hir::def_id::DefId; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::ty::layout::{ @@ -340,14 +340,14 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } } - fn br_with_attrs(&mut self, dest: &'ll BasicBlock, attributes: &[AttributeKind]) { + fn br_with_attrs(&mut self, dest: &'ll BasicBlock, loop_hint_attrs: &[AttributeKind]) { unsafe { let val = llvm::LLVMBuildBr(self.llbuilder, dest); let mut nodes = Vec::new(); - for attribute in attributes { - let AttributeKind::Unroll(unroll) = attribute else { + for loop_hint_attr in loop_hint_attrs { + let AttributeKind::Unroll(unroll) = loop_hint_attr else { continue; }; // UnrollAttr::Count needs a second operand, the provided count, but the other @@ -919,9 +919,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { self.set_metadata_node(store, llvm::MD_nontemporal, &[one]); } } - if flags.contains(MemFlags::CAPTURES_READ_ONLY) - && crate::llvm_util::get_version() >= (22, 0, 0) - { + if flags.contains(MemFlags::CAPTURES_READ_ONLY) { assert!( self.type_kind(self.val_ty(val)) == TypeKind::Pointer, "CAPTURED_READ_ONLY is only supported on pointer stores" @@ -1523,21 +1521,6 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { ) }; - if let Some(callee_instance) = callee_instance { - // Attributes on the function definition being called - let callee_attrs = self.cx.tcx.codegen_fn_attrs(callee_instance.def_id()); - - if let Some(inlining_rule) = - attributes::inline_attr(&self.cx, self.cx.tcx, callee_instance, callee_attrs) - { - attributes::apply_to_callsite( - call, - llvm::AttributePlace::Function, - &[inlining_rule], - ); - } - } - if let Some(fn_abi) = fn_abi { fn_abi.apply_attrs_callsite(self, call); } @@ -1918,14 +1901,10 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { return; } - if crate::llvm_util::get_version() >= (22, 0, 0) { - // LLVM 22 requires the lifetime intrinsic to act directly on the alloca, - // there can't be an addrspacecast in between. - let ptr = unsafe { llvm::LLVMRustStripPointerCasts(ptr) }; - self.call_intrinsic(intrinsic, &[self.val_ty(ptr)], &[ptr]); - } else { - self.call_intrinsic(intrinsic, &[self.val_ty(ptr)], &[self.cx.const_u64(size), ptr]); - } + // LLVM 22 requires the lifetime intrinsic to act directly on the alloca, + // there can't be an addrspacecast in between. + let ptr = unsafe { llvm::LLVMRustStripPointerCasts(ptr) }; + self.call_intrinsic(intrinsic, &[self.val_ty(ptr)], &[ptr]); } } impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { @@ -2080,17 +2059,24 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { let is_diag = self.tcx.sess.opts.unstable_opts.sanitizer_cfi_diag.unwrap_or(false); let is_recover = self.tcx.sess.opts.unstable_opts.sanitizer_cfi_recover.unwrap_or(false); + let is_minimal = + self.tcx.sess.opts.unstable_opts.sanitizer_cfi_minimal_runtime.unwrap_or(false); if is_diag || is_recover { - let fty = self.cx.type_func( - &[self.cx.type_ptr(), self.cx.type_isize(), self.cx.type_isize()], - self.cx.type_void(), - ); + let fty = if is_minimal { + self.cx.type_func(&[], self.cx.type_void()) + } else { + self.cx.type_func( + &[self.cx.type_ptr(), self.cx.type_isize(), self.cx.type_isize()], + self.cx.type_void(), + ) + }; let ubsan_handler = self.declare_cfn( - if is_recover { - "__ubsan_handle_cfi_check_fail" - } else { - "__ubsan_handle_cfi_check_fail_abort" + match (is_minimal, is_recover) { + (true, true) => "__ubsan_handle_cfi_check_fail_minimal", + (true, false) => "__ubsan_handle_cfi_check_fail_minimal_abort", + (false, true) => "__ubsan_handle_cfi_check_fail", + (false, false) => "__ubsan_handle_cfi_check_fail_abort", }, llvm::UnnamedAddr::Global, fty, @@ -2116,13 +2102,18 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { self.generate_ubsan_cfi_diag_data(self.span, expected_ty, check_kind); let function_address = self.ptrtoint(llfn, self.cx.type_isize()); + let arguments: &[_] = if is_minimal { + &[] + } else { + &[diag_data, function_address, self.const_usize(0)] + }; self.call( fty, None, None, ubsan_handler, ReturnSlot::Direct, - &[diag_data, function_address, self.const_usize(0)], + arguments, None, None, ); @@ -2132,7 +2123,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { self.unreachable(); } } else { - self.abort(); + self.abort_immediate(); self.unreachable(); } diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index 4860c79095c28..03058ba02d9dc 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -2,13 +2,13 @@ use std::ptr; use rustc_ast::expand::autodiff_attrs::{DiffActivity, DiffMode}; use rustc_ast::expand::typetree::FncTree; +use rustc_attr_ir::RustcAutodiff; use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::mir::IntrinsicResult; use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; use rustc_codegen_ssa::mir::place::PlaceValue; use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, BuilderMethods, ReturnSlot}; use rustc_data_structures::thin_vec::ThinVec; -use rustc_hir::attrs::RustcAutodiff; use rustc_middle::ty; use rustc_middle::ty::{PseudoCanonicalInput, Ty, TyCtxt, TypingEnv}; use rustc_span::bug; diff --git a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs index 8ff98b498aa94..d3bb8d7c3e6a4 100644 --- a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs +++ b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs @@ -10,11 +10,12 @@ use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, BuilderMethods, ReturnSl use rustc_middle::ty::offload_meta::{MappingFlags, OffloadMetadata, OffloadSize}; use rustc_span::bug; +use crate::attributes; use crate::builder::Builder; use crate::common::CodegenCx; +use crate::context::SimpleCx; use crate::llvm::AttributePlace::Function; use crate::llvm::{self, Linkage, Type, Value}; -use crate::{SimpleCx, attributes}; // LLVM kernel-independent globals required for offloading pub(crate) struct OffloadGlobals<'ll> { diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs index c64c9ee3266c1..df18b029b02c0 100644 --- a/compiler/rustc_codegen_llvm/src/callee.rs +++ b/compiler/rustc_codegen_llvm/src/callee.rs @@ -4,6 +4,7 @@ //! and methods are represented as just a fn ptr and not a full //! closure. +use rustc_attr_ir::InlineAttr; use rustc_codegen_ssa::common; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv}; use rustc_middle::ty::{self, Instance, TypeVisitableExt}; @@ -98,8 +99,7 @@ pub(crate) fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'t let is_hidden = if is_generic { // This is a monomorphization of a generic function. if !(cx.tcx.sess.opts.share_generics() - || tcx.codegen_instance_attrs(instance.def).inline - == rustc_hir::attrs::InlineAttr::Never) + || tcx.codegen_instance_attrs(instance.def).inline == InlineAttr::Never) { // When not sharing generics, all instances are in the same // crate and have hidden visibility. diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 7a05bf718663c..a5a4f0b3ddb25 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -67,30 +67,30 @@ pub(crate) fn maybe_sign_fn_ptr<'ll, 'tcx>( } /* -* A note on nomenclature of linking: "extern", "foreign", and "upcall". -* -* An "extern" is an LLVM symbol we wind up emitting an undefined external -* reference to. This means "we don't have the thing in this compilation unit, -* please make sure you link it in at runtime". This could be a reference to -* C code found in a C library, or rust code found in a rust crate. -* -* Most "externs" are implicitly declared (automatically) as a result of a -* user declaring an extern _module_ dependency; this causes the rust driver -* to locate an extern crate, scan its compilation metadata, and emit extern -* declarations for any symbols used by the declaring crate. -* -* A "foreign" is an extern that references C (or other non-rust ABI) code. -* There is no metadata to scan for extern references so in these cases either -* a header-digester like bindgen, or manual function prototypes, have to -* serve as declarators. So these are usually given explicitly as prototype -* declarations, in rust code, with ABI attributes on them noting which ABI to -* link via. -* -* An "upcall" is a foreign call generated by the compiler (not corresponding -* to any user-written call in the code) into the runtime library, to perform -* some helper task such as bringing a task to life, allocating memory, etc. -* -*/ + * A note on nomenclature of linking: "extern", "foreign", and "upcall". + * + * An "extern" is an LLVM symbol we wind up emitting an undefined external + * reference to. This means "we don't have the thing in this compilation unit, + * please make sure you link it in at runtime". This could be a reference to + * C code found in a C library, or rust code found in a rust crate. + * + * Most "externs" are implicitly declared (automatically) as a result of a + * user declaring an extern _module_ dependency; this causes the rust driver + * to locate an extern crate, scan its compilation metadata, and emit extern + * declarations for any symbols used by the declaring crate. + * + * A "foreign" is an extern that references C (or other non-rust ABI) code. + * There is no metadata to scan for extern references so in these cases either + * a header-digester like bindgen, or manual function prototypes, have to + * serve as declarators. So these are usually given explicitly as prototype + * declarations, in rust code, with ABI attributes on them noting which ABI to + * link via. + * + * An "upcall" is a foreign call generated by the compiler (not corresponding + * to any user-written call in the code) into the runtime library, to perform + * some helper task such as bringing a task to life, allocating memory, etc. + * + */ /// A structure representing an active landing pad for the duration of a basic /// block. diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index bbb1141a92e89..ec58c3cb6ad7a 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -1,10 +1,10 @@ use std::ops::Range; use rustc_abi::{Align, ExternAbi, HasDataLayout, Primitive, Scalar, Size, WrappingRange}; +use rustc_attr_ir::Linkage; +use rustc_attr_ir::lang_items::LangItem; use rustc_codegen_ssa::common; use rustc_codegen_ssa::traits::*; -use rustc_hir::attrs::Linkage; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index ac1bfcfb45afd..1059de3c0aec0 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -195,33 +195,6 @@ pub(crate) unsafe fn create_module<'ll>( let mut target_data_layout = sess.target.data_layout.to_string(); let llvm_version = llvm_util::get_version(); - if llvm_version < (22, 0, 0) { - if sess.target.arch == Arch::Avr { - // LLVM 22.0 updated the default layout on avr: https://github.com/llvm/llvm-project/pull/153010 - target_data_layout = target_data_layout.replace("n8:16", "n8") - } - if sess.target.arch == Arch::Nvptx64 { - // LLVM 22 updated the NVPTX layout to indicate 256-bit vector load/store: https://github.com/llvm/llvm-project/pull/155198 - target_data_layout = target_data_layout.replace("-i256:256", ""); - } - if sess.target.arch == Arch::PowerPC64 { - // LLVM 22 updated the ABI alignment for double on AIX: https://github.com/llvm/llvm-project/pull/144673 - target_data_layout = target_data_layout.replace("-f64:32:64", ""); - - // LLVM 22 fixed the data layout calculation for targets that default to ELFv1 - // when the ABI is set to ELFv2. With LLVM 21, the ELFv1 datalayout must be used, - // which will overalign function entries. - // https://github.com/llvm/llvm-project/pull/149725 - if sess.target.llvm_target == "powerpc64-unknown-linux-gnu" { - target_data_layout = target_data_layout.replace("-Fn32", "-Fi64"); - } - } - if sess.target.arch == Arch::AmdGpu { - // LLVM 22 specified ELF mangling in the amdgpu data layout: - // https://github.com/llvm/llvm-project/pull/163011 - target_data_layout = target_data_layout.replace("-m:e", ""); - } - } if llvm_version < (23, 0, 0) { if sess.target.arch == Arch::S390x { // LLVM 23 updated the s390x layout to specify the stack alignment: https://github.com/llvm/llvm-project/pull/176041 @@ -559,12 +532,12 @@ pub(crate) unsafe fn create_module<'ll>( let name_metadata = cx.create_metadata(rustc_producer.as_bytes()); cx.module_add_named_metadata_node(llmod, c"llvm.ident", &[name_metadata]); - // Emit RISC-V specific target-abi metadata - // to workaround lld as the LTO plugin not - // correctly setting target-abi for the LTO object - // FIXME: https://github.com/llvm/llvm-project/issues/50591 + // Emit the target-abi module flag for LoongArch and RISC-V to pass the ABI to LTO. let llvm_abiname = &sess.target.options.llvm_abiname; - if matches!(sess.target.arch, Arch::RiscV32 | Arch::RiscV64) { + if matches!( + sess.target.arch, + Arch::LoongArch32 | Arch::LoongArch64 | Arch::RiscV32 | Arch::RiscV64 + ) { llvm::add_module_flag_str( llmod, llvm::ModuleFlagMergeBehavior::Error, @@ -1261,7 +1234,7 @@ impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { /// Helper method for the sequence of calls: /// - `LLVMMDNodeInContext2` (to create an `llvm::MDNode` from a list of metadata) - /// - `LLVMRustGlobalAddMetadata` (to set that node as metadata of `kind_id` for `global`) + /// - `LLVMGlobalAddMetadata` (to set that node as metadata of `kind_id` for `global`) pub(crate) fn global_add_metadata_node( &self, global: &'ll Value, @@ -1269,7 +1242,7 @@ impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { md_list: &[&'ll Metadata], ) { let md = self.md_node_in_context(md_list); - unsafe { llvm::LLVMRustGlobalAddMetadata(global, kind_id, md) }; + unsafe { llvm::LLVMGlobalAddMetadata(global, kind_id, md) }; } /// Helper method for the sequence of calls: diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index 3dac4b3e0167f..c77a19658c814 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -11,7 +11,8 @@ use tracing::debug; use crate::common::CodegenCx; use crate::coverageinfo::llvm_cov; use crate::coverageinfo::mapgen::covfun::prepare_covfun_record; -use crate::{TryFromU32, llvm}; +use crate::llvm; +use crate::macros::TryFromU32; mod covfun; mod spans; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs index fee852106c9b7..7665c824f1c48 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs @@ -1,9 +1,9 @@ // .debug_gdb_scripts binary section. use rustc_abi::Align; +use rustc_attr_ir::DebuggerVisualizerType; use rustc_codegen_ssa::base::collect_debugger_visualizers_transitive; use rustc_codegen_ssa::traits::*; -use rustc_hir::attrs::DebuggerVisualizerType; use rustc_hir::def_id::LOCAL_CRATE; use rustc_session::config::DebugInfo; use rustc_span::bug; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index e0fc60e1a9b7a..a3bcce34566cb 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -5,11 +5,12 @@ use std::path::PathBuf; use std::{assert_matches, iter, ptr}; use libc::{c_longlong, c_uint}; -use rustc_abi::{Align, Layout, NumScalableVectors, Size}; +use rustc_abi::{Align, Endian, Layout, NumScalableVectors, Size}; use rustc_codegen_ssa::debuginfo::type_names::{VTableNameKind, cpp_like_debuginfo}; use rustc_codegen_ssa::traits::*; use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::layout::{ HasTypingEnv, LayoutOf, TyAndLayout, WIDE_PTR_ADDR, WIDE_PTR_EXTRA, }; @@ -21,7 +22,7 @@ use rustc_span::{ DUMMY_SP, FileName, RemapPathScopeComponents, SourceFile, Span, Symbol, bug, hygiene, }; use rustc_symbol_mangling::typeid_for_trait_ref; -use rustc_target::spec::{Arch, DebuginfoKind}; +use rustc_target::spec::{Arch, DebuginfoKind, HasTargetSpec}; use smallvec::smallvec; use tracing::{debug, instrument}; @@ -693,33 +694,22 @@ impl MsvcBasicName for ty::UintTy { } } -impl MsvcBasicName for ty::FloatTy { - fn msvc_basic_name(self) -> &'static str { - // FIXME(f128): `f128` has no MSVC representation. We could improve the debuginfo. - // See: - match self { - ty::FloatTy::F16 => { - bug!("`f16` should have been handled in `build_basic_type_di_node`") - } - ty::FloatTy::F32 => "float", - ty::FloatTy::F64 => "double", - ty::FloatTy::F128 => "fp128", - } - } -} - -fn build_cpp_f16_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) -> DINodeCreationResult<'ll> { - // MSVC has no native support for `f16`. Instead, emit `struct f16 { bits: u16 }` to allow the - // `f16`'s value to be displayed using a Natvis visualiser in `intrinsic.natvis`. - let float_ty = cx.tcx.types.f16; - let bits_ty = cx.tcx.types.u16; - let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { - match float_ty.kind() { - ty::Adt(def, _) => Some(file_metadata_from_def_id(cx, Some(def.did()))), - _ => None, - } +/// `float_ty` must be a [`ty::Float`] and `bits_ty` must be a [`ty::Uint`]. +/// `cx.size_of(bits_ty) * bits_names.len()` must equal `cx.size_of(float_ty)`. +fn build_cpp_float_struct_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + float_ty: Ty<'tcx>, + bits_ty: Ty<'tcx>, + bits_names: &[&str], +) -> DINodeCreationResult<'ll> { + debug_assert!(matches!(bits_ty.kind(), ty::Uint(_))); + debug_assert_eq!(cx.size_of(bits_ty) * (bits_names.len() as u64), cx.size_of(float_ty)); + // MSVC has no native support for `f16` or `f128`. Instead, emit a struct containing the bits as + // field(s) to allow the value to be displayed using a Natvis visualiser in `intrinsic.natvis`. + let name = if let ty::Float(f) = float_ty.kind() { + f.name_str() } else { - None + bug!("{float_ty:?} was not a float"); }; type_map::build_type_with_children( cx, @@ -727,32 +717,33 @@ fn build_cpp_f16_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) -> DINodeCreation cx, Stub::Struct, UniqueTypeId::for_ty(cx.tcx, float_ty), - "f16", - def_location, + name, + None, cx.size_and_align_of(float_ty), NO_SCOPE_METADATA, DIFlags::FlagZero, ), // Fields: |cx, float_di_node| { - let def_id = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { - match bits_ty.kind() { - ty::Adt(def, _) => Some(def.did()), - _ => None, - } - } else { - None - }; - smallvec![build_field_di_node( - cx, - float_di_node, - "bits", - cx.layout_of(bits_ty), - Size::ZERO, - DIFlags::FlagZero, - type_di_node(cx, bits_ty), - def_id, - )] + let bits_layout = cx.layout_of(bits_ty); + let bits_node = type_di_node(cx, bits_ty); + bits_names + .iter() + .copied() + .enumerate() + .map(|(i, field_name)| { + build_field_di_node( + cx, + float_di_node, + field_name, + bits_layout, + bits_layout.size * (i as u64), + DIFlags::FlagZero, + bits_node, + None, + ) + }) + .collect() }, NO_GENERICS, ) @@ -784,9 +775,20 @@ fn build_basic_type_di_node<'ll, 'tcx>( ty::Int(int_ty) if cpp_like_debuginfo => (int_ty.msvc_basic_name(), DW_ATE_signed), ty::Uint(uint_ty) if cpp_like_debuginfo => (uint_ty.msvc_basic_name(), DW_ATE_unsigned), ty::Float(ty::FloatTy::F16) if cpp_like_debuginfo => { - return build_cpp_f16_di_node(cx); + return build_cpp_float_struct_di_node(cx, t, cx.tcx.types.u16, &["bits"]); + } + ty::Float(ty::FloatTy::F128) if cpp_like_debuginfo => { + // All MSVC architectures are little endian. + assert_eq!(cx.target_spec().endian, Endian::Little); + return build_cpp_float_struct_di_node( + cx, + t, + cx.tcx.types.u64, + &["low_bits", "high_bits"], + ); } - ty::Float(float_ty) if cpp_like_debuginfo => (float_ty.msvc_basic_name(), DW_ATE_float), + ty::Float(ty::FloatTy::F32) if cpp_like_debuginfo => ("float", DW_ATE_float), + ty::Float(ty::FloatTy::F64) if cpp_like_debuginfo => ("double", DW_ATE_float), ty::Int(int_ty) => (int_ty.name_str(), DW_ATE_signed), ty::Uint(uint_ty) => (uint_ty.name_str(), DW_ATE_unsigned), ty::Float(float_ty) => (float_ty.name_str(), DW_ATE_float), diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index 3511056f79306..23f9fc93a4ba4 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -43,9 +43,8 @@ pub(crate) fn declare_simple_fn<'ll>( ty: &'ll Type, ) -> &'ll Value { debug!("declare_simple_fn(name={:?}, ty={:?})", name, ty); - let llfn = unsafe { - llvm::LLVMRustGetOrInsertFunction(cx.llmod, name.as_c_char_ptr(), name.len(), ty) - }; + let llfn = + unsafe { llvm::LLVMGetOrInsertFunction(cx.llmod, name.as_c_char_ptr(), name.len(), ty) }; llvm::SetFunctionCallConv(llfn, callconv); llvm::set_unnamed_address(llfn, unnamed); diff --git a/compiler/rustc_codegen_llvm/src/diagnostics.rs b/compiler/rustc_codegen_llvm/src/diagnostics.rs index 78e502f13af75..08b96770e46d1 100644 --- a/compiler/rustc_codegen_llvm/src/diagnostics.rs +++ b/compiler/rustc_codegen_llvm/src/diagnostics.rs @@ -20,10 +20,10 @@ pub(crate) struct SanitizerMemtagRequiresMte; pub(crate) struct ParseTargetMachineConfig<'a>(pub LlvmError<'a>); -impl Diagnostic<'_, G> for ParseTargetMachineConfig<'_> { - fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { +impl Diagnostic<'_> for ParseTargetMachineConfig<'_> { + fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_> { // Reuse the formatted primary message from `LlvmError` without emitting it. - let diag: Diag<'_, ()> = self.0.into_diag(dcx, level); + let diag = self.0.into_diag(dcx, level); let (message, _) = diag.messages.first().expect("`LlvmError` with no message"); let message = format_diag_message(message, &diag.args).into_owned(); diag.cancel(); @@ -101,10 +101,6 @@ pub(crate) struct OffloadNonexistingPath; #[diag("call to BundleImages failed, `device.bin` was not created")] pub(crate) struct OffloadBundleImagesFailed; -#[derive(Diagnostic)] -#[diag("call to EmbedBufferInModule failed, `host.o` was not created")] -pub(crate) struct OffloadEmbedFailed; - #[derive(Diagnostic)] #[diag("call to WrapImages failed, device image was not wrapped into the host module")] pub(crate) struct OffloadWrapImagesFailed; @@ -141,8 +137,8 @@ pub(crate) enum LlvmError<'a> { pub(crate) struct WithLlvmError<'a>(pub LlvmError<'a>, pub String); -impl Diagnostic<'_, G> for WithLlvmError<'_> { - fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { +impl Diagnostic<'_> for WithLlvmError<'_> { + fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_> { use LlvmError::*; let msg_with_llvm_err = match &self.0 { WriteOutput { .. } => msg!("could not write output to {$path}: {$llvm_err}"), diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index db896fa9c0f2b..da0460b2a9290 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -6,6 +6,7 @@ use rustc_abi::{ AddressSpace, Align, BackendRepr, CVariadicStatus, Float, HasDataLayout, NumScalableVectors, Primitive, Size, WrappingRange, }; +use rustc_attr_ir::find_attr; use rustc_codegen_ssa::RetagInfo; use rustc_codegen_ssa::base::{compare_simd_types, wants_msvc_seh, wants_wasm_eh}; use rustc_codegen_ssa::common::{IntPredicate, TypeKind}; @@ -16,9 +17,9 @@ use rustc_codegen_ssa::mir::place::{PlaceRef, PlaceValue}; use rustc_codegen_ssa::traits::*; use rustc_hir as hir; use rustc_hir::def_id::LOCAL_CRATE; -use rustc_hir::find_attr; use rustc_lint_defs::builtin::DEPRECATED_LLVM_INTRINSIC; use rustc_middle::mir::BinOp; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv, LayoutOf}; use rustc_middle::ty::offload_meta::OffloadMetadata; use rustc_middle::ty::{self, GenericArgsRef, Instance, SimdAlign, Ty, TyCtxt, TypingEnv}; @@ -191,7 +192,6 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { let simple = call_simple_intrinsic(self, name, args); let llval = match name { _ if simple.is_some() => simple.unwrap(), - // Need at least LLVM 22 for `min/maximumnum` to not crash LLVM. sym::minimum_number_nsz_f16 | sym::minimum_number_nsz_f32 | sym::minimum_number_nsz_f64 @@ -199,9 +199,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { | sym::maximum_number_nsz_f16 | sym::maximum_number_nsz_f32 | sym::maximum_number_nsz_f64 - | sym::maximum_number_nsz_f128 - if llvm_version >= (22, 0, 0) => - { + | sym::maximum_number_nsz_f128 => { let intrinsic_name = if name.as_str().starts_with("min") { "llvm.minimumnum" } else { @@ -337,6 +335,9 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { Primitive::Float(Float::F16) => { bug!("the va_arg intrinsic does not support `f16`") } + Primitive::Float(Float::F16B) => { + bug!("the va_arg intrinsic does not support `f16b`") + } Primitive::Float(Float::F32) => { // c_double is actually f32 on avr. if self.cx().sess().target.arch != Arch::Avr { @@ -347,12 +348,11 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { // 64-bit floats are always OK. } Primitive::Float(Float::F128) => { - // FIXME(f128) figure out whether we should support this. - bug!("the va_arg intrinsic does not support `f128`") + // Supported on some targets, especially where long double is IEEE f128. } } - emit_va_arg(self, args[0], result_layout.ty) + emit_va_arg(self, args[0], result_layout) } sym::volatile_load | sym::unaligned_volatile_load => { @@ -454,26 +454,6 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { pair } - // FIXME move into the branch below when LLVM 22 is the lowest version we support. - sym::carryless_mul if llvm_version >= (22, 0, 0) => { - let ty = args[0].layout.ty; - if !ty.is_integral() { - let err = tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType { - span, - name, - ty, - }); - return IntrinsicResult::Err(err); - } - let (size, _) = ty.int_size_and_signed(self.tcx); - let width = size.bits(); - let llty = self.type_ix(width); - - let lhs = args[0].immediate(); - let rhs = args[1].immediate(); - self.call_intrinsic("llvm.clmul", &[llty], &[lhs, rhs]) - } - sym::ctlz | sym::ctlz_nonzero | sym::cttz @@ -481,6 +461,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { | sym::ctpop | sym::bswap | sym::bitreverse + | sym::carryless_mul | sym::integer_max | sym::integer_min | sym::saturating_add @@ -527,6 +508,11 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { sym::bitreverse => { self.call_intrinsic("llvm.bitreverse", &[llty], &[args[0].immediate()]) } + sym::carryless_mul => { + let lhs = args[0].immediate(); + let rhs = args[1].immediate(); + self.call_intrinsic("llvm.clmul", &[llty], &[lhs, rhs]) + } sym::integer_min | sym::integer_max => { let lhs = args[0].immediate(); let rhs = args[1].immediate(); @@ -1037,7 +1023,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { autocast(self, llret, src_ty, dest_ty) } - fn abort(&mut self) { + fn abort_immediate(&mut self) { self.call_intrinsic("llvm.trap", &[], &[]); } @@ -2115,8 +2101,6 @@ fn generic_simd_intrinsic<'ll, 'tcx>( }; } - let llvm_version = crate::llvm_util::get_version(); - /// Converts a vector mask, where each element has a bit width equal to the data elements it is used with, /// down to an i1 based mask that can be used by llvm intrinsics. /// @@ -2663,22 +2647,16 @@ fn generic_simd_intrinsic<'ll, 'tcx>( // Type of the vector of elements: let llvm_elem_vec_ty = llvm_vector_ty(bx, element_ty0, in_len); - let args: &[&'ll Value] = if llvm_version < (22, 0, 0) { - let alignment = bx.const_i32(alignment as i32); - &[args[1].immediate(), alignment, mask, args[0].immediate()] - } else { - &[args[1].immediate(), mask, args[0].immediate()] - }; - - let call = - bx.call_intrinsic("llvm.masked.gather", &[llvm_elem_vec_ty, llvm_pointer_vec_ty], args); - if llvm_version >= (22, 0, 0) { - crate::attributes::apply_to_callsite( - call, - crate::llvm::AttributePlace::Argument(0), - &[crate::llvm::CreateAlignmentAttr(bx.llcx, alignment)], - ) - } + let call = bx.call_intrinsic( + "llvm.masked.gather", + &[llvm_elem_vec_ty, llvm_pointer_vec_ty], + &[args[1].immediate(), mask, args[0].immediate()], + ); + crate::attributes::apply_to_callsite( + call, + crate::llvm::AttributePlace::Argument(0), + &[crate::llvm::CreateAlignmentAttr(bx.llcx, alignment)], + ); return Ok(call); } @@ -2769,22 +2747,16 @@ fn generic_simd_intrinsic<'ll, 'tcx>( // Type of the vector of elements: let llvm_elem_vec_ty = llvm_vector_ty(bx, values_elem, values_len); - let args: &[&'ll Value] = if llvm_version < (22, 0, 0) { - let alignment = bx.const_i32(alignment as i32); - - &[args[1].immediate(), alignment, mask, args[2].immediate()] - } else { - &[args[1].immediate(), mask, args[2].immediate()] - }; - - let call = bx.call_intrinsic("llvm.masked.load", &[llvm_elem_vec_ty, llvm_pointer], args); - if llvm_version >= (22, 0, 0) { - crate::attributes::apply_to_callsite( - call, - crate::llvm::AttributePlace::Argument(0), - &[crate::llvm::CreateAlignmentAttr(bx.llcx, alignment)], - ) - } + let call = bx.call_intrinsic( + "llvm.masked.load", + &[llvm_elem_vec_ty, llvm_pointer], + &[args[1].immediate(), mask, args[2].immediate()], + ); + crate::attributes::apply_to_callsite( + call, + crate::llvm::AttributePlace::Argument(0), + &[crate::llvm::CreateAlignmentAttr(bx.llcx, alignment)], + ); return Ok(call); } @@ -2856,21 +2828,16 @@ fn generic_simd_intrinsic<'ll, 'tcx>( // Type of the vector of elements: let llvm_elem_vec_ty = llvm_vector_ty(bx, values_elem, values_len); - let args: &[&'ll Value] = if llvm_version < (22, 0, 0) { - let alignment = bx.const_i32(alignment as i32); - &[args[2].immediate(), args[1].immediate(), alignment, mask] - } else { - &[args[2].immediate(), args[1].immediate(), mask] - }; - - let call = bx.call_intrinsic("llvm.masked.store", &[llvm_elem_vec_ty, llvm_pointer], args); - if llvm_version >= (22, 0, 0) { - crate::attributes::apply_to_callsite( - call, - crate::llvm::AttributePlace::Argument(1), - &[crate::llvm::CreateAlignmentAttr(bx.llcx, alignment)], - ) - } + let call = bx.call_intrinsic( + "llvm.masked.store", + &[llvm_elem_vec_ty, llvm_pointer], + &[args[2].immediate(), args[1].immediate(), mask], + ); + crate::attributes::apply_to_callsite( + call, + crate::llvm::AttributePlace::Argument(1), + &[crate::llvm::CreateAlignmentAttr(bx.llcx, alignment)], + ); return Ok(call); } @@ -2946,24 +2913,17 @@ fn generic_simd_intrinsic<'ll, 'tcx>( // Type of the vector of elements: let llvm_elem_vec_ty = llvm_vector_ty(bx, element_ty0, in_len); - let args: &[&'ll Value] = if llvm_version < (22, 0, 0) { - let alignment = bx.const_i32(alignment as i32); - &[args[0].immediate(), args[1].immediate(), alignment, mask] - } else { - &[args[0].immediate(), args[1].immediate(), mask] - }; + let call = bx.call_intrinsic( "llvm.masked.scatter", &[llvm_elem_vec_ty, llvm_pointer_vec_ty], - args, + &[args[0].immediate(), args[1].immediate(), mask], + ); + crate::attributes::apply_to_callsite( + call, + crate::llvm::AttributePlace::Argument(1), + &[crate::llvm::CreateAlignmentAttr(bx.llcx, alignment)], ); - if llvm_version >= (22, 0, 0) { - crate::attributes::apply_to_callsite( - call, - crate::llvm::AttributePlace::Argument(1), - &[crate::llvm::CreateAlignmentAttr(bx.llcx, alignment)], - ) - } return Ok(call); } @@ -3445,17 +3405,11 @@ fn generic_simd_intrinsic<'ll, 'tcx>( &[vec_ty], &[args[0].immediate(), args[1].immediate(), args[2].immediate()], )), - sym::simd_carryless_mul => { - if crate::llvm_util::get_version() >= (22, 0, 0) { - Ok(bx.call_intrinsic( - llvm_intrinsic, - &[vec_ty], - &[args[0].immediate(), args[1].immediate()], - )) - } else { - span_bug!(span, "`simd_carryless_mul` needs LLVM 22 or higher"); - } - } + sym::simd_carryless_mul => Ok(bx.call_intrinsic( + llvm_intrinsic, + &[vec_ty], + &[args[0].immediate(), args[1].immediate()], + )), _ => unreachable!(), }; } diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 2554a518facd0..93ef87eea026d 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -5,7 +5,6 @@ //! This API is completely unstable and subject to change. // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(trim_prefix_suffix))] #![feature(extern_types)] #![feature(file_buffered)] #![feature(impl_trait_in_assoc_type)] @@ -15,34 +14,7 @@ #![feature(try_blocks)] // tidy-alphabetical-end -use std::any::Any; -use std::ffi::CStr; -use std::mem::ManuallyDrop; -use std::path::PathBuf; - -use back::owned_target_machine::OwnedTargetMachine; -use back::write::{create_informational_target_machine, create_target_machine}; -use context::SimpleCx; -use llvm_util::target_config; -use rustc_ast::expand::allocator::AllocatorMethod; -use rustc_codegen_ssa::back::lto::ThinModule; -use rustc_codegen_ssa::back::write::{ - CodegenContext, FatLtoInput, ModuleConfig, SharedEmitter, TargetMachineFactoryConfig, - TargetMachineFactoryFn, ThinLtoInput, -}; -use rustc_codegen_ssa::traits::*; -use rustc_codegen_ssa::{CompiledModule, CompiledModules, CrateInfo, ModuleCodegen, TargetConfig}; -use rustc_data_structures::profiling::SelfProfilerRef; -use rustc_errors::{DiagCtxt, DiagCtxtHandle}; -use rustc_metadata::EncodedMetadata; -use rustc_middle::dep_graph::{WorkProduct, WorkProductMap}; -use rustc_middle::ty::TyCtxt; -use rustc_session::config::{OptLevel, OutputFilenames, PrintKind, PrintRequest}; -use rustc_session::{CodegenBackendInit, EarlySession, IncrCompSession, Session}; -use rustc_span::{Symbol, sym}; -use rustc_target::spec::{RelocModel, TlsModel}; - -use crate::llvm::ToLlvmBool; +pub use crate::back::llvm_backend::{LlvmCodegenBackend, ModuleLlvm}; mod abi; mod allocator; @@ -69,467 +41,3 @@ mod type_of; mod typetree; mod va_arg; mod value; - -pub(crate) use macros::TryFromU32; - -#[derive(Clone)] -pub struct LlvmCodegenBackend(()); - -struct TimeTraceProfiler {} - -impl TimeTraceProfiler { - fn new() -> Self { - unsafe { llvm::LLVMRustTimeTraceProfilerInitialize() } - TimeTraceProfiler {} - } -} - -impl Drop for TimeTraceProfiler { - fn drop(&mut self) { - unsafe { llvm::LLVMRustTimeTraceProfilerFinishThread() } - } -} - -impl ExtraBackendMethods for LlvmCodegenBackend { - type Module = ModuleLlvm; - - fn codegen_allocator<'tcx>( - &self, - tcx: TyCtxt<'tcx>, - module_name: &str, - methods: &[AllocatorMethod], - ) -> ModuleLlvm { - let module_llvm = ModuleLlvm::new_metadata(tcx, module_name); - let cx = - SimpleCx::new(module_llvm.llmod(), &module_llvm.llcx, tcx.data_layout.pointer_size()); - unsafe { - allocator::codegen(tcx, cx, module_name, methods); - } - module_llvm - } - fn compile_codegen_unit( - &self, - tcx: TyCtxt<'_>, - cgu_name: Symbol, - bitcode_needed: bool, - ) -> (ModuleCodegen, u64) { - base::compile_codegen_unit(tcx, cgu_name, bitcode_needed) - } -} - -impl WriteBackendMethods for LlvmCodegenBackend { - type Module = ModuleLlvm; - type ModuleBuffer = back::lto::ModuleBuffer; - type TargetMachine = OwnedTargetMachine; - type ThinData = back::lto::ThinData; - - fn thread_profiler() -> Box { - Box::new(TimeTraceProfiler::new()) - } - fn target_machine_factory( - &self, - sess: &Session, - optlvl: OptLevel, - ) -> TargetMachineFactoryFn { - back::write::target_machine_factory(sess, optlvl) - } - fn optimize_and_codegen_fat_lto( - sess: &Session, - cgcx: &CodegenContext, - shared_emitter: &SharedEmitter, - tm_factory: TargetMachineFactoryFn, - exported_symbols_for_lto: &[String], - each_linked_rlib_for_lto: &[PathBuf], - modules: Vec>, - ) -> CompiledModule { - let mut module = back::lto::run_fat( - cgcx, - &sess.prof, - shared_emitter, - tm_factory, - exported_symbols_for_lto, - each_linked_rlib_for_lto, - modules, - ); - - let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); - let dcx = dcx.handle(); - back::lto::run_pass_manager(cgcx, &sess.prof, dcx, &mut module, false); - - back::write::codegen(cgcx, &sess.prof, shared_emitter, module, &cgcx.module_config) - } - fn run_thin_lto( - cgcx: &CodegenContext, - prof: &SelfProfilerRef, - dcx: DiagCtxtHandle<'_>, - exported_symbols_for_lto: &[String], - each_linked_rlib_for_lto: &[PathBuf], - modules: Vec>, - ) -> (Vec>, Vec) { - back::lto::run_thin( - cgcx, - prof, - dcx, - exported_symbols_for_lto, - each_linked_rlib_for_lto, - modules, - ) - } - fn optimize( - cgcx: &CodegenContext, - prof: &SelfProfilerRef, - shared_emitter: &SharedEmitter, - module: &mut ModuleCodegen, - config: &ModuleConfig, - ) { - back::write::optimize(cgcx, prof, shared_emitter, module, config) - } - fn optimize_and_codegen_thin( - cgcx: &CodegenContext, - prof: &SelfProfilerRef, - shared_emitter: &SharedEmitter, - tm_factory: TargetMachineFactoryFn, - thin: ThinModule, - ) -> CompiledModule { - back::lto::optimize_and_codegen_thin_module(cgcx, prof, shared_emitter, tm_factory, thin) - } - fn codegen( - cgcx: &CodegenContext, - prof: &SelfProfilerRef, - shared_emitter: &SharedEmitter, - module: ModuleCodegen, - config: &ModuleConfig, - ) -> CompiledModule { - back::write::codegen(cgcx, prof, shared_emitter, module, config) - } - fn serialize_module(module: Self::Module, is_thin: bool) -> Self::ModuleBuffer { - back::lto::ModuleBuffer::new(module.llmod(), is_thin) - } -} - -impl LlvmCodegenBackend { - pub fn new() -> Box { - Box::new(LlvmCodegenBackend(())) - } -} - -impl CodegenBackend for LlvmCodegenBackend { - fn name(&self) -> &'static str { - "llvm" - } - - fn init(&mut self, sess: &EarlySession) -> CodegenBackendInit { - llvm_util::init(sess); // Make sure llvm is inited - - let global_backend_features = - llvm_util::global_llvm_features(sess, /* for_cfg */ false); - - // autodiff is based on Enzyme, a library which we might not have available, when it was - // neither build, nor downloaded via rustup. If autodiff is used, but not available we emit - // an early error here and abort compilation. - { - use rustc_session::config::AutoDiff; - - use crate::back::lto::enable_autodiff_settings; - if sess.opts.unstable_opts.autodiff.contains(&AutoDiff::Enable) { - match llvm::EnzymeWrapper::get_or_init(&sess.opts.sysroot) { - Ok(_) => {} - Err(llvm::EnzymeLibraryError::NotFound { err }) => { - sess.dcx().emit_fatal(crate::diagnostics::AutoDiffComponentMissing { err }); - } - Err(llvm::EnzymeLibraryError::LoadFailed { err }) => { - sess.dcx() - .emit_fatal(crate::diagnostics::AutoDiffComponentUnavailable { err }); - } - } - enable_autodiff_settings(&sess.opts.unstable_opts.autodiff); - } - } - - // Intrinsics whose fallback body will not be used by the LLVM backend. - let replaced_intrinsics = { - #[rustfmt::skip] - let mut will_not_use_fallback = vec![ - // These are mapped to LLVM intrinsics instead. - sym::unchecked_funnel_shl, - sym::unchecked_funnel_shr, - sym::carrying_mul_add, - sym::integer_max, - sym::integer_min, - - // Fallback via libm, but the LLVM intrinsic is used instead. - sym::sin, - sym::cos, - sym::powf16, sym::powf32, sym::powf64, - sym::exp, - sym::exp2, - sym::log, - sym::log10, - sym::log2, - - // Fallback via f32 or f64, but the LLVM intrinsic is used instead. - sym::floorf16, sym::ceilf16, sym::truncf16, - sym::round_ties_even_f16, sym::roundf16, - sym::sqrtf16, sym::powif16, - sym::fmaf16, - - sym::copysignf16, sym::copysignf32, sym::copysignf64, sym::copysignf128, - ]; - - if llvm_util::get_version() >= (22, 0, 0) { - will_not_use_fallback.push(sym::carryless_mul); - } - - will_not_use_fallback - }; - - // `type_id_eq` is a safe choice since *all* backends use the fallback body for that. When - // adding more intrinsics, keep in mind that the distributed standard library is compiled - // with the LLVM backend but might later be included in a project built with cranelift or - // GCC. Adding an intrinsic here can therefore mean the fallback body is used with - // cranelift/GCC even if they have dedicated implementations. - let fallback_intrinsics = vec![sym::type_id_eq]; - - CodegenBackendInit { - global_backend_features, - replaced_intrinsics, - fallback_intrinsics, - thin_lto_supported: true, - } - } - - fn print(&self, req: &PrintRequest, out: &mut String, sess: &Session) { - use std::fmt::Write; - match req.kind { - PrintKind::RelocationModels => { - writeln!(out, "Available relocation models:").unwrap(); - for name in RelocModel::ALL.iter().map(RelocModel::desc).chain(["default"]) { - writeln!(out, " {name}").unwrap(); - } - writeln!(out).unwrap(); - } - PrintKind::CodeModels => { - writeln!(out, "Available code models:").unwrap(); - for name in &["tiny", "small", "kernel", "medium", "large"] { - writeln!(out, " {name}").unwrap(); - } - writeln!(out).unwrap(); - } - PrintKind::TlsModels => { - writeln!(out, "Available TLS models:").unwrap(); - for name in TlsModel::ALL.iter().map(TlsModel::desc) { - writeln!(out, " {name}").unwrap(); - } - writeln!(out).unwrap(); - } - PrintKind::StackProtectorStrategies => { - writeln!( - out, - r#"Available stack protector strategies: - all - Generate stack canaries in all functions. - - strong - Generate stack canaries in a function if it either: - - has a local variable of `[T; N]` type, regardless of `T` and `N` - - takes the address of a local variable. - - (Note that a local variable being borrowed is not equivalent to its - address being taken: e.g. some borrows may be removed by optimization, - while by-value argument passing may be implemented with reference to a - local stack variable in the ABI.) - - basic - Generate stack canaries in functions with local variables of `[T; N]` - type, where `T` is byte-sized and `N` >= 8. - - none - Do not generate stack canaries. -"# - ) - .unwrap(); - } - _other => llvm_util::print(req, out, sess), - } - } - - fn print_passes(&self) { - llvm_util::print_passes(); - } - - fn print_version(&self) { - llvm_util::print_version(); - } - - fn has_zstd(&self) -> bool { - llvm::LLVMRustLLVMHasZstdCompression() - } - - fn has_mnemonic(&self, sess: &Session, mnemonic: &str) -> bool { - llvm_util::target_has_mnemonic(sess, mnemonic) - } - - fn target_config(&self, sess: &EarlySession) -> TargetConfig { - target_config(sess) - } - - fn target_cpu(&self, sess: &Session) -> String { - crate::llvm_util::target_cpu(sess).to_string() - } - - fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Box { - use rustc_session::config::Offload; - - if tcx.sess.opts.unstable_opts.offload.iter().any(|o| matches!(o, Offload::Device(_))) - || tcx.sess.opts.unstable_opts.offload.iter().any(|o| matches!(o, Offload::Host(_))) - { - match llvm::RustOffloadWrapper::get_or_init(&tcx.sess.opts.sysroot) { - Ok(_) => {} - Err(llvm::RustOffloadLibraryError::NotFound { err }) => { - tcx.sess - .dcx() - .emit_fatal(crate::diagnostics::RustOffloadComponentMissing { err }); - } - Err(llvm::RustOffloadLibraryError::LoadFailed { err }) => { - tcx.sess - .dcx() - .emit_fatal(crate::diagnostics::RustOffloadComponentUnavailable { err }); - } - } - } - - Box::new(rustc_codegen_ssa::base::codegen_crate(LlvmCodegenBackend(()), tcx)) - } - - fn join_codegen( - &self, - ongoing_codegen: Box, - sess: &Session, - incr_comp_session: Option<&IncrCompSession>, - outputs: &OutputFilenames, - crate_info: &CrateInfo, - ) -> (CompiledModules, WorkProductMap) { - let (compiled_modules, work_products) = ongoing_codegen - .downcast::>() - .expect("Expected LlvmCodegenBackend's OngoingCodegen, found Box") - .join(sess, incr_comp_session, crate_info); - - if sess.opts.unstable_opts.llvm_time_trace { - sess.time("llvm_dump_timing_file", || { - let file_name = outputs.with_extension("llvm_timings.json"); - llvm_util::time_trace_profiler_finish(&file_name); - }); - } - - (compiled_modules, work_products) - } - - fn print_pass_timings(&self) { - let timings = llvm::build_string(|s| unsafe { llvm::LLVMRustPrintPassTimings(s) }).unwrap(); - print!("{timings}"); - } - - fn print_statistics(&self) { - let stats = llvm::build_string(|s| unsafe { llvm::LLVMRustPrintStatistics(s) }).unwrap(); - print!("{stats}"); - } - - fn print_statistics_json(&self) -> String { - llvm::build_string(|s| unsafe { llvm::LLVMRustPrintStatisticsJSON(s) }).unwrap() - } - - fn link( - &self, - sess: &Session, - compiled_modules: CompiledModules, - crate_info: CrateInfo, - metadata: EncodedMetadata, - outputs: &OutputFilenames, - ) { - use rustc_codegen_ssa::back::link::link_binary; - - use crate::back::archive::LlvmArchiveBuilderBuilder; - - // Run the linker on any artifacts that resulted from the LLVM run. - // This should produce either a finished executable or library. - link_binary( - sess, - &LlvmArchiveBuilderBuilder, - compiled_modules, - crate_info, - metadata, - outputs, - self.name(), - ); - } -} - -pub struct ModuleLlvm { - llcx: &'static mut llvm::Context, - llmod_raw: *const llvm::Module, - - // This field is `ManuallyDrop` because it is important that the `TargetMachine` - // is disposed prior to the `Context` being disposed otherwise UAFs can occur. - tm: ManuallyDrop, -} - -unsafe impl Send for ModuleLlvm {} -unsafe impl Sync for ModuleLlvm {} - -impl ModuleLlvm { - fn new(tcx: TyCtxt<'_>, mod_name: &str) -> Self { - unsafe { - let llcx = llvm::LLVMContextCreate(); - llvm::LLVMContextSetDiscardValueNames(llcx, tcx.sess.fewer_names().to_llvm_bool()); - let llmod_raw = context::create_module(tcx, llcx, mod_name) as *const _; - ModuleLlvm { - llmod_raw, - llcx, - tm: ManuallyDrop::new(create_target_machine(tcx, mod_name)), - } - } - } - - fn new_metadata(tcx: TyCtxt<'_>, mod_name: &str) -> Self { - unsafe { - let llcx = llvm::LLVMContextCreate(); - llvm::LLVMContextSetDiscardValueNames(llcx, tcx.sess.fewer_names().to_llvm_bool()); - let llmod_raw = context::create_module(tcx, llcx, mod_name) as *const _; - ModuleLlvm { - llmod_raw, - llcx, - tm: ManuallyDrop::new(create_informational_target_machine(tcx.sess)), - } - } - } - - fn parse( - cgcx: &CodegenContext, - tm_factory: TargetMachineFactoryFn, - name: &CStr, - buffer: &[u8], - dcx: DiagCtxtHandle<'_>, - ) -> Self { - unsafe { - let llcx = llvm::LLVMContextCreate(); - llvm::LLVMContextSetDiscardValueNames(llcx, cgcx.fewer_names.to_llvm_bool()); - let llmod_raw = back::lto::parse_module(llcx, name, buffer, dcx); - let tm = tm_factory(dcx, TargetMachineFactoryConfig::new(cgcx, name.to_str().unwrap())); - - ModuleLlvm { llmod_raw, llcx, tm: ManuallyDrop::new(tm) } - } - } - - fn llmod(&self) -> &llvm::Module { - unsafe { &*self.llmod_raw } - } -} - -impl Drop for ModuleLlvm { - fn drop(&mut self) { - unsafe { - ManuallyDrop::drop(&mut self.tm); - llvm::LLVMContextDispose(&mut *(self.llcx as *mut _)); - } - } -} diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index d1cdf7bada0b1..f576f29a17cca 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -25,8 +25,9 @@ use super::debuginfo::{ DIArray, DIBuilder, DIDerivedType, DIDescriptor, DIFile, DIFlags, DILocation, DISPFlags, DIScope, DISubprogram, DITemplateTypeParameter, DIType, DebugEmissionKind, DebugNameTableKind, }; +use crate::llvm; use crate::llvm::MetadataKindId; -use crate::{TryFromU32, llvm}; +use crate::macros::TryFromU32; /// In the LLVM-C API, boolean values are passed as `typedef int LLVMBool`, /// which has a different ABI from Rust or C++ `bool`. @@ -714,8 +715,6 @@ unsafe extern "C" { } #[repr(C)] pub(crate) struct Builder<'a>(InvariantOpaque<'a>); -#[repr(C)] -pub(crate) struct PassManager<'a>(InvariantOpaque<'a>); unsafe extern "C" { pub type TargetMachine; } @@ -994,6 +993,11 @@ unsafe extern "C" { KindID: MetadataKindId, Metadata: &'a Metadata, ); + pub(crate) fn LLVMGlobalAddMetadata<'ll>( + Global: &'ll Value, + Kind: MetadataKindId, + MD: &'ll Metadata, + ); pub(crate) safe fn LLVMValueAsMetadata(Node: &Value) -> &Metadata; // Operations on constants of any type @@ -1126,6 +1130,12 @@ unsafe extern "C" { FunctionTy: &'a Type, ) -> &'a Value; pub(crate) fn LLVMDeleteFunction(Fn: &Value); + pub(crate) fn LLVMGetOrInsertFunction<'a>( + M: &'a Module, + Name: *const c_char, + NameLen: size_t, + FunctionTy: &'a Type, + ) -> &'a Value; // Operations about llvm intrinsics pub(crate) fn LLVMLookupIntrinsicID(Name: *const c_char, NameLen: size_t) -> c_uint; @@ -1636,11 +1646,6 @@ unsafe extern "C" { /// Writes a module to the specified path. Returns 0 on success. pub(crate) fn LLVMWriteBitcodeToFile(M: &Module, Path: *const c_char) -> c_int; - /// Creates a legacy pass manager -- only used for final codegen. - pub(crate) fn LLVMCreatePassManager<'a>() -> &'a mut PassManager<'a>; - - pub(crate) fn LLVMAddAnalysisPasses<'a>(T: &'a TargetMachine, PM: &PassManager<'a>); - pub(crate) fn LLVMGetHostCPUFeatures() -> *mut c_char; pub(crate) fn LLVMDisposeMessage(message: *mut c_char); @@ -1968,12 +1973,6 @@ unsafe extern "C" { pub(crate) fn LLVMRustDisableSystemDialogsOnCrash(); // Operations on all values - /// FIXME: After dropping LLVM 21, migrate to LLVM-C's `LLVMGlobalAddMetadata`. - pub(crate) fn LLVMRustGlobalAddMetadata<'a>( - Val: &'a Value, - KindID: MetadataKindId, - Metadata: &'a Metadata, - ); pub(crate) fn LLVMRustIsNonGVFunctionPointerTy(Val: &Value) -> bool; pub(crate) fn LLVMRustStripPointerCasts<'a>(Val: &'a Value) -> &'a Value; @@ -2015,6 +2014,7 @@ unsafe extern "C" { pub(crate) fn LLVMRustCreateDereferenceableAttr(C: &Context, bytes: u64) -> &Attribute; pub(crate) fn LLVMRustCreateDereferenceableOrNullAttr(C: &Context, bytes: u64) -> &Attribute; pub(crate) fn LLVMRustCreateByValAttr<'a>(C: &'a Context, ty: &'a Type) -> &'a Attribute; + pub(crate) fn LLVMRustCreateByRefAttr<'a>(C: &'a Context, ty: &'a Type) -> &'a Attribute; pub(crate) fn LLVMRustCreateStructRetAttr<'a>(C: &'a Context, ty: &'a Type) -> &'a Attribute; pub(crate) fn LLVMRustCreateElementTypeAttr<'a>(C: &'a Context, ty: &'a Type) -> &'a Attribute; pub(crate) fn LLVMRustCreateUWTableAttr(C: &Context, async_: bool) -> &Attribute; @@ -2038,13 +2038,6 @@ unsafe extern "C" { ) -> &Attribute; // Operations on functions - /// FIXME: After dropping LLVM 21, migrate to LLVM-C's `LLVMGetOrInsertFunction`. - pub(crate) fn LLVMRustGetOrInsertFunction<'a>( - M: &'a Module, - Name: *const c_char, - NameLen: size_t, - FunctionTy: &'a Type, - ) -> &'a Value; pub(crate) fn LLVMRustAddFunctionAttributes<'a>( Fn: &'a Value, index: c_uint, @@ -2420,27 +2413,22 @@ unsafe extern "C" { Features: *const c_char, ) -> *mut MCSubtargetInfo; - pub(crate) fn LLVMRustMCSubtargetInfoHasFeature( + pub(crate) fn LLVMRustMCSubtargetInfoCheckFeatures( MCInfo: &MCSubtargetInfo, - Feature: *const c_char, + Features: *const c_uchar, // See "PTR_LEN_STR". + FeaturesLen: usize, ) -> bool; pub(crate) fn LLVMRustDisposeMCSubtargetInfo(MCInfo: ptr::NonNull); - pub(crate) fn LLVMRustAddLibraryInfo<'a>( - T: &TargetMachine, - PM: &PassManager<'a>, - M: &'a Module, - DisableSimplifyLibCalls: bool, - ); pub(crate) fn LLVMRustWriteOutputFile<'a>( T: &'a TargetMachine, - PM: *mut PassManager<'a>, M: &'a Module, Output: *const c_char, DwoOutput: *const c_char, FileType: FileType, VerifyIR: bool, + DisableSimplifyLibCalls: bool, ) -> LLVMRustResult; pub(crate) fn LLVMRustOptimize<'a>( M: &'a Module, diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index 5452f4abc5c33..89e4d60656d34 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -122,6 +122,10 @@ pub(crate) fn CreateByValAttr<'ll>(llcx: &'ll Context, ty: &'ll Type) -> &'ll At unsafe { LLVMRustCreateByValAttr(llcx, ty) } } +pub(crate) fn CreateByRefAttr<'ll>(llcx: &'ll Context, ty: &'ll Type) -> &'ll Attribute { + unsafe { LLVMRustCreateByRefAttr(llcx, ty) } +} + pub(crate) fn CreateStructRetAttr<'ll>(llcx: &'ll Context, ty: &'ll Type) -> &'ll Attribute { unsafe { LLVMRustCreateStructRetAttr(llcx, ty) } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/offload_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/offload_ffi.rs index fecb9e40eab88..da3da6bd7abd4 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/offload_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/offload_ffi.rs @@ -5,7 +5,6 @@ use std::sync::OnceLock; use super::ffi::{Module, TargetMachine, Value}; type LLVMRustBundleImagesFn = unsafe extern "C" fn(&Module, &TargetMachine, *const c_char) -> bool; -type LLVMRustOffloadEmbedBufferInModuleFn = unsafe extern "C" fn(&Module, *const c_char) -> bool; type LLVMRustOffloadMapperFn = unsafe extern "C" fn(&Value, &Value, *const &Value); type LLVMRustOffloadWrapImagesFn = unsafe extern "C" fn(&Module, *const c_char, *const c_char) -> bool; @@ -18,7 +17,6 @@ use crate::llvm; pub(crate) struct RustOffloadWrapper { LLVMRustBundleImages: LLVMRustBundleImagesFn, - LLVMRustOffloadEmbedBufferInModule: LLVMRustOffloadEmbedBufferInModuleFn, LLVMRustOffloadMapper: LLVMRustOffloadMapperFn, LLVMRustOffloadWrapImages: LLVMRustOffloadWrapImagesFn, lld_path: Option, @@ -65,14 +63,6 @@ impl RustOffloadWrapper { unsafe { (self.LLVMRustBundleImages)(m, tm, c.as_ptr()) } } - pub(crate) unsafe fn llvm_rust_offload_embed_buffer_in_module( - &self, - m: &Module, - i: &CStr, - ) -> bool { - unsafe { (self.LLVMRustOffloadEmbedBufferInModule)(m, i.as_ptr()) } - } - pub(crate) unsafe fn llvm_rust_offload_wrapper(&self, v1: &Value, v2: &Value, vs: &[&Value]) { unsafe { (self.LLVMRustOffloadMapper)(v1, v2, vs.as_ptr()) } } @@ -96,11 +86,6 @@ impl RustOffloadWrapper { let llvm_rust_bundle_images = *unsafe { lib.get::(b"LLVMRustBundleImages\0")? }; - let llvm_rust_offload_embed_buffer_in_module = *unsafe { - lib.get::( - b"LLVMRustOffloadEmbedBufferInModule\0", - )? - }; let llvm_rust_offload_wrapper = *unsafe { lib.get::(b"LLVMRustOffloadMapper\0")? }; let llvm_rust_offload_wrap_images = @@ -108,7 +93,6 @@ impl RustOffloadWrapper { Ok(Self { LLVMRustBundleImages: llvm_rust_bundle_images, - LLVMRustOffloadEmbedBufferInModule: llvm_rust_offload_embed_buffer_in_module, LLVMRustOffloadMapper: llvm_rust_offload_wrapper, LLVMRustOffloadWrapImages: llvm_rust_offload_wrap_images, lld_path, diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 90f31e0598f2d..a582e897eef9c 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -6,6 +6,7 @@ use std::sync::Once; use std::{ptr, slice, str}; use libc::c_int; +use rustc_abi::Endian; use rustc_codegen_ssa::back::versioned_llvm_target; use rustc_codegen_ssa::base::wants_wasm_eh; use rustc_codegen_ssa::target_features::internal_target_features; @@ -15,7 +16,7 @@ use rustc_data_structures::small_c_str::SmallCStr; use rustc_fs_util::path_to_c_string; use rustc_session::config::{NATIVE_CPU, PrintKind, PrintRequest}; use rustc_session::{EarlySession, Session}; -use rustc_span::bug; +use rustc_span::{bug, sym}; use rustc_target::spec::{ Arch, CfgAbi, Env, MergeFunctions, Os, PanicStrategy, SmallDataThresholdSupport, Target, }; @@ -119,14 +120,14 @@ unsafe fn configure_llvm(sess: &EarlySession) { add("-generate-arange-section", false); } - match sess.opts.unstable_opts.merge_functions.unwrap_or(sess.target.merge_functions) { + match sess.merge_functions() { MergeFunctions::Disabled | MergeFunctions::Trampolines => {} MergeFunctions::Aliases => { add("-mergefunc-use-aliases", false); } } - if wants_wasm_eh(&sess.target) { + if get_version() < (24, 0, 0) && wants_wasm_eh(&sess.target) { add("-wasm-enable-eh", false); } @@ -261,7 +262,7 @@ pub(crate) fn to_llvm_features<'a>(target: &Target, s: &'a str) -> Option None, // only existed in 18 // Withdrawn by ARM; removed from LLVM in 22 - "tme" if major >= 22 => None, + "tme" => None, s => Some(LLVMFeature::new(s)), } } @@ -269,10 +270,6 @@ pub(crate) fn to_llvm_features<'a>(target: &Target, s: &'a str) -> Option Some(LLVMFeature::new("fullfp16")), s => Some(LLVMFeature::new(s)), }, - Arch::Bpf => match s { - "allows-misaligned-mem-access" if major < 22 => None, - s => Some(LLVMFeature::new(s)), - }, Arch::Nvptx64 => match s { "sm_101" if major >= 24 => Some(LLVMFeature::new("sm_110")), "sm_101a" if major >= 24 => Some(LLVMFeature::new("sm_110a")), @@ -293,43 +290,30 @@ pub(crate) fn to_llvm_features<'a>(target: &Target, s: &'a str) -> Option Some(LLVMFeature::new("hasleoncasa")), s => Some(LLVMFeature::new(s)), }, - Arch::Wasm32 | Arch::Wasm64 => match s { - "gc" if major < 22 => None, + Arch::X86 | Arch::X86_64 => match s { + "sse4.2" => Some(LLVMFeature::with_dependencies( + "sse4.2", + smallvec![TargetFeatureFoldStrength::EnableOnly("crc32")], + )), + "pclmulqdq" => Some(LLVMFeature::new("pclmul")), + "rdrand" => Some(LLVMFeature::new("rdrnd")), + "bmi1" => Some(LLVMFeature::new("bmi")), + "cmpxchg16b" => Some(LLVMFeature::new("cx16")), + "lahfsahf" => Some(LLVMFeature::new("sahf")), + "apxf" => Some(LLVMFeature::with_dependencies( + "egpr", + smallvec![ + TargetFeatureFoldStrength::Both("push2pop2"), + TargetFeatureFoldStrength::Both("ppx"), + TargetFeatureFoldStrength::Both("ndd"), + TargetFeatureFoldStrength::Both("ccmp"), + TargetFeatureFoldStrength::Both("cf"), + TargetFeatureFoldStrength::Both("nf"), + TargetFeatureFoldStrength::Both("zu"), + ], + )), s => Some(LLVMFeature::new(s)), }, - Arch::X86 | Arch::X86_64 => { - match s { - "sse4.2" => Some(LLVMFeature::with_dependencies( - "sse4.2", - smallvec![TargetFeatureFoldStrength::EnableOnly("crc32")], - )), - "pclmulqdq" => Some(LLVMFeature::new("pclmul")), - "rdrand" => Some(LLVMFeature::new("rdrnd")), - "bmi1" => Some(LLVMFeature::new("bmi")), - "cmpxchg16b" => Some(LLVMFeature::new("cx16")), - "lahfsahf" => Some(LLVMFeature::new("sahf")), - // Enable the evex512 target feature if an avx512 target feature is enabled. - s if s.starts_with("avx512") && major < 22 => Some(LLVMFeature::with_dependencies( - s, - smallvec![TargetFeatureFoldStrength::EnableOnly("evex512")], - )), - "avx10.1" if major < 22 => Some(LLVMFeature::new("avx10.1-512")), - "avx10.2" if major < 22 => Some(LLVMFeature::new("avx10.2-512")), - "apxf" => Some(LLVMFeature::with_dependencies( - "egpr", - smallvec![ - TargetFeatureFoldStrength::Both("push2pop2"), - TargetFeatureFoldStrength::Both("ppx"), - TargetFeatureFoldStrength::Both("ndd"), - TargetFeatureFoldStrength::Both("ccmp"), - TargetFeatureFoldStrength::Both("cf"), - TargetFeatureFoldStrength::Both("nf"), - TargetFeatureFoldStrength::Both("zu"), - ], - )), - s => Some(LLVMFeature::new(s)), - } - } _ => Some(LLVMFeature::new(s)), } } @@ -360,17 +344,9 @@ pub(crate) fn target_config(sess: &EarlySession) -> TargetConfig { // do *not* consider the `-Ctarget-feature`s here (that's why we passed `for_cfg: true` // to `global_llvm_features` above) because that will be handled later in // `internal_target_features`. - if let Some(feat) = to_llvm_features(&sess.target, feature) { - // All the LLVM features this expands to must be enabled. - for llvm_feature in feat { - let cstr = SmallCStr::new(llvm_feature); - // `has_feature` is moderately expensive. On targets with many - // features (e.g. x86) these calls take a non-trivial fraction of runtime - // when compiling very small programs. - if !mc_subtarget_info.has_feature(&cstr) { - return false; - } - } + if let Some(feat) = to_llvm_features(&sess.target, feature) + && mc_subtarget_info.has_features(feat) + { true } else { false @@ -382,6 +358,7 @@ pub(crate) fn target_config(sess: &EarlySession) -> TargetConfig { internal_target_features, has_reliable_f16: true, has_reliable_f16_math: true, + has_reliable_f16b: true, has_reliable_f128: true, has_reliable_f128_math: true, }; @@ -394,6 +371,7 @@ pub(crate) fn target_config(sess: &EarlySession) -> TargetConfig { fn update_target_reliable_float_cfg(target: &Target, cfg: &mut TargetConfig) { let target_arch = &target.arch; let target_os = &target.options.os; + let target_endian = &target.options.endian; let target_env = &target.options.env; let target_abi = &target.options.cfg_abi; let target_pointer_width = target.pointer_width; @@ -401,24 +379,35 @@ fn update_target_reliable_float_cfg(target: &Target, cfg: &mut TargetConfig) { let (major, _, _) = version; cfg.has_reliable_f16 = match (target_arch, target_os) { - // Unsupported (fixed in llvm22) - (Arch::Arm64EC, _) if major < 22 => false, // MinGW ABI bugs resolved in GCC 16 // but our toolchain hasn't been updated. (Arch::X86_64, Os::Windows) if *target_env == Env::Gnu && *target_abi != CfgAbi::Llvm => { false } - // Infinite recursion - (Arch::CSky, _) if major < 22 => false, // (fixed in llvm22) - (Arch::PowerPC | Arch::PowerPC64, _) if major < 22 => false, // (fixed in llvm22) - (Arch::Sparc | Arch::Sparc64, _) if major < 22 => false, // (fixed in llvm22) - (Arch::Wasm32 | Arch::Wasm64, _) if major < 22 => false, // (fixed in llvm22) // `f16` support only requires that symbols converting to and from `f32` are available. We // provide these in `compiler-builtins`, so `f16` should be available on all platforms that // do not have other ABI issues or LLVM crashes. _ => true, }; + // The heuristic for evaluating to true is twofold, namely; + // + // 1. Can LLVM compile an IR snippet containing `fpext bfloat % to float` + // 2. Does the documentation indicate `bf16` support, can be seen in the + // tracking issue; + cfg.has_reliable_f16b = match (target_arch, target_os) { + // This is similar to , however + // does not work until LLVM 23 on Windows. + (Arch::Arm64EC, _) => major >= 23, + (Arch::AArch64, _) => true, + // FIXME(f16b) until + // is resolved the below do not have a reliable `f16b`, on a widening + // path a call to `__truncsfbf2` is emitted. Or when using architectural + // extensions a non-portable narrowing instruction is emitted. + (Arch::X86_64 | Arch::RiscV64 | Arch::LoongArch64, _) => false, + _ => false, + }; + cfg.has_reliable_f128 = match (target_arch, target_os) { // Unsupported https://github.com/llvm/llvm-project/issues/121122 (Arch::AmdGpu, _) => false, @@ -426,11 +415,14 @@ fn update_target_reliable_float_cfg(target: &Target, cfg: &mut TargetConfig) { // Selection bug . This issue is closed // but basic math still does not work. (Arch::Nvptx64, _) => false, - // ABI bugs et al. (full - // list at ) - (Arch::PowerPC | Arch::PowerPC64, _) => false, - // ABI unsupported (fixed in llvm22) - (Arch::Sparc, _) if major < 22 => false, + // ABI/LLVM bugs: + // - with +vsx + // - without +vsx + (Arch::PowerPC, _) => false, + // AIX does not support f128. + (Arch::PowerPC64, Os::Aix) => false, + // ABI bugs on BE without +vsx . + (Arch::PowerPC64, _) => cfg.internal_target_features.contains(&sym::vsx), // MinGW ABI bugs (fixed in llvm23) (Arch::X86_64, Os::Windows) if *target_env == Env::Gnu && *target_abi != CfgAbi::Llvm && major < 23 => @@ -456,9 +448,13 @@ fn update_target_reliable_float_cfg(target: &Target, cfg: &mut TargetConfig) { // (ld is `f64`), anything other than Linux (Windows and MacOS use `f64`), and `x86` // (ld is 80-bit extended precision). // + // On big-endian powerpc the symbol selection is correct, despite __ibmf128 being + // long double on the target, but the f128 symbols are not defined. + // // musl does not implement the symbols required for f128 math at all. _ if *target_env == Env::Musl => false, (Arch::X86_64, _) => false, + (Arch::PowerPC | Arch::PowerPC64, _) if *target_endian == Endian::Big => false, (_, Os::Linux) if target_pointer_width == 64 => true, _ => false, } && cfg.has_reliable_f128; diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index a25624550e3ad..6d9a35d5fea89 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -2,8 +2,8 @@ use std::borrow::Cow; use std::ffi::CString; use rustc_abi::AddressSpace; +use rustc_attr_ir::Linkage; use rustc_codegen_ssa::traits::*; -use rustc_hir::attrs::Linkage; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index 881b8e3a8bd0e..ff1b5851db0f9 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -214,6 +214,10 @@ impl<'ll, CX: Borrow>> BaseTypeCodegenMethods for GenericCx<'ll, CX> { unsafe { llvm::LLVMHalfTypeInContext(self.llcx()) } } + fn type_f16b(&self) -> &'ll Type { + unsafe { llvm::LLVMBFloatTypeInContext(self.llcx()) } + } + fn type_f32(&self) -> &'ll Type { unsafe { llvm::LLVMFloatTypeInContext(self.llcx()) } } diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index ca57bee3e6271..8408e2eefb13e 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -95,7 +95,7 @@ fn get_param_type_alignment<'ll, 'tcx>( Integer::I128 => return Align::EIGHT, }, Primitive::Float(float) => match float { - Float::F16 | Float::F32 => unreachable!(), + Float::F16 | Float::F16B | Float::F32 => unreachable!(), Float::F64 => { /* fall through */ } Float::F128 => return Align::from_bytes(16).unwrap(), }, @@ -150,7 +150,7 @@ enum ForceRightAdjust { fn emit_ptr_va_arg<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, list: OperandRef<'tcx, &'ll Value>, - target_ty: Ty<'tcx>, + layout: TyAndLayout<'tcx>, pass_mode: PassMode, slot_size: SlotSize, allow_higher_align: AllowHigherAlign, @@ -161,10 +161,9 @@ fn emit_ptr_va_arg<'ll, 'tcx>( let force_right_adjust = matches!(force_right_adjust, ForceRightAdjust::Yes); let slot_size = Align::from_bytes(slot_size as u64).unwrap(); - let layout = bx.cx.layout_of(target_ty); let (llty, size, align) = if indirect { ( - bx.cx.layout_of(Ty::new_imm_ptr(bx.cx.tcx, target_ty)).llvm_type(bx.cx), + bx.cx.layout_of(Ty::new_imm_ptr(bx.cx.tcx, layout.ty)).llvm_type(bx.cx), bx.cx.data_layout().pointer_size(), bx.cx.data_layout().pointer_align().abi, ) @@ -191,7 +190,7 @@ fn emit_ptr_va_arg<'ll, 'tcx>( fn emit_aapcs_va_arg<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, list: OperandRef<'tcx, &'ll Value>, - target_ty: Ty<'tcx>, + layout: TyAndLayout<'tcx>, ) -> &'ll Value { let dl = bx.cx.data_layout(); @@ -218,8 +217,6 @@ fn emit_aapcs_va_arg<'ll, 'tcx>( let gr_offs = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(3 * ptr_offset)); let vr_offs = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(3 * ptr_offset + i32_offset)); - let layout = bx.cx.layout_of(target_ty); - let maybe_reg = bx.append_sibling_block("va_arg.maybe_reg"); let in_reg = bx.append_sibling_block("va_arg.in_reg"); let on_stack = bx.append_sibling_block("va_arg.on_stack"); @@ -227,7 +224,7 @@ fn emit_aapcs_va_arg<'ll, 'tcx>( let zero = bx.const_i32(0); let offset_align = Align::from_bytes(4).unwrap(); - let gr_type = target_ty.is_any_ptr() || target_ty.is_integral(); + let gr_type = layout.ty.is_any_ptr() || layout.ty.is_integral(); let (reg_off, reg_top, slot_size) = if gr_type { let nreg = layout.size.bytes().div_ceil(8); (gr_offs, gr_top, nreg * 8) @@ -279,7 +276,7 @@ fn emit_aapcs_va_arg<'ll, 'tcx>( let stack_value = emit_ptr_va_arg( bx, list, - target_ty, + layout, PassMode::Direct, SlotSize::Bytes8, AllowHigherAlign::Yes, @@ -297,7 +294,7 @@ fn emit_aapcs_va_arg<'ll, 'tcx>( fn emit_powerpc_va_arg<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, list: OperandRef<'tcx, &'ll Value>, - target_ty: Ty<'tcx>, + layout: TyAndLayout<'tcx>, ) -> &'ll Value { let dl = bx.cx.data_layout(); @@ -311,15 +308,7 @@ fn emit_powerpc_va_arg<'ll, 'tcx>( let va_list_addr = list.immediate(); // Peel off any newtype wrappers. - let layout = { - let mut layout = bx.cx.layout_of(target_ty); - - while let Some((_, inner)) = layout.non_1zst_field(bx.cx) { - layout = inner; - } - - layout - }; + let layout = layout.peel_transparent_wrappers(bx.cx); // Rust does not currently support any powerpc softfloat targets. let target = &bx.cx.tcx.sess.target; @@ -435,7 +424,7 @@ fn emit_powerpc_va_arg<'ll, 'tcx>( fn emit_s390x_va_arg<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, list: OperandRef<'tcx, &'ll Value>, - target_ty: Ty<'tcx>, + layout: TyAndLayout<'tcx>, ) -> &'ll Value { let dl = bx.cx.data_layout(); @@ -460,15 +449,13 @@ fn emit_s390x_va_arg<'ll, 'tcx>( let reg_save_area = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(2 * i64_offset + ptr_offset)); - let layout = bx.cx.layout_of(target_ty); - let in_reg = bx.append_sibling_block("va_arg.in_reg"); let in_mem = bx.append_sibling_block("va_arg.in_mem"); let end = bx.append_sibling_block("va_arg.end"); let ptr_align_abi = dl.pointer_align().abi; // FIXME: vector ABI not yet supported. - let target_ty_size = bx.cx.size_of(target_ty).bytes(); + let target_ty_size = layout.size.bytes(); let indirect: bool = target_ty_size > 8 || !target_ty_size.is_power_of_two(); let unpadded_size = if indirect { 8 } else { target_ty_size }; let padded_size = 8; @@ -481,7 +468,11 @@ fn emit_s390x_va_arg<'ll, 'tcx>( Primitive::Float(Float::F16 | Float::F32 | Float::F64) => true, Primitive::Float(Float::F128) => false, Primitive::Int(_, _) | Primitive::Pointer(_) => false, + Primitive::Float(Float::F16B) => { + bug!("`f16b` use in varadics unsupported on s390x") + } }, + _ => false, } }; @@ -535,7 +526,7 @@ fn emit_s390x_va_arg<'ll, 'tcx>( fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, list: OperandRef<'tcx, &'ll Value>, - target_ty: Ty<'tcx>, + layout: TyAndLayout<'tcx>, ) -> &'ll Value { let dl = bx.cx.data_layout(); @@ -570,15 +561,7 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>( // #[repr(C)] // struct Foo([Empty; 8], i32); // ``` - let layout = { - let mut layout = bx.cx.layout_of(target_ty); - - while let Some((_, inner)) = layout.non_1zst_field(bx.cx) { - layout = inner; - } - - layout - }; + let layout = layout.peel_transparent_wrappers(bx.cx); // AMD64-ABI 3.5.7p5: Step 1. Determine whether type may be passed // in the registers. If not go to step 7. @@ -847,7 +830,7 @@ fn x86_64_sysv64_va_arg_from_memory<'ll, 'tcx>( fn emit_hexagon_va_arg_musl<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, list: OperandRef<'tcx, &'ll Value>, - target_ty: Ty<'tcx>, + layout: TyAndLayout<'tcx>, ) -> &'ll Value { // Implementation of va_arg for Hexagon musl target. // Based on LLVM's HexagonBuiltinVaList implementation. @@ -861,7 +844,6 @@ fn emit_hexagon_va_arg_musl<'ll, 'tcx>( // All variadic arguments are passed on the stack, but the musl implementation // uses a register save area for compatibility. let va_list_addr = list.immediate(); - let layout = bx.cx.layout_of(target_ty); let ptr_align_abi = bx.tcx().data_layout.pointer_align().abi; let ptr_size = bx.tcx().data_layout.pointer_size().bytes(); @@ -881,7 +863,7 @@ fn emit_hexagon_va_arg_musl<'ll, 'tcx>( // Align current pointer based on argument type size (following LLVM's implementation) // Arguments <= 32 bits (4 bytes) use 4-byte alignment, > 32 bits use 8-byte alignment - let type_size_bits = bx.cx.size_of(target_ty).bits(); + let type_size_bits = layout.size.bits(); let arg_align = if type_size_bits > 32 { Align::from_bytes(8).unwrap() } else { @@ -931,14 +913,13 @@ fn emit_hexagon_va_arg_musl<'ll, 'tcx>( fn emit_hexagon_va_arg_bare_metal<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, list: OperandRef<'tcx, &'ll Value>, - target_ty: Ty<'tcx>, + layout: TyAndLayout<'tcx>, ) -> &'ll Value { // Implementation of va_arg for Hexagon bare-metal (non-musl) targets. // Based on LLVM's EmitVAArgForHexagon implementation. // // va_list is a simple pointer (char *) let va_list_addr = list.immediate(); - let layout = bx.cx.layout_of(target_ty); let ptr_align_abi = bx.tcx().data_layout.pointer_align().abi; // Load current pointer from va_list @@ -969,7 +950,7 @@ fn emit_hexagon_va_arg_bare_metal<'ll, 'tcx>( fn emit_xtensa_va_arg<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, list: OperandRef<'tcx, &'ll Value>, - target_ty: Ty<'tcx>, + layout: TyAndLayout<'tcx>, ) -> &'ll Value { // Implementation of va_arg for Xtensa. There doesn't seem to be an authoritative source for // this, other than "what GCC does". @@ -989,7 +970,6 @@ fn emit_xtensa_va_arg<'ll, 'tcx>( // primitive value and va_ndx = 20, we instead bump the offset and read everything from va_stk. let va_list_addr = list.immediate(); // FIXME: handle multi-field structs that split across regsave/stack? - let layout = bx.cx.layout_of(target_ty); let from_stack = bx.append_sibling_block("va_arg.from_stack"); let from_regsave = bx.append_sibling_block("va_arg.from_regsave"); let end = bx.append_sibling_block("va_arg.end"); @@ -1071,35 +1051,45 @@ fn emit_xtensa_va_arg<'ll, 'tcx>( pub(super) fn emit_va_arg<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, addr: OperandRef<'tcx, &'ll Value>, - target_ty: Ty<'tcx>, + layout: TyAndLayout<'tcx>, ) -> &'ll Value { - let layout = bx.cx.layout_of(target_ty); - let target_ty_size = layout.layout.size().bytes(); - // Some ABIs have special behavior for zero-sized types. currently `VaArgSafe` is not // implemented for any zero-sized types, so this assert should always hold. - assert!(!bx.layout_of(target_ty).is_zst()); + assert!(!layout.is_zst()); let target = &bx.cx.tcx.sess.target; let stability = target.supports_c_variadic_definitions(); match target.arch { - Arch::X86 => emit_ptr_va_arg( - bx, - addr, - target_ty, - PassMode::Direct, - SlotSize::Bytes4, - if target.is_like_windows { AllowHigherAlign::No } else { AllowHigherAlign::Yes }, - ForceRightAdjust::No, - ), + Arch::X86 => { + // A small deviation from clang to get the right behavior for f128. + // + // Note that i64 and f64 have an alignment of only 4 on this architecture. + // We need to be careful when adding future types with an alignment bigger + // than 4 (e.g. i128), clang has a bunch of custom logic for them. + let allow_higher_align = if layout.ty == bx.tcx().types.f128 { + AllowHigherAlign::Yes + } else { + AllowHigherAlign::No + }; + + emit_ptr_va_arg( + bx, + addr, + layout, + PassMode::Direct, + SlotSize::Bytes4, + allow_higher_align, + ForceRightAdjust::No, + ) + } Arch::Arm64EC => emit_ptr_va_arg( bx, addr, - target_ty, + layout, // MS x64 ABI requirement: "Any argument that doesn't fit in 8 bytes, or is // not 1, 2, 4, or 8 bytes, must be passed by reference." - if target_ty_size > 8 || !target_ty_size.is_power_of_two() { + if layout.size.bytes() > 8 || !layout.size.bytes().is_power_of_two() { PassMode::Indirect } else { PassMode::Direct @@ -1111,35 +1101,35 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( Arch::AArch64 if target.is_like_windows || target.is_like_darwin => emit_ptr_va_arg( bx, addr, - target_ty, + layout, PassMode::Direct, SlotSize::Bytes8, AllowHigherAlign::Yes, ForceRightAdjust::No, ), - Arch::AArch64 => emit_aapcs_va_arg(bx, addr, target_ty), + Arch::AArch64 => emit_aapcs_va_arg(bx, addr, layout), Arch::Arm => { // Types wider than 16 bytes are not currently supported. Clang has special logic for // such types, but `VaArgSafe` is not implemented for any type that is this large on // arm (i.e. 32-bit) targets. - assert!(bx.cx.size_of(target_ty).bytes() <= 16); + assert!(layout.size.bytes() <= 16); emit_ptr_va_arg( bx, addr, - target_ty, + layout, PassMode::Direct, SlotSize::Bytes4, AllowHigherAlign::Yes, ForceRightAdjust::No, ) } - Arch::S390x => emit_s390x_va_arg(bx, addr, target_ty), - Arch::PowerPC => emit_powerpc_va_arg(bx, addr, target_ty), + Arch::S390x => emit_s390x_va_arg(bx, addr, layout), + Arch::PowerPC => emit_powerpc_va_arg(bx, addr, layout), Arch::PowerPC64 => emit_ptr_va_arg( bx, addr, - target_ty, + layout, PassMode::Direct, SlotSize::Bytes8, AllowHigherAlign::Yes, @@ -1159,8 +1149,8 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( Arch::RiscV32 | Arch::LoongArch32 => emit_ptr_va_arg( bx, addr, - target_ty, - if target_ty_size > 2 * 4 { PassMode::Indirect } else { PassMode::Direct }, + layout, + if layout.size.bytes() > 2 * 4 { PassMode::Indirect } else { PassMode::Direct }, SlotSize::Bytes4, AllowHigherAlign::Yes, ForceRightAdjust::No, @@ -1168,8 +1158,8 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( Arch::RiscV64 | Arch::LoongArch64 => emit_ptr_va_arg( bx, addr, - target_ty, - if target_ty_size > 2 * 8 { PassMode::Indirect } else { PassMode::Direct }, + layout, + if layout.size.bytes() > 2 * 8 { PassMode::Indirect } else { PassMode::Direct }, SlotSize::Bytes8, AllowHigherAlign::Yes, ForceRightAdjust::No, @@ -1177,7 +1167,7 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( Arch::AmdGpu => emit_ptr_va_arg( bx, addr, - target_ty, + layout, PassMode::Direct, SlotSize::Bytes4, AllowHigherAlign::No, @@ -1186,7 +1176,7 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( Arch::Nvptx64 => emit_ptr_va_arg( bx, addr, - target_ty, + layout, PassMode::Direct, SlotSize::Bytes1, AllowHigherAlign::Yes, @@ -1195,7 +1185,7 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( Arch::Wasm32 | Arch::Wasm64 => emit_ptr_va_arg( bx, addr, - target_ty, + layout, if layout.is_aggregate() || layout.is_zst() || layout.is_1zst() { PassMode::Indirect } else { @@ -1208,7 +1198,7 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( Arch::CSky => emit_ptr_va_arg( bx, addr, - target_ty, + layout, PassMode::Direct, SlotSize::Bytes4, AllowHigherAlign::Yes, @@ -1218,8 +1208,8 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( Arch::X86_64 if target.is_like_windows => emit_ptr_va_arg( bx, addr, - target_ty, - if target_ty_size > 8 || !target_ty_size.is_power_of_two() { + layout, + if layout.size.bytes() > 8 || !layout.size.bytes().is_power_of_two() { PassMode::Indirect } else { PassMode::Direct @@ -1229,17 +1219,17 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( ForceRightAdjust::No, ), // This includes `target.is_like_darwin`, which on x86_64 targets is like sysv64. - Arch::X86_64 => emit_x86_64_sysv64_va_arg(bx, addr, target_ty), - Arch::Xtensa => emit_xtensa_va_arg(bx, addr, target_ty), + Arch::X86_64 => emit_x86_64_sysv64_va_arg(bx, addr, layout), + Arch::Xtensa => emit_xtensa_va_arg(bx, addr, layout), Arch::Hexagon => match target.env { - Env::Musl => emit_hexagon_va_arg_musl(bx, addr, target_ty), - _ => emit_hexagon_va_arg_bare_metal(bx, addr, target_ty), + Env::Musl => emit_hexagon_va_arg_musl(bx, addr, layout), + _ => emit_hexagon_va_arg_bare_metal(bx, addr, layout), }, Arch::Sparc64 => emit_ptr_va_arg( bx, addr, - target_ty, - if target_ty_size > 2 * 8 { PassMode::Indirect } else { PassMode::Direct }, + layout, + if layout.size.bytes() > 2 * 8 { PassMode::Indirect } else { PassMode::Direct }, SlotSize::Bytes8, AllowHigherAlign::Yes, // sparc64 is a big-endian target and stores variable arguments right-adjusted. @@ -1260,7 +1250,7 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( emit_ptr_va_arg( bx, addr, - target_ty, + layout, pass_mode, SlotSize::Bytes4, AllowHigherAlign::No, @@ -1270,7 +1260,7 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( Arch::Mips | Arch::Mips32r6 | Arch::Mips64 | Arch::Mips64r6 => emit_ptr_va_arg( bx, addr, - target_ty, + layout, PassMode::Direct, match &target.llvm_abiname { LlvmAbi::N32 | LlvmAbi::N64 => SlotSize::Bytes8, @@ -1294,7 +1284,7 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( std::assert_matches!(stability, CVariadicStatus::Unstable { .. }); // Clang uses the LLVM implementation for these architectures. - bx.va_arg(addr.immediate(), bx.cx.layout_of(target_ty).llvm_type(bx.cx)) + bx.va_arg(addr.immediate(), layout.llvm_type(bx.cx)) } Arch::Other(ref arch) => { diff --git a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs index a25131c5ff9b4..431783552bcd0 100644 --- a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs +++ b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs @@ -26,11 +26,10 @@ use std::borrow::Cow; use std::fmt; +use rustc_attr_ir::{CguFields, CguKind, find_attr}; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::{DiagArgValue, IntoDiagArg}; -use rustc_hir::attrs::{CguFields, CguKind}; use rustc_hir::def_id::LOCAL_CRATE; -use rustc_hir::{self as hir, find_attr}; use rustc_middle::mono::CodegenUnitNameBuilder; use rustc_middle::ty::TyCtxt; use rustc_session::Session; @@ -87,7 +86,7 @@ struct AssertModuleSource<'tcx> { } impl<'tcx> AssertModuleSource<'tcx> { - fn check_attrs(&mut self, attrs: &[hir::Attribute]) { + fn check_attrs(&mut self, attrs: &[rustc_attr_ir::Attribute]) { for &(span, cgu_fields) in find_attr!(attrs, RustcCguTestAttr(e) => e) .into_flat_iter() diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index c283e464821e5..45ab9b8bc3b30 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -834,24 +834,33 @@ fn link_staticlib( let hide = sess.opts.unstable_opts.staticlib_hide_internal_symbols; let rename = sess.opts.unstable_opts.staticlib_rename_internal_symbols; + let hide_supported = + matches!(sess.target.binary_format, BinaryFormat::Elf | BinaryFormat::MachO); + // Rename only rewrites symbol names, so it also works on COFF; hide + // needs a visibility concept COFF lacks. + let rename_supported = matches!( + sess.target.binary_format, + BinaryFormat::Elf | BinaryFormat::MachO | BinaryFormat::Coff + ); + let exported_symbols = if hide || rename { - if !matches!(sess.target.binary_format, BinaryFormat::Elf | BinaryFormat::MachO) { - if hide { - sess.dcx().emit_warn(diagnostics::StaticlibHideInternalSymbolsUnsupported { - binary_format: sess.target.archive_format.to_string(), - }); - } - if rename { - sess.dcx().emit_warn(diagnostics::StaticlibRenameInternalSymbolsUnsupported { - binary_format: sess.target.archive_format.to_string(), - }); - } - None - } else { + if hide && !hide_supported { + sess.dcx().emit_warn(diagnostics::StaticlibHideInternalSymbolsUnsupported { + binary_format: sess.target.archive_format.to_string(), + }); + } + if rename && !rename_supported { + sess.dcx().emit_warn(diagnostics::StaticlibRenameInternalSymbolsUnsupported { + binary_format: sess.target.archive_format.to_string(), + }); + } + if (hide && hide_supported) || (rename && rename_supported) { crate_info .exported_symbols .get(&CrateType::StaticLib) .map(|symbols| symbols.iter().map(|symbol| symbol.name.clone()).collect()) + } else { + None } } else { None @@ -859,8 +868,11 @@ fn link_staticlib( let symbols = exported_symbols.map(|exported| ArchiveSymbols { exported, - rename_suffix: rename.then(|| crate_info.symbol_rename_suffix.clone()), - hide, + rename_suffix: (rename && rename_supported) + .then(|| crate_info.symbol_rename_suffix.clone()), + // A warning was already emitted above if hiding was requested for an + // unsupported format; don't also ask the backend to hide there. + hide: hide && hide_supported, }); ab.build(out_filename, symbols); @@ -1764,7 +1776,11 @@ fn add_sanitizer_libraries( && (sess.opts.unstable_opts.sanitizer_cfi_diag.unwrap_or(false) || sess.opts.unstable_opts.sanitizer_cfi_recover.unwrap_or(false)) { - link_sanitizer_runtime(sess, flavor, linker, "ubsan"); + if sess.is_sanitizer_cfi_minimal_runtime_enabled() { + link_sanitizer_runtime(sess, flavor, linker, "ubsan_minimal"); + } else { + link_sanitizer_runtime(sess, flavor, linker, "ubsan"); + } } } diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 2b9ed32f22773..ffb9130ed1a08 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -5,7 +5,7 @@ use std::path::{Path, PathBuf}; use std::{env, iter, mem, str}; use find_msvc_tools; -use rustc_hir::attrs::WindowsSubsystemKind; +use rustc_attr_ir::WindowsSubsystemKind; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::{ @@ -900,7 +900,14 @@ impl<'a> Linker for GccLinker<'a> { fn linker_plugin_lto(&mut self) { match self.sess.opts.cg.linker_plugin_lto { LinkerPluginLto::Disabled => { - // Nothing to do + // GCC, when used as a linker, will perform LTO even without -flto when it sees + // object files with the GCC IR (which can happen when using rustc_codegen_gcc). + // Since the std is compiled with -Cembed-bitcode=yes, any program using the std + // will have the linker do LTO. + // So, we disable this with -fno-lto when linker plugin LTO is not requested. + if self.is_cc() && self.codegen_backend == "gcc" { + self.cc_arg("-fno-lto"); + } } LinkerPluginLto::LinkerPluginAuto => { self.push_linker_plugin_lto_args(None); diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_edit.rs b/compiler/rustc_codegen_ssa/src/back/symbol_edit.rs index ef8008216187a..fa6fc1368e133 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_edit.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_edit.rs @@ -7,9 +7,10 @@ use std::borrow::Cow; use std::mem; +use object::read::coff::{CoffHeader, ImageSymbol as _}; use object::read::elf::{SectionHeader as _, Sym as _}; use object::read::macho::Nlist; -use object::{Endianness, elf, macho}; +use object::{Endianness, elf, macho, pe}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; struct Patch { @@ -57,6 +58,20 @@ pub(super) fn apply_edits<'a>( rename, mem::offset_of!(macho::Nlist32, n_type), ), + Some(object::File::Coff(f)) => coff_edit_impl( + data, + f.coff_header(), + hide, + rename, + coff_strip_underscore(f.coff_header()), + ), + Some(object::File::CoffBig(f)) => coff_edit_impl( + data, + f.coff_header(), + hide, + rename, + coff_strip_underscore(f.coff_header()), + ), _ => None, }; match result { @@ -84,10 +99,30 @@ pub(super) fn collect_internal_names( object::File::MachO32(_) => { macho_collect_impl::>(data, exported, out) } + object::File::Coff(f) => coff_collect_impl( + data, + f.coff_header(), + exported, + out, + coff_strip_underscore(f.coff_header()), + ), + object::File::CoffBig(f) => coff_collect_impl( + data, + f.coff_header(), + exported, + out, + coff_strip_underscore(f.coff_header()), + ), _ => {} } } +/// Whether this machine's COFF ABI decorates external symbols with a leading `_` +/// (i686 only). +fn coff_strip_underscore(header: &impl CoffHeader) -> bool { + header.machine() == pe::IMAGE_FILE_MACHINE_I386 +} + fn elf_collect_impl>( data: &[u8], exported: &FxHashSet, @@ -441,6 +476,128 @@ fn macho_rebuild_strtab( Some(result) } +// --------------------------------------------------------------------------- +// COFF: single-pass collection + apply +// --------------------------------------------------------------------------- + +fn coff_collect_impl<'data, Coff: CoffHeader>( + data: &'data [u8], + header: &'data Coff, + exported: &FxHashSet, + out: &mut FxHashSet, + strip_underscore: bool, +) { + let Ok(symbols) = header.symbols(data) else { return }; + let strings = symbols.strings(); + + for (_index, sym) in symbols.iter() { + let sclass = sym.storage_class(); + if sclass != pe::IMAGE_SYM_CLASS_EXTERNAL && sclass != pe::IMAGE_SYM_CLASS_WEAK_EXTERNAL { + continue; + } + if sym.section_number() <= 0 { + continue; + } + let Ok(name_bytes) = sym.name(strings) else { continue }; + let Ok(mut name) = str::from_utf8(name_bytes).map(String::from) else { continue }; + if strip_underscore { + name = name.strip_prefix('_').unwrap_or(&name).to_string(); + } + let is_exported = exported.contains(&name) + || (header.machine() == pe::IMAGE_FILE_MACHINE_ARM64EC + && name.strip_prefix('#').is_some_and(|name| exported.contains(name))); + if !is_exported { + out.insert(name); + } + } +} + +fn coff_edit_impl<'data, Coff: CoffHeader>( + data: &'data [u8], + header: &'data Coff, + hide: bool, + rename: Option<&(FxHashSet, &str)>, + strip_underscore: bool, +) -> Option> { + // COFF has no visibility concept, so hiding is unsupported; the caller + // has already warned and does not request it. + assert!(!hide); + + let pointer_to_symbol_table = header.pointer_to_symbol_table() as usize; + let number_of_symbols = header.number_of_symbols() as usize; + // ImageSymbol is 18 bytes; ImageSymbolEx (bigobj) is 20. + let sym_size = mem::size_of::(); + + let symbol_bytes_offset = pointer_to_symbol_table; + let strtab_base = pointer_to_symbol_table + number_of_symbols * sym_size; + if strtab_base > data.len() { + return None; + } + + if data.len() < strtab_base + 4 { + return None; + } + let old_len = + u32::from_le_bytes(data[strtab_base..strtab_base + 4].try_into().unwrap()) as usize; + if strtab_base + old_len > data.len() { + return None; + } + + let Ok(symbols) = header.symbols(data) else { return None }; + let strings = symbols.strings(); + + let mut renames = Vec::new(); + for (index, sym) in symbols.iter() { + let sclass = sym.storage_class(); + if sclass != pe::IMAGE_SYM_CLASS_EXTERNAL && sclass != pe::IMAGE_SYM_CLASS_WEAK_EXTERNAL { + continue; + } + let Ok(name_bytes) = sym.name(strings) else { continue }; + let Ok(name) = str::from_utf8(name_bytes) else { continue }; + let check_name = + if strip_underscore { name.strip_prefix('_').unwrap_or(name) } else { name }; + if rename.is_some_and(|(rename_set, _)| rename_set.contains(check_name)) { + renames.push(RenameEntry { + name_field_offset: symbol_bytes_offset + index.0 * sym_size, + name: name.to_string(), + }); + } + } + if renames.is_empty() { + return None; + } + let suffix = rename.unwrap().1; + + let mut new_strtab = Vec::new(); + let mut map: FxHashMap = FxHashMap::default(); + let mut sorted_names: Vec<&str> = renames.iter().map(|r| r.name.as_str()).collect(); + sorted_names.sort(); + sorted_names.dedup(); + for name in &sorted_names { + let rel_offset = (old_len + new_strtab.len()) as u32; + new_strtab.extend_from_slice(name.as_bytes()); + new_strtab.extend_from_slice(suffix.as_bytes()); + new_strtab.push(0); + map.insert(name.to_string(), rel_offset); + } + + let mut result = data.to_vec(); + let new_len = (old_len + new_strtab.len()) as u32; + result[strtab_base..strtab_base + 4].copy_from_slice(&new_len.to_le_bytes()); + result.extend_from_slice(&new_strtab); + + // Long names are stored as all-zero name[0..4] plus the string table + // offset in name[4..8]. + for r in &renames { + let rel = map[&r.name]; + let field = &mut result[r.name_field_offset..r.name_field_offset + 8]; + field[..4].fill(0); + field[4..].copy_from_slice(&rel.to_le_bytes()); + } + + Some(result) +} + // --------------------------------------------------------------------------- // Shared helpers // --------------------------------------------------------------------------- diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index c0e525471aeeb..b11dee2ca3827 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -315,7 +315,7 @@ fn exported_generic_symbols_provider_local<'tcx>( .any(|o| matches!(o, rustc_session::config::Offload::Device(_))); if export_generics || is_device_offload { - use rustc_hir::attrs::Linkage; + use rustc_attr_ir::{InlineAttr, Linkage}; use rustc_middle::mono::{MonoItem, Visibility}; use rustc_middle::ty::InstanceKind; @@ -354,9 +354,8 @@ fn exported_generic_symbols_provider_local<'tcx>( .types() .chain(did.into_iter().map(move |did| tcx.type_of(did).skip_binder())) .all(move |arg| { - arg.walk().all(|ty| { - ty.as_type().map_or(true, |ty| !is_local_to_current_crate(ty)) - }) + arg.walk() + .all(|ty| ty.as_type().is_none_or(|ty| !is_local_to_current_crate(ty))) }) }; @@ -386,9 +385,7 @@ fn exported_generic_symbols_provider_local<'tcx>( let item_is_offload = is_offload_instance(mono_item); if !item_is_offload && !tcx.sess.opts.share_generics() { - if tcx.codegen_fn_attrs(mono_item.def_id()).inline - == rustc_hir::attrs::InlineAttr::Never - { + if tcx.codegen_fn_attrs(mono_item.def_id()).inline == InlineAttr::Never { // this is OK, we explicitly allow sharing inline(never) across crates even // without share-generics. } else { diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index c93aa87b25077..04be7956e8278 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -4,18 +4,20 @@ use std::panic::AssertUnwindSafe; use std::path::{Path, PathBuf}; use std::sync::Arc; use std::sync::mpsc::{Receiver, Sender, channel}; -use std::{assert_matches, fs, io, mem, str, thread}; +use std::{assert_matches, fs, io, str, thread}; use rustc_abi::Size; use rustc_data_structures::jobserver::{self, Acquired}; use rustc_data_structures::profiling::{SelfProfilerRef, VerboseTimingGuard}; use rustc_errors::emitter::Emitter; use rustc_errors::{ - Diag, DiagArgMap, DiagCtxt, DiagCtxtHandle, DiagMessage, ErrCode, FatalError, FatalErrorMarker, - Level, MultiSpan, Style, Sublevel, Suggestions, catch_fatal_errors, + Diag, DiagCtxt, DiagCtxtHandle, DiagInner, FatalError, FatalErrorMarker, Level, + catch_fatal_errors, }; use rustc_fs_util::link_or_copy; -use rustc_incremental::{copy_cgu_workproduct_to_incr_comp_cache_dir, in_incr_comp_dir_sess}; +use rustc_incremental::{ + copy_cgu_workproduct_to_incr_comp_cache_dir, in_incr_comp_dir_sess, in_old_incr_comp_dir_sess, +}; use rustc_macros::{Decodable, Encodable}; use rustc_metadata::fs::copy_to_stdout; use rustc_middle::dep_graph::{WorkProduct, WorkProductMap}; @@ -25,7 +27,7 @@ use rustc_session::config::{ }; use rustc_session::{IncrCompSession, Session}; use rustc_span::source_map::SourceMap; -use rustc_span::{FileName, InnerSpan, Span, SpanData, bug}; +use rustc_span::{BytePos, FileName, InnerSpan, Span, SyntaxContext, bug}; use rustc_structures::CrateType; use rustc_target::spec::{MergeFunctions, SanitizerSet}; use tracing::debug; @@ -242,12 +244,7 @@ impl ModuleConfig { // backends (again, NVPTX). Therefore, allow targets to opt out of // the MergeFunctions pass, but otherwise keep the pass enabled (at // O2 and O3) since it can be useful for reducing code size. - merge_functions: match sess - .opts - .unstable_opts - .merge_functions - .unwrap_or(sess.target.merge_functions) - { + merge_functions: match sess.merge_functions() { MergeFunctions::Disabled => false, MergeFunctions::Trampolines | MergeFunctions::Aliases => { use config::OptLevel::*; @@ -351,9 +348,12 @@ pub struct CodegenContext { /// Directory into which should the LLVM optimization remarks be written. /// If `None`, they will be written to stderr. pub remark_dir: Option, + /// The previous incremental compilation session directory, or None if we + /// are not compiling incrementally or there is no previous session. + pub old_incr_comp_session_dir: Option, /// The incremental compilation session directory, or None if we are not /// compiling incrementally - pub incr_comp_session_dir: Option, + pub new_incr_comp_session_dir: Option, /// `Some(limit)` if the codegen should be run in parallel. /// /// Depends on [`WriteBackendMethods::supports_parallel()`] and `--jobs-backend`. @@ -497,7 +497,6 @@ fn copy_all_cgu_workproducts_to_incr_comp_cache_dir( incr_comp_session.unwrap(), &module.name, files.as_slice(), - &module.links_from_incr_cache, ); work_products.insert(id, product); } @@ -839,7 +838,7 @@ fn execute_optimize_work_item( // save our module to disk first. let bitcode = if cgcx.module_config.emit_pre_lto_bc { let filename = pre_lto_bitcode_filename(&module.name); - cgcx.incr_comp_session_dir.as_ref().map(|path| path.join(&filename)) + cgcx.new_incr_comp_session_dir.as_ref().map(|path| path.join(&filename)) } else { None }; @@ -886,11 +885,9 @@ fn execute_copy_from_cache_work_item( let dcx = DiagCtxt::new(Box::new(shared_emitter)); let dcx = dcx.handle(); - let incr_comp_session_dir = cgcx.incr_comp_session_dir.as_ref().unwrap(); - - let mut links_from_incr_cache = Vec::new(); + let incr_comp_session_dir = cgcx.old_incr_comp_session_dir.as_ref().unwrap(); - let mut load_from_incr_comp_dir = |output_path: PathBuf, saved_path: &str| { + let load_from_incr_comp_dir = |output_path: PathBuf, saved_path: &str| { let source_file_in_incr_comp_dir = incr_comp_session_dir.join(saved_path); debug!( "copying preexisting module `{}` from {:?} to {}", @@ -899,10 +896,7 @@ fn execute_copy_from_cache_work_item( output_path.display() ); match link_or_copy(&source_file_in_incr_comp_dir, &output_path) { - Ok(_) => { - links_from_incr_cache.push(source_file_in_incr_comp_dir); - Some(output_path) - } + Ok(_) => Some(output_path), Err(error) => { dcx.emit_err(diagnostics::CopyPathBuf { source_file: source_file_in_incr_comp_dir, @@ -925,7 +919,7 @@ fn execute_copy_from_cache_work_item( load_from_incr_comp_dir(dwarf_obj_out, saved_dwarf_object_file) }); - let mut load_from_incr_cache = |perform, output_type: OutputType| { + let load_from_incr_cache = |perform, output_type: OutputType| { if perform { let saved_file = module.source.saved_files.get(output_type.extension())?; let output_path = cgcx.output_filenames.temp_path_for_cgu(output_type, &module.name); @@ -953,7 +947,6 @@ fn execute_copy_from_cache_work_item( } CompiledModule { - links_from_incr_cache, kind: ModuleKind::Regular, name: module.name, object, @@ -1000,6 +993,7 @@ fn do_thin_lto( exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], needs_thin_lto: Vec>, + stack_size: usize, ) -> Vec { let _timer = prof.verbose_generic_activity("LLVM_thinlto"); @@ -1090,6 +1084,7 @@ fn do_thin_lto( Arc::clone(&tm_factory), coordinator_send.clone(), item, + stack_size, ); used_token_count += 1; } @@ -1193,32 +1188,6 @@ pub(crate) enum ThinLtoMessage { /// process another codegen unit. pub struct CguMessage; -// A cut-down version of `rustc_errors::DiagInner` that impls `Send`, which -// can be used to send diagnostics from codegen threads to the main thread. -// It's missing the following fields from `rustc_errors::DiagInner`. -// - `span`: it doesn't impl `Send`. -// - `suggestions`: it doesn't impl `Send`, and isn't used for codegen -// diagnostics. -// - `sort_span`: it doesn't impl `Send`. -// - `is_lint`: lints aren't relevant during codegen. -// - `emitted_at`: not used for codegen diagnostics. -struct Diagnostic { - span: Vec, - level: Level, - messages: Vec<(DiagMessage, Style)>, - code: Option, - children: Vec, - args: DiagArgMap, -} - -// A cut-down version of `rustc_errors::Subdiag` that impls `Send`. It's -// missing the following fields from `rustc_errors::Subdiag`. -// - `span`: it doesn't impl `Send`. -struct Subdiagnostic { - level: Sublevel, - messages: Vec<(DiagMessage, Style)>, -} - #[derive(PartialEq, Clone, Copy, Debug)] enum MainThreadState { /// Doing nothing. @@ -1283,6 +1252,8 @@ fn start_executing_work( None }; + let stack_size = sess.opts.recommended_stack_size; + let cgcx = CodegenContext { crate_types: tcx.crate_types().to_vec(), lto: sess.lto(), @@ -1294,10 +1265,15 @@ fn start_executing_work( time_trace: sess.opts.unstable_opts.llvm_time_trace, remark: sess.opts.cg.remark.clone(), remark_dir, - incr_comp_session_dir: tcx + old_incr_comp_session_dir: tcx + .incr_comp_session + .as_ref() + .and_then(|incr_comp_session| incr_comp_session.old_session_directory.as_deref()) + .map(ToOwned::to_owned), + new_incr_comp_session_dir: tcx .incr_comp_session .as_ref() - .map(|incr_comp_session| incr_comp_session.session_directory.clone()), + .map(|incr_comp_session| (&*incr_comp_session.new_session_directory).to_owned()), output_filenames: Arc::clone(tcx.output_filenames(())), module_config: regular_config, opt_level, @@ -1540,6 +1516,7 @@ fn start_executing_work( coordinator_send.clone(), &mut llvm_start_time, item, + stack_size, ); } } @@ -1565,6 +1542,7 @@ fn start_executing_work( coordinator_send.clone(), &mut llvm_start_time, item, + stack_size, ); } else { // There is no unstarted work, so let the main thread @@ -1608,6 +1586,7 @@ fn start_executing_work( coordinator_send.clone(), &mut llvm_start_time, item, + stack_size, ); running_with_own_token += 1; } @@ -1776,6 +1755,7 @@ fn start_executing_work( &exported_symbols_for_lto, &[], needs_thin_lto, + stack_size, )); } else { if let Some(allocator_module) = allocator_module.take() { @@ -1871,6 +1851,7 @@ fn spawn_work<'a, B: WriteBackendMethods>( coordinator_send: Sender>, llvm_start_time: &mut Option>, work: WorkItem, + stack_size: usize, ) { if llvm_start_time.is_none() { *llvm_start_time = Some(prof.verbose_generic_activity("LLVM_passes")); @@ -1904,7 +1885,11 @@ fn spawn_work<'a, B: WriteBackendMethods>( }; drop(coordinator_send.send(msg)); }; - std::thread::Builder::new().name(name).spawn(f).expect("failed to spawn work thread"); + std::thread::Builder::new() + .name(name) + .stack_size(stack_size) + .spawn(f) + .expect("failed to spawn work thread"); } fn spawn_thin_lto_work( @@ -1914,6 +1899,7 @@ fn spawn_thin_lto_work( tm_factory: TargetMachineFactoryFn, coordinator_send: Sender, work: ThinLtoWorkItem, + stack_size: usize, ) { let cgcx = cgcx.clone(); let prof = prof.clone(); @@ -1946,17 +1932,26 @@ fn spawn_thin_lto_work( }; drop(coordinator_send.send(msg)); }; - std::thread::Builder::new().name(name).spawn(f).expect("failed to spawn work thread"); + std::thread::Builder::new() + .name(name) + .stack_size(stack_size) + .spawn(f) + .expect("failed to spawn work thread"); } enum SharedEmitterMessage { - Diagnostic(Diagnostic), + Diagnostic(DiagInner), InlineAsmError(InlineAsmError), Fatal(String), } pub struct InlineAsmError { - pub span: SpanData, + // We store byte positions rather than a `Span` because this error is created on a codegen + // thread which doesn't have access to the span interner within the session globals. The byte + // positions are converted to a `Span` once they reach a thread with access to the span + // interner. + pub lo: BytePos, + pub hi: BytePos, pub msg: String, pub level: Level, pub source: Option<(String, Vec)>, @@ -1988,30 +1983,8 @@ impl SharedEmitter { } impl Emitter for SharedEmitter { - fn emit_diagnostic(&mut self, mut diag: rustc_errors::DiagInner) { - // Check that we aren't missing anything interesting when converting to - // the cut-down local `DiagInner`. - assert!(!diag.span.has_span_labels()); - assert_eq!(diag.suggestions, Suggestions::Enabled(vec![])); - assert_eq!(diag.sort_span, rustc_span::DUMMY_SP); - assert_eq!(diag.is_lint, None); - // No sensible check for `diag.emitted_at`. - - let args = mem::take(&mut diag.args); - drop( - self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic { - span: diag.span.primary_spans().iter().map(|span| span.data()).collect::>(), - level: diag.level(), - messages: diag.messages, - code: diag.code, - children: diag - .children - .into_iter() - .map(|child| Subdiagnostic { level: child.level, messages: child.messages }) - .collect(), - args, - })), - ); + fn emit_diagnostic(&mut self, diag: DiagInner) { + drop(self.sender.send(SharedEmitterMessage::Diagnostic(diag))); } fn source_map(&self) -> Option<&SourceMap> { @@ -2036,33 +2009,16 @@ impl SharedEmitterMain { match message { Ok(SharedEmitterMessage::Diagnostic(diag)) => { - // The diagnostic has been received on the main thread. - // Convert it back to a full `Diagnostic` and emit. let dcx = sess.dcx(); - let mut d = - rustc_errors::DiagInner::new_with_messages(diag.level, diag.messages); - d.span = MultiSpan::from_spans( - diag.span.into_iter().map(|span| span.span()).collect(), - ); - d.code = diag.code; // may be `None`, that's ok - d.children = diag - .children - .into_iter() - .map(|sub| rustc_errors::Subdiag { - level: sub.level, - messages: sub.messages, - span: MultiSpan::new(), - }) - .collect(); - d.args = diag.args; - dcx.emit_diagnostic(d); + dcx.emit_diagnostic(diag); sess.dcx().abort_if_errors(); } Ok(SharedEmitterMessage::InlineAsmError(inner)) => { - assert_matches!(inner.level, Level::Error | Level::Warning | Level::Note); - let mut err = Diag::<()>::new(sess.dcx(), inner.level, inner.msg); - if !inner.span.is_dummy() { - err.span(inner.span.span()); + assert_matches!(inner.level, Level::Error | Level::Warning(None) | Level::Note); + let mut err = Diag::new(sess.dcx(), inner.level, inner.msg); + let span = Span::new(inner.lo, inner.hi, SyntaxContext::root(), None); + if !span.is_dummy() { + err.span(span); } // Point to the generated assembly if it is available. @@ -2188,6 +2144,7 @@ impl OngoingCodegen { &crate_info.exported_symbols_for_lto, &crate_info.each_linked_rlib_file_for_lto, needs_thin_lto, + sess.opts.recommended_stack_size, ), allocator_module: None, } @@ -2262,7 +2219,22 @@ pub(crate) fn submit_pre_lto_module_to_llvm( module: CachedModuleCodegen, ) { let filename = pre_lto_bitcode_filename(&module.name); + let old_bitcode_path = + in_old_incr_comp_dir_sess(tcx.incr_comp_session.unwrap(), &filename).unwrap(); let bitcode_path = in_incr_comp_dir_sess(tcx.incr_comp_session.unwrap(), &filename); + + match link_or_copy(&old_bitcode_path, &bitcode_path) { + Ok(_) => {} + Err(error) => { + tcx.sess.dcx().emit_err(diagnostics::CopyPathBuf { + source_file: old_bitcode_path, + output_path: bitcode_path, + error, + }); + return; + } + } + // Schedule the module to be loaded drop( coordinator diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index de921a9cb0d1e..f9dfd04faf2ac 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -9,14 +9,15 @@ use rustc_ast::expand::allocator::{ ALLOC_ERROR_HANDLER, ALLOCATOR_METHODS, AllocatorKind, AllocatorMethod, AllocatorMethodInput, AllocatorTy, }; +use rustc_attr_ir::lang_items::LangItem; +use rustc_attr_ir::target::Target; +use rustc_attr_ir::{DebuggerVisualizerType, EiiDecl, EiiImpl, OptimizeAttr, find_attr}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry}; use rustc_data_structures::sync::{IntoDynSyncSend, par_map}; use rustc_data_structures::unord::UnordMap; -use rustc_hir::attrs::lang_items::LangItem; -use rustc_hir::attrs::{DebuggerVisualizerType, EiiDecl, EiiImpl, OptimizeAttr}; +use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; -use rustc_hir::{ItemId, Target, find_attr}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::dependency_format::{Dependencies, Linkage}; @@ -26,6 +27,7 @@ use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, ErrorHandled, Scalar}; use rustc_middle::mir::{BinOp, ConstValue}; use rustc_middle::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem, MonoItemPartitions}; use rustc_middle::query::Providers; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Instance, PatternKind, Ty, TyCtxt, UintTy, Unnormalized}; use rustc_session::config::{self, EntryFnType}; @@ -403,7 +405,7 @@ pub(crate) fn codegen_instance<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>( mir::codegen_mir::(cx, instance); } -pub fn codegen_global_asm<'tcx, Cx>(cx: &mut Cx, item_id: ItemId) +pub fn codegen_global_asm<'tcx, Cx>(cx: &mut Cx, item_id: hir::ItemId) where Cx: LayoutOf<'tcx, LayoutOfResult = TyAndLayout<'tcx>> + AsmCodegenMethods<'tcx>, { @@ -877,6 +879,10 @@ pub fn codegen_crate< source: cgu.previous_work_product(tcx), }, ); + // This will unwind if there are errors, which triggers our `AbortCodegenOnDrop` + // guard. Unfortunately, just skipping the `submit_pre_lto_module_to_llvm` makes + // compilation hang on post-monomorphization errors. + tcx.dcx().abort_if_errors(); } CguReuse::PostLto => { submit_post_lto_module_to_llvm( diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 496e1ca3ce17e..9bdafe70fb553 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -53,7 +53,7 @@ struct InterestingAttributeDiagnosticSpans { no_mangle: Option, } -/// Process the builtin attrs ([`hir::Attribute`]) on the item. +/// Process the builtin attrs ([`rustc_attr_ir::Attribute`]) on the item. /// Many of them directly translate to codegen attrs. fn process_builtin_attrs( tcx: TyCtxt<'_>, @@ -66,7 +66,7 @@ fn process_builtin_attrs( let parsed_attrs = attrs .iter() - .filter_map(|attr| if let hir::Attribute::Parsed(attr) = attr { Some(attr) } else { None }); + .filter_map(|attr| if let Attribute::Parsed(attr) = attr { Some(attr) } else { None }); for attr in parsed_attrs { match attr { AttributeKind::Cold => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD, @@ -497,13 +497,11 @@ fn check_result( let span = find_attr!(tcx, did, TargetFeature{attr_span: span, ..} => *span) .unwrap_or_else(|| tcx.def_span(did)); - tcx.dcx() - .create_err(diagnostics::TargetFeatureDisableOrEnable { - features, - span: Some(span), - missing_features: Some(diagnostics::MissingFeatures), - }) - .emit(); + tcx.dcx().emit_err(diagnostics::TargetFeatureDisableOrEnable { + features, + span: Some(span), + missing_features: Some(diagnostics::MissingFeatures), + }); } } diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs index 4b54235cf71bc..9338a6b3769ed 100644 --- a/compiler/rustc_codegen_ssa/src/common.rs +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -1,8 +1,8 @@ #![allow(non_camel_case_types)] +use rustc_attr_ir::PeImportNameType; +use rustc_attr_ir::lang_items::LangItem; use rustc_crate_store::{DllCallingConvention, DllImport, DllImportSymbolType}; -use rustc_hir::attrs::PeImportNameType; -use rustc_hir::attrs::lang_items::LangItem; use rustc_middle::mir::interpret::{GlobalAlloc, PointerArithmetic, Scalar}; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{self, Instance, ScalarInt, TyCtxt}; diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 810253956b186..9cf68c6e3909c 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -20,6 +20,7 @@ use rustc_hashes::Hash64; use rustc_hir::def_id::DefId; use rustc_hir::definitions::{DefPathData, DefPathDataName, DisambiguatedDefPathData}; use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, Mutability}; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::layout::{IntegerExt, TyAndLayout}; use rustc_middle::ty::{ self, ExistentialProjection, GenericArgKind, GenericArgsRef, Ty, TyCtxt, Unnormalized, diff --git a/compiler/rustc_codegen_ssa/src/diagnostics.rs b/compiler/rustc_codegen_ssa/src/diagnostics.rs index 8aa2da904bd07..09faa71468bf7 100644 --- a/compiler/rustc_codegen_ssa/src/diagnostics.rs +++ b/compiler/rustc_codegen_ssa/src/diagnostics.rs @@ -202,8 +202,8 @@ pub enum LinkRlibError { pub(crate) struct ThorinErrorWrapper(pub thorin::Error); -impl Diagnostic<'_, G> for ThorinErrorWrapper { - fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { +impl Diagnostic<'_> for ThorinErrorWrapper { + fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_> { let build = |msg| Diag::new(dcx, level, msg); match self.0 { thorin::Error::ReadInput(_) => build(msg!("failed to read input file")), @@ -334,8 +334,8 @@ pub(crate) struct LinkingFailed<'a> { pub sysroot_dir: PathBuf, } -impl Diagnostic<'_, G> for LinkingFailed<'_> { - fn into_diag(mut self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { +impl Diagnostic<'_> for LinkingFailed<'_> { + fn into_diag(mut self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_> { let mut diag = Diag::new(dcx, level, msg!("linking with `{$linker_path}` failed: {$exit_status}")); diag.arg("linker_path", format!("{}", self.linker_path.display())); @@ -463,8 +463,8 @@ pub(crate) struct LinkExeUnexpectedError; pub(crate) struct LinkExeStatusStackBufferOverrun; -impl<'a, G> Diagnostic<'a, G> for LinkExeStatusStackBufferOverrun { - fn into_diag(self, dcx: rustc_errors::DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { +impl<'a> Diagnostic<'a> for LinkExeStatusStackBufferOverrun { + fn into_diag(self, dcx: rustc_errors::DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let mut diag = Diag::new(dcx, level, msg!("0xc0000409 is `STATUS_STACK_BUFFER_OVERRUN`")); diag.note(msg!( "this may have been caused by a program abort and not a stack buffer overrun" @@ -695,7 +695,7 @@ pub(crate) struct StaticlibHideInternalSymbolsUnsupported { #[derive(Diagnostic)] #[diag( - "-Zstaticlib-rename-internal-symbols only supports ELF and Mach-O targets, but the target uses `{$binary_format}`" + "-Zstaticlib-rename-internal-symbols only supports ELF, Mach-O, and COFF targets, but the target uses `{$binary_format}`" )] pub(crate) struct StaticlibRenameInternalSymbolsUnsupported { pub binary_format: String, @@ -1265,8 +1265,8 @@ pub(crate) struct TargetFeatureDisableOrEnable<'a> { #[help("add the missing features in a `target_feature` attribute")] pub(crate) struct MissingFeatures; -impl Diagnostic<'_, G> for TargetFeatureDisableOrEnable<'_> { - fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { +impl Diagnostic<'_> for TargetFeatureDisableOrEnable<'_> { + fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_> { let mut diag = Diag::new( dcx, level, diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 1272b26ca0612..af37694136d24 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -18,11 +18,11 @@ use std::path::{Path, PathBuf}; use std::sync::Arc; use rustc_abi::Size; +use rustc_attr_ir::{CfgEntry, WindowsSubsystemKind}; use rustc_crate_store::{self as cstore, CrateSource}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_hir::CRATE_HIR_ID; -use rustc_hir::attrs::{CfgEntry, WindowsSubsystemKind}; use rustc_hir::def_id::CrateNum; use rustc_lint_defs::builtin::{LINKER_INFO, LINKER_MESSAGES}; use rustc_macros::{Decodable, Encodable}; @@ -114,7 +114,6 @@ impl ModuleCodegen { bytecode, assembly, llvm_ir, - links_from_incr_cache: Vec::new(), } } } @@ -129,7 +128,6 @@ pub struct CompiledModule { pub bytecode: Option, pub assembly: Option, // --emit=asm pub llvm_ir: Option, // --emit=llvm-ir, llvm-bc is in bytecode - pub links_from_incr_cache: Vec, } impl CompiledModule { @@ -315,6 +313,9 @@ pub struct TargetConfig { pub has_reliable_f16: bool, /// Option for `cfg(target_has_reliable_f16_math)`, true if `f16` math calls work. pub has_reliable_f16_math: bool, + /// Option for `cfg(target_has_reliable_f16b)`, presently true if both the ABI + /// and LLVM version supports `f16b`. + pub has_reliable_f16b: bool, /// Option for `cfg(target_has_reliable_f128)`, true if `f128` basic arithmetic works. pub has_reliable_f128: bool, /// Option for `cfg(target_has_reliable_f128_math)`, true if `f128` math calls work. diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 6b0def4ffa182..9db1dd700e95e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -7,9 +7,9 @@ use rustc_abi::{ }; use rustc_ast as ast; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; +use rustc_attr_ir::AttributeKind; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::packed::Pu128; -use rustc_hir::attrs::AttributeKind; -use rustc_hir::attrs::lang_items::LangItem; use rustc_lint_defs::builtin::TAIL_CALL_TRACK_CALLER; use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, Scalar}; use rustc_middle::mir::{self, AssertKind, InlineAsmMacro, SwitchTargets, UnwindTerminateReason}; @@ -18,7 +18,7 @@ use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; use rustc_middle::ty::{self, Instance, Ty, TypeVisitableExt}; use rustc_session::config::OptLevel; use rustc_span::{Span, Spanned, bug, span_bug}; -use rustc_target::callconv::{ArgAbi, ArgAttributes, CastTarget, FnAbi, PassMode}; +use rustc_target::callconv::{ArgAbi, ArgAttributes, CastTarget, FnAbi, IndirectMode, PassMode}; use tracing::{debug, info}; use super::operand::OperandRef; @@ -140,7 +140,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { bx: &mut Bx, target: mir::BasicBlock, mergeable_succ: bool, - attributes: &[AttributeKind], + loop_hint_attrs: &[AttributeKind], ) -> MergingSucc { let (needs_landing_pad, is_cleanupret) = self.llbb_characteristics(fx, target); if mergeable_succ && !needs_landing_pad && !is_cleanupret { @@ -156,7 +156,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { // to a trampoline. bx.cleanup_ret(self.funclet(fx).unwrap(), Some(lltarget)); } else { - bx.br_with_attrs(lltarget, attributes); + bx.br_with_attrs(lltarget, loop_hint_attrs); } MergingSucc::False } @@ -199,7 +199,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { "compiler_builtins call to diverging function {:?} replaced with abort", instance.def_id() ); - bx.abort(); + bx.abort_immediate(); bx.unreachable(); return MergingSucc::False; } @@ -587,7 +587,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // so we should make sure that we never actually do. // We play it safe by using a well-defined `abort`, but we could go for immediate UB // if that turns out to be helpful. - bx.abort(); + bx.abort_immediate(); // `abort` does not terminate the block, so we still need to generate // an `unreachable` terminator after it. bx.unreachable(); @@ -1099,7 +1099,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } // Also we need to terminate the block to avoid an LLVM assertion, // even though we're not going to actually use the IR. - bx.abort(); + bx.abort_immediate(); return MergingSucc::False; } IntrinsicResult::Fallback(instance) => { @@ -1257,7 +1257,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { (args, None) }; - // Special logic for tail calls with `PassMode::Indirect { on_stack: false, .. }` arguments. + // Special logic for tail calls with `PassMode::Indirect { mode: IndirectMode::Pointer, .. }` arguments. // // Normally an indirect argument that is allocated in the caller's stack frame // would be passed as a pointer into the callee's stack frame. @@ -1282,10 +1282,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let mut tail_call_temporaries = vec![]; if kind == CallKind::Tail { tail_call_temporaries = vec![None; first_args.len()]; - // Copy the arguments that use `PassMode::Indirect { on_stack: false , ..}` + // Copy the arguments that use `PassMode::Indirect { mode: IndirectMode::Pointer , ..}` // to temporary stack allocations. See the comment above. for (i, arg) in first_args.iter().enumerate() { - if !matches!(fn_abi.args[i].mode, PassMode::Indirect { on_stack: false, .. }) { + if !matches!( + fn_abi.args[i].mode, + PassMode::Indirect { mode: IndirectMode::Pointer, .. } + ) { continue; } @@ -1353,10 +1356,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - let by_move = if let PassMode::Indirect { on_stack: false, .. } = fn_abi.args[i].mode + let by_move = if let PassMode::Indirect { mode: IndirectMode::Pointer, .. } = + fn_abi.args[i].mode && kind == CallKind::Tail { - // Special logic for tail calls with `PassMode::Indirect { on_stack: false, .. }` arguments. + // Special logic for tail calls with `PassMode::Indirect { mode: IndirectMode::Pointer, .. }` arguments. // // Normally an indirect argument that is allocated in the caller's stack frame // would be passed as a pointer into the callee's stack frame. @@ -1673,7 +1677,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } mir::TerminatorKind::Goto { target } => { - helper.funclet_br(self, bx, target, mergeable_succ(), &terminator.attributes) + helper.funclet_br(self, bx, target, mergeable_succ(), &terminator.loop_hint_attrs) } mir::TerminatorKind::SwitchInt { ref discr, ref targets } => { @@ -1977,14 +1981,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } _ => bug!("codegen_argument: {:?} invalid for pair argument", op), }, - PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => match op.val { - Ref(PlaceValue { llval: a, llextra: Some(b), .. }) => { - llargs.push(a); - llargs.push(b); - return; + PassMode::Indirect { attrs: _, meta_attrs: Some(_), address_space: _, mode: _ } => { + match op.val { + Ref(PlaceValue { llval: a, llextra: Some(b), .. }) => { + llargs.push(a); + llargs.push(b); + return; + } + _ => bug!("codegen_argument: {:?} invalid for unsized indirect argument", op), } - _ => bug!("codegen_argument: {:?} invalid for unsized indirect argument", op), - }, + } _ => {} } @@ -2014,7 +2020,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { PassMode::Ignore | PassMode::Pair(..) => unreachable!("handled above"), }, Ref(op_place_val) => match arg.mode { - PassMode::Indirect { attrs, on_stack, .. } => { + PassMode::Indirect { attrs, mode, .. } => { + if mode == IndirectMode::AmdgpuKernelArg { + bug!("{op:?} passed as amdgpu kernel argument with abi {arg:?}"); + } // For `foo(packed.large_field)`, and types with <4 byte alignment on x86, // alignment requirements may be higher than the type's alignment, so copy // to a higher-aligned alloca. @@ -2023,7 +2032,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { None => arg.layout.align.abi, }; // Copy to an alloca when the argument is neither by-val nor by-move. - if op_place_val.align < required_align || (!on_stack && !by_move) { + if op_place_val.align < required_align + || (mode == IndirectMode::Pointer && !by_move) + { let scratch = PlaceValue::alloca(bx, arg.layout.size, required_align); bx.lifetime_start(scratch.llval, arg.layout.size); op.store_with_annotation(bx, scratch.with_type(arg.layout)); @@ -2036,8 +2047,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { _ => (op_place_val.llval, op_place_val.align, true), }, ZeroSized => match arg.mode { - PassMode::Indirect { on_stack, .. } => { - if on_stack { + PassMode::Indirect { mode, .. } => { + if mode == IndirectMode::AmdgpuKernelArg { + bug!("{op:?} passed as amdgpu kernel argument with abi {arg:?}"); + } + if mode == IndirectMode::OnStack { // It doesn't seem like any target can have `byval` ZSTs, so this assert // is here to replace a would-be untested codepath. bug!("ZST {op:?} passed on stack with abi {arg:?}"); @@ -2384,7 +2398,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let (fn_abi, fn_ptr, instance) = common::build_langcall(&bx, self.mir.span, reason.lang_item()); if is_call_from_compiler_builtins_to_upstream_monomorphization(bx.tcx(), instance) { - bx.abort(); + bx.abort_immediate(); } else { let fn_ty = bx.fn_decl_backend_type(fn_abi); diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs index 625e1dd8491db..b6f16f72b021e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/constant.rs +++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs @@ -1,6 +1,7 @@ use rustc_abi::BackendRepr; use rustc_middle::mir; use rustc_middle::mir::interpret::ErrorHandled; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv}; use rustc_middle::ty::{self, Ty}; use rustc_span::{bug, span_bug}; diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 22ab228e15b5f..650b21bf78a9c 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -1,5 +1,6 @@ use rustc_abi::{Align, FieldIdx, WrappingRange}; use rustc_middle::mir::SourceInfo; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::config::OptLevel; use rustc_span::{ErrorGuaranteed, bug, span_bug, sym}; @@ -120,7 +121,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if args.is_empty() { match name { - sym::abort + sym::abort_immediate | sym::unreachable | sym::cold_path | sym::gpu_launch_sized_workgroup_mem @@ -151,8 +152,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } let op_val: OperandValue<_> = match name { - sym::abort => { - bx.abort(); + sym::abort_immediate => { + bx.abort_immediate(); OperandValue::ZeroSized } diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index b5cecf4b5c434..aefa8356536dc 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -8,7 +8,7 @@ use rustc_middle::mir::{Body, Local, UnwindTerminateReason, traversal}; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv, TyAndLayout}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; use rustc_span::{ErrorGuaranteed, bug, span_bug}; -use rustc_target::callconv::{FnAbi, PassMode}; +use rustc_target::callconv::{FnAbi, IndirectMode, PassMode}; use tracing::{debug, instrument}; use crate::base; @@ -561,15 +561,21 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( match arg.mode { // Sized indirect arguments - PassMode::Indirect { attrs, meta_attrs: None, on_stack: _ } => { + PassMode::Indirect { attrs, meta_attrs: None, address_space: _, mode } => { // Don't copy an indirect argument to an alloca, the caller already put it // in a temporary alloca and gave it up. + // AmdgpuKernelArg/byref arguments must not be modified, so always create a + // local alloca for them. + // If the argument is underaligned, then we need to copy it to a higher-aligned + // alloca. // FIXME: lifetimes + let mut needs_alloca = mode == IndirectMode::AmdgpuKernelArg; if let Some(pointee_align) = attrs.pointee_align && pointee_align < arg.layout.align.abi { - // ...unless the argument is underaligned, then we need to copy it to - // a higher-aligned alloca. + needs_alloca = true; + } + if needs_alloca { let tmp = PlaceRef::alloca(bx, arg.layout); bx.store_fn_arg(arg, &mut llarg_idx, tmp); LocalRef::Place(tmp) @@ -580,7 +586,7 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } } // Unsized indirect arguments - PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { + PassMode::Indirect { attrs: _, meta_attrs: Some(_), address_space: _, mode: _ } => { // As the storage for the indirect argument lives during // the whole function call, we just copy the wide pointer. let llarg = bx.get_param(llarg_idx); diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index ea58b1cd3c53c..5433b9da6f2b6 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -1,5 +1,5 @@ use rustc_abi::{BackendRepr, Float, Integer, Primitive, RegKind}; -use rustc_hir::attrs::{InstructionSetAttr, Linkage}; +use rustc_attr_ir::{InstructionSetAttr, Linkage}; use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, Scalar}; use rustc_middle::mir::{self, InlineAsmOperand, START_BLOCK}; @@ -228,7 +228,6 @@ fn prefix_and_suffix<'tcx>( // LTO can fail when internal linkage is used. emit_fatal("naked functions may not have internal linkage") } - Linkage::Common => emit_fatal("Functions may not have common linkage"), Linkage::AvailableExternally => { // this would make the function equal an extern definition emit_fatal("Functions may not have available_externally linkage") @@ -508,6 +507,9 @@ fn wasm_primitive(primitive: Primitive, ptr_type: &'static str) -> &'static str Integer::I128 => "i64, i64", }, Primitive::Float(float) => match float { + // This could probably use an f32 for WASM however has not been + // verified so leaving as a `bug!(...)` for now. + Float::F16B => bug!("`f16b` unsupported on wasm"), Float::F16 | Float::F32 => "f32", Float::F64 => "f64", Float::F128 => "i64, i64", diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 5b07eb84f40d7..e017220c549b0 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -5,9 +5,10 @@ use rustc_abi as abi; use rustc_abi::{ Align, BackendRepr, FIRST_VARIANT, FieldIdx, Primitive, Size, TagEncoding, VariantIdx, Variants, }; -use rustc_hir::attrs::lang_items::LangItem; +use rustc_attr_ir::lang_items::LangItem; use rustc_middle::mir::interpret::{Pointer, Scalar, alloc_range}; use rustc_middle::mir::{self, ConstValue}; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Ty}; use rustc_session::config::{AnnotateMoves, DebugInfo, OptLevel}; diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index 02e125fd31717..c74a5e788689f 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -264,7 +264,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { Err(UninhabitedVariantError) => { // We play it safe by using a well-defined `abort`, but we could go for immediate UB // if that turns out to be helpful. - bx.abort(); + bx.abort_immediate(); } Ok(Some((tag_field, imm))) => { let tag_place = self.project_field(bx, tag_field.as_usize()); diff --git a/compiler/rustc_codegen_ssa/src/mir/retag.rs b/compiler/rustc_codegen_ssa/src/mir/retag.rs index a71a57f02c82a..6a3316fa93992 100644 --- a/compiler/rustc_codegen_ssa/src/mir/retag.rs +++ b/compiler/rustc_codegen_ssa/src/mir/retag.rs @@ -14,9 +14,10 @@ use rustc_middle::mir::{Rvalue, WithRetag}; use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout}; use rustc_middle::ty::{self, Ty}; -use crate::mir::FunctionCx; +use crate::common::IntPredicate; use crate::mir::operand::{OperandRef, OperandRefBuilder, OperandValue}; use crate::mir::place::PlaceRef; +use crate::mir::{FunctionCx, bug}; use crate::traits::{ BaseTypeCodegenMethods, BuilderMethods, ConstCodegenMethods, StaticCodegenMethods, }; @@ -35,17 +36,31 @@ enum RetagPlan { /// Indicates that one or more fields or variants of this type /// contain pointers that need to be retagged. Recurse { - field_plans: FxIndexMap>, + field_plans: Option>, variant_plans: FxIndexMap>, }, } +/// The layout of the fields that need to be retagged. +/// +/// This mirrors [`FieldsShape`] but excludes unions, +/// which are never retagged, and primitives, which fall +/// into [`RetagPlan::EmitRetag`]. +#[derive(Debug)] +enum RetagFields { + Arbitrary(FxIndexMap>), + Array(Box>), +} + impl RetagPlan { /// A helper function to move a [`RetagPlan`] into a particular field. fn for_field(self, ix: FieldIdx) -> Self { let mut field_plans = FxIndexMap::default(); field_plans.insert(ix, self); - RetagPlan::Recurse { field_plans, variant_plans: FxIndexMap::default() } + RetagPlan::Recurse { + field_plans: Some(RetagFields::Arbitrary(field_plans)), + variant_plans: FxIndexMap::default(), + } } } @@ -84,21 +99,27 @@ impl<'a, 'tcx, V> RetagPlan { layout: TyAndLayout<'tcx>, is_fn_entry: bool, ) -> Option> { - let mut field_plans = FxIndexMap::default(); - let mut variant_plans = FxIndexMap::default(); - - match &layout.fields { - FieldsShape::Union(_) | FieldsShape::Primitive => {} - _ => { + let field_plans = match &layout.fields { + FieldsShape::Union(_) | FieldsShape::Primitive => None, + FieldsShape::Arbitrary { .. } => { + let mut field_plans = FxIndexMap::default(); for ix in layout.fields.index_by_increasing_offset() { let field_layout = layout.field(bx, ix); if let Some(plan) = Self::build(bx, field_layout, is_fn_entry) { field_plans.insert(FieldIdx::from_usize(ix), plan); } } + (!field_plans.is_empty()).then_some(RetagFields::Arbitrary(field_plans)) } - } + FieldsShape::Array { count, .. } if *count > 0 => { + let field_layout = layout.field(bx, 0); + Self::build(bx, field_layout, is_fn_entry) + .map(|plan| RetagFields::Array(Box::new(plan))) + } + FieldsShape::Array { .. } => None, + }; + let mut variant_plans = FxIndexMap::default(); match &layout.variants { Variants::Single { .. } | Variants::Empty => {} Variants::Multiple { variants, .. } => { @@ -111,7 +132,7 @@ impl<'a, 'tcx, V> RetagPlan { } } - (!field_plans.is_empty() || !variant_plans.is_empty()) + (field_plans.is_some() || !variant_plans.is_empty()) .then(|| RetagPlan::Recurse { field_plans, variant_plans }) } @@ -157,8 +178,10 @@ impl<'a, 'tcx, V> RetagPlan { field_plans.insert(FieldIdx::ONE, plan); } - (!field_plans.is_empty()) - .then(|| RetagPlan::Recurse { field_plans, variant_plans: FxIndexMap::default() }) + (!field_plans.is_empty()).then(|| RetagPlan::Recurse { + field_plans: Some(RetagFields::Arbitrary(field_plans)), + variant_plans: FxIndexMap::default(), + }) } /// Determines if a pointer needs to be retagged, when it points to @@ -418,21 +441,38 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } RetagPlan::Recurse { field_plans, variant_plans } => { let layout = curr_operand.layout; - for (ix, plan) in field_plans { - let inner_offset = layout.fields.offset(ix.as_usize()); - let field_offset = offset + inner_offset; - - let field_layout = curr_operand.layout.field(bx, ix.index()); - // Part of https://github.com/rust-lang/compiler-team/issues/838 - if curr_operand.layout.is_ssa_standalone() && !field_layout.is_ssa_standalone() - { - // FIXME: support vector types, requires insert_element as part of cg-ssa - // FIXME: Nothing should be looking at the *array* inside a `repr(simd)` type, - // as that array doesn't really exist. Perhaps this should be a `bug!`, - // with simd types handled before getting here? - } else { - let field_operand = curr_operand.extract_field(self, bx, ix.as_usize()); - self.retag_operand(bx, &plan, field_operand, builder, field_offset); + if let Some(plans) = field_plans { + let mut retag_field = |ix: FieldIdx, plan: &RetagPlan| { + let inner_offset = layout.fields.offset(ix.as_usize()); + let field_offset = offset + inner_offset; + let field_layout = curr_operand.layout.field(bx, ix.index()); + if curr_operand.layout.is_ssa_standalone() + && !field_layout.is_ssa_standalone() + { + // FIXME: Nothing should be looking at the *array* inside a `repr(simd)` type, + // as that array doesn't really exist. Perhaps this should be a `bug!`, + // with simd types handled before getting here? + } else { + let field_operand = curr_operand.extract_field(self, bx, ix.as_usize()); + self.retag_operand(bx, plan, field_operand, builder, field_offset); + } + }; + + match plans { + RetagFields::Arbitrary(field_plans) => { + for (ix, plan) in field_plans { + retag_field(*ix, plan); + } + } + RetagFields::Array(plan) => { + let FieldsShape::Array { count, .. } = layout.fields else { + bug!("Expected `FieldsShape::Array` but found {:?}", layout.fields); + }; + for idx in 0..count { + let idx = FieldIdx::from_usize(idx as usize); + retag_field(idx, plan) + } + } } } @@ -449,8 +489,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.retag_operand(bx, plan, variant_op, builder, offset); } } else { - // We create a temporary place to store the operand, because its value will differ - // depending on the variant that we have. + // We create a temporary place to store the operand, because its + // value will differ depending on the variant that we have. let scratch = PlaceRef::alloca(bx, curr_operand.layout); scratch.storage_live(bx); curr_operand.store_with_annotation(bx, scratch); @@ -487,9 +527,33 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.retag_mem(place.val.llval, info); } RetagPlan::Recurse { field_plans, variant_plans } => { - for (ix, plan) in field_plans { - let field_place = place.project_field(bx, ix.as_usize()); - self.retag_place(bx, &plan, field_place); + if let Some(field_plans) = field_plans { + let mut retag_field = |ix: FieldIdx, plan: &RetagPlan| { + let field_place = place.project_field(bx, ix.as_usize()); + self.retag_place(bx, plan, field_place); + }; + match field_plans { + RetagFields::Arbitrary(fields) => { + for (ix, plan) in fields { + retag_field(*ix, plan); + } + } + RetagFields::Array(plan) => { + let FieldsShape::Array { count, .. } = place.layout.fields else { + bug!( + "Expected `FieldsShape::Array` but found {:?}", + place.layout.fields + ); + }; + // When an array of N references needs to be retagged (e.g. `[&T; N]`), + // it is more efficient to emit a loop for large values of N than + // to emit N retags in succession. + within_loop(bx, count, |bx: &mut Bx, ix: Bx::Value| { + let field_place = place.project_index(bx, ix); + self.retag_place(bx, plan, field_place); + }); + } + } } if !variant_plans.is_empty() { let operand = bx.load_operand(place); @@ -534,3 +598,45 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.switch_to_block(join_block); } } + +/// Creates a simple loop, iterating the specified number of times. +/// The dynamic value of the loop counter is passed as an argument +/// to the closure. +fn within_loop<'a, 'tcx, F, Bx: BuilderMethods<'a, 'tcx>>(bx: &mut Bx, max_iter: u64, mut op: F) +where + F: FnMut(&mut Bx, Bx::Value), +{ + // Avoid emitting a no-op loop + if max_iter == 0 { + return; + } + + // Initialize the loop counter to zero. + let counter_layout = bx.layout_of(bx.tcx().types.usize); + let counter_alloca = PlaceRef::alloca(bx, counter_layout); + counter_alloca.storage_live(bx); + bx.store_to_place(bx.const_usize(0), counter_alloca.val); + + // Jump to a new block for the body of the loop. + let loop_body = bx.append_sibling_block("retag_loop"); + bx.br(loop_body); + + bx.switch_to_block(loop_body); + + // Load the value of the counter, and pass it to the closure. + let curr_count = bx.load_from_place(bx.type_isize(), counter_alloca.val); + op(bx, curr_count); + + let next_count = bx.unchecked_uadd(curr_count, bx.const_usize(1)); + bx.store_to_place(next_count, counter_alloca.val); + + // If the next count is equal to the maximum number of + // iterations, then exit the loop. + let max_count = bx.const_usize(max_iter); + let loop_exit = bx.append_sibling_block("retag_loop_exit"); + let should_exit_loop = bx.icmp(IntPredicate::IntEQ, next_count, max_count); + bx.cond_br(should_exit_loop, loop_exit, loop_body); + + bx.switch_to_block(loop_exit); + counter_alloca.storage_dead(bx); +} diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 130958f3705c9..6391fcd58f4bb 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -5,6 +5,7 @@ use rustc_abi::{self as abi, BackendRepr, FIRST_VARIANT}; use rustc_index::IndexVec; use rustc_middle::mir; use rustc_middle::ty::adjustment::PointerCoercion; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Instance, Mutability, Ty, TyCtxt}; use rustc_session::config::OptLevel; @@ -833,8 +834,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { Err(super::place::UninhabitedVariantError) => { // Like codegen_set_discr we use a sound abort, but could // potentially `unreachable` or just return the poison for - // more optimizability, if that turns out to be helpful. - bx.abort(); + // optimizability, if that turns out to be helpful. + bx.abort_immediate(); let val = OperandValue::poison(bx, layout); OperandRef { val, layout, move_annotation: None } } diff --git a/compiler/rustc_codegen_ssa/src/mono_item.rs b/compiler/rustc_codegen_ssa/src/mono_item.rs index f2cd9e228f282..5150b8da8ff4d 100644 --- a/compiler/rustc_codegen_ssa/src/mono_item.rs +++ b/compiler/rustc_codegen_ssa/src/mono_item.rs @@ -1,4 +1,4 @@ -use rustc_hir::attrs::Linkage; +use rustc_attr_ir::Linkage; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mono::{MonoItem, MonoItemData, Visibility}; use rustc_middle::ty::layout::HasTyCtxt; diff --git a/compiler/rustc_codegen_ssa/src/size_of_val.rs b/compiler/rustc_codegen_ssa/src/size_of_val.rs index d49dc61fc66fb..2467a2354a3dd 100644 --- a/compiler/rustc_codegen_ssa/src/size_of_val.rs +++ b/compiler/rustc_codegen_ssa/src/size_of_val.rs @@ -1,7 +1,7 @@ //! Computing the size and alignment of a value. use rustc_abi::{Align, WrappingRange}; -use rustc_hir::attrs::lang_items::LangItem; +use rustc_attr_ir::lang_items::LangItem; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; use rustc_middle::ty::{self, Ty}; use rustc_span::{DUMMY_SP, Span, bug}; diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index b630155c144ea..c5f3665383e21 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -1,6 +1,6 @@ +use rustc_attr_ir::InstructionSetAttr; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_data_structures::unord::{UnordMap, UnordSet}; -use rustc_hir::attrs::InstructionSetAttr; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_lint_defs::builtin::{AARCH64_SOFTFLOAT_NEON, X86_SOFTFLOAT_SSE}; @@ -383,8 +383,10 @@ pub fn internal_target_features<'a, const N: usize>( } else { sess.dcx().emit_warn(diag); } - } else if stability.requires_nightly(/* in_cfg */ false).is_some() { - // An unstable feature. Warn about using it. It makes little sense + } else if stability.requires_nightly(/* in_cfg */ false).is_some() + && !sess.is_nightly_build() + { + // An unstable feature. Warn about using it on stable. It makes little sense // to hard-error here since we just warn about fully unknown // features above. let note = if stability.is_cfg_stable_toggle_unstable() { diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 38d562d028f34..e06ac36fd758a 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -52,6 +52,7 @@ pub trait CodegenBackend { // support the float types, rather than accidentally quietly skipping all tests. has_reliable_f16: true, has_reliable_f16_math: true, + has_reliable_f16b: true, has_reliable_f128: true, has_reliable_f128_math: true, } diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index b7b694922bcfa..59e1f6e9d0098 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -3,7 +3,7 @@ use std::ops::Deref; use rustc_abi::{Align, Scalar, Size, WrappingRange}; use rustc_ast::expand::typetree::{FncTree, TypeTree}; -use rustc_hir::attrs::AttributeKind; +use rustc_attr_ir::AttributeKind; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::mir; use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout}; @@ -94,7 +94,7 @@ pub trait BuilderMethods<'a, 'tcx>: fn ret_void(&mut self); fn ret(&mut self, v: Self::Value); fn br(&mut self, dest: Self::BasicBlock); - fn br_with_attrs(&mut self, dest: Self::BasicBlock, _attributes: &[AttributeKind]) { + fn br_with_attrs(&mut self, dest: Self::BasicBlock, _loop_hint_attrs: &[AttributeKind]) { self.br(dest) } fn cond_br( diff --git a/compiler/rustc_codegen_ssa/src/traits/declare.rs b/compiler/rustc_codegen_ssa/src/traits/declare.rs index fc978024b8414..0a49678330b5a 100644 --- a/compiler/rustc_codegen_ssa/src/traits/declare.rs +++ b/compiler/rustc_codegen_ssa/src/traits/declare.rs @@ -1,4 +1,4 @@ -use rustc_hir::attrs::Linkage; +use rustc_attr_ir::Linkage; use rustc_hir::def_id::DefId; use rustc_middle::mono::Visibility; use rustc_middle::ty::Instance; diff --git a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs index 47144834b5072..0aacbb3b5f7fb 100644 --- a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs @@ -38,7 +38,8 @@ pub trait IntrinsicCallBuilderMethods<'tcx>: BackendTypes { is_cleanup: bool, ) -> Self::Value; - fn abort(&mut self); + /// Ungracefully stop execution in the simplest way possible. + fn abort_immediate(&mut self); fn assume(&mut self, val: Self::Value); fn expect(&mut self, cond: Self::Value, expected: bool) -> Self::Value; /// Trait method used to load a function while testing if it is associated with a type diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs index 1b70d38dc9e85..986481e7f132f 100644 --- a/compiler/rustc_codegen_ssa/src/traits/type_.rs +++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs @@ -18,6 +18,7 @@ pub trait BaseTypeCodegenMethods: BackendTypes { fn type_isize(&self) -> Self::Type; fn type_f16(&self) -> Self::Type; + fn type_f16b(&self) -> Self::Type; fn type_f32(&self) -> Self::Type; fn type_f64(&self) -> Self::Type; fn type_f128(&self) -> Self::Type; @@ -67,6 +68,7 @@ pub trait DerivedTypeCodegenMethods<'tcx>: use Float::*; match f { F16 => self.type_f16(), + F16B => self.type_f16b(), F32 => self.type_f32(), F64 => self.type_f64(), F128 => self.type_f128(), diff --git a/compiler/rustc_const_eval/Cargo.toml b/compiler/rustc_const_eval/Cargo.toml index 296368c42034b..8d14d86581a66 100644 --- a/compiler/rustc_const_eval/Cargo.toml +++ b/compiler/rustc_const_eval/Cargo.toml @@ -9,6 +9,7 @@ either = "1" rustc_abi = { path = "../rustc_abi" } rustc_apfloat = "0.2.0" rustc_ast = { path = "../rustc_ast" } +rustc_attr_ir = { path = "../rustc_attr_ir" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_hir = { path = "../rustc_hir" } diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 4d268c8a1f8cd..192bb8556a75d 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -5,11 +5,12 @@ use std::num::NonZero; use std::ops::Deref; use std::{assert_matches, mem}; +use rustc_attr_ir::lang_items::LangItem; +use rustc_attr_ir::{ConstStability, StabilityLevel, find_attr}; use rustc_errors::{Diag, ErrorGuaranteed}; -use rustc_hir::attrs::lang_items::LangItem; +use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, find_attr}; use rustc_index::bit_set::DenseBitSet; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::visit::Visitor; @@ -175,7 +176,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { let secondary_errors = mem::take(&mut self.secondary_errors); if self.error_emitted.is_none() { for error in secondary_errors { - self.error_emitted = Some(error.emit()); + self.error_emitted = Some(error.emit_err()); } } else { assert!(self.tcx.dcx().has_errors().is_some()); @@ -276,8 +277,8 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { match op.importance() { ops::DiagImportance::Primary => { - let reported = err.emit(); - self.error_emitted = Some(reported); + let guar = err.emit_err(); + self.error_emitted = Some(guar); } ops::DiagImportance::Secondary => { @@ -427,7 +428,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { /// Check the const stability of the given item (fn or trait). fn check_callee_stability(&mut self, def_id: DefId) { match self.tcx.lookup_const_stability(def_id) { - Some(hir::ConstStability { level: hir::StabilityLevel::Stable { .. }, .. }) => { + Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => { // All good. } None => { @@ -443,8 +444,8 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { }); } } - Some(hir::ConstStability { - level: hir::StabilityLevel::Unstable { implied_by: implied_feature, issue, .. }, + Some(ConstStability { + level: StabilityLevel::Unstable { implied_by: implied_feature, issue, .. }, feature, .. }) => { @@ -858,8 +859,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { }); } } - Some(hir::ConstStability { - level: hir::StabilityLevel::Unstable { .. }, + Some(ConstStability { + level: StabilityLevel::Unstable { .. }, feature, .. }) => { @@ -878,10 +879,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { const_stable_indirect, }); } - Some(hir::ConstStability { - level: hir::StabilityLevel::Stable { .. }, - .. - }) => { + Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => { // All good. Note that a `#[rustc_const_stable]` intrinsic (meaning it // can be *directly* invoked from stable const code) does not always // have the `#[rustc_intrinsic_const_stable_indirect]` attribute (which controls diff --git a/compiler/rustc_const_eval/src/check_consts/mod.rs b/compiler/rustc_const_eval/src/check_consts/mod.rs index df7d0a2182428..99b6062a23ac4 100644 --- a/compiler/rustc_const_eval/src/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/check_consts/mod.rs @@ -4,9 +4,10 @@ //! has interior mutability or needs to be dropped, as well as the visitor that emits errors when //! it finds operations that are invalid in a certain context. +use rustc_attr_ir::find_attr; use rustc_errors::DiagCtxtHandle; +use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::{self as hir, find_attr}; use rustc_middle::mir; use rustc_middle::ty::{self, PolyFnSig, TyCtxt}; use rustc_span::{Symbol, bug}; diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index 1d1a99d2c6095..ace12201abf02 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -1,10 +1,10 @@ //! Concrete error types for all operations which may be invalid in a certain const context. use hir::ConstContext; +use rustc_attr_ir::lang_items::LangItem; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, MultiSpan, msg}; use rustc_hir as hir; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def_id::DefId; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::{ImplSource, Obligation, ObligationCause}; diff --git a/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs b/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs index 34d4ce9f9f2c2..e8ddff445133e 100644 --- a/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs +++ b/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs @@ -1,4 +1,4 @@ -use rustc_hir::find_attr; +use rustc_attr_ir::find_attr; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::{self, BasicBlock, Location}; use rustc_middle::ty::TyCtxt; diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index 9331c50b84b37..4d1a72808972b 100644 --- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -5,8 +5,8 @@ // FIXME(const_trait_impl): This API should be really reworked. It's dangerously general for // having basically only two use-cases that act in different ways. +use rustc_attr_ir::lang_items::LangItem; use rustc_errors::ErrorGuaranteed; -use rustc_hir::attrs::lang_items::LangItem; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir; use rustc_middle::mir::*; diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index f06ef0dbcf575..1cb6a8aed947d 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -30,6 +30,7 @@ pub enum ConstEvalErrKind { col: u32, file: Symbol, }, + Abort, WriteThroughImmutablePointer, /// Called `const_make_global` twice. ConstMakeGlobalPtrAlreadyMadeGlobal(AllocId), @@ -51,6 +52,7 @@ impl fmt::Display for ConstEvalErrKind { write!(f, "modifying a static's initial value from another static's initializer") } Panic { msg, .. } => write!(f, "evaluation panicked: {msg}"), + Abort => write!(f, "the program aborted execution"), RecursiveStatic => { write!(f, "encountered static that tried to access itself during initialization") } @@ -223,11 +225,11 @@ pub(super) fn report<'tcx>( ); mk(&mut err, span, frames); - let g = err.emit(); + let guar = err.emit_err(); let reported = if allowed_in_infallible { - ReportedErrorInfo::allowed_in_infallible(g) + ReportedErrorInfo::allowed_in_infallible(guar) } else { - ReportedErrorInfo::const_eval_error(g) + ReportedErrorInfo::const_eval_error(guar) }; ErrorHandled::Reported(reported, span) } @@ -243,7 +245,7 @@ pub(super) fn lint<'tcx, L>( lint: &'static rustc_lint_defs::Lint, decorator: impl FnOnce(Vec) -> L, ) where - L: for<'a> rustc_errors::Diagnostic<'a, ()>, + L: for<'a> rustc_errors::Diagnostic<'a>, { let (span, frames) = get_span_and_frames(tcx, &machine.stack); diff --git a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs index 8267cb66e9e15..4da9d79a8434a 100644 --- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs @@ -1,7 +1,8 @@ +use rustc_attr_ir::find_attr; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{ Constness, ExprKind, ForeignItemKind, ImplItem, ImplItemImplKind, ImplItemKind, Item, ItemKind, - Node, TraitItem, TraitItemKind, VariantData, find_attr, + Node, TraitItem, TraitItemKind, VariantData, }; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index d176ee1628cb5..1b99f54719fb4 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -4,17 +4,19 @@ use std::{fmt, mem}; use rustc_abi::{Align, FIRST_VARIANT, FieldIdx, Size, VariantIdx}; use rustc_ast::Mutability; +use rustc_attr_ir::find_attr; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, IndexEntry}; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::{self as hir, CRATE_HIR_ID, find_attr}; +use rustc_hir::{CRATE_HIR_ID, HirId}; use rustc_lint_defs::builtin::LONG_RUNNING_CONST_EVAL; -use rustc_middle::mir; use rustc_middle::mir::AssertMessage; use rustc_middle::mir::interpret::ReportedErrorInfo; use rustc_middle::query::TyCtxtAt; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, TyAndLayout, ValidityRequirement}; use rustc_middle::ty::{self, FieldInfo, ScalarInt, Ty, TyCtxt}; +use rustc_middle::{mir, throw_machine_stop}; use rustc_span::{Span, Symbol, bug, span_bug, sym}; use rustc_target::callconv::FnAbi; use tracing::debug; @@ -393,7 +395,7 @@ impl<'tcx> CompileTimeMachine<'tcx> { #[inline(always)] /// Find the first stack frame that is within the current crate, if any. /// Otherwise, return the crate's HirId - pub fn best_lint_scope(&self, tcx: TyCtxt<'tcx>) -> hir::HirId { + pub fn best_lint_scope(&self, tcx: TyCtxt<'tcx>) -> HirId { self.stack.iter().find_map(|frame| frame.lint_root(tcx)).unwrap_or(CRATE_HIR_ID) } } @@ -482,6 +484,10 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { // CTFE-specific intrinsics. match intrinsic_name { + sym::abort_immediate => { + // Note that `abort_immediate` is also hooked separately in Miri. + throw_machine_stop!(ConstEvalErrKind::Abort); + } sym::ptr_guaranteed_cmp => { let a = ecx.read_scalar(&args[0])?; let b = ecx.read_scalar(&args[1])?; @@ -602,7 +608,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { } } - sym::type_of => { + sym::type_id_type_of => { let ty = ecx.read_type_id(&args[0])?; ecx.write_type_info(ty, dest)?; } @@ -621,7 +627,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { ecx.write_scalar(Scalar::from_bool(is_mutable), dest)?; } - sym::size_of_type_id => { + sym::type_id_size_of => { let ty = ecx.read_type_id(&args[0])?; let layout = ecx.layout_of(ty)?; let variant_index = if layout.is_sized() { @@ -758,7 +764,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { ecx.write_scalar(Scalar::from_target_usize(variants_num as u64, ecx), dest)?; } - sym::variant_name => { + sym::type_id_variant_name => { let base = ecx.read_type_id(&args[0])?; let field_name = if let ty::Adt(def, _) = base.kind() { @@ -784,7 +790,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { )?; } - sym::variant_non_exhaustive => { + sym::type_id_variant_non_exhaustive => { let base = ecx.read_type_id(&args[0])?; let non_exhaustive = if let ty::Adt(def, _) = base.kind() { @@ -884,7 +890,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { ecx.write_type_id_generics(dest, ty)?; } - sym::non_exhaustive => { + sym::type_id_non_exhaustive => { let ty = ecx.read_type_id(&args[0])?; // FIXME(reflection): need a way to obtain non-exhaustiveness of a variant's fields. diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index 58b8418707eff..1a08c166770cf 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -2,6 +2,7 @@ use rustc_abi::{FieldIdx, VariantIdx}; use rustc_middle::mir; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::{DUMMY_SP, bug}; use tracing::instrument; diff --git a/compiler/rustc_const_eval/src/const_eval/type_info.rs b/compiler/rustc_const_eval/src/const_eval/type_info.rs index f0875952a5039..0df2e9b5fd0ae 100644 --- a/compiler/rustc_const_eval/src/const_eval/type_info.rs +++ b/compiler/rustc_const_eval/src/const_eval/type_info.rs @@ -3,7 +3,7 @@ mod adt; use std::borrow::Cow; use rustc_abi::{ExternAbi, FieldIdx}; -use rustc_hir::attrs::lang_items::LangItem; +use rustc_attr_ir::lang_items::LangItem; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{self, FnHeader, FnSigKind, FnSigTys, ScalarInt, Ty, TyCtxt}; use rustc_span::{Symbol, span_bug, sym}; @@ -91,9 +91,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { self.project_downcast_named(&field_dest, sym::Slice)?; variant } - ty::Adt(adt_def, generics) => { - self.write_adt_type_info(&field_dest, (ty, *adt_def), generics)? - } + ty::Adt(adt_def, _) => self.write_adt_type_info(&field_dest, *adt_def)?, ty::Bool => { let (variant, _variant_place) = self.project_downcast_named(&field_dest, sym::Bool)?; diff --git a/compiler/rustc_const_eval/src/const_eval/type_info/adt.rs b/compiler/rustc_const_eval/src/const_eval/type_info/adt.rs index e360eaba99aae..4ff1b37e953d4 100644 --- a/compiler/rustc_const_eval/src/const_eval/type_info/adt.rs +++ b/compiler/rustc_const_eval/src/const_eval/type_info/adt.rs @@ -1,13 +1,12 @@ use rustc_abi::{FieldIdx, VariantIdx}; -use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{ - AdtDef, AdtKind, Const, ConstKind, GenericArgKind, GenericArgs, Region, Ty, VariantDef, + AdtDef, AdtKind, Const, ConstKind, GenericArgKind, GenericArgs, Region, Ty, }; use rustc_span::{bug, span_bug, sym}; use crate::const_eval::CompileTimeMachine; use crate::interpret::{ - CtfeProvenance, InterpCx, InterpResult, MPlaceTy, Projectable, Scalar, Writeable, interp_ok, + CtfeProvenance, InterpCx, InterpResult, MPlaceTy, Projectable, Writeable, interp_ok, }; impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { @@ -15,190 +14,25 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { pub(crate) fn write_adt_type_info( &mut self, place: &impl Writeable<'tcx, CtfeProvenance>, - adt: (Ty<'tcx>, AdtDef<'tcx>), - generics: &'tcx GenericArgs<'tcx>, + adt_def: AdtDef<'tcx>, ) -> InterpResult<'tcx, VariantIdx> { - let (adt_ty, adt_def) = adt; let variant_idx = match adt_def.adt_kind() { AdtKind::Struct => { - let (variant, variant_place) = self.project_downcast_named(place, sym::Struct)?; - let place = self.project_field(&variant_place, FieldIdx::ZERO)?; - self.write_struct_type_info( - place, - (adt_ty, adt_def.variant(VariantIdx::ZERO)), - generics, - )?; + let (variant, _) = self.project_downcast_named(place, sym::Struct)?; variant } AdtKind::Union => { - let (variant, variant_place) = self.project_downcast_named(place, sym::Union)?; - let place = self.project_field(&variant_place, FieldIdx::ZERO)?; - self.write_union_type_info( - place, - (adt_ty, adt_def.variant(VariantIdx::ZERO)), - generics, - )?; + let (variant, _) = self.project_downcast_named(place, sym::Union)?; variant } AdtKind::Enum => { - let (variant, variant_place) = self.project_downcast_named(place, sym::Enum)?; - let place = self.project_field(&variant_place, FieldIdx::ZERO)?; - self.write_enum_type_info(place, adt, generics)?; + let (variant, _) = self.project_downcast_named(place, sym::Enum)?; variant } }; interp_ok(variant_idx) } - pub(crate) fn write_struct_type_info( - &mut self, - place: impl Writeable<'tcx, CtfeProvenance>, - struct_: (Ty<'tcx>, &'tcx VariantDef), - generics: &'tcx GenericArgs<'tcx>, - ) -> InterpResult<'tcx> { - let (struct_ty, struct_def) = struct_; - let struct_layout = self.layout_of(struct_ty)?; - - for (field_idx, field) in - place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated() - { - let field_place = self.project_field(&place, field_idx)?; - - match field.name { - sym::generics => self.write_generics(&field_place, generics)?, - sym::fields => { - self.write_variant_fields(field_place, struct_def, struct_layout, generics)? - } - sym::non_exhaustive => { - let is_non_exhaustive = struct_def.is_field_list_non_exhaustive(); - self.write_scalar(Scalar::from_bool(is_non_exhaustive), &field_place)? - } - other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"), - } - } - - interp_ok(()) - } - - pub(crate) fn write_union_type_info( - &mut self, - place: impl Writeable<'tcx, CtfeProvenance>, - union_: (Ty<'tcx>, &'tcx VariantDef), - generics: &'tcx GenericArgs<'tcx>, - ) -> InterpResult<'tcx> { - let (union_ty, union_def) = union_; - let union_layout = self.layout_of(union_ty)?; - - for (field_idx, field) in - place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated() - { - let field_place = self.project_field(&place, field_idx)?; - - match field.name { - sym::generics => self.write_generics(&field_place, generics)?, - sym::fields => { - self.write_variant_fields(field_place, union_def, union_layout, generics)? - } - sym::non_exhaustive => { - let is_non_exhaustive = union_def.is_field_list_non_exhaustive(); - self.write_scalar(Scalar::from_bool(is_non_exhaustive), &field_place)? - } - other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"), - } - } - - interp_ok(()) - } - - pub(crate) fn write_enum_type_info( - &mut self, - place: impl Writeable<'tcx, CtfeProvenance>, - enum_: (Ty<'tcx>, AdtDef<'tcx>), - generics: &'tcx GenericArgs<'tcx>, - ) -> InterpResult<'tcx> { - let (enum_ty, enum_def) = enum_; - let enum_layout = self.layout_of(enum_ty)?; - - for (field_idx, field) in - place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated() - { - let field_place = self.project_field(&place, field_idx)?; - - match field.name { - sym::generics => self.write_generics(&field_place, generics)?, - sym::variants => { - self.allocate_fill_and_write_slice_ptr( - &field_place, - enum_def.variants().len() as u64, - |this, i, place| { - let variant_idx = VariantIdx::from_usize(i as usize); - let variant_def = &enum_def.variants()[variant_idx]; - let variant_layout = enum_layout.for_variant(this, variant_idx); - this.write_enum_variant(place, (variant_layout, &variant_def), generics) - }, - )?; - } - sym::non_exhaustive => { - let is_non_exhaustive = enum_def.is_variant_list_non_exhaustive(); - self.write_scalar(Scalar::from_bool(is_non_exhaustive), &field_place)? - } - other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"), - } - } - - interp_ok(()) - } - - fn write_enum_variant( - &mut self, - place: impl Writeable<'tcx, CtfeProvenance>, - variant: (TyAndLayout<'tcx>, &'tcx VariantDef), - generics: &'tcx GenericArgs<'tcx>, - ) -> InterpResult<'tcx> { - let (variant_layout, variant_def) = variant; - - for (field_idx, field_def) in - place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated() - { - let field_place = self.project_field(&place, field_idx)?; - match field_def.name { - sym::name => { - let name_place = self.allocate_str_dedup(variant_def.name.as_str())?; - let ptr = self.mplace_to_imm_ptr(&name_place, None)?; - self.write_immediate(*ptr, &field_place)? - } - sym::fields => { - self.write_variant_fields(field_place, &variant_def, variant_layout, generics)? - } - sym::non_exhaustive => { - let is_non_exhaustive = variant_def.is_field_list_non_exhaustive(); - self.write_scalar(Scalar::from_bool(is_non_exhaustive), &field_place)? - } - other => span_bug!(self.tcx.def_span(field_def.did), "unimplemented field {other}"), - } - } - interp_ok(()) - } - - // Write fields for struct, enum variants - fn write_variant_fields( - &mut self, - place: impl Writeable<'tcx, CtfeProvenance>, - variant_def: &'tcx VariantDef, - variant_layout: TyAndLayout<'tcx>, - generics: &'tcx GenericArgs<'tcx>, - ) -> InterpResult<'tcx> { - self.allocate_fill_and_write_slice_ptr( - &place, - variant_def.fields.len() as u64, - |this, i, place| { - let field_def = &variant_def.fields[FieldIdx::from_usize(i as usize)]; - let field_ty = field_def.ty(*this.tcx, generics).skip_norm_wip(); - this.write_field(field_ty, place, variant_layout, Some(field_def.name), i) - }, - ) - } - pub(super) fn write_generics( &mut self, place: &impl Writeable<'tcx, CtfeProvenance>, diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index de8dd989c6a00..6cb327f1efca6 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -3,6 +3,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_middle::mir; use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId, ValTreeCreationError}; use rustc_middle::traits::ObligationCause; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::layout::{LayoutCx, TyAndLayout}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::{DUMMY_SP, bug}; diff --git a/compiler/rustc_const_eval/src/diagnostics.rs b/compiler/rustc_const_eval/src/diagnostics.rs index 2d8568f7f909b..995356fd3b488 100644 --- a/compiler/rustc_const_eval/src/diagnostics.rs +++ b/compiler/rustc_const_eval/src/diagnostics.rs @@ -317,7 +317,7 @@ pub(crate) struct FrameNote { } impl Subdiagnostic for FrameNote { - fn add_to_diag(self, diag: &mut Diag<'_, G>) { + fn add_to_diag(self, diag: &mut Diag<'_>) { let mut span: MultiSpan = self.span.into(); if self.has_label && !self.span.is_dummy() { span.push_span_label(self.span, msg!("the failure occurred here")); diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 3bf020009beed..c7615a216312e 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -6,8 +6,8 @@ use std::borrow::Cow; use either::{Left, Right}; use rustc_abi::{self as abi, ExternAbi, FieldIdx, Integer, VariantIdx}; +use rustc_attr_ir::find_attr; use rustc_hir::def_id::DefId; -use rustc_hir::find_attr; use rustc_middle::mir; use rustc_middle::ty::layout::{IntegerExt, TyAndLayout}; use rustc_middle::ty::{self, AdtDef, FieldDef, Instance, Ty, VariantDef}; diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 79a3887d5123d..1f16aab095843 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -6,6 +6,7 @@ use rustc_apfloat::{Float, FloatConvert}; use rustc_middle::mir::CastKind; use rustc_middle::mir::interpret::{InterpResult, PointerArithmetic, Scalar}; use rustc_middle::ty::adjustment::PointerCoercion; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::layout::{IntegerExt, TyAndLayout}; use rustc_middle::ty::{self, FloatTy, Ty}; use rustc_span::{bug, span_bug}; diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 99218a30e16b1..a74b323c395e6 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -285,12 +285,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { #[inline(always)] pub fn frame(&self) -> &Frame<'tcx, M::Provenance, M::FrameExtra> { - self.stack().last().expect("no call frames exist") + self.stack().last().expect("a call frame should exist") } #[inline(always)] pub fn frame_mut(&mut self) -> &mut Frame<'tcx, M::Provenance, M::FrameExtra> { - self.stack_mut().last_mut().expect("no call frames exist") + self.stack_mut().last_mut().expect("a call frame should exist") } #[inline(always)] diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/atomic.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/atomic.rs index 48218703c8ec1..6eee95eb2750d 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics/atomic.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics/atomic.rs @@ -1,4 +1,5 @@ use rustc_middle::mir::BinOp; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::{mir, ty}; use rustc_span::{Symbol, span_bug, sym}; use tracing::trace; @@ -126,7 +127,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { _ => return interp_ok(false), } - trace!("{:?}", self.dump_place(&dest.clone().into())); + trace!("{:?}", self.dump_place(&dest)); self.return_to_block(ret)?; interp_ok(true) } diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs index 476ba405f19ca..ad191e9b4d763 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs @@ -3,6 +3,7 @@ use rustc_abi::{BackendRepr, Endian}; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; use rustc_apfloat::{Float, Round}; use rustc_middle::mir::interpret::{InterpErrorKind, Pointer, UndefinedBehaviorInfo}; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::{FloatTy, ScalarInt, SimdAlign}; use rustc_middle::{err_ub_format, mir, throw_unsup_format, ty}; use rustc_span::{Symbol, bug, span_bug, sym}; diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 561c2b79343ff..e9f1d06187cd6 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -583,7 +583,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { with_caller_location, &dest_place, target, - if fn_abi.map_or(false, |fn_abi| fn_abi.can_unwind) { + if fn_abi.is_some_and(|fn_abi| fn_abi.can_unwind) { unwind } else { mir::UnwindAction::Unreachable diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index a853dd63d3435..981813f13c97f 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -1,5 +1,4 @@ // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(never_type))] #![feature(array_try_map)] #![feature(decl_macro)] #![feature(deref_patterns)] diff --git a/compiler/rustc_const_eval/src/util/caller_location.rs b/compiler/rustc_const_eval/src/util/caller_location.rs index 99220a23de4fc..c76da6c6a36c8 100644 --- a/compiler/rustc_const_eval/src/util/caller_location.rs +++ b/compiler/rustc_const_eval/src/util/caller_location.rs @@ -1,5 +1,5 @@ use rustc_abi::FieldIdx; -use rustc_hir::attrs::lang_items::LangItem; +use rustc_attr_ir::lang_items::LangItem; use rustc_middle::mir; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::{Symbol, bug}; diff --git a/compiler/rustc_crate_store/src/cstore.rs b/compiler/rustc_crate_store/src/cstore.rs index fba2fc288f5a9..c8fe5562bdd59 100644 --- a/compiler/rustc_crate_store/src/cstore.rs +++ b/compiler/rustc_crate_store/src/cstore.rs @@ -4,9 +4,11 @@ use std::any::Any; use std::path::PathBuf; +use std::sync::OnceLock; use rustc_abi::ExternAbi; use rustc_attr_ir::{CfgEntry, PeImportNameType}; +use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::{self, AppendOnlyIndexVec, FreezeLock}; use rustc_hir_id::definitions::{DefKey, DefPath, Definitions}; use rustc_macros::{BlobDecodable, Decodable, Encodable, StableHash}; @@ -220,6 +222,8 @@ pub struct Untracked { pub definitions: FreezeLock, /// The interned [StableCrateId]s. pub stable_crate_ids: FreezeLock, + /// The hash of the local crate as computed in metadata encoding. + pub local_crate_hash: OnceLock, } impl Untracked { diff --git a/compiler/rustc_data_structures/src/flock.rs b/compiler/rustc_data_structures/src/flock.rs index 9bd7d06c27f5e..deb779a4996dd 100644 --- a/compiler/rustc_data_structures/src/flock.rs +++ b/compiler/rustc_data_structures/src/flock.rs @@ -6,7 +6,8 @@ use std::fs::{File, OpenOptions}; use std::io; -use std::path::Path; +use std::ops::Deref; +use std::path::{Path, PathBuf}; #[derive(Debug)] pub enum Lock { @@ -68,3 +69,31 @@ cfg_select! { use unsupported as fallback; } } + +/// A directory together with a locked lockfile. +pub struct LockedDir { + dir: PathBuf, + /// `_lock_file` is never directly used, but its presence + /// alone has an effect, because the file will unlock when the session is + /// dropped. + _lock_file: Lock, +} + +impl LockedDir { + pub fn try_lock( + dir: PathBuf, + lock_file: &Path, + create: bool, + exclusive: bool, + ) -> io::Result { + Ok(LockedDir { dir, _lock_file: Lock::try_lock(lock_file, create, exclusive)? }) + } +} + +impl Deref for LockedDir { + type Target = Path; + + fn deref(&self) -> &Path { + &self.dir + } +} diff --git a/compiler/rustc_data_structures/src/fx.rs b/compiler/rustc_data_structures/src/fx.rs index cad775cc98641..76b2c428ec84d 100644 --- a/compiler/rustc_data_structures/src/fx.rs +++ b/compiler/rustc_data_structures/src/fx.rs @@ -28,7 +28,7 @@ macro_rules! define_stable_id_collections { } pub mod default { - use super::{FxBuildHasher, FxHashMap, FxHashSet}; + use super::{FxBuildHasher, FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; // FIXME: These two functions will become unnecessary after // lands and we start using the corresponding @@ -40,4 +40,12 @@ pub mod default { pub const fn fx_hash_set() -> FxHashSet { FxHashSet::with_hasher(FxBuildHasher) } + + pub const fn fx_index_map() -> FxIndexMap { + FxIndexMap::with_hasher(FxBuildHasher) + } + + pub const fn fx_index_set() -> FxIndexSet { + FxIndexSet::with_hasher(FxBuildHasher) + } } diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index ab30806bd2a59..aae378939452b 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -10,10 +10,10 @@ #![allow(internal_features)] #![allow(rustc::default_hash_types)] #![allow(rustc::potential_query_instability)] -#![cfg_attr(bootstrap, feature(never_type))] +#![cfg_attr(bootstrap, feature(unwrap_infallible))] #![cfg_attr(test, feature(test))] #![deny(unsafe_op_in_unsafe_fn)] -#![feature(allocator_api)] +#![feature(allocator_ext)] #![feature(ascii_char)] #![feature(ascii_char_variants)] #![feature(auto_traits)] @@ -34,7 +34,6 @@ #![feature(thread_id_value)] #![feature(trusted_len)] #![feature(type_alias_impl_trait)] -#![feature(unwrap_infallible)] // tidy-alphabetical-end // This allows derive macros to reference this crate diff --git a/compiler/rustc_data_structures/src/svh.rs b/compiler/rustc_data_structures/src/svh.rs index 67594f6dae79d..0b80c655d8a5a 100644 --- a/compiler/rustc_data_structures/src/svh.rs +++ b/compiler/rustc_data_structures/src/svh.rs @@ -32,6 +32,10 @@ impl Svh { pub fn to_hex(self) -> String { format!("{:032x}", self.hash.as_u128()) } + + pub fn as_fingerprint(self) -> Fingerprint { + self.hash + } } impl fmt::Display for Svh { diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index fce99655f54ab..1ba7ce953b5d4 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -5,7 +5,6 @@ //! This API is completely unstable and subject to change. // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(trim_prefix_suffix))] #![feature(decl_macro)] #![feature(file_buffered)] #![feature(panic_backtrace_config)] @@ -170,7 +169,7 @@ impl Callbacks for TimePassesCallbacks { } /// This is the primary entry point for rustc. -pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) { +pub fn compiler_entrypoint(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) { let mut default_early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default()); // Throw away the first argument, the name of the binary. @@ -185,14 +184,24 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) let args = args::arg_expand_all(&default_early_dcx, at_args); + // For the purposes of checking that an --edition was passed, we only emit a message if the only + // argument passed to rustc is a file name. + let warn_unspecified_edition = match &args[..] { + [] => false, + // We explicitly don't emit the note if we're consuming code from stdin, or passing non-file + // flags. This can happen on some cargo invocations too. + [name] if name.starts_with("-") => false, + [_] => true, + _ => false, + }; let (matches, help_only) = match handle_options(&default_early_dcx, &args) { HandledOptions::None => return, HandledOptions::Normal(matches) => (matches, false), HandledOptions::HelpOnly(matches) => (matches, true), }; - let sopts = config::build_session_options(&mut default_early_dcx, &matches); - // fully initialize ice path static once unstable options are available as context + let sopts = + config::build_session_options(&mut default_early_dcx, &matches, warn_unspecified_edition); let ice_file = ice_path_with_config(Some(&sopts.unstable_opts)).clone(); if let Some(ref code) = matches.opt_str("explain") { @@ -315,10 +324,6 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) tcx.ensure_ok().analysis(()); - if let Some(metrics_dir) = &sess.opts.unstable_opts.metrics_dir { - dump_feature_usage_metrics(tcx, metrics_dir); - } - if callbacks.after_analysis(compiler, tcx) == Compilation::Stop { return None; } @@ -331,6 +336,10 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) let linker = Linker::codegen_and_build_linker(tcx, codegen_backend); + if let Some(metrics_dir) = &sess.opts.unstable_opts.metrics_dir { + dump_feature_usage_metrics(tcx, metrics_dir); + } + tcx.report_unused_features(); Some(linker) @@ -1387,7 +1396,7 @@ static ICE_PATH: OnceLock> = OnceLock::new(); // This function should only be called from the ICE hook. // -// The intended behavior is that `run_compiler` will invoke `ice_path_with_config` early in the +// The intended behavior is that `compiler_entrypoint` will invoke `ice_path_with_config` early in the // initialization process to properly initialize the ICE_PATH static based on parsed CLI flags. // // Subsequent calls to either function will then return the proper ICE path as configured by @@ -1675,7 +1684,7 @@ pub fn main() -> ExitCode { install_ctrlc_handler(); let exit_code = - catch_with_exit_code(|| run_compiler(&args::raw_args(&early_dcx), &mut callbacks)); + catch_with_exit_code(|| compiler_entrypoint(&args::raw_args(&early_dcx), &mut callbacks)); if let Some(format) = callbacks.time_passes { let end_rss = get_resident_set_size(); diff --git a/compiler/rustc_error_codes/src/error_codes/E0091.md b/compiler/rustc_error_codes/src/error_codes/E0091.md index 3bf4e907ecb1f..5fbc4b7a63e25 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0091.md +++ b/compiler/rustc_error_codes/src/error_codes/E0091.md @@ -4,7 +4,9 @@ Erroneous code example: ```compile_fail,E0091 type Foo = u32; // error: type parameter `T` is never used -// or: +``` +or: +```compile_fail,E0091 type Foo = Box; // error: type parameter `B` is never used ``` @@ -12,5 +14,7 @@ Please check you didn't write too many parameters. Example: ``` type Foo = u32; // ok! -type Foo2 = Box; // ok! +``` +``` +type Foo = Box; // ok! ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0634.md b/compiler/rustc_error_codes/src/error_codes/E0634.md index 0c4ed2596e2aa..6b6563a2e8128 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0634.md +++ b/compiler/rustc_error_codes/src/error_codes/E0634.md @@ -5,7 +5,8 @@ Erroneous code examples: ```compile_fail,E0634 #[repr(packed, packed(2))] // error! struct Company(i32); - +``` +```compile_fail,E0634 #[repr(packed(2))] // error! #[repr(packed)] struct Company(i32); diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index bedcfe998b638..881f11ff17b0a 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -101,7 +101,7 @@ impl MultiSpan { } pub fn from_spans(mut vec: Vec) -> MultiSpan { - vec.sort(); + vec.sort_by_key(|span| span.lo_hi()); MultiSpan { primary_spans: vec, span_labels: vec![], span_context: vec![] } } @@ -216,6 +216,12 @@ impl From for MultiSpan { } } +impl From> for MultiSpan { + fn from(span: Option) -> MultiSpan { + MultiSpan::from_spans(span.into_iter().collect()) + } +} + impl From> for MultiSpan { fn from(spans: Vec) -> MultiSpan { MultiSpan::from_spans(spans) diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index d7e38148806f7..15a435357544d 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -124,12 +124,10 @@ fn annotation_level_for_level(level: Level) -> annotate_snippets::level::Level<' annotate_snippets::Level::ERROR.with_name("error: internal compiler error") } Level::Fatal | Level::Error => annotate_snippets::level::ERROR, - Level::ForceWarning | Level::Warning => annotate_snippets::Level::WARNING, + Level::Warning(_) => annotate_snippets::Level::WARNING, Level::Note => annotate_snippets::Level::NOTE, Level::Help => annotate_snippets::Level::HELP, Level::FailureNote => annotate_snippets::Level::NOTE.no_name(), - Level::Allow => panic!("Should not call with Allow"), - Level::Expect => panic!("Should not call with Expect"), } } diff --git a/compiler/rustc_errors/src/decorate_diag.rs b/compiler/rustc_errors/src/decorate_diag.rs index 3a222fd8a10cf..382a3b2758407 100644 --- a/compiler/rustc_errors/src/decorate_diag.rs +++ b/compiler/rustc_errors/src/decorate_diag.rs @@ -13,7 +13,7 @@ pub struct DecorateDiagCompat( /// The third argument of the closure is a `Session`. However, due to the dependency tree, /// we don't have access to `rustc_session` here, so we downcast it when needed. pub Box< - dyn for<'a> FnOnce(DiagCtxtHandle<'a>, Level, &dyn Any) -> Diag<'a, ()> + dyn for<'a> FnOnce(DiagCtxtHandle<'a>, Level, &dyn Any) -> Diag<'a> + DynSync + DynSend + 'static, @@ -26,7 +26,7 @@ impl std::fmt::Debug for DecorateDiagCompat { } } -impl Diagnostic<'a, ()> + DynSync + DynSend + 'static> From for DecorateDiagCompat { +impl Diagnostic<'a> + DynSync + DynSend + 'static> From for DecorateDiagCompat { #[inline] fn from(d: D) -> Self { Self(Box::new(|dcx, level, _| d.into_diag(dcx, level))) @@ -82,7 +82,7 @@ impl LintBuffer { } pub fn dyn_buffer_lint< - F: for<'a> FnOnce(DiagCtxtHandle<'a>, Level) -> Diag<'a, ()> + DynSync + DynSend + 'static, + F: for<'a> FnOnce(DiagCtxtHandle<'a>, Level) -> Diag<'a> + DynSync + DynSend + 'static, >( &mut self, lint: &'static Lint, @@ -99,7 +99,7 @@ impl LintBuffer { } pub fn dyn_buffer_lint_any< - F: for<'a> FnOnce(DiagCtxtHandle<'a>, Level, &dyn Any) -> Diag<'a, ()> + F: for<'a> FnOnce(DiagCtxtHandle<'a>, Level, &dyn Any) -> Diag<'a> + DynSend + DynSync + 'static, diff --git a/compiler/rustc_errors/src/dedup_hash.rs b/compiler/rustc_errors/src/dedup_hash.rs new file mode 100644 index 0000000000000..b8b2da69bc1d3 --- /dev/null +++ b/compiler/rustc_errors/src/dedup_hash.rs @@ -0,0 +1,63 @@ +use std::hash::Hash; + +use rustc_data_structures::stable_hash::StableHasher; +use rustc_serialize::Encoder; +use rustc_span::def_id::{CrateNum, DefId, DefIndex}; +use rustc_span::{ByteSymbol, ExpnId, Span, SpanEncoder, Symbol, SyntaxContext}; + +/// Hasher used to determine if two diagnostics are the same and should be deduplicated. Used by +/// `DiagCtxtInner::emitted_diagnostics`. It's a stable hash with one special behaviour: parents of +/// spans within the diagnostics are ignored so that incremental and non-incremental compilation +/// get the same behaviour. +/// +/// Although this has nothing to do with encoding data to/from file, it is implemented on top of +/// `Encoder`/`SpanEncoder` because they provide traversals of all the relevant types used within +/// diagnostics. There is no corresponding decoder. +pub(crate) struct DedupHashEncoder(pub(crate) StableHasher); + +macro_rules! encoder_methods { + ($($name:ident($ty:ty);)*) => { + $( + #[inline] + fn $name(&mut self, value: $ty) { + value.hash(&mut self.0) + } + )* + } +} + +impl Encoder for DedupHashEncoder { + encoder_methods! { + emit_usize(usize); + emit_u128(u128); + emit_u64(u64); + emit_u32(u32); + emit_u16(u16); + emit_u8(u8); + + emit_isize(isize); + emit_i128(i128); + emit_i64(i64); + emit_i32(i32); + emit_i16(i16); + + emit_raw_bytes(&[u8]); + } +} + +impl SpanEncoder for DedupHashEncoder { + fn encode_span(&mut self, span: Span) { + // The raison d'être of `DedupHashEncoder` is to ignore `parent` here. + span.with_parent(None).hash(&mut self.0); + } + + encoder_methods! { + encode_symbol(Symbol); + encode_byte_symbol(ByteSymbol); + encode_expn_id(ExpnId); + encode_syntax_context(SyntaxContext); + encode_crate_num(CrateNum); + encode_def_index(DefIndex); + encode_def_id(DefId); + } +} diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 987c9818bf0e5..65dc8042fd409 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -1,77 +1,51 @@ use std::borrow::Cow; use std::fmt::{self, Debug}; -use std::hash::{Hash, Hasher}; -use std::marker::PhantomData; +use std::hash::Hash; use std::ops::{Deref, DerefMut}; use std::panic; use std::path::PathBuf; use std::thread::panicking; use rustc_ast::attr::version::RustcVersion; -use rustc_error_messages::{DiagArgMap, DiagArgName, DiagArgValue, IntoDiagArg}; -use rustc_lint_defs::{Applicability, LintExpectationId}; +use rustc_data_structures::stable_hash::StableHasher; +use rustc_error_messages::{DiagArgMap, DiagArgName, IntoDiagArg}; +use rustc_hashes::Hash128; +use rustc_lint_defs::Applicability; use rustc_macros::{Decodable, Encodable}; -use rustc_span::{DUMMY_SP, Span, Spanned, Symbol}; +use rustc_serialize::Encodable; +use rustc_span::{Span, Spanned, Symbol}; use tracing::debug; +use crate::dedup_hash::DedupHashEncoder; use crate::{ CodeSuggestion, DiagCtxtHandle, DiagMessage, ErrCode, ErrorGuaranteed, ExplicitBug, Level, MultiSpan, StashKey, Style, Sublevel, Substitution, SubstitutionPart, SuggestionStyle, Suggestions, }; -/// Marker type which enables implementation of `create_bug` and `emit_bug` functions for -/// bug diagnostics. -#[derive(Copy, Clone)] -pub struct BugAbort; - -/// Marker type which enables implementation of `create_fatal` and `emit_fatal` functions for -/// fatal diagnostics. -#[derive(Copy, Clone)] -pub struct FatalAbort; - /// Trait implemented by error types. This is rarely implemented manually. Instead, use /// `#[derive(Diagnostic)]` -- see [rustc_macros::Diagnostic]. -/// -/// When implemented manually, it should be generic over the emission -/// guarantee, i.e.: -/// ```ignore (fragment) -/// impl<'a, G> Diagnostic<'a, G> for Foo { ... } -/// ``` -/// rather than being specific: -/// ```ignore (fragment) -/// impl<'a> Diagnostic<'a> for Bar { ... } // the default type param is `ErrorGuaranteed` -/// impl<'a> Diagnostic<'a, ()> for Baz { ... } -/// ``` -/// There are two reasons for this. -/// - A diagnostic like `Foo` *could* be emitted at any level -- `level` is -/// passed in to `into_diag` from outside. Even if in practice it is -/// always emitted at a single level, we let the diagnostic creation/emission -/// site determine the level (by using `create_err`, `emit_warn`, etc.) -/// rather than the `Diagnostic` impl. -/// - Derived impls are always generic, and it's good for the hand-written -/// impls to be consistent with them. -pub trait Diagnostic<'a, G = ErrorGuaranteed> { +pub trait Diagnostic<'a> { /// Write out as a diagnostic out of `DiagCtxt`. #[must_use] #[track_caller] - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G>; + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a>; } -impl<'a, T, G> Diagnostic<'a, G> for Spanned +impl<'a, T> Diagnostic<'a> for Spanned where - T: Diagnostic<'a, G>, + T: Diagnostic<'a>, { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { self.node.into_diag(dcx, level).with_span(self.span) } } /// Type used to emit diagnostic through a closure instead of implementing the `Diagnostic` trait. -pub struct DiagDecorator)>(pub F); +pub struct DiagDecorator)>(pub F); -impl<'a, F: FnOnce(&mut Diag<'_, ()>)> Diagnostic<'a, ()> for DiagDecorator { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { +impl<'a, F: FnOnce(&mut Diag<'_>)> Diagnostic<'a> for DiagDecorator { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let mut diag = Diag::new(dcx, level, ""); (self.0)(&mut diag); diag @@ -82,7 +56,7 @@ impl<'a, F: FnOnce(&mut Diag<'_, ()>)> Diagnostic<'a, ()> for DiagDecorator { /// `#[derive(Subdiagnostic)]` -- see [rustc_macros::Subdiagnostic]. pub trait Subdiagnostic { /// Add a subdiagnostic to an existing diagnostic. - fn add_to_diag(self, diag: &mut Diag<'_, G>); + fn add_to_diag(self, diag: &mut Diag<'_>); } #[derive(Clone, Debug, Encodable, Decodable)] @@ -93,10 +67,13 @@ pub struct DiagLocation { } impl DiagLocation { + pub fn from_location(loc: &'static panic::Location<'static>) -> Self { + DiagLocation { file: loc.file().into(), line: loc.line(), col: loc.column() } + } + #[track_caller] pub fn caller() -> Self { - let loc = panic::Location::caller(); - DiagLocation { file: loc.file().into(), line: loc.line(), col: loc.column() } + Self::from_location(panic::Location::caller()) } } @@ -215,19 +192,11 @@ pub struct DiagInner { pub messages: Vec<(DiagMessage, Style)>, pub code: Option, - pub lint_id: Option, pub span: MultiSpan, pub children: Vec, pub suggestions: Suggestions, pub args: DiagArgMap, - - /// This is not used for highlighting or rendering any error message. Rather, it can be used - /// as a sort key to sort a buffer of diagnostics. By default, it is the primary span of - /// `span` if there is one. Otherwise, it is `DUMMY_SP`. - pub sort_span: Span, - pub is_lint: Option, - pub long_ty_path: Option, /// With `-Ztrack_diagnostics` enabled, /// we print where in rustc this error was emitted. @@ -244,14 +213,12 @@ impl DiagInner { pub fn new_with_messages(level: Level, messages: Vec<(DiagMessage, Style)>) -> Self { DiagInner { level, - lint_id: None, messages, code: None, span: MultiSpan::new(), children: vec![], suggestions: Suggestions::Enabled(vec![]), args: Default::default(), - sort_span: DUMMY_SP, is_lint: None, long_ty_path: None, emitted_at: DiagLocation::caller(), @@ -267,13 +234,7 @@ impl DiagInner { match self.level { Level::Bug | Level::Fatal | Level::Error | Level::DelayedBug => true, - Level::ForceWarning - | Level::Warning - | Level::Note - | Level::Help - | Level::FailureNote - | Level::Allow - | Level::Expect => false, + Level::Warning(_) | Level::Note | Level::Help | Level::FailureNote => false, } } @@ -287,16 +248,6 @@ impl DiagInner { self.is_lint.as_ref().and_then(|is| is.rust_version) } - pub(crate) fn is_force_warn(&self) -> bool { - match self.level { - Level::ForceWarning => { - assert!(self.is_lint.is_some()); - true - } - _ => false, - } - } - pub(crate) fn sub( &mut self, level: Sublevel, @@ -332,46 +283,39 @@ impl DiagInner { } } - /// Fields used for Hash, and PartialEq trait. - fn keys( - &self, - ) -> ( - &Level, - &[(DiagMessage, Style)], - &Option, - &MultiSpan, - &[Subdiag], - &Suggestions, - Vec<(&DiagArgName, &DiagArgValue)>, - &Option, - ) { - ( - &self.level, - &self.messages, - &self.code, - &self.span, - &self.children, - &self.suggestions, - self.args.iter().collect(), - // omit self.sort_span - &self.is_lint, - // omit self.emitted_at - ) - } -} + /// Hash used to determine if two diagnostics are the same. Used by + /// `DiagCtxtInner::emitted_diagnostics`. Some fields are ignored for the hash. + pub(crate) fn dedup_hash(&self) -> Hash128 { + // Deconstruct to ensure all fields are considered. + let DiagInner { + level, + messages, + code, + span, + children, + suggestions, + args, + is_lint, + long_ty_path: _, // ignore + emitted_at: _, // ignore + } = self; -impl Hash for DiagInner { - fn hash(&self, state: &mut H) - where - H: Hasher, - { - self.keys().hash(state); - } -} + let mut e = DedupHashEncoder(StableHasher::new()); + + // We use `discriminant` because we want to ignore the field within `Warning`. + // `Encoder`/`SpanEncoder` can't traverse a `Discriminant` so we hash it directly. + std::mem::discriminant(level).hash(&mut e.0); + + // All the other fields are hashed via `encode`. + messages.encode(&mut e); + code.encode(&mut e); + span.encode(&mut e); + children.encode(&mut e); + suggestions.encode(&mut e); + args.encode(&mut e); + is_lint.encode(&mut e); -impl PartialEq for DiagInner { - fn eq(&self, other: &Self) -> bool { - self.keys() == other.keys() + e.0.finish() } } @@ -384,20 +328,30 @@ pub struct Subdiag { pub span: MultiSpan, } +impl Subdiag { + /// Hash used to determine if two subdiagnostics are the same. Used by + /// `DiagCtxtInner::emitted_diagnostics`. + pub(crate) fn dedup_hash(&self) -> Hash128 { + let mut e = DedupHashEncoder(StableHasher::new()); + self.encode(&mut e); + e.0.finish() + } +} + /// Used for emitting structured error messages and other diagnostic information. /// Wraps a `DiagInner`, adding some useful things. /// - The `dcx` field, allowing it to (a) emit itself, and (b) do a drop check /// that it has been emitted or cancelled. -/// - `G`, which determines the type returned from `emit`. /// -/// Each constructed `Diag` must be consumed by a function such as `emit`, -/// `cancel`, or `delay_as_bug`. A panic occurs if a `Diag` is dropped without -/// being consumed by one of these functions. +/// Each constructed `Diag` must be consumed by a function such as +/// `emit_bug`/`emit_fatal`/`emit_err`/`emit`, `cancel`, or `delay_as_bug`. A +/// panic occurs if a `Diag` is dropped without being consumed by one of these +/// functions. /// /// If there is some state in a downstream crate you would like to access in /// the methods of `Diag` here, consider extending `DiagCtxtFlags`. #[must_use] -pub struct Diag<'a, G = ErrorGuaranteed> { +pub struct Diag<'a> { pub dcx: DiagCtxtHandle<'a>, /// Why the `Option`? It is always `Some` until the `Diag` is consumed via @@ -410,17 +364,15 @@ pub struct Diag<'a, G = ErrorGuaranteed> { /// theory, return value optimization (RVO) should avoid unnecessary /// copying. In practice, it does not (at the time of writing). diag: Option>, - - _marker: PhantomData, } // Cloning a `Diag` is a recipe for a diagnostic being emitted twice, which // would be bad. -impl !Clone for Diag<'_, G> {} +impl !Clone for Diag<'_> {} -rustc_data_structures::static_assert_size!(Diag<'_, ()>, 3 * size_of::()); +rustc_data_structures::static_assert_size!(Diag<'_>, 3 * size_of::()); -impl Deref for Diag<'_, G> { +impl Deref for Diag<'_> { type Target = DiagInner; fn deref(&self) -> &DiagInner { @@ -428,80 +380,18 @@ impl Deref for Diag<'_, G> { } } -impl DerefMut for Diag<'_, G> { +impl DerefMut for Diag<'_> { fn deref_mut(&mut self) -> &mut DiagInner { self.diag.as_mut().unwrap() } } -impl Debug for Diag<'_, G> { +impl Debug for Diag<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.diag.fmt(f) } } -impl Diag<'_, BugAbort> { - #[track_caller] - pub fn emit(self) -> ! { - assert_eq!(self.level, Level::Bug); - self.emit_producing_nothing(); - panic::panic_any(ExplicitBug); - } -} - -impl Diag<'_, FatalAbort> { - #[track_caller] - pub fn emit(self) -> ! { - assert_eq!(self.level, Level::Fatal); - self.emit_producing_nothing(); - crate::FatalError.raise() - } -} - -impl Diag<'_, ErrorGuaranteed> { - #[track_caller] - pub fn emit(self) -> ErrorGuaranteed { - self.emit_producing_error_guaranteed() - } - - /// Emit the diagnostic unless `delay` is true, - /// in which case the emission will be delayed as a bug. - /// - /// See `emit` and `delay_as_bug` for details. - #[track_caller] - pub fn emit_unless_delay(mut self, delay: bool) -> ErrorGuaranteed { - if delay { - self.downgrade_to_delayed_bug(); - } - self.emit() - } - - /// Delay emission of this diagnostic as a bug. - /// - /// This can be useful in contexts where an error indicates a bug but - /// typically this only happens when other compilation errors have already - /// happened. In those cases this can be used to defer emission of this - /// diagnostic as a bug in the compiler only if no other errors have been - /// emitted. - /// - /// In the meantime, though, callsites are required to deal with the "bug" - /// locally in whichever way makes the most sense. - #[track_caller] - pub fn delay_as_bug(mut self) -> ErrorGuaranteed { - self.downgrade_to_delayed_bug(); - self.emit() - } -} - -impl Diag<'_, ()> { - #[track_caller] - pub fn emit(self) { - assert_ne!(self.level, Level::Bug); - assert_ne!(self.level, Level::Fatal); - self.emit_producing_nothing(); - } -} - /// `Diag` impls many `&mut self -> &mut Self` methods. Each one modifies an /// existing diagnostic, either in a standalone fashion, e.g. /// `err.code(code);`, or in a chained fashion to make multiple modifications, @@ -545,22 +435,22 @@ macro_rules! with_fn { }; } -impl<'a, G> Diag<'a, G> { +impl<'a> Diag<'a> { #[track_caller] pub fn new(dcx: DiagCtxtHandle<'a>, level: Level, message: impl Into) -> Self { Self::new_diagnostic(dcx, DiagInner::new(level, message)) } /// Allow moving diagnostics between different error tainting contexts - pub fn with_dcx(mut self, dcx: DiagCtxtHandle<'_>) -> Diag<'_, G> { - Diag { dcx, diag: self.diag.take(), _marker: PhantomData } + pub fn with_dcx(mut self, dcx: DiagCtxtHandle<'_>) -> Diag<'_> { + Diag { dcx, diag: self.diag.take() } } /// Creates a new `Diag` with an already constructed diagnostic. #[track_caller] pub(crate) fn new_diagnostic(dcx: DiagCtxtHandle<'a>, diag: DiagInner) -> Self { debug!("Created new diagnostic"); - Self { dcx, diag: Some(Box::new(diag)), _marker: PhantomData } + Self { dcx, diag: Some(Box::new(diag)) } } /// Delay emission of this diagnostic as a bug. @@ -585,7 +475,7 @@ impl<'a, G> Diag<'a, G> { /// Make emitting this diagnostic fatal. #[track_caller] - pub fn upgrade_to_fatal(mut self) -> Diag<'a, FatalAbort> { + pub fn upgrade_to_fatal(mut self) -> Diag<'a> { assert!( matches!(self.level, Level::Error), "upgrade_to_fatal: cannot upgrade {:?} to Fatal: not an error", @@ -596,7 +486,7 @@ impl<'a, G> Diag<'a, G> { // Take is okay since we immediately rewrap it in another diagnostic. // i.e. we do emit it despite defusing the original diagnostic's drop bomb. let diag = self.diag.take(); - Diag { dcx: self.dcx, diag, _marker: PhantomData } + Diag { dcx: self.dcx, diag } } with_fn! { with_span_label, @@ -1085,7 +975,7 @@ impl<'a, G> Diag<'a, G> { .map(|(span, snippet)| SubstitutionPart { snippet, span }) .collect::>(); - parts.sort_unstable_by_key(|part| part.span); + parts.sort_unstable_by_key(|part| part.span.lo_hi()); assert!(!parts.is_empty()); debug_assert_eq!( @@ -1192,9 +1082,6 @@ impl<'a, G> Diag<'a, G> { /// Add a span. pub fn span(&mut self, sp: impl Into) -> &mut Self { self.span = sp.into(); - if let Some(span) = self.span.primary_span() { - self.sort_span = span; - } self } } @@ -1215,16 +1102,6 @@ impl<'a, G> Diag<'a, G> { self } } - with_fn! { with_lint_id, - /// Add an argument. - pub fn lint_id( - &mut self, - id: LintExpectationId, - ) -> &mut Self { - self.lint_id = Some(id); - self - } } - with_fn! { with_primary_message, /// Add a primary message. pub fn primary_message(&mut self, msg: impl Into) -> &mut Self { @@ -1299,21 +1176,45 @@ impl<'a, G> Diag<'a, G> { self } - /// Most `emit` methods use this as a starting point. - pub fn emit_producing_nothing(mut self) { + /// Emit the diagnostic. Will also abort appropriately if the level is `Bug` or `Fatal`. + #[track_caller] + pub fn emit(mut self) { + let level = self.level; // get level before taking the inner diag let diag = self.take_diag(); self.dcx.emit_diagnostic(diag); + + match level { + Level::Bug => panic::panic_any(ExplicitBug), + Level::Fatal => crate::FatalError.raise(), + _ => {} + } } - /// `Diag<'_, ErrorGuaranteed>::emit` uses this. - fn emit_producing_error_guaranteed(mut self) -> ErrorGuaranteed { + /// Use this on a `Bug` diagnostic if you need the `!` return type. Otherwise `emit` suffices. + /// Aborts if used on a non-`Bug` diagnostic. + #[track_caller] + pub fn emit_bug(self) -> ! { + assert_eq!(self.level, Level::Bug); + self.emit(); + unreachable!(); // `emit` will have aborted + } + + /// Use this on a `Fatal` diagnostic if you need the `!` return type. Otherwise `emit` + /// suffices. Aborts if used on a non-`Fatal` diagnostic. + #[track_caller] + pub fn emit_fatal(self) -> ! { + assert_eq!(self.level, Level::Fatal); + self.emit(); + unreachable!(); // `emit` will have aborted + } + + /// Use this on an `Error`/`DelayedBug` diagnostic if you need the `ErrorGuaranteed` return + /// type. Otherwise `emit` suffices. Aborts if used on a non-`Error`/`DelayedBug` diagnostic. + #[track_caller] + pub fn emit_err(mut self) -> ErrorGuaranteed { let diag = self.take_diag(); - // The only error levels that produce `ErrorGuaranteed` are - // `Error` and `DelayedBug`. But `DelayedBug` should never occur here - // because delayed bugs have their level changed to `Bug` when they are - // actually printed, so they produce an ICE. - // + // The only error levels that should reach here are `Error` and `DelayedBug`. // (Also, even though `level` isn't `pub`, the whole `DiagInner` could // be overwritten with a new one thanks to `DerefMut`. So this assert // protects against that, too.) @@ -1327,6 +1228,18 @@ impl<'a, G> Diag<'a, G> { guar.unwrap() } + /// Emit the diagnostic unless `delay` is true, + /// in which case the emission will be delayed as a bug. + /// + /// See `emit` and `delay_as_bug` for details. + #[track_caller] + pub fn emit_err_unless_delay(mut self, delay: bool) -> ErrorGuaranteed { + if delay { + self.downgrade_to_delayed_bug(); + } + self.emit_err() + } + /// Cancel and consume the diagnostic. (A diagnostic must either be emitted or /// cancelled or it will panic when dropped). pub fn cancel(mut self) { @@ -1346,11 +1259,27 @@ impl<'a, G> Diag<'a, G> { let diag = self.take_diag(); self.dcx.stash_diagnostic(span, key, diag) } + + /// Delay emission of this diagnostic as a bug. + /// + /// This can be useful in contexts where an error indicates a bug but + /// typically this only happens when other compilation errors have already + /// happened. In those cases this can be used to defer emission of this + /// diagnostic as a bug in the compiler only if no other errors have been + /// emitted. + /// + /// In the meantime, though, callsites are required to deal with the "bug" + /// locally in whichever way makes the most sense. + #[track_caller] + pub fn delay_as_bug(mut self) -> ErrorGuaranteed { + self.downgrade_to_delayed_bug(); + self.emit_err() + } } /// Destructor bomb: every `Diag` must be consumed (emitted, cancelled, etc.) /// or we emit a bug. -impl Drop for Diag<'_, G> { +impl Drop for Diag<'_> { fn drop(&mut self) { match self.diag.take() { Some(diag) if !panicking() => { diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index b002b8932a239..fa9e3544f12ae 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -42,7 +42,7 @@ pub struct SingleLabelManySpans { pub label: &'static str, } impl Subdiagnostic for SingleLabelManySpans { - fn add_to_diag(self, diag: &mut Diag<'_, G>) { + fn add_to_diag(self, diag: &mut Diag<'_>) { diag.span_labels(self.spans, self.label); } } diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 749b58e5d4b82..aee5afdfd3b04 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -166,8 +166,8 @@ pub trait Emitter { let has_macro_spans: Vec<_> = iter::once(&*span) .chain(children.iter().map(|child| &child.span)) .flat_map(|span| span.primary_spans()) - .flat_map(|sp| sp.macro_backtrace()) - .filter_map(|expn_data| { + .flat_map(|sp| sp.macro_backtrace().map(move |expn_data| (sp, expn_data))) + .filter_map(|(sp, expn_data)| { match expn_data.kind { ExpnKind::Root => None, @@ -176,7 +176,16 @@ pub trait Emitter { ExpnKind::Desugaring(..) | ExpnKind::AstPass(..) => None, ExpnKind::Macro(macro_kind, name) => { - Some((macro_kind, name, expn_data.diagnostic_opaque)) + let same_line = sp + .overlaps(expn_data.def_site) + .then(|| expn_data.def_site.with_hi(sp.lo())); + Some(( + macro_kind, + name, + expn_data.diagnostic_opaque, + expn_data.def_site, + same_line, + )) } } }) @@ -188,27 +197,47 @@ pub trait Emitter { self.render_multispans_macro_backtrace(span, children, backtrace); - if !backtrace { + if !backtrace // Skip macros annotated with `#[diagnostic::opaque]`. Builtin macros are "opaque" too. - if let Some((macro_kind, name, _)) = has_macro_spans.first() - && let Some((_, _, false)) = has_macro_spans.last() + && let Some((macro_kind, name, _, sp, same_line_span)) = has_macro_spans.first() + && let Some((_, _, false, _, _)) = has_macro_spans.last() + { + // Mark the actual macro this originates from + let and_then = if let Some((macro_kind, last_name, _, _, _)) = has_macro_spans.last() + && last_name != name { - // Mark the actual macro this originates from - let and_then = if let Some((macro_kind, last_name, _)) = has_macro_spans.last() - && last_name != name + let descr = macro_kind.descr(); + format!(" which comes from the expansion of the {descr} `{last_name}`") + } else { + "".to_string() + }; + + let descr = macro_kind.descr(); + let msg = format!("this {level} originates in the {descr} `{name}`{and_then}"); + + if let Some(source_map) = self.source_map() { + if source_map.is_imported(*sp) || same_line_span.is_none() { + let msg = format!( + "{msg} (in Nightly builds, run with -Z macro-backtrace for more info)" + ); + children.push(Subdiag { + level: Sublevel::Note, + messages: vec![(DiagMessage::from(msg), Style::NoStyle)], + span: MultiSpan::new(), + }); + } else if let Some(def) = same_line_span + && source_map.is_multiline(*def) + && !and_then.is_empty() { - let descr = macro_kind.descr(); - format!(" which comes from the expansion of the {descr} `{last_name}`") + // We only point at the macro definition when it is not on the same line as the + // error produced and there are more levels of macro nesting. + span.push_span_label(*sp, msg); } else { - "".to_string() - }; - - let descr = macro_kind.descr(); - let msg = format!( - "this {level} originates in the {descr} `{name}`{and_then} \ - (in Nightly builds, run with -Z macro-backtrace for more info)", - ); - + // We point at the macro def line, but without an underline or message, we + // leave it implied. + span.push_span_context(sp.shrink_to_lo()); + } + } else { children.push(Subdiag { level: Sublevel::Note, messages: vec![(DiagMessage::from(msg), Style::NoStyle)], diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index 1f5a8c2fe94a6..59d79754314c1 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -136,20 +136,8 @@ impl Emitter for JsonEmitter { fn emit_future_breakage_report(&mut self, diags: Vec) { let data: Vec> = diags .into_iter() - .map(|mut diag| { - // Allowed or expected lints don't normally (by definition) emit a lint - // but future incompat lints are special and are emitted anyway. - // - // So to avoid ICEs and confused users we "upgrade" the lint level for - // those `FutureBreakageItem` to warn. - if matches!(diag.level, crate::Level::Allow | crate::Level::Expect) { - diag.level = crate::Level::Warning; - } - FutureBreakageItem { - diagnostic: EmitTyped::Diagnostic(Diagnostic::from_errors_diagnostic( - diag, self, - )), - } + .map(|diag| FutureBreakageItem { + diagnostic: EmitTyped::Diagnostic(Diagnostic::from_errors_diagnostic(diag, self)), }) .collect(); let report = FutureIncompatReport { future_incompat_report: data }; diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index f7f42c9d366f5..a1ccefefbcb2a 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -32,8 +32,8 @@ pub use anstyle::{ pub use codes::*; pub use decorate_diag::{BufferedEarlyLint, DecorateDiagCompat, LintBuffer}; pub use diagnostic::{ - BugAbort, Diag, DiagDecorator, DiagInner, DiagLocation, DiagStyledString, Diagnostic, - FatalAbort, StringPart, Subdiag, Subdiagnostic, + Diag, DiagDecorator, DiagInner, DiagLocation, DiagStyledString, Diagnostic, StringPart, + Subdiag, Subdiagnostic, }; pub use diagnostic_impls::{ DiagSymbolList, ElidedLifetimeInPathSubdiag, ExpectedLifetimeParameter, @@ -44,7 +44,6 @@ use emitter::{DynEmitter, Emitter}; use rustc_ast::attr::version::RustcVersion; use rustc_data_structures::AtomicRef; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; -use rustc_data_structures::stable_hash::StableHasher; use rustc_data_structures::sync::{DynSend, Lock}; pub use rustc_error_messages::{ DiagArg, DiagArgFromDisplay, DiagArgMap, DiagArgName, DiagArgValue, DiagMessage, IntoDiagArg, @@ -57,6 +56,7 @@ pub use rustc_macros::msg; use rustc_macros::{Decodable, Encodable}; pub use rustc_span::ErrorGuaranteed; pub use rustc_span::fatal_error::{FatalError, FatalErrorMarker, catch_fatal_errors}; +pub use rustc_span::macros::ExplicitBug; use rustc_span::source_map::SourceMap; use rustc_span::{DUMMY_SP, Span}; use tracing::debug; @@ -69,6 +69,7 @@ use crate::timings::TimingRecord; pub mod annotate_snippet_emitter_writer; pub mod codes; mod decorate_diag; +mod dedup_hash; mod diagnostic; mod diagnostic_impls; pub mod emitter; @@ -256,10 +257,6 @@ fn as_substr<'a>(original: &'a str, suggestion: &'a str) -> Option<(usize, &'a s } } -/// Signifies that the compiler died with an explicit call to `.bug` -/// or `.span_bug` rather than a failed assertion, etc. -pub struct ExplicitBug; - /// Signifies that the compiler died due to a delayed bug rather than a failed /// assertion, etc. pub struct DelayedBugPanic; @@ -328,8 +325,10 @@ struct DiagCtxtInner { emitted_diagnostic_codes: FxIndexSet, /// This set contains a hash of every diagnostic that has been emitted by - /// this `DiagCtxt`. These hashes is used to avoid emitting the same error - /// twice. + /// this `DiagCtxt`. These hashes are used to avoid emitting the same error + /// twice. (Because we don't store the diagnostics themselves, two + /// different diagnostics with the same hash value will be considered + /// equivalent. Such collisions should be vanishingly rare...) emitted_diagnostics: FxHashSet, /// We only want to emit `recursion_depth_exceeding_limit` once per @@ -351,9 +350,8 @@ struct DiagCtxtInner { future_breakage_diagnostics: Vec, - /// expected diagnostic will have the level `Expect` which additionally - /// carries the [`LintExpectationId`] of the expectation that can be - /// marked as fulfilled. This is a collection of all [`LintExpectationId`]s + /// Any `expect` lint will carry the [`LintExpectationId`] of the expectation that can be + /// marked as fulfilled. This field is a collection of all [`LintExpectationId`]s /// that have been marked as fulfilled this way. /// /// Emitting expectations after having stolen this field can happen. In particular, an @@ -376,7 +374,6 @@ struct DiagCtxtInner { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub enum StashKey { ItemNoType, - UnderscoreForArrayLengths, EarlySyntaxWarning, CallIntoMethod, /// When an invalid lifetime e.g. `'2` should be reinterpreted @@ -386,7 +383,6 @@ pub enum StashKey { /// FRU syntax MaybeFruTypo, CallAssocMethod, - AssociatedTypeSuggestion, UndeterminedMacroResolution, /// Used by `Parser::maybe_recover_trailing_expr` ExprInPat, @@ -615,7 +611,7 @@ impl<'a> DiagCtxtHandle<'a> { DelayedBug => { return self.dcx.inner.borrow_mut().emit_diagnostic(diag, self.tainted_with_errors); } - ForceWarning | Warning | Note | Help | FailureNote | Allow | Expect => None, + Warning(_) | Note | Help | FailureNote => None, }; // FIXME(Centril, #69537): Consider reintroducing panic on overwriting a stashed diagnostic @@ -635,7 +631,7 @@ impl<'a> DiagCtxtHandle<'a> { /// Steal a previously stashed non-error diagnostic with the given `Span` /// and [`StashKey`] as the key. Panics if the found diagnostic is an /// error. - pub fn steal_non_err(self, span: Span, key: StashKey) -> Option> { + pub fn steal_non_err(self, span: Span, key: StashKey) -> Option> { // FIXME(#120456) - is `swap_remove` correct? let (diag, guar, _) = self.dcx.inner.borrow_mut().stashed_diagnostics.get_mut(&key).and_then( @@ -664,13 +660,12 @@ impl<'a> DiagCtxtHandle<'a> { |stashed_diagnostics| stashed_diagnostics.swap_remove(&span.with_parent(None)), ); err.map(|(err, guar, _)| { - // The use of `::` is safe because level is `Level::Error`. assert_eq!(err.level, Error); assert!(guar.is_some()); - let mut err = Diag::::new_diagnostic(self, err); + let mut err = Diag::new_diagnostic(self, err); modify_err(&mut err); assert_eq!(err.level, Error); - err.emit() + err.emit_err() }) } @@ -693,11 +688,11 @@ impl<'a> DiagCtxtHandle<'a> { assert!(guar.is_some()); // Because `old_err` has already been counted, it can only be // safely cancelled because the `new_err` supplants it. - Diag::::new_diagnostic(self, old_err).cancel(); + Diag::new_diagnostic(self, old_err).cancel(); } None => {} }; - new_err.emit() + new_err.emit_err() } pub fn has_stashed_diagnostic(&self, span: Span, key: StashKey) -> bool { @@ -791,12 +786,12 @@ impl<'a> DiagCtxtHandle<'a> { match (errors.len(), warnings.len()) { (0, 0) => return, (0, _) => { - // Use `ForceWarning` rather than `Warning` to guarantee emission, e.g. with a - // configuration like `--cap-lints allow --force-warn bare_trait_objects`. - inner.emit_diagnostic( - DiagInner::new(ForceWarning, DiagMessage::Str(warnings)), - None, + // Force emission so this message always prints. + let diag = DiagInner::new( + Warning(Some(EmissionOverride::Forced { lint_id: None })), + DiagMessage::Str(warnings), ); + inner.emit_diagnostic(diag, None); } (_, 0) => { inner.emit_diagnostic(DiagInner::new(Error, errors), self.tainted_with_errors); @@ -922,6 +917,17 @@ impl<'a> DiagCtxtHandle<'a> { inner.emitter.emit_unused_externs(lint_level, unused_externs) } + /// We need to make sure that submitted expectation ids are correctly fulfilled, suppressed, + /// and stored between compilation sessions. To avoid doing these steps manually, we create a + /// dummy diagnostic and emit it as usual, which will be suppressed and stored like a normal + /// expected lint diagnostic. + #[track_caller] + pub fn fulfill_expectation(self, expectation: impl Into) { + let emission_override = Some(EmissionOverride::Expected { lint_id: expectation.into() }); + let msg = "this is a dummy diagnostic, to submit and store an expectation"; + Diag::new(self, Warning(emission_override), msg).emit() + } + /// This methods steals all [`LintExpectationId`]s that are stored inside /// [`DiagCtxtInner`] and indicate that the linked expectation has been fulfilled. #[must_use] @@ -955,13 +961,13 @@ impl<'a> DiagCtxtHandle<'a> { // functions create and emit a diagnostic all in one go. impl<'a> DiagCtxtHandle<'a> { #[track_caller] - pub fn struct_bug(self, msg: impl Into>) -> Diag<'a, BugAbort> { + pub fn struct_bug(self, msg: impl Into>) -> Diag<'a> { Diag::new(self, Bug, msg.into()) } #[track_caller] pub fn bug(self, msg: impl Into>) -> ! { - self.struct_bug(msg).emit() + self.struct_bug(msg).emit_bug() } #[track_caller] @@ -969,33 +975,33 @@ impl<'a> DiagCtxtHandle<'a> { self, span: impl Into, msg: impl Into>, - ) -> Diag<'a, BugAbort> { + ) -> Diag<'a> { self.struct_bug(msg).with_span(span) } #[track_caller] pub fn span_bug(self, span: impl Into, msg: impl Into>) -> ! { - self.struct_span_bug(span, msg.into()).emit() + self.struct_span_bug(span, msg.into()).emit_bug() } #[track_caller] - pub fn create_bug(self, bug: impl Diagnostic<'a, BugAbort>) -> Diag<'a, BugAbort> { + pub fn create_bug(self, bug: impl Diagnostic<'a>) -> Diag<'a> { bug.into_diag(self, Bug) } #[track_caller] - pub fn emit_bug(self, bug: impl Diagnostic<'a, BugAbort>) -> ! { - self.create_bug(bug).emit() + pub fn emit_bug(self, bug: impl Diagnostic<'a>) -> ! { + self.create_bug(bug).emit_bug() } #[track_caller] - pub fn struct_fatal(self, msg: impl Into) -> Diag<'a, FatalAbort> { + pub fn struct_fatal(self, msg: impl Into) -> Diag<'a> { Diag::new(self, Fatal, msg) } #[track_caller] pub fn fatal(self, msg: impl Into) -> ! { - self.struct_fatal(msg).emit() + self.struct_fatal(msg).emit_fatal() } #[track_caller] @@ -1003,26 +1009,25 @@ impl<'a> DiagCtxtHandle<'a> { self, span: impl Into, msg: impl Into, - ) -> Diag<'a, FatalAbort> { + ) -> Diag<'a> { self.struct_fatal(msg).with_span(span) } #[track_caller] pub fn span_fatal(self, span: impl Into, msg: impl Into) -> ! { - self.struct_span_fatal(span, msg).emit() + self.struct_span_fatal(span, msg).emit_fatal() } #[track_caller] - pub fn create_fatal(self, fatal: impl Diagnostic<'a, FatalAbort>) -> Diag<'a, FatalAbort> { + pub fn create_fatal(self, fatal: impl Diagnostic<'a>) -> Diag<'a> { fatal.into_diag(self, Fatal) } #[track_caller] - pub fn emit_fatal(self, fatal: impl Diagnostic<'a, FatalAbort>) -> ! { - self.create_fatal(fatal).emit() + pub fn emit_fatal(self, fatal: impl Diagnostic<'a>) -> ! { + self.create_fatal(fatal).emit_fatal() } - // FIXME: This method should be removed (every error should have an associated error code). #[track_caller] pub fn struct_err(self, msg: impl Into) -> Diag<'a> { Diag::new(self, Error, msg) @@ -1030,7 +1035,7 @@ impl<'a> DiagCtxtHandle<'a> { #[track_caller] pub fn err(self, msg: impl Into) -> ErrorGuaranteed { - self.struct_err(msg).emit() + self.struct_err(msg).emit_err() } #[track_caller] @@ -1048,7 +1053,7 @@ impl<'a> DiagCtxtHandle<'a> { span: impl Into, msg: impl Into, ) -> ErrorGuaranteed { - self.struct_span_err(span, msg).emit() + self.struct_span_err(span, msg).emit_err() } #[track_caller] @@ -1058,13 +1063,13 @@ impl<'a> DiagCtxtHandle<'a> { #[track_caller] pub fn emit_err(self, err: impl Diagnostic<'a>) -> ErrorGuaranteed { - self.create_err(err).emit() + self.create_err(err).emit_err() } /// Ensures that an error is printed. See [`Level::DelayedBug`]. #[track_caller] pub fn delayed_bug(self, msg: impl Into>) -> ErrorGuaranteed { - Diag::::new(self, DelayedBug, msg.into()).emit() + Diag::new(self, DelayedBug, msg.into()).emit_err() } /// Ensures that an error is printed. See [`Level::DelayedBug`]. @@ -1077,12 +1082,12 @@ impl<'a> DiagCtxtHandle<'a> { sp: impl Into, msg: impl Into>, ) -> ErrorGuaranteed { - Diag::::new(self, DelayedBug, msg.into()).with_span(sp).emit() + Diag::new(self, DelayedBug, msg.into()).with_span(sp).emit_err() } #[track_caller] - pub fn struct_warn(self, msg: impl Into) -> Diag<'a, ()> { - Diag::new(self, Warning, msg) + pub fn struct_warn(self, msg: impl Into) -> Diag<'a> { + Diag::new(self, Warning(None), msg) } #[track_caller] @@ -1095,7 +1100,7 @@ impl<'a> DiagCtxtHandle<'a> { self, span: impl Into, msg: impl Into, - ) -> Diag<'a, ()> { + ) -> Diag<'a> { self.struct_warn(msg).with_span(span) } @@ -1105,17 +1110,17 @@ impl<'a> DiagCtxtHandle<'a> { } #[track_caller] - pub fn create_warn(self, warning: impl Diagnostic<'a, ()>) -> Diag<'a, ()> { - warning.into_diag(self, Warning) + pub fn create_warn(self, warning: impl Diagnostic<'a>) -> Diag<'a> { + warning.into_diag(self, Warning(None)) } #[track_caller] - pub fn emit_warn(self, warning: impl Diagnostic<'a, ()>) { + pub fn emit_warn(self, warning: impl Diagnostic<'a>) { self.create_warn(warning).emit() } #[track_caller] - pub fn struct_note(self, msg: impl Into) -> Diag<'a, ()> { + pub fn struct_note(self, msg: impl Into) -> Diag<'a> { Diag::new(self, Note, msg) } @@ -1129,7 +1134,7 @@ impl<'a> DiagCtxtHandle<'a> { self, span: impl Into, msg: impl Into, - ) -> Diag<'a, ()> { + ) -> Diag<'a> { self.struct_note(msg).with_span(span) } @@ -1139,24 +1144,14 @@ impl<'a> DiagCtxtHandle<'a> { } #[track_caller] - pub fn create_note(self, note: impl Diagnostic<'a, ()>) -> Diag<'a, ()> { + pub fn create_note(self, note: impl Diagnostic<'a>) -> Diag<'a> { note.into_diag(self, Note) } #[track_caller] - pub fn emit_note(self, note: impl Diagnostic<'a, ()>) { + pub fn emit_note(self, note: impl Diagnostic<'a>) { self.create_note(note).emit() } - - #[track_caller] - pub fn struct_allow(self, msg: impl Into) -> Diag<'a, ()> { - Diag::new(self, Allow, msg) - } - - #[track_caller] - pub fn struct_expect(self, msg: impl Into, id: LintExpectationId) -> Diag<'a, ()> { - Diag::new(self, Expect, msg).with_lint_id(id) - } } impl DiagCtxtInner { @@ -1190,13 +1185,13 @@ impl DiagCtxtInner { let has_errors = !self.err_guars.is_empty(); for (_, stashed_diagnostics) in mem::take(&mut self.stashed_diagnostics).into_iter() { for (_, (diag, _guar, _thread)) in stashed_diagnostics { - if !diag.is_error() { - // Unless they're forced, don't flush stashed warnings when - // there are errors, to avoid causing warning overload. The - // stash would've been stolen already if it were important. - if !diag.is_force_warn() && has_errors { - continue; - } + // When there are errors, skip flushing of stashed unforced warnings, to avoid + // warning overload. (They would have been stolen already if they were important.) + if has_errors + && let Warning(emission_override) = diag.level + && !matches!(emission_override, Some(EmissionOverride::Forced { .. })) + { + continue; } guar = guar.or(self.emit_diagnostic(diag, None)); } @@ -1211,10 +1206,10 @@ impl DiagCtxtInner { taint: Option<&Cell>>, ) -> Option { if diagnostic.has_future_breakage() { - // Future breakages aren't emitted if they're `Level::Allow` or - // `Level::Expect`, but they still need to be constructed and - // stashed below, so they'll trigger the must_produce_diag check. - assert_matches!(diagnostic.level, Error | ForceWarning | Warning | Allow | Expect); + // About the `allow`/`expect` lint sub-cases of `Warning`: future breakages aren't + // emitted for them, but they still need to be handled below so they'll trigger the + // `must_produce_diag` check. + assert_matches!(diagnostic.level, Error | Warning(_)); self.future_breakage_diagnostics.push(diagnostic.clone()); } @@ -1262,36 +1257,37 @@ impl DiagCtxtInner { }; } } - ForceWarning if diagnostic.lint_id.is_none() => {} // `ForceWarning(Some(...))` is below, with `Expect` - Warning => { - if !self.flags.can_emit_warnings { - // We are not emitting warnings. - if diagnostic.has_future_breakage() { - // The side-effect is at the top of this method. + Warning(emission_override) => { + match emission_override { + None => { + if !self.flags.can_emit_warnings { + // We are not emitting warnings. + if diagnostic.has_future_breakage() { + // The side-effect is at the top of this method. + TRACK_DIAGNOSTIC(diagnostic, &mut |_| None); + } + return None; + } + } + Some(EmissionOverride::Forced { lint_id: None }) => {} + Some(EmissionOverride::Forced { lint_id: Some(lint_id) }) => { + self.fulfilled_expectations.insert(lint_id); + } + Some(EmissionOverride::Allowed) => { + assert!(diagnostic.has_future_breakage()); TRACK_DIAGNOSTIC(diagnostic, &mut |_| None); + self.suppressed_expected_diag = true; + return None; + } + Some(EmissionOverride::Expected { lint_id }) => { + self.fulfilled_expectations.insert(lint_id); + TRACK_DIAGNOSTIC(diagnostic, &mut |_| None); + self.suppressed_expected_diag = true; + return None; } - return None; } } Note | Help | FailureNote => {} - Allow => { - // Nothing emitted for allowed lints. - if diagnostic.has_future_breakage() { - // The side-effect is at the top of this method. - TRACK_DIAGNOSTIC(diagnostic, &mut |_| None); - self.suppressed_expected_diag = true; - } - return None; - } - Expect | ForceWarning => { - self.fulfilled_expectations.insert(diagnostic.lint_id.unwrap()); - if let Expect = diagnostic.level { - // Nothing emitted here for expected lints. - TRACK_DIAGNOSTIC(diagnostic, &mut |_| None); - self.suppressed_expected_diag = true; - return None; - } - } } if let (Some(msrv), Some(diag_msrv)) = (self.msrv, diagnostic.rust_version()) @@ -1305,12 +1301,7 @@ impl DiagCtxtInner { self.emitted_diagnostic_codes.insert(code); } - let already_emitted = { - let mut hasher = StableHasher::new(); - diagnostic.hash(&mut hasher); - let diagnostic_hash = hasher.finish(); - !self.emitted_diagnostics.insert(diagnostic_hash) - }; + let already_emitted = !self.emitted_diagnostics.insert(diagnostic.dedup_hash()); let is_error = diagnostic.is_error(); let is_lint = diagnostic.is_lint.is_some(); @@ -1322,7 +1313,7 @@ impl DiagCtxtInner { ) && mem::replace(&mut self.emitted_recursion_depth_exceeding_limit, true) }); - // Only emit the diagnostic if we've been asked to deduplicate or + // Only emit the diagnostic if deduplication is disabled or we // haven't already emitted an equivalent diagnostic. if !silence_recursion_depth_exceeded_limit && !(self.flags.deduplicate_diagnostics && already_emitted) @@ -1330,21 +1321,18 @@ impl DiagCtxtInner { debug!(?diagnostic); debug!(?self.emitted_diagnostics); - let not_yet_emitted = |sub: &mut Subdiag| { + let show_sub = |sub: &Subdiag| { debug!(?sub); match sub.level { Sublevel::Error | Sublevel::Warning | Sublevel::Note | Sublevel::Help => { - return true; + true + } + Sublevel::OnceNote | Sublevel::OnceHelp => { + self.emitted_diagnostics.insert(sub.dedup_hash()) } - Sublevel::OnceNote | Sublevel::OnceHelp => {} } - let mut hasher = StableHasher::new(); - sub.hash(&mut hasher); - let diagnostic_hash = hasher.finish(); - debug!(?diagnostic_hash); - self.emitted_diagnostics.insert(diagnostic_hash) }; - diagnostic.children.retain_mut(not_yet_emitted); + diagnostic.children.retain(show_sub); if already_emitted { let msg = "duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`"; diagnostic.sub(Sublevel::Note, msg, MultiSpan::new()); @@ -1352,7 +1340,7 @@ impl DiagCtxtInner { if is_error { self.deduplicated_err_count += 1; - } else if matches!(diagnostic.level, ForceWarning | Warning) { + } else if matches!(diagnostic.level, Warning(_)) { self.deduplicated_warn_count += 1; } self.has_printed = true; @@ -1553,19 +1541,45 @@ impl DelayedDiagInner { } } -/// | Level | is_error | emit return type | Top-level | Used in lints? -/// | ----- | -------- | ---------------- | --------- | -------------- -/// | Bug | yes | BugAbort | yes | - -/// | Fatal | yes | FatalAbort | yes | - -/// | Error | yes | ErrorGuaranteed | yes | yes -/// | DelayedBug | yes | ErrorGuaranteed | yes | - -/// | ForceWarning | - | () | yes | lint-only -/// | Warning | - | () | yes | yes -/// | Note | - | () | rare | - -/// | Help | - | () | don't use | - -/// | FailureNote | - | () | rare | - -/// | Allow | - | () | yes | lint-only -/// | Expect | - | () | yes | lint-only +/// Special emission behaviours on warning diagnostics. Combines with `DiagInner::is_lint` in the +/// following ways. +/// +/// | case | is_lint | `Option` field in `Warning` +/// | ---- | ------- | --------------------------------------------- +/// | ordinary non-lint diagnostic | None | None +/// | warning-count summary | None | Some(Forced { lint_id: None }) +/// | N/A | None | Some(Forced { lint_id: Some(lint_id) }) +/// | dummy expectation fulfillment | None | Some(Expected { lint_id }) +/// | N/A | None | Some(Allowed) +/// | ordinary `warn` lint | Some | None +/// | `warn` lint at `force-warn` | Some | Some(Forced { lint_id: None }) +/// | `expect` lint at `force-warn` | Some | Some(Forced { lint_id: Some(lint_id) }) +/// | `expect` lint | Some | Some(Expected { lint_id }) +/// | `allow` lint with future breakage | Some | Some(Allowed) +/// +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] +pub enum EmissionOverride { + /// Cases where emission is forced. + Forced { lint_id: Option }, + + /// Used for `allow` lints with future breakage. (Allow lints without future breakage are + /// ignored and therefore never need an `EmissionOverride` value.) + Allowed, + + /// Used for `expect` lints. + Expected { lint_id: LintExpectationId }, +} + +/// | Level | is_error | usable emit fns | Top-level | Used in lints? +/// | ----- | -------- | --------------- | --------- | -------------- +/// | Bug | yes | emit, emit_bug | yes | - +/// | Fatal | yes | emit, emit_fatal | yes | - +/// | Error | yes | emit, emit_err | yes | yes +/// | DelayedBug | yes | emit, emit_err | yes | - +/// | Warning | - | emit | yes | yes +/// | Note | - | emit | rare | - +/// | Help | - | emit | don't use | - +/// | FailureNote | - | emit | rare | - /// #[derive(Copy, PartialEq, Eq, Clone, Hash, Debug, Encodable, Decodable)] pub enum Level { @@ -1586,16 +1600,9 @@ pub enum Level { /// that should only be reached when compiling erroneous code. DelayedBug, - /// A `force-warn` lint warning about the code being compiled. Does not prevent compilation - /// from finishing. - /// - /// Requires a [`LintExpectationId`] for expected lint diagnostics. In all other cases this - /// should be `None`. - ForceWarning, - /// A warning about the code being compiled. Does not prevent compilation from finishing. - /// Will be skipped if `can_emit_warnings` is false. - Warning, + /// Might not be emitted, depending on the value of `EmissionOverride` and `can_emit_warnings`. + Warning(Option), /// A rarely-used level for output that isn't an error or a warning. Note, @@ -1610,12 +1617,6 @@ pub enum Level { /// Similar to `Note`, but even rarer. Lacks the a trailing blank line that all other /// diagnostics have. Also, when printed for human consumption it doesn't have a `note:` label. FailureNote, - - /// Only used for lints. - Allow, - - /// Only used for lints. Requires a [`LintExpectationId`] for silencing the lints. - Expect, } impl fmt::Display for Level { @@ -1629,16 +1630,15 @@ impl Level { match self { Bug | DelayedBug => "error: internal compiler error", Fatal | Error => "error", - ForceWarning | Warning => "warning", + Warning(_) => "warning", Note => "note", Help => "help", FailureNote => "failure-note", - Allow | Expect => unreachable!(), } } - pub fn is_failure_note(&self) -> bool { - matches!(*self, FailureNote) + pub fn is_failure_note(self) -> bool { + matches!(self, FailureNote) } } diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index fda75319b087b..01e8cef49e445 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1,6 +1,5 @@ use std::any::Any; use std::default::Default; -use std::iter; use std::path::PathBuf; use std::rc::Rc; use std::sync::Arc; @@ -24,7 +23,7 @@ use rustc_span::def_id::{CrateNum, DefId, LocalDefId, ModId}; use rustc_span::edition::Edition; use rustc_span::hygiene::{AstPass, ExpnData, ExpnKind, LocalExpnId, MacroKind}; use rustc_span::source_map::SourceMap; -use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw}; +use rustc_span::{DUMMY_SP, Ident, Span, Symbol}; use rustc_structures::{CollapseMacroDebuginfo, Limit}; use smallvec::{SmallVec, smallvec}; use thin_vec::ThinVec; @@ -1324,16 +1323,6 @@ impl<'a> ExtCtxt<'a> { pub fn set_trace_macros(&mut self, x: bool) { self.ecfg.trace_mac = x } - pub fn std_path(&self, components: &[Symbol]) -> Vec { - let def_site = self.with_def_site_ctxt(DUMMY_SP); - iter::once(Ident::new(kw::DollarCrate, def_site)) - .chain(components.iter().map(|&s| Ident::new(s, def_site))) - .collect() - } - pub fn def_site_path(&self, components: &[Symbol]) -> Vec { - let def_site = self.with_def_site_ctxt(DUMMY_SP); - components.iter().map(|&s| Ident::new(s, def_site)).collect() - } pub fn check_unused_macros(&mut self) { self.resolver.check_unused_macros(); diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 0e9d9ec5e8b7e..2a4089b1213ab 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -1,3 +1,5 @@ +use std::iter; + use rustc_ast::token::Delimiter; use rustc_ast::tokenstream::TokenStream; use rustc_ast::util::literal; @@ -11,6 +13,13 @@ use thin_vec::{ThinVec, thin_vec}; use crate::base::ExtCtxt; impl<'a> ExtCtxt<'a> { + pub fn std_path(&self, components: &[Symbol]) -> Vec { + let def_site = self.with_def_site_ctxt(DUMMY_SP); + iter::once(Ident::new(kw::DollarCrate, def_site)) + .chain(components.iter().map(|&s| Ident::new(s, def_site))) + .collect() + } + pub fn path(&self, span: Span, strs: Vec) -> ast::Path { self.path_all(span, false, strs, vec![]) } @@ -68,10 +77,6 @@ impl<'a> ExtCtxt<'a> { }) } - pub fn ty_mt(&self, ty: Box, mutbl: ast::Mutability) -> ast::MutTy { - ast::MutTy { ty, mutbl } - } - pub fn ty(&self, span: Span, kind: ast::TyKind) -> Box { Box::new(ast::Ty { id: ast::DUMMY_NODE_ID, span, kind }) } @@ -118,22 +123,32 @@ impl<'a> ExtCtxt<'a> { lifetime: Option, mutbl: ast::Mutability, ) -> Box { - self.ty(span, ast::TyKind::Ref(lifetime, self.ty_mt(ty, mutbl))) + self.ty(span, ast::TyKind::Ref(lifetime, ty, mutbl)) } pub fn ty_ptr(&self, span: Span, ty: Box, mutbl: ast::Mutability) -> Box { - self.ty(span, ast::TyKind::Ptr(self.ty_mt(ty, mutbl))) + self.ty(span, ast::TyKind::Ptr(ty, mutbl)) + } + + pub fn ty_unit(&self, span: Span) -> Box { + self.ty(span, ast::TyKind::Tup(ThinVec::new())) + } + + pub fn ty_self(&self, span: Span) -> Box { + self.ty_path(self.path_ident(span, Ident::new(kw::SelfUpper, span))) + } + pub fn ty_self_ref(&self, span: Span) -> Box { + self.ty_ref(span, self.ty_self(span), None, ast::Mutability::Not) } pub fn typaram( &self, - span: Span, ident: Ident, bounds: ast::GenericBounds, default: Option>, ) -> ast::GenericParam { ast::GenericParam { - ident: ident.with_span_pos(span), + ident, id: ast::DUMMY_NODE_ID, attrs: AttrVec::new(), bounds, @@ -143,15 +158,10 @@ impl<'a> ExtCtxt<'a> { } } - pub fn lifetime_param( - &self, - span: Span, - ident: Ident, - bounds: ast::GenericBounds, - ) -> ast::GenericParam { + pub fn lifetime_param(&self, ident: Ident, bounds: ast::GenericBounds) -> ast::GenericParam { ast::GenericParam { id: ast::DUMMY_NODE_ID, - ident: ident.with_span_pos(span), + ident, attrs: AttrVec::new(), bounds, is_placeholder: false, @@ -162,7 +172,6 @@ impl<'a> ExtCtxt<'a> { pub fn const_param( &self, - span: Span, ident: Ident, bounds: ast::GenericBounds, ty: Box, @@ -170,7 +179,7 @@ impl<'a> ExtCtxt<'a> { ) -> ast::GenericParam { ast::GenericParam { id: ast::DUMMY_NODE_ID, - ident: ident.with_span_pos(span), + ident, attrs: AttrVec::new(), bounds, is_placeholder: false, @@ -264,10 +273,6 @@ impl<'a> ExtCtxt<'a> { self.stmt_local(local, span) } - pub fn stmt_semi(&self, expr: Box) -> ast::Stmt { - ast::Stmt { id: ast::DUMMY_NODE_ID, span: expr.span, kind: ast::StmtKind::Semi(expr) } - } - pub fn stmt_local(&self, local: Box, span: Span) -> ast::Stmt { ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Let(local), span } } @@ -310,6 +315,9 @@ impl<'a> ExtCtxt<'a> { pub fn expr_self(&self, span: Span) -> Box { self.expr_ident(span, Ident::new(kw::SelfLower, span)) } + pub fn expr_ident_sym(&self, span: Span, sym: Symbol) -> Box { + self.expr_ident(span, Ident::new(sym, span)) + } pub fn expr_macro_call(&self, span: Span, call: Box) -> Box { self.expr(span, ast::ExprKind::MacCall(call)) @@ -364,12 +372,6 @@ impl<'a> ExtCtxt<'a> { ) -> Box { self.expr(span, ast::ExprKind::Call(expr, args)) } - pub fn expr_loop(&self, sp: Span, block: Box) -> Box { - self.expr(sp, ast::ExprKind::Loop(block, None, sp)) - } - pub fn expr_asm(&self, sp: Span, expr: Box) -> Box { - self.expr(sp, ast::ExprKind::InlineAsm(expr)) - } pub fn expr_call_ident( &self, span: Span, @@ -476,64 +478,17 @@ impl<'a> ExtCtxt<'a> { self.expr(sp, ast::ExprKind::Tup(exprs)) } - pub fn expr_unreachable(&self, span: Span) -> Box { - self.expr_macro_call( - span, - self.macro_call( - span, - self.path_global( - span, - [sym::std, sym::unreachable].map(|s| Ident::new(s, span)).to_vec(), - ), - Delimiter::Parenthesis, - TokenStream::default(), - ), - ) - } - pub fn expr_ok(&self, sp: Span, expr: Box) -> Box { let ok = self.std_path(&[sym::result, sym::Result, sym::Ok]); self.expr_call_global(sp, ok, thin_vec![expr]) } - pub fn expr_try(&self, sp: Span, head: Box) -> Box { - let ok = self.std_path(&[sym::result, sym::Result, sym::Ok]); - let ok_path = self.path_global(sp, ok); - let err = self.std_path(&[sym::result, sym::Result, sym::Err]); - let err_path = self.path_global(sp, err); - - let binding_variable = Ident::new(sym::__try_var, sp); - let binding_pat = self.pat_ident(sp, binding_variable); - let binding_expr = self.expr_ident(sp, binding_variable); - - // `Ok(__try_var)` pattern - let ok_pat = self.pat_tuple_struct(sp, ok_path, thin_vec![binding_pat.clone()]); - - // `Err(__try_var)` (pattern and expression respectively) - let err_pat = self.pat_tuple_struct(sp, err_path.clone(), thin_vec![binding_pat]); - let err_inner_expr = - self.expr_call(sp, self.expr_path(err_path), thin_vec![binding_expr.clone()]); - // `return Err(__try_var)` - let err_expr = self.expr(sp, ast::ExprKind::Ret(Some(err_inner_expr))); - - // `Ok(__try_var) => __try_var` - let ok_arm = self.arm(sp, ok_pat, binding_expr); - // `Err(__try_var) => return Err(__try_var)` - let err_arm = self.arm(sp, err_pat, err_expr); - - // `match head { Ok() => ..., Err() => ... }` - self.expr_match(sp, head, thin_vec![ok_arm, err_arm]) - } - pub fn pat(&self, span: Span, kind: PatKind) -> ast::Pat { ast::Pat { id: ast::DUMMY_NODE_ID, kind, span } } pub fn pat_wild(&self, span: Span) -> ast::Pat { self.pat(span, PatKind::Wild) } - pub fn pat_lit(&self, span: Span, expr: Box) -> ast::Pat { - self.pat(span, PatKind::Expr(expr)) - } pub fn pat_ident(&self, span: Span, ident: Ident) -> ast::Pat { self.pat_ident_binding_mode(span, ident, ast::BindingMode::NONE) } @@ -588,10 +543,6 @@ impl<'a> ExtCtxt<'a> { } } - pub fn arm_unreachable(&self, span: Span) -> ast::Arm { - self.arm(span, self.pat_wild(span), self.expr_unreachable(span)) - } - pub fn expr_match( &self, span: Span, @@ -612,15 +563,15 @@ impl<'a> ExtCtxt<'a> { self.expr(span, ast::ExprKind::If(cond, self.block_expr(then), els)) } - pub fn lambda(&self, span: Span, ids: Vec, body: Box) -> Box { + pub fn closure(&self, span: Span, ids: Vec, body: Box) -> Box { let fn_decl = self.fn_decl( ids.iter().map(|id| self.param(span, *id, self.ty(span, ast::TyKind::Infer))).collect(), ast::FnRetTy::Default(span), ); // FIXME -- We are using `span` as the span of the `|...|` - // part of the lambda, but it probably (maybe?) corresponds to - // the entire lambda body. Probably we should extend the API + // part of the closure, but it probably (maybe?) corresponds to + // the entire closure body. Probably we should extend the API // here, but that's not entirely clear. self.expr( span, @@ -640,23 +591,6 @@ impl<'a> ExtCtxt<'a> { ) } - pub fn lambda0(&self, span: Span, body: Box) -> Box { - self.lambda(span, Vec::new(), body) - } - - pub fn lambda1(&self, span: Span, body: Box, ident: Ident) -> Box { - self.lambda(span, vec![ident], body) - } - - pub fn lambda_stmts_1( - &self, - span: Span, - stmts: ThinVec, - ident: Ident, - ) -> Box { - self.lambda1(span, self.expr_block(self.block(span, stmts)), ident) - } - pub fn param(&self, span: Span, ident: Ident, ty: Box) -> ast::Param { let pat = Box::new(self.pat_ident(span, ident)); ast::Param { @@ -688,9 +622,39 @@ impl<'a> ExtCtxt<'a> { }) } + pub fn item_trait_impl( + &self, + span: Span, + attrs: ast::AttrVec, + generics: ast::Generics, + safety: ast::Safety, + is_const: bool, + trait_ref: ast::TraitRef, + self_ty: Box, + items: ThinVec>, + ) -> Box { + self.item( + span, + attrs, + ast::ItemKind::Impl(ast::Impl { + generics, + of_trait: Some(Box::new(ast::TraitImplHeader { + safety, + polarity: ast::ImplPolarity::Positive, + defaultness: ast::Defaultness::Implicit, + trait_ref, + })), + constness: if is_const { ast::Const::Yes(DUMMY_SP) } else { ast::Const::No }, + self_ty, + items, + }), + ) + } + pub fn item_static( &self, span: Span, + attrs: ast::AttrVec, ident: Ident, ty: Box, mutability: ast::Mutability, @@ -698,7 +662,7 @@ impl<'a> ExtCtxt<'a> { ) -> Box { self.item( span, - AttrVec::new(), + attrs, ast::ItemKind::Static( ast::StaticItem { ident, diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 51c517ee24a66..5a2442fbccf35 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -898,7 +898,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } } Err(err) => { - let _guar = err.emit(); + err.emit(); fragment_kind.expect_from_annotatables(iter::once(item)) } } @@ -1068,7 +1068,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { err.span(span); } annotate_err_with_kind(&mut err, kind, span); - let guar = err.emit(); + let guar = err.emit_err(); self.cx.macro_error_and_trace_macros_diag(); kind.dummy(span, guar) } diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs index 0e79dadb3503e..f7603f25bb137 100644 --- a/compiler/rustc_expand/src/mbe/diagnostics.rs +++ b/compiler/rustc_expand/src/mbe/diagnostics.rs @@ -140,7 +140,7 @@ pub(super) fn failed_to_match_macro( } } } - let guar = err.emit(); + let guar = err.emit_err(); (sp, guar) } @@ -497,7 +497,7 @@ pub(super) fn emit_frag_parse_err( } } } - e.emit() + e.emit_err() } pub(crate) fn annotate_err_with_kind(err: &mut Diag<'_>, kind: AstFragmentKind, span: Span) { diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs index 7ab595dc73d89..f070009fc3d57 100644 --- a/compiler/rustc_expand/src/mbe/macro_check.rs +++ b/compiler/rustc_expand/src/mbe/macro_check.rs @@ -105,7 +105,7 @@ //! stored when entering a macro definition starting from the state in which the meta-variable is //! bound. -use rustc_ast::token::{Delimiter, IdentIsRaw, Token, TokenKind}; +use rustc_ast::token::{Delimiter, IdentKind, Token, TokenKind}; use rustc_ast::{DUMMY_NODE_ID, NodeId}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::DecorateDiagCompat; @@ -396,7 +396,10 @@ fn check_nested_occurrences( match (state, tt) { ( NestedMacroState::Empty, - &TokenTree::Token(Token { kind: TokenKind::Ident(name, IdentIsRaw::No), .. }), + &TokenTree::Token(Token { + kind: TokenKind::Ident(name, IdentKind::Normal | IdentKind::ForcedKeyword), + .. + }), ) => { if name == kw::MacroRules { state = NestedMacroState::MacroRules; diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index 3e94e0ca34773..4505bcf394ace 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -713,13 +713,13 @@ impl TtParser { fn nt_parsing_error(&self, loc: &MatcherLoc, err: Diag<'_>) -> ParseResult { let &MatcherLoc::MetaVarDecl { span, kind, .. } = loc else { unreachable!() }; - let guarantee = err + let guar = err .with_span_label( span, format!("while parsing argument for this `{kind}` macro fragment"), ) - .emit(); - ErrorReported(guarantee) + .emit_err(); + ErrorReported(guar) } fn nameize>( diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 2212724c68bc1..12f308fc546c3 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -3,7 +3,7 @@ use std::collections::hash_map::Entry; use std::sync::Arc; use std::{mem, slice}; -use ast::token::IdentIsRaw; +use ast::token::IdentKind; use rustc_ast::token::NtPatKind::*; use rustc_ast::token::TokenKind::*; use rustc_ast::token::{self, Delimiter, NonterminalKind, Token, TokenKind}; @@ -255,7 +255,7 @@ impl MacroRulesMacroExpander { let id = cx.current_expansion.id; let tts = transcribe(psess, &named_matches, rhs, *rhs_span, self.transparency, id) - .map_err(|e| e.emit())?; + .map_err(|e| e.emit_err())?; if cx.trace_macros() { let msg = format!("to `{}`", pprust::tts_to_string(&tts)); @@ -464,7 +464,7 @@ fn expand_macro<'cx, 'a: 'cx>( let tts = match transcribe(psess, &named_matches, rhs, *rhs_span, transparency, id) { Ok(tts) => tts, Err(err) => { - let guar = err.emit(); + let guar = err.emit_err(); return DummyResult::any(arm_span, guar); } }; @@ -562,7 +562,7 @@ fn expand_macro_attr( let id = cx.current_expansion.id; let tts = transcribe(psess, &named_matches, rhs, *rhs_span, transparency, id) - .map_err(|e| e.emit())?; + .map_err(|e| e.emit_err())?; if cx.trace_macros() { let msg = format!("to `{}`", pprust::tts_to_string(&tts)); @@ -858,7 +858,7 @@ pub fn compile_declarative_macro( if args_not_empty { err.span_label(derive_keyword_span, "need `()` after this `derive`"); } - return dummy_syn_ext(err.emit()); + return dummy_syn_ext(err.emit_err()); } (None, true) } else { @@ -873,7 +873,7 @@ pub fn compile_declarative_macro( let lhs_tt = parse_one_tt(lhs_tt, RulePart::Pattern, sess, node_id, features, edition); check_emission(check_lhs(sess, features, node_id, &lhs_tt)); if let Err(e) = p.expect(exp!(FatArrow)) { - return dummy_syn_ext(e.emit()); + return dummy_syn_ext(e.emit_err()); } if let Some(guar) = check_no_eof(sess, &p, "expected right-hand side of macro rule") { return dummy_syn_ext(guar); @@ -906,7 +906,7 @@ pub fn compile_declarative_macro( break; } if let Err(e) = p.expect(exp_sep) { - return dummy_syn_ext(e.emit()); + return dummy_syn_ext(e.emit_err()); } } @@ -952,7 +952,7 @@ fn check_no_eof(sess: &Session, p: &Parser<'_>, msg: &'static str) -> Option Result<(), let mut err = sess.dcx().struct_span_err(sp, "repetition matches empty token tree"); check_redundant_vis_repetition(&mut err, sess, seq, span); - return Err(err.emit()); + return Err(err.emit_err()); } check_lhs_no_empty_seq(sess, &seq.tts)? } @@ -1660,7 +1660,7 @@ fn check_matcher_core<'tt>( )); } } - errored = Err(err.emit()); + errored = Err(err.emit_err()); } } } @@ -1750,7 +1750,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { match tok { TokenTree::Token(token) => match token.kind { FatArrow | Comma | Eq | Or => IsInFollow::Yes, - Ident(name, IdentIsRaw::No) if name == kw::If || name == kw::In => { + Ident(kw::If | kw::In, IdentKind::Normal | IdentKind::ForcedKeyword) => { IsInFollow::Yes } _ => IsInFollow::No(TOKENS), @@ -1764,7 +1764,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { match tok { TokenTree::Token(token) => match token.kind { FatArrow | Comma | Eq => IsInFollow::Yes, - Ident(name, IdentIsRaw::No) if name == kw::If || name == kw::In => { + Ident(kw::If | kw::In, IdentKind::Normal | IdentKind::ForcedKeyword) => { IsInFollow::Yes } _ => IsInFollow::No(TOKENS), @@ -1792,7 +1792,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { TokenTree::Token(token) => match token.kind { OpenBrace | OpenBracket | Comma | FatArrow | Colon | Eq | Gt | Shr | Semi | Or => IsInFollow::Yes, - Ident(name, IdentIsRaw::No) if name == kw::As || name == kw::Where => { + Ident(kw::As | kw::Where, IdentKind::Normal | IdentKind::ForcedKeyword) => { IsInFollow::Yes } _ => IsInFollow::No(TOKENS), @@ -1820,7 +1820,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { match tok { TokenTree::Token(token) => match token.kind { Comma => IsInFollow::Yes, - Ident(_, IdentIsRaw::Yes) => IsInFollow::Yes, + Ident(_, IdentKind::Raw) => IsInFollow::Yes, Ident(name, _) if name != kw::Priv => IsInFollow::Yes, _ => { if token.can_begin_type() { diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs index a02b84204cb39..807d4fae6de15 100644 --- a/compiler/rustc_expand/src/mbe/metavar_expr.rs +++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs @@ -1,4 +1,4 @@ -use rustc_ast::token::{self, Delimiter, IdentIsRaw, Lit, Token, TokenKind}; +use rustc_ast::token::{self, Delimiter, IdentKind, Lit, Token, TokenKind}; use rustc_ast::tokenstream::{TokenStream, TokenStreamIter, TokenTree}; use rustc_ast::{LitIntType, LitKind}; use rustc_ast_pretty::pprust; @@ -9,14 +9,16 @@ use rustc_span::{Ident, Span, Symbol, sym}; use crate::diagnostics; -pub(crate) const RAW_IDENT_ERR: &str = "`${concat(..)}` currently does not support raw identifiers"; pub(crate) const UNSUPPORTED_CONCAT_ELEM_ERR: &str = "expected identifier or string literal"; /// A meta-variable expression, for expansions based on properties of meta-variables. #[derive(Debug, PartialEq, Encodable, Decodable)] pub(crate) enum MetaVarExpr { - /// Unification of two or more identifiers. - Concat(Box<[MetaVarExprConcatElem]>), + /// Unification of two or more identifiers/literals/metavariables into an identifier. + ConcatIdent(Box<[MetaVarExprConcatElem]>), + + /// Unification of two or more identifiers/literals/metavariables into a string literal. + ConcatStr(Box<[MetaVarExprConcatElem]>), /// The number of repetitions of an identifier. Count(Ident, usize), @@ -73,7 +75,12 @@ impl MetaVarExpr { let mut iter = args.iter(); let rslt = match ident.name { - sym::concat => parse_concat(&mut iter, psess, outer_span, ident.span)?, + sym::concat => { + MetaVarExpr::ConcatIdent(parse_concat(&mut iter, psess, outer_span, ident.span)?) + } + sym::concat_str => { + MetaVarExpr::ConcatStr(parse_concat(&mut iter, psess, outer_span, ident.span)?) + } sym::count => parse_count(&mut iter, psess, ident.span)?, sym::ignore => { eat_dollar(&mut iter, psess, ident.span)?; @@ -95,7 +102,7 @@ impl MetaVarExpr { pub(crate) fn for_each_metavar(&self, mut aux: A, mut cb: impl FnMut(A, &Ident) -> A) -> A { match self { - MetaVarExpr::Concat(elems) => { + MetaVarExpr::ConcatIdent(elems) | MetaVarExpr::ConcatStr(elems) => { for elem in elems { if let MetaVarExprConcatElem::Var(ident) = elem { aux = cb(aux, ident) @@ -175,7 +182,7 @@ fn parse_concat<'psess>( psess: &'psess ParseSess, outer_span: Span, expr_ident_span: Span, -) -> PResult<'psess, MetaVarExpr> { +) -> PResult<'psess, Box<[MetaVarExprConcatElem]>> { let mut result = Vec::new(); loop { let is_var = try_eat_dollar(iter); @@ -189,6 +196,10 @@ fn parse_concat<'psess>( } else { match parse_ident_from_token(psess, token) { Err(err) => { + // FIXME: Canceling this error means we emit a worse message when encountering + // raw identifiers (`r#ident`). However, we also don't want to forward + // (the current version of) this error as is since we also want to + // mentioning string literals as a valid token kind. err.cancel(); return Err(psess .dcx() @@ -210,7 +221,7 @@ fn parse_concat<'psess>( .dcx() .struct_span_err(expr_ident_span, "`concat` must have at least two elements")); } - Ok(MetaVarExpr::Concat(result.into())) + Ok(result.into()) } /// Parse a meta-variable `count` expression: `count(ident[, depth])` @@ -272,17 +283,15 @@ fn parse_ident_from_token<'psess>( psess: &'psess ParseSess, token: &Token, ) -> PResult<'psess, Ident> { - if let Some((elem, is_raw)) = token.ident() { - if let IdentIsRaw::Yes = is_raw { - return Err(psess.dcx().struct_span_err(elem.span, RAW_IDENT_ERR)); - } + if let Some((elem, kind)) = token.ident() { + validate_ident_kind(psess.dcx(), kind, elem.span)?; return Ok(elem); } let token_str = pprust::token_to_string(token); let mut err = psess .dcx() .struct_span_err(token.span, format!("expected identifier, found `{token_str}`")); - err.span_suggestion( + err.span_suggestion_short( token.span, format!("try removing `{token_str}`"), "", @@ -339,3 +348,16 @@ fn eat_dollar<'psess>( "meta-variables within meta-variable expressions must be referenced using a dollar sign", )) } + +pub(crate) fn validate_ident_kind<'a>( + dcx: rustc_errors::DiagCtxtHandle<'a>, + kind: IdentKind, + span: Span, +) -> PResult<'a, ()> { + let kind = match kind { + IdentKind::Normal => return Ok(()), + IdentKind::Raw => "raw identifiers", + IdentKind::ForcedKeyword => "forced keywords", + }; + Err(dcx.struct_span_err(span, format!("`${{concat(..)}}` currently does not support {kind}"))) +} diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index 2779291abf361..80e258e94510c 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -1,4 +1,4 @@ -use rustc_ast::token::{self, Delimiter, IdentIsRaw, NonterminalKind, Token}; +use rustc_ast::token::{self, Delimiter, IdentKind, NonterminalKind, Token}; use rustc_ast::tokenstream::TokenStreamIter; use rustc_ast::{NodeId, tokenstream}; use rustc_ast_pretty::pprust; @@ -7,7 +7,7 @@ use rustc_feature::Features; use rustc_session::Session; use rustc_session::diagnostics::feature_err; use rustc_span::edition::Edition; -use rustc_span::{Ident, Span, kw, sym}; +use rustc_span::{Ident, Span, Symbol, kw, sym}; use crate::diagnostics; use crate::mbe::macro_parser::count_metavar_decls; @@ -204,9 +204,14 @@ fn maybe_emit_macro_metavar_expr_feature(features: &Features, sess: &Session, sp } } -fn maybe_emit_macro_metavar_expr_concat_feature(features: &Features, sess: &Session, span: Span) { +fn maybe_emit_macro_metavar_expr_concat_feature( + features: &Features, + sess: &Session, + span: Span, + what: Symbol, +) { if !features.macro_metavar_expr_concat() { - let msg = "the `concat` meta-variable expression is unstable"; + let msg = format!("the `{what}` meta-variable expression is unstable"); feature_err(sess, sym::macro_metavar_expr_concat, span, msg).emit(); } } @@ -278,11 +283,19 @@ fn parse_tree<'a>( return TokenTree::token(token::Dollar, dollar_span); } Ok(elem) => { - if let MetaVarExpr::Concat(_) = elem { + if matches!(elem, MetaVarExpr::ConcatIdent(_)) { maybe_emit_macro_metavar_expr_concat_feature( features, sess, delim_span.entire(), + sym::concat, + ); + } else if matches!(elem, MetaVarExpr::ConcatStr(_)) { + maybe_emit_macro_metavar_expr_concat_feature( + features, + sess, + delim_span.entire(), + sym::concat_str, ); } else { maybe_emit_macro_metavar_expr_feature( @@ -324,11 +337,14 @@ fn parse_tree<'a>( // `tree` is followed by an `ident`. This could be `$meta_var` or the `$crate` // special metavariable that names the crate of the invocation. - Some(tokenstream::TokenTree::Token(token, _)) if token.is_ident() => { - let (ident, is_raw) = token.ident().unwrap(); + Some(tokenstream::TokenTree::Token(token, _)) + if let Some((ident, kind)) = token.ident() => + { let span = ident.span.with_lo(dollar_span.lo()); - if ident.name == kw::Crate && matches!(is_raw, IdentIsRaw::No) { - TokenTree::token(token::Ident(kw::DollarCrate, is_raw), span) + if let kw::Crate = ident.name + && let IdentKind::Normal | IdentKind::ForcedKeyword = kind + { + TokenTree::token(token::Ident(kw::DollarCrate, kind), span) } else { TokenTree::MetaVar(span, ident) } diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index 2627e327c0f71..736a8d1de4c19 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -1,7 +1,7 @@ use std::mem; use rustc_ast::token::{ - self, Delimiter, IdentIsRaw, InvisibleOrigin, Lit, LitKind, MetaVarKind, Token, TokenKind, + self, Delimiter, IdentKind, InvisibleOrigin, Lit, LitKind, MetaVarKind, Token, TokenKind, }; use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree}; use rustc_ast::{ExprKind, StmtKind, TyKind, UnOp}; @@ -25,7 +25,7 @@ use crate::diagnostics::{ }; use crate::mbe::macro_parser::NamedMatch; use crate::mbe::macro_parser::NamedMatch::*; -use crate::mbe::metavar_expr::{MetaVarExprConcatElem, RAW_IDENT_ERR}; +use crate::mbe::metavar_expr::{MetaVarExprConcatElem, validate_ident_kind}; use crate::mbe::{self, KleeneOp, MetaVarExpr}; /// Context needed to perform transcription of metavariable expressions. @@ -498,10 +498,10 @@ fn transcribe_pnr<'tx>( // parsing priorities. maybe_use_metavar_location(tscx.psess, &tscx.stack, sp, tt, &mut tscx.marker) } - ParseNtResult::Ident(ident, is_raw) => { + ParseNtResult::Ident(ident, kind) => { tscx.marker.mark_span(&mut sp); with_metavar_spans(|mspans| mspans.insert(ident.span, sp)); - let kind = token::NtIdent(*ident, *is_raw); + let kind = token::NtIdent(*ident, *kind); TokenTree::token_alone(kind, sp) } ParseNtResult::Lifetime(ident, is_raw) => { @@ -575,7 +575,7 @@ fn transcribe_pnr<'tx>( let leading_if_span = guard.span_with_leading_if.with_hi(guard.span_with_leading_if.lo() + BytePos(2)); let ts = std::iter::once(TokenTree::token_alone( - token::Ident(kw::If, IdentIsRaw::No), + token::Ident(kw::If, IdentKind::Normal), leading_if_span, )) .chain(TokenStream::from_ast(&guard.cond).iter().cloned()) @@ -597,7 +597,8 @@ fn transcribe_metavar_expr<'tx>( ) -> PResult<'tx, ()> { let dcx = tscx.psess.dcx(); let tt = match *expr { - MetaVarExpr::Concat(ref elements) => metavar_expr_concat(tscx, dspan, elements)?, + MetaVarExpr::ConcatIdent(ref elements) => metavar_expr_concat_ident(tscx, dspan, elements)?, + MetaVarExpr::ConcatStr(ref elements) => metavar_expr_concat_str(tscx, dspan, elements)?, MetaVarExpr::Count(original_ident, depth) => { let matched = matched_from_ident(dcx, original_ident, tscx.interp)?; let count = count_repetitions(dcx, depth, matched, &tscx.repeats, &dspan)?; @@ -635,11 +636,52 @@ fn transcribe_metavar_expr<'tx>( } /// Handle the `${concat(...)}` metavariable expression. -fn metavar_expr_concat<'tx>( +fn metavar_expr_concat_ident<'tx>( tscx: &mut TranscrCtx<'tx, '_>, dspan: DelimSpan, elements: &[MetaVarExprConcatElem], ) -> PResult<'tx, TokenTree> { + let (symbol, concatenated_span) = metavar_expr_concat(tscx, dspan, elements)?; + if !rustc_lexer::is_ident(symbol.as_str()) { + return Err(tscx.psess.dcx().create_err(ConcatInvalidIdent { + span: concatenated_span, + reason: InvalidIdentReason::new(symbol), + })); + } + tscx.psess.symbol_gallery.insert(symbol, concatenated_span); + + // The current implementation marks the span as coming from the macro regardless of + // contexts of the concatenated identifiers but this behavior may change in the + // future. + Ok(TokenTree::Token( + Token::from_ast_ident(Ident::new(symbol, concatenated_span)), + Spacing::Alone, + )) +} + +/// Handle the `${concat_str(...)}` metavariable expression. +fn metavar_expr_concat_str<'tx>( + tscx: &mut TranscrCtx<'tx, '_>, + dspan: DelimSpan, + elements: &[MetaVarExprConcatElem], +) -> PResult<'tx, TokenTree> { + let (symbol, concatenated_span) = metavar_expr_concat(tscx, dspan, elements)?; + + // The current implementation marks the span as coming from the macro regardless of + // contexts of the concatenated identifiers but this behavior may change in the + // future. + Ok(TokenTree::Token( + Token::new(TokenKind::lit(LitKind::Str, symbol, None), concatenated_span), + Spacing::Alone, + )) +} + +/// Shared logic for concat/concat_str metavariable expressions +fn metavar_expr_concat<'tx>( + tscx: &mut TranscrCtx<'tx, '_>, + dspan: DelimSpan, + elements: &[MetaVarExprConcatElem], +) -> PResult<'tx, (Symbol, Span)> { let dcx = tscx.psess.dcx(); let mut concatenated = String::new(); for element in elements { @@ -668,21 +710,7 @@ fn metavar_expr_concat<'tx>( } let symbol = nfc_normalize(&concatenated); let concatenated_span = tscx.visited_dspan(dspan); - if !rustc_lexer::is_ident(symbol.as_str()) { - return Err(dcx.create_err(ConcatInvalidIdent { - span: concatenated_span, - reason: InvalidIdentReason::new(symbol), - })); - } - tscx.psess.symbol_gallery.insert(symbol, concatenated_span); - - // The current implementation marks the span as coming from the macro regardless of - // contexts of the concatenated identifiers but this behavior may change in the - // future. - Ok(TokenTree::Token( - Token::from_ast_ident(Ident::new(symbol, concatenated_span)), - Spacing::Alone, - )) + Ok((symbol, concatenated_span)) } /// Store the metavariable span for this original span into a side table. @@ -1004,22 +1032,16 @@ fn extract_symbol_from_pnr<'a>( span_err: Span, ) -> PResult<'a, Symbol> { match pnr { - ParseNtResult::Ident(nt_ident, is_raw) => { - if let IdentIsRaw::Yes = is_raw { - Err(dcx.struct_span_err(span_err, RAW_IDENT_ERR)) - } else { - Ok(nt_ident.name) - } + ParseNtResult::Ident(nt_ident, kind) => { + validate_ident_kind(dcx, *kind, span_err)?; + Ok(nt_ident.name) } ParseNtResult::Tt(TokenTree::Token( - Token { kind: TokenKind::Ident(symbol, is_raw), .. }, + Token { kind: TokenKind::Ident(symbol, kind), .. }, _, )) => { - if let IdentIsRaw::Yes = is_raw { - Err(dcx.struct_span_err(span_err, RAW_IDENT_ERR)) - } else { - Ok(*symbol) - } + validate_ident_kind(dcx, *kind, span_err)?; + Ok(*symbol) } ParseNtResult::Tt(TokenTree::Token( Token { diff --git a/compiler/rustc_expand/src/module.rs b/compiler/rustc_expand/src/module.rs index d49eae9830b3f..8e1641c8fb60c 100644 --- a/compiler/rustc_expand/src/module.rs +++ b/compiler/rustc_expand/src/module.rs @@ -300,7 +300,7 @@ impl ModError<'_> { secondary_path: secondary_path.display().to_string(), }) } - ModError::ParserError(err) => err.emit(), + ModError::ParserError(err) => err.emit_err(), } } } diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index c0a9a43c64cdf..e00958836a5a4 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -11,7 +11,8 @@ use rustc_parse::lexer::{StripTokens, nfc_normalize}; use rustc_parse::parser::Parser; use rustc_parse::{exp, new_parser_from_source_str, source_str_to_stream}; use rustc_proc_macro::bridge::{ - DelimSpan, Diagnostic, ExpnGlobals, Group, Ident, LitKind, Literal, Punct, TokenTree, server, + DelimSpan, Diagnostic, ExpnGlobals, Group, Ident, IdentKind, LitKind, Literal, Punct, + TokenTree, server, }; use rustc_proc_macro::{Delimiter, Level}; use rustc_session::Session; @@ -103,6 +104,26 @@ impl ToInternal for LitKind { } } +impl FromInternal for IdentKind { + fn from_internal(kind: tk::IdentKind) -> Self { + match kind { + tk::IdentKind::Normal => IdentKind::Normal, + tk::IdentKind::Raw => IdentKind::Raw, + tk::IdentKind::ForcedKeyword => IdentKind::ForcedKeyword, + } + } +} + +impl ToInternal for IdentKind { + fn to_internal(self) -> tk::IdentKind { + match self { + IdentKind::Normal => tk::IdentKind::Normal, + IdentKind::Raw => tk::IdentKind::Raw, + IdentKind::ForcedKeyword => tk::IdentKind::ForcedKeyword, + } + } +} + impl FromInternal for Vec> { fn from_internal(stream: TokenStream) -> Self { // Estimate the capacity as `stream.len()` rounded up to the next power @@ -127,7 +148,7 @@ impl FromInternal for Vec> { } trees.push(TokenTree::Group(Group { - delimiter: rustc_proc_macro::Delimiter::from_internal(delim), + delimiter: Delimiter::from_internal(delim), stream: Some(stream), span: DelimSpan { open: span.open, @@ -228,33 +249,33 @@ impl FromInternal for Vec> { tk::Question => op("?"), tk::SingleQuote => op("'"), - tk::Ident(sym, is_raw) => trees.push(TokenTree::Ident(Ident { + tk::Ident(sym, kind) => trees.push(TokenTree::Ident(Ident { sym, - is_raw: matches!(is_raw, tk::IdentIsRaw::Yes), + kind: IdentKind::from_internal(kind), span, })), - tk::NtIdent(ident, is_raw) => trees.push(TokenTree::Ident(Ident { + tk::NtIdent(ident, kind) => trees.push(TokenTree::Ident(Ident { sym: ident.name, - is_raw: matches!(is_raw, tk::IdentIsRaw::Yes), + kind: IdentKind::from_internal(kind), span: ident.span, })), - tk::Lifetime(name, is_raw) => { + tk::Lifetime(name, kind) => { let ident = rustc_span::Ident::new(name, span).without_first_quote(); trees.extend([ TokenTree::Punct(Punct { ch: b'\'', joint: true, span }), TokenTree::Ident(Ident { sym: ident.name, - is_raw: matches!(is_raw, tk::IdentIsRaw::Yes), + kind: IdentKind::from_internal(kind), span, }), ]); } - tk::NtLifetime(ident, is_raw) => { + tk::NtLifetime(ident, kind) => { let stream = - TokenStream::token_alone(tk::Lifetime(ident.name, is_raw), ident.span); + TokenStream::token_alone(tk::Lifetime(ident.name, kind), ident.span); trees.push(TokenTree::Group(Group { - delimiter: rustc_proc_macro::Delimiter::None, + delimiter: Delimiter::None, stream: Some(stream), span: DelimSpan::from_single(span), })) @@ -274,7 +295,7 @@ impl FromInternal for Vec> { escaped.extend(ch.escape_debug()); } let stream = [ - tk::Ident(sym::doc, tk::IdentIsRaw::No), + tk::Ident(sym::doc, tk::IdentKind::Normal), tk::Eq, tk::TokenKind::lit(tk::Str, Symbol::intern(&escaped), None), ] @@ -286,7 +307,7 @@ impl FromInternal for Vec> { trees.push(TokenTree::Punct(Punct { ch: b'!', joint: false, span })); } trees.push(TokenTree::Group(Group { - delimiter: rustc_proc_macro::Delimiter::Bracket, + delimiter: Delimiter::Bracket, stream: Some(stream), span: DelimSpan::from_single(span), })); @@ -364,16 +385,16 @@ impl ToInternal> stream.unwrap_or_default(), )] } - TokenTree::Ident(self::Ident { sym, is_raw, span }) => { + TokenTree::Ident(Ident { sym, kind, span }) => { rustc.psess().symbol_gallery.insert(sym, span); - smallvec![tokenstream::TokenTree::token_alone(tk::Ident(sym, is_raw.into()), span)] + smallvec![tokenstream::TokenTree::token_alone( + tk::Ident(sym, kind.to_internal()), + span + )] } - TokenTree::Literal(self::Literal { - kind: self::LitKind::Integer, - symbol, - suffix, - span, - }) if let Some(symbol) = symbol.as_str().strip_prefix('-') => { + TokenTree::Literal(self::Literal { kind: LitKind::Integer, symbol, suffix, span }) + if let Some(symbol) = symbol.as_str().strip_prefix('-') => + { let symbol = Symbol::intern(symbol); let integer = tk::TokenKind::lit(tk::Integer, symbol, suffix); let a = tokenstream::TokenTree::token_joint_hidden(tk::Minus, span); @@ -406,7 +427,7 @@ impl ToInternal for Level { fn to_internal(self) -> rustc_errors::Level { match self { Level::Error => rustc_errors::Level::Error, - Level::Warning => rustc_errors::Level::Warning, + Level::Warning => rustc_errors::Level::Warning(None), Level::Note => rustc_errors::Level::Note, Level::Help => rustc_errors::Level::Help, _ => unreachable!("unknown proc_macro::Level variant: {:?}", self), @@ -559,8 +580,7 @@ impl server::Server for Rustc<'_, '_> { fn emit_diagnostic(&mut self, diagnostic: Diagnostic) { let message = rustc_errors::DiagMessage::from(diagnostic.message); - let mut diag: Diag<'_, ()> = - Diag::new(self.psess().dcx(), diagnostic.level.to_internal(), message); + let mut diag = Diag::new(self.psess().dcx(), diagnostic.level.to_internal(), message); diag.span(MultiSpan::from_spans(diagnostic.spans)); for child in diagnostic.children { diag.sub(child.level.to_internal(), child.message, MultiSpan::from_spans(child.spans)); @@ -625,7 +645,7 @@ impl server::Server for Rustc<'_, '_> { match &expr.kind { ast::ExprKind::Lit(token_lit) if token_lit.kind == tk::Bool => { Ok(tokenstream::TokenStream::token_alone( - tk::Ident(token_lit.symbol, tk::IdentIsRaw::No), + tk::Ident(token_lit.symbol, tk::IdentKind::Normal), expr.span, )) } diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 37a3594e374ea..4eb7408244728 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -47,7 +47,7 @@ declare_features! ( /// Allows `#[target_feature(...)]` on aarch64 platforms (accepted, aarch64_target_feature, "1.61.0", Some(44839)), /// Allows `extern "custom" fn()`. - (accepted, abi_custom, "CURRENT_RUSTC_VERSION", Some(140829)), + (accepted, abi_custom, "1.100.0", Some(140829)), /// Allows using the `efiapi` ABI. (accepted, abi_efiapi, "1.68.0", Some(65815)), /// Allows the sysV64 ABI to be specified on all platforms @@ -336,7 +336,7 @@ declare_features! ( /// Allows specifying the whole-archive link modifier (accepted, native_link_modifiers_whole_archive, "1.61.0", Some(81490)), /// Allows the `!` type. - (accepted, never_type, "CURRENT_RUSTC_VERSION", Some(35121)), + (accepted, never_type, "1.100.0", Some(35121)), /// Allows using non lexical lifetimes (RFC 2094). (accepted, nll, "1.63.0", Some(43234)), /// Allows using `#![no_std]`. diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index bb35a3281ccdc..0f96eb7f158ed 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -40,6 +40,11 @@ const GATED_CFGS: &[GatedCfg] = &[ sym::cfg_target_has_reliable_f16_f128, Features::cfg_target_has_reliable_f16_f128, ), + ( + sym::target_has_reliable_f16b, + sym::cfg_target_has_reliable_f16b, + Features::cfg_target_has_reliable_f16b, + ), ( sym::target_has_reliable_f128, sym::cfg_target_has_reliable_f16_f128, diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index 5b9b899fc463f..83bf4f47a8ac9 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -133,6 +133,6 @@ pub use builtin_attrs::{ }; pub use removed::REMOVED_LANG_FEATURES; pub use unstable::{ - DEPENDENT_FEATURES, EnabledLangFeature, EnabledLibFeature, Features, INCOMPATIBLE_FEATURES, - TRACK_FEATURE, UNSTABLE_LANG_FEATURES, + DEPENDENT_FEATURES, DependentFeature, EnabledLangFeature, EnabledLibFeature, Features, + INCOMPATIBLE_FEATURES, TRACK_FEATURE, UNSTABLE_LANG_FEATURES, }; diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index 403617f7bfa6f..eca2567ab20ff 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -55,7 +55,7 @@ declare_features! ( /// Allows using the `amdgpu-kernel` ABI. (removed, abi_amdgpu_kernel, "1.77.0", Some(51575), None, 120495), (removed, abi_c_cmse_nonsecure_call, "1.90.0", Some(81391), Some("renamed to abi_cmse_nonsecure_call"), 142146), - (removed, abi_unadjusted, "CURRENT_RUSTC_VERSION", None, Some("merged into link_llvm_intrinsics"), 161398), + (removed, abi_unadjusted, "1.100.0", None, Some("merged into link_llvm_intrinsics"), 161398), (removed, advanced_slice_patterns, "1.42.0", Some(62254), Some("merged into `#![feature(slice_patterns)]`"), 67712), (removed, allocator, "1.0.0", None, None), @@ -63,11 +63,11 @@ declare_features! ( (removed, allow_fail, "1.60.0", Some(46488), Some("removed due to no clear use cases"), 93416), /// Allows users to enforce equality of associated constants `TraitImpl`. (removed, associated_const_equality, "1.94.0", Some(92827), - Some("merged into `min_generic_const_args`")), + Some("merged into `gca_min_const_items`")), (removed, await_macro, "1.38.0", Some(50547), Some("subsumed by `.await` syntax"), 62293), /// Allows using `box` in patterns (RFC 469). - (removed, box_patterns, "CURRENT_RUSTC_VERSION", Some(29641), Some("superseded by `deref_patterns`")), + (removed, box_patterns, "1.100.0", Some(29641), Some("superseded by `deref_patterns`")), /// Allows using the `box $expr` syntax. (removed, box_syntax, "1.70.0", Some(49733), Some("replaced with `#[rustc_box]`"), 108471), /// Allows capturing disjoint fields in a closure/coroutine (RFC 2229). @@ -167,6 +167,7 @@ declare_features! ( ), 133768 ), + (removed, generic_const_args, "1.100.0", Some(151972), Some("renamed to `gca_const_items`")), (removed, import_shadowing, "1.0.0", None, None), /// Allows in-band quantification of lifetime bindings (e.g., `fn foo(x: &'a u8) -> &'a u8`). (removed, in_band_lifetimes, "1.61.0", Some(44524), @@ -192,9 +193,12 @@ declare_features! ( which is available from cargo build scripts with `cargo:rustc-link-arg` now"), 83820), (removed, macro_reexport, "1.0.0", Some(29638), Some("subsumed by `pub use`"), 49982), + (removed, macroless_const_item_generic_const_args, "1.100.0", Some(162540), Some("renamed to `gca_macroless_items`")), + (removed, macroless_generic_const_args, "1.100.0", Some(159006), Some("renamed to `gca_macroless_args`")), /// Allows using `#[main]` to replace the entrypoint `#[lang = "start"]` calls. (removed, main, "1.53.0", Some(29634), None, 84217), (removed, managed_boxes, "1.0.0", None, None), + (removed, min_generic_const_args, "1.100.0", Some(132980), Some("renamed to `gca_min_const_items`")), /// Allows the use of type alias impl trait in function return positions (removed, min_type_alias_impl_trait, "1.56.0", Some(63063), Some("removed in favor of full type_alias_impl_trait"), 87564), diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index a7138a88ee399..1aebf0c9ec56e 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -119,6 +119,13 @@ impl Features { false } } + + /// The generic_const_args family of features has a set of common behavior that can be enabled + /// by either `gca_min_const_items` or `gca_adts`. There is no actual `gca` base feature, but + /// this method acts as one. + pub fn gca(&self) -> bool { + self.gca_min_const_items() || self.gca_adts() + } } macro_rules! declare_features { @@ -227,6 +234,8 @@ declare_features! ( (unstable, anonymous_lifetime_in_impl_trait, "1.63.0", None), /// Allows checking whether or not the backend correctly supports unstable float types. (internal, cfg_target_has_reliable_f16_f128, "1.88.0", None), + /// Allows checking whether or not the backend correctly supports the unstable `f16b` type. + (internal, cfg_target_has_reliable_f16b, "1.100.0", None), /// Allows checking whether or not the target might have thread support. (internal, cfg_target_has_threads, "1.99.0", None), /// Allows identifying the `compiler_builtins` crate. @@ -248,7 +257,7 @@ declare_features! ( /// Allows `#[link(..., cfg(..))]`; perma-unstable per #37406 (internal, link_cfg, "1.14.0", None), /// Allows using `#[link_name="__enzyme_*"]`. - (internal, link_enzyme_intrinsics, "CURRENT_RUSTC_VERSION", None), + (internal, link_enzyme_intrinsics, "1.100.0", None), /// Allows using `?Trait` trait bounds in more contexts. (internal, more_maybe_bounds, "1.82.0", None), /// Allow negative trait bounds. This is an internal-only feature for testing the trait solver! @@ -264,7 +273,7 @@ declare_features! ( /// Allows using the `#[stable]` and `#[unstable]` attributes. (internal, staged_api, "1.0.0", None), /// Perma-unstable, only used in the test suite for binders (`for<'a>`). - (internal, test_binder_constraints, "CURRENT_RUSTC_VERSION", None), + (internal, test_binder_constraints, "1.100.0", None), /// Perma-unstable, only used to test the `incomplete_features` lint. (incomplete, test_incomplete_feature, "1.96.0", None), /// Added for testing unstable lints; perma-unstable. @@ -520,6 +529,8 @@ declare_features! ( (unstable, f128, "1.78.0", Some(116909)), /// Allow using 16-bit (half precision) floating point numbers. (unstable, f16, "1.78.0", Some(116909)), + /// Allow using bfloat16 floating point numbers. + (unstable, f16b, "1.100.0", Some(160630)), /// Allows the use of `#[ffi_const]` on foreign functions. (unstable, ffi_const, "1.45.0", Some(58328)), /// Allows the use of `#[ffi_pure]` on foreign functions. @@ -537,15 +548,27 @@ declare_features! ( /// Support delegating implementation of functions to other already implemented functions. (incomplete, fn_delegation, "1.76.0", Some(118212)), /// Traits for function pointers and items - (unstable, fn_static, "CURRENT_RUSTC_VERSION", Some(148768)), + (unstable, fn_static, "1.100.0", Some(148768)), + /// Allows using forced keywords `k#fn`. + (unstable, forced_keywords, "CURRENT_RUSTC_VERSION", Some(153839)), /// Allows impls for the Freeze trait. (internal, freeze_impls, "1.78.0", Some(121675)), /// Frontmatter `---` blocks for use by external tools. (unstable, frontmatter, "1.88.0", Some(136889)), + /// Allows using ADTs in directly represented generic const args. + (incomplete, gca_adts, "CURRENT_RUSTC_VERSION", Some(163420)), + /// Allows using generics in more complex const expressions, based on definitional equality. + (incomplete, gca_const_items, "1.95.0", Some(151972)), + /// Allows directly represented gca_const_items without the `gca!` macro. + (incomplete, gca_macroless_args, "1.99.0", Some(159006)), + /// Allows directly represented gca_const_items as the rhs of const items without the + /// `gca!` macro. + (incomplete, gca_macroless_items, "1.100.0", Some(162540)), + /// Enables the generic const args MVP (paths to type const items and constructors + /// for ADTs and primitives). + (incomplete, gca_min_const_items, "1.84.0", Some(132980)), /// Allows defining gen blocks and `gen fn`. (unstable, gen_blocks, "1.75.0", Some(117078)), - /// Allows using generics in more complex const expressions, based on definitional equality. - (incomplete, generic_const_args, "1.95.0", Some(151972)), /// Allows non-trivial generic constants which have to be shown to successfully evaluate /// to a value by being part of an item signature. (incomplete, generic_const_exprs, "1.56.0", Some(76560)), @@ -605,19 +628,11 @@ declare_features! ( (unstable, macro_metavar_expr, "1.61.0", Some(83527)), /// Provides a way to concatenate identifiers using metavariable expressions. (unstable, macro_metavar_expr_concat, "1.81.0", Some(124225)), - /// Allows directly represented generic_const_args as the rhs of const items without the - /// `direct_const_arg!` macro. - (incomplete, macroless_const_item_generic_const_args, "CURRENT_RUSTC_VERSION", Some(162540)), - /// Allows directly represented generic_const_args without the `direct_const_arg!` macro. - (incomplete, macroless_generic_const_args, "1.99.0", Some(159006)), /// Allows `#[marker]` on certain traits allowing overlapping implementations. (unstable, marker_trait_attr, "1.30.0", Some(29864)), /// Allows additional const parameter types, such as [u8; 10] or user defined types. /// User defined types must not have fields more private than the type itself. (unstable, min_adt_const_params, "1.96.0", Some(154042)), - /// Enables the generic const args MVP (paths to type const items and constructors - /// for ADTs and primitives). - (incomplete, min_generic_const_args, "1.84.0", Some(132980)), /// A minimal, sound subset of specialization intended to be used by the /// standard library until the soundness issues with specialization /// are fixed. @@ -626,6 +641,10 @@ declare_features! ( (unstable, mips_target_feature, "1.27.0", Some(150253)), /// Allows qualified paths in struct expressions, struct patterns and tuple struct patterns. (unstable, more_qualified_paths, "1.54.0", Some(86935)), + /// The `movdir64b` target feature on x86. + (unstable, movdir64b_target_feature, "CURRENT_RUSTC_VERSION", Some(163741)), + /// The `movdiri` target feature on x86. + (unstable, movdiri_target_feature, "CURRENT_RUSTC_VERSION", Some(163741)), /// Allows `move(expr)` in closures. (incomplete, move_expr, "1.97.0", Some(155050)), /// The `movrs` target feature on x86. @@ -853,10 +872,25 @@ pub const INCOMPATIBLE_FEATURES: &[(Symbol, Symbol)] = &[ (sym::ref_pat_eat_one_layer_2024, sym::ref_pat_eat_one_layer_2024_structural), ]; +pub enum DependentFeature { + And(&'static [DependentFeature]), + Or(&'static [DependentFeature]), + Leaf(Symbol), +} + /// Some features require one or more other features to be enabled. -pub const DEPENDENT_FEATURES: &[(Symbol, &[Symbol])] = &[ - (sym::generic_const_args, &[sym::min_generic_const_args]), - (sym::macroless_generic_const_args, &[sym::min_generic_const_args]), - (sym::macroless_const_item_generic_const_args, &[sym::min_generic_const_args]), - (sym::unsized_const_params, &[sym::adt_const_params]), +pub const DEPENDENT_FEATURES: &[(Symbol, DependentFeature)] = &[ + // tidy-alphabetical-start + ( + sym::gca_adts, + DependentFeature::Or(&[ + DependentFeature::Leaf(sym::min_adt_const_params), + DependentFeature::Leaf(sym::adt_const_params), + ]), + ), + (sym::gca_const_items, DependentFeature::Leaf(sym::gca_min_const_items)), + (sym::gca_macroless_args, DependentFeature::Leaf(sym::gca_min_const_items)), + (sym::gca_macroless_items, DependentFeature::Leaf(sym::gca_min_const_items)), + (sym::unsized_const_params, DependentFeature::Leaf(sym::adt_const_params)), + // tidy-alphabetical-end ]; diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 6deab943b836a..a16c55a53bf09 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -55,6 +55,17 @@ impl From for MacroKinds { } impl MacroKinds { + // Constants to allow easy pattern-matching on any combination. + /// `ATTR | BANG` + pub const ATTR_BANG: MacroKinds = MacroKinds::ATTR.union(MacroKinds::BANG); + /// `DERIVE | BANG` + pub const DERIVE_BANG: MacroKinds = MacroKinds::DERIVE.union(MacroKinds::BANG); + /// `DERIVE | ATTR` + pub const DERIVE_ATTR: MacroKinds = MacroKinds::DERIVE.union(MacroKinds::ATTR); + /// `DERIVE | ATTR | BANG` + pub const DERIVE_ATTR_BANG: MacroKinds = + MacroKinds::DERIVE.union(MacroKinds::ATTR).union(MacroKinds::BANG); + /// Convert the MacroKinds to a static string. /// /// This hardcodes all the possibilities, in order to return a static string. @@ -64,10 +75,10 @@ impl MacroKinds { Self::BANG => "macro", Self::ATTR => "attribute macro", Self::DERIVE => "derive macro", - _ if self == (Self::ATTR | Self::BANG) => "attribute/function macro", - _ if self == (Self::DERIVE | Self::BANG) => "derive/function macro", - _ if self == (Self::ATTR | Self::DERIVE) => "attribute/derive macro", - _ if self.is_all() => "attribute/derive/function macro", + Self::ATTR_BANG => "attribute/function macro", + Self::DERIVE_BANG => "derive/function macro", + Self::DERIVE_ATTR => "attribute/derive macro", + Self::DERIVE_ATTR_BANG => "attribute/derive/function macro", _ if self.is_empty() => "useless macro", _ => unreachable!(), } @@ -576,6 +587,25 @@ pub enum Res { Err, } +impl Res { + pub fn in_namespace(self) -> PerNS> { + match self { + Res::Def(DefKind::Mod | DefKind::Trait, _) => { + PerNS { type_ns: Some(self), value_ns: None, macro_ns: None } + } + Res::Def(DefKind::Enum, _) => { + PerNS { type_ns: None, value_ns: Some(self), macro_ns: None } + } + Res::Err => { + // Propagate the error to all namespaces, just to be sure. + let err = Some(Res::Err); + PerNS { type_ns: err, value_ns: err, macro_ns: err } + } + _ => panic!("bad path segment res {self:?}"), + } + } +} + impl IntoDiagArg for Res { fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { DiagArgValue::Str(Cow::Borrowed(self.descr())) diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index ee79680d7d1e9..4f4ed83be06a3 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -30,7 +30,7 @@ use rustc_span::{ BytePos, DUMMY_SP, DesugaringKind, ErrorGuaranteed, Ident, LocalExpnId, Span, Spanned, Symbol, kw, sym, }; -use rustc_target::asm::InlineAsmRegOrRegClass; +use rustc_target::asm; use tracing::debug; use crate::def::{CtorKind, DefKind, MacroKinds, PerNS, Res}; @@ -554,7 +554,7 @@ pub enum InferArgKind { /// determined during HIR ty lowering. TypeOrConst, /// An infer argument with unambiguous const syntax, e.g. S<{ _ }> or - /// S. It can only be inferred to a const. + /// S. It can only be inferred to a const. Const, } @@ -1405,6 +1405,16 @@ impl<'tcx> OwnerInfo<'tcx> { pub fn node(&self) -> OwnerNode<'tcx> { self.nodes.node() } + + // A fingerprint that identifies the contents of the OwnerInfo. + // It only depends on `nodes` and `attrs` because `parenting` and `trait_map` are + // deterministically calculated from `nodes` and `attrs`. + #[inline] + pub fn fingerprint(&self) -> Fingerprint { + let body = self.nodes.opt_hash.expect("HIR hash requested without needs_hir_hash"); + let attrs = self.attrs.opt_hash.expect("HIR hash requested without needs_hir_hash"); + body.combine(attrs) + } } #[derive(Copy, Clone, Debug, StableHash)] @@ -2886,14 +2896,6 @@ impl fmt::Display for YieldSource { } } -// N.B., if you change this, you'll probably want to change the corresponding -// type structure in middle/ty.rs as well. -#[derive(Debug, Clone, Copy, StableHash)] -pub struct MutTy<'hir> { - pub ty: &'hir Ty<'hir>, - pub mutbl: Mutability, -} - /// Represents a function's signature in a trait declaration, /// trait implementation, or a free function. #[derive(Debug, Clone, Copy, StableHash)] @@ -3093,7 +3095,7 @@ pub enum ImplItemKind<'hir> { /// * the `G = Ty` in `Trait = Ty>` /// * the `A: Bound` in `Trait` /// * the `RetTy` in `Trait(ArgTy, ArgTy) -> RetTy` -/// * the `C = { Ct }` in `Trait` (feature `min_generic_const_args`) +/// * the `C = { Ct }` in `Trait` (feature `gca_min_const_items`) /// * the `f(..): Bound` in `Trait` (feature `return_type_notation`) #[derive(Debug, Clone, Copy, StableHash)] pub struct AssocItemConstraint<'hir> { @@ -3224,7 +3226,7 @@ impl<'hir> Ty<'hir> { impl<'hir> Ty<'hir, AmbigArg> { pub fn peel_refs(&self) -> &Ty<'hir> { let mut final_ty = self.as_unambig_ty(); - while let TyKind::Ref(_, MutTy { ty, .. }) = &final_ty.kind { + while let TyKind::Ref(_, ty, _) = &final_ty.kind { final_ty = ty; } final_ty @@ -3234,7 +3236,7 @@ impl<'hir> Ty<'hir, AmbigArg> { impl<'hir> Ty<'hir> { pub fn peel_refs(&self) -> &Self { let mut final_ty = self; - while let TyKind::Ref(_, MutTy { ty, .. }) = &final_ty.kind { + while let TyKind::Ref(_, ty, _) = &final_ty.kind { final_ty = ty; } final_ty @@ -3298,7 +3300,7 @@ impl<'hir> Ty<'hir> { ty.is_suggestable_infer_ty() || matches!(length.kind, ConstArgKind::Infer(..)) } TyKind::Tup(tys) => tys.iter().any(Self::is_suggestable_infer_ty), - TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty) => mut_ty.ty.is_suggestable_infer_ty(), + TyKind::Ptr(ty, _) | TyKind::Ref(_, ty, _) => ty.is_suggestable_infer_ty(), TyKind::Path(QPath::TypeRelative(ty, segment)) => { ty.is_suggestable_infer_ty() || are_suggestable_generic_args(segment.args().args) } @@ -3567,9 +3569,9 @@ pub enum TyKind<'hir, Unambig = ()> { /// A fixed length array (i.e., `[T; n]`). Array(&'hir Ty<'hir>, &'hir ConstArg<'hir>), /// A raw pointer (i.e., `*const T` or `*mut T`). - Ptr(MutTy<'hir>), + Ptr(&'hir Ty<'hir>, Mutability), /// A reference (i.e., `&'a T` or `&'a mut T`). - Ref(&'hir Lifetime, MutTy<'hir>), + Ref(&'hir Lifetime, &'hir Ty<'hir>, Mutability), /// A function pointer (e.g., `fn(usize) -> bool`). FnPtr(&'hir FnPtrTy<'hir>), /// An unsafe binder type (e.g. `unsafe<'a> Foo<'a>`). @@ -3611,6 +3613,44 @@ pub enum TyKind<'hir, Unambig = ()> { Infer(Unambig), } +/// Stores explicit register name from source +/// for diagnostics only. +#[derive(Debug, Clone, Copy, StableHash)] +pub enum InlineAsmRegOrRegClass { + Reg { reg: asm::InlineAsmReg, source_name: Option }, + RegClass(asm::InlineAsmRegClass), +} + +impl InlineAsmRegOrRegClass { + // For `rustc_mir_build` and `clippy_utils` + pub fn as_target(self) -> asm::InlineAsmRegOrRegClass { + match self { + Self::Reg { reg, .. } => asm::InlineAsmRegOrRegClass::Reg(reg), + Self::RegClass(reg_class) => asm::InlineAsmRegOrRegClass::RegClass(reg_class), + } + } + + // For `rustc_ast_lowering` + pub fn reg_class(self) -> asm::InlineAsmRegClass { + self.as_target().reg_class() + } + + // For `rustc_ast_lowering` + pub fn source_name(self) -> Option { + match self { + Self::Reg { source_name, .. } => source_name, + Self::RegClass(_) => None, + } + } +} + +// For `rustc_hir_pretty` +impl fmt::Display for InlineAsmRegOrRegClass { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.as_target().fmt(f) + } +} + #[derive(Debug, Clone, Copy, StableHash)] pub enum InlineAsmOperand<'hir> { In { @@ -3665,7 +3705,7 @@ impl<'hir> InlineAsmOperand<'hir> { pub fn is_clobber(&self) -> bool { matches!( self, - InlineAsmOperand::Out { reg: InlineAsmRegOrRegClass::Reg(_), late: _, expr: None } + InlineAsmOperand::Out { reg: InlineAsmRegOrRegClass::Reg { .. }, expr: None, .. } ) } } @@ -4072,8 +4112,32 @@ pub struct Variant<'hir> { pub span: Span, } -#[derive(Copy, Clone, PartialEq, Debug, StableHash)] -pub enum UseKind { +#[derive(Copy, Clone, Debug, StableHash)] +pub struct UseTree<'hir> { + pub prefix: &'hir UsePath<'hir>, + pub kind: UseKind<'hir>, +} + +impl UseTree<'_> { + pub fn resolutions(&self) -> impl Iterator>> { + Box::new(std::iter::iter!(|| { + match self.kind { + UseKind::Glob => yield self.prefix.res, + UseKind::Single(_) => yield self.prefix.res, + UseKind::Nested { items } => { + for (item, _, _) in items { + for res in item.resolutions() { + yield res; + } + } + } + } + })()) + } +} + +#[derive(Copy, Clone, Debug, StableHash)] +pub enum UseKind<'hir> { /// One import, e.g., `use foo::bar` or `use foo::bar as baz`. /// Also produced for each element of a list `use`, e.g. /// `use foo::{a, b}` lowers to `use foo::a; use foo::b;`. @@ -4085,10 +4149,8 @@ pub enum UseKind { /// Glob import, e.g., `use foo::*`. Glob, - /// Degenerate list import, e.g., `use foo::{a, b}` produces - /// an additional `use foo::{}` for performing checks such as - /// unstable feature gating. May be removed in the future. - ListStem, + /// `use prefix::{...}` + Nested { items: &'hir [(UseTree<'hir>, HirId, LocalDefId)] }, } /// References to traits in impls. @@ -4267,7 +4329,7 @@ impl<'hir> Item<'hir> { expect_extern_crate, (Option, Ident), ItemKind::ExternCrate(s, ident), (*s, *ident); - expect_use, (&'hir UsePath<'hir>, UseKind), ItemKind::Use(p, uk), (p, *uk); + expect_use, UseTree<'hir>, ItemKind::Use(ut), *ut; expect_static, (Mutability, Ident, &'hir Ty<'hir>, BodyId), ItemKind::Static(mutbl, ident, ty, body), (*mutbl, *ident, ty, *body); @@ -4520,7 +4582,7 @@ pub enum ItemKind<'hir> { /// or just /// /// `use foo::bar::baz;` (with `as baz` implicitly on the right). - Use(&'hir UsePath<'hir>, UseKind), + Use(UseTree<'hir>), /// A `static` item. Static(Mutability, Ident, &'hir Ty<'hir>, BodyId), @@ -4615,7 +4677,7 @@ impl ItemKind<'_> { pub fn ident(&self) -> Option { match *self { ItemKind::ExternCrate(_, ident) - | ItemKind::Use(_, UseKind::Single(ident)) + | ItemKind::Use(UseTree { kind: UseKind::Single(ident), .. }) | ItemKind::Static(_, ident, ..) | ItemKind::Const(ident, ..) | ItemKind::Fn { ident, .. } @@ -4628,7 +4690,7 @@ impl ItemKind<'_> { | ItemKind::Trait { ident, .. } | ItemKind::TraitAlias(_, ident, ..) => Some(ident), - ItemKind::Use(_, UseKind::Glob | UseKind::ListStem) + ItemKind::Use(UseTree { kind: UseKind::Glob | UseKind::Nested { .. }, .. }) | ItemKind::ForeignMod { .. } | ItemKind::GlobalAsm { .. } | ItemKind::Impl(_) @@ -4885,6 +4947,7 @@ impl<'hir> From> for Node<'hir> { pub enum Node<'hir> { Param(&'hir Param<'hir>), Item(&'hir Item<'hir>), + NestedUseTree(&'hir UseTree<'hir>), ForeignItem(&'hir ForeignItem<'hir>), TraitItem(&'hir TraitItem<'hir>), ImplItem(&'hir ImplItem<'hir>), @@ -4950,6 +5013,7 @@ impl<'hir> Node<'hir> { Node::TraitItem(TraitItem { ident, .. }) | Node::ImplItem(ImplItem { ident, .. }) | Node::ForeignItem(ForeignItem { ident, .. }) + | Node::NestedUseTree(UseTree { kind: UseKind::Single(ident), .. }) | Node::Field(FieldDef { ident, .. }) | Node::Variant(Variant { ident, .. }) | Node::PathSegment(PathSegment { ident, .. }) => Some(*ident), @@ -4977,6 +5041,7 @@ impl<'hir> Node<'hir> { | Node::Ty(..) | Node::TraitRef(..) | Node::OpaqueTy(..) + | Node::NestedUseTree(_) | Node::Infer(..) | Node::WherePredicate(..) | Node::TestBinderForall(..) diff --git a/compiler/rustc_hir/src/hir/tests.rs b/compiler/rustc_hir/src/hir/tests.rs index 2ac33a369cbdd..d267533ea3cb1 100644 --- a/compiler/rustc_hir/src/hir/tests.rs +++ b/compiler/rustc_hir/src/hir/tests.rs @@ -21,7 +21,7 @@ macro_rules! define_tests { define_tests! { cast_never TyKind Never {} cast_tup TyKind Tup { 0: &[Ty { span: DUMMY_SP, hir_id: HirId::INVALID, kind: TyKind::Never }] } - cast_ptr TyKind Ptr { 0: MutTy { ty: &Ty { span: DUMMY_SP, hir_id: HirId::INVALID, kind: TyKind::Never }, mutbl: Mutability::Not }} + cast_ptr TyKind Ptr { 0: &Ty { span: DUMMY_SP, hir_id: HirId::INVALID, kind: TyKind::Never }, 1: Mutability::Not } cast_array TyKind Array { 0: &Ty { span: DUMMY_SP, hir_id: HirId::INVALID, kind: TyKind::Never }, 1: &ConstArg { diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 9cd4b5d7d001f..ad96fe492e6e7 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -68,7 +68,7 @@ use rustc_ast::Label; use rustc_ast::visit::{VisitorResult, try_visit, visit_opt, walk_list}; use rustc_attr_ir::Attribute; use rustc_hir_id::HirId; -use rustc_span::def_id::LocalDefId; +use rustc_span::def_id::{LocalDefId, LocalModId}; use rustc_span::{Ident, Span, Symbol}; use crate::hir::*; @@ -311,7 +311,7 @@ pub trait Visitor<'v>: Sized { fn visit_ident(&mut self, ident: Ident) -> Self::Result { walk_ident(self, ident) } - fn visit_mod(&mut self, m: &'v Mod<'v>, _s: Span, _n: HirId) -> Self::Result { + fn visit_mod(&mut self, m: &'v Mod<'v>, _s: Span, _id: LocalModId) -> Self::Result { walk_mod(self, m) } fn visit_foreign_item(&mut self, i: &'v ForeignItem<'v>) -> Self::Result { @@ -421,8 +421,13 @@ pub trait Visitor<'v>: Sized { ) -> Self::Result { walk_fn(self, fk, fd, b, id) } - fn visit_use(&mut self, path: &'v UsePath<'v>, hir_id: HirId) -> Self::Result { - walk_use(self, path, hir_id) + fn visit_use( + &mut self, + tree: &'v UseTree<'v>, + hir_id: HirId, + _def_id: LocalDefId, + ) -> Self::Result { + walk_use(self, tree, hir_id) } fn visit_trait_item(&mut self, ti: &'v TraitItem<'v>) -> Self::Result { walk_trait_item(self, ti) @@ -550,12 +555,8 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V:: visit_opt!(visitor, visit_name, orig_name); try_visit!(visitor.visit_ident(ident)); } - ItemKind::Use(ref path, kind) => { - try_visit!(visitor.visit_use(path, item.hir_id())); - match kind { - UseKind::Single(ident) => try_visit!(visitor.visit_ident(ident)), - UseKind::Glob | UseKind::ListStem => {} - } + ItemKind::Use(ref tree) => { + try_visit!(visitor.visit_use(tree, item.hir_id(), item.owner_id.def_id)); } ItemKind::Static(_, ident, ref typ, body) => { try_visit!(visitor.visit_ident(ident)); @@ -583,7 +584,11 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V:: } ItemKind::Mod(ident, ref module) => { try_visit!(visitor.visit_ident(ident)); - try_visit!(visitor.visit_mod(module, item.span, item.hir_id())); + try_visit!(visitor.visit_mod( + module, + item.span, + LocalModId::new_unchecked(item.owner_id.def_id) + )); } ItemKind::ForeignMod { abi: _, items } => { walk_list!(visitor, visit_foreign_item_ref, items); @@ -1022,10 +1027,10 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v, AmbigArg>) - match *kind { TyKind::Slice(ref ty) => try_visit!(visitor.visit_ty_unambig(ty)), - TyKind::Ptr(ref mutable_type) => try_visit!(visitor.visit_ty_unambig(mutable_type.ty)), - TyKind::Ref(ref lifetime, ref mutable_type) => { + TyKind::Ptr(ref ty, _) => try_visit!(visitor.visit_ty_unambig(ty)), + TyKind::Ref(ref lifetime, ref ty, _) => { try_visit!(visitor.visit_lifetime(lifetime)); - try_visit!(visitor.visit_ty_unambig(mutable_type.ty)); + try_visit!(visitor.visit_ty_unambig(ty)); } TyKind::Never => {} TyKind::Tup(tuple_element_types) => { @@ -1264,13 +1269,25 @@ pub fn walk_fn_kind<'v, V: Visitor<'v>>(visitor: &mut V, function_kind: FnKind<' pub fn walk_use<'v, V: Visitor<'v>>( visitor: &mut V, - path: &'v UsePath<'v>, + tree: &'v UseTree<'v>, hir_id: HirId, ) -> V::Result { - let UsePath { segments, ref res, span } = *path; + visitor.visit_id(hir_id); + let UseTree { prefix, kind } = *tree; + let UsePath { segments, ref res, span } = *prefix; for res in res.present_items() { try_visit!(visitor.visit_path(&Path { segments, res, span }, hir_id)); } + + match kind { + UseKind::Single(ident) => try_visit!(visitor.visit_ident(ident)), + UseKind::Glob => {} + UseKind::Nested { items } => { + for (tree, id, def_id) in items { + try_visit!(visitor.visit_use(tree, *id, *def_id)); + } + } + } V::Result::output() } diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index 0f31ed196c3df..0f6041b44b2f2 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -3,7 +3,6 @@ //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(never_type))] #![feature(associated_type_defaults)] #![feature(closure_track_caller)] #![feature(const_default)] @@ -12,6 +11,8 @@ #![feature(derive_const)] #![feature(exhaustive_patterns)] #![feature(final_associated_functions)] +#![feature(iter_macro)] +#![feature(yield_expr)] #![recursion_limit = "256"] // tidy-alphabetical-end @@ -26,18 +27,7 @@ mod target_impls; #[doc(no_inline)] pub use hir::*; -pub use rustc_attr_ir::{self as attrs, find_attr}; pub use rustc_hir_id::*; pub use rustc_span::def_id; -// FIXME: Remove this use tree, replace by `rustc_hir::attrs` or `rustc_attr_ir` imports -#[doc(hidden)] -pub use { - attrs::target::{self, AssocCtxt, MethodKind, Target}, - attrs::{ - AttrArgs, AttrItem, AttrPath, Attribute, ConstStability, DefaultBodyStability, - HashIgnoredAttrId, PartialConstStability, Stability, StabilityLevel, StableSince, - UnstableReason, VERSION_PLACEHOLDER, - }, -}; pub use crate::arena::Arena; diff --git a/compiler/rustc_hir/src/lints.rs b/compiler/rustc_hir/src/lints.rs index 200f1af2142a7..926c9c91ecb61 100644 --- a/compiler/rustc_hir/src/lints.rs +++ b/compiler/rustc_hir/src/lints.rs @@ -17,7 +17,7 @@ pub struct DelayedLint { pub id: HirId, pub span: MultiSpan, pub callback: Box< - dyn for<'a> FnOnce(DiagCtxtHandle<'a>, Level, &dyn std::any::Any) -> Diag<'a, ()> + dyn for<'a> FnOnce(DiagCtxtHandle<'a>, Level, &dyn std::any::Any) -> Diag<'a> + DynSend + DynSync + 'static, diff --git a/compiler/rustc_hir_analysis/Cargo.toml b/compiler/rustc_hir_analysis/Cargo.toml index 06092f53272ce..a250ed77bb8c2 100644 --- a/compiler/rustc_hir_analysis/Cargo.toml +++ b/compiler/rustc_hir_analysis/Cargo.toml @@ -13,6 +13,7 @@ itertools = "0.15" rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } +rustc_attr_ir = { path = "../rustc_attr_ir" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } diff --git a/compiler/rustc_hir_analysis/src/check/always_applicable.rs b/compiler/rustc_hir_analysis/src/check/always_applicable.rs index 11960d57016f6..a7e02cee4ea75 100644 --- a/compiler/rustc_hir_analysis/src/check/always_applicable.rs +++ b/compiler/rustc_hir_analysis/src/check/always_applicable.rs @@ -4,11 +4,12 @@ //! //! This is necessary for `Drop` and negative impls to be well-formed. +use rustc_attr_ir::find_attr; use rustc_data_structures::fx::FxHashSet; use rustc_errors::codes::*; use rustc_errors::{ErrorGuaranteed, struct_span_code_err}; use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt}; -use rustc_infer::traits::{Obligation, ObligationCause, ObligationCauseCode}; +use rustc_infer::traits::{ObligationCause, ObligationCauseCode}; use rustc_middle::ty::util::CheckRegions; use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, TypingMode}; use rustc_span::{span_bug, sym}; @@ -133,45 +134,6 @@ pub(crate) fn check_negative_auto_trait_impl<'tcx>( } } -/// Checks if the self ty's where-clauses are able to be proven. For instance, if we have multiple -/// overlapping drop impls, and we have `[T]: Sized` on both the impls and the self ty, we shouldn't -/// error or ICE, since neither the ADT nor the impls are nameable in practice. -/// -/// We already emit errors for the case where the impossible bound exists only on the self ty, or -/// only on the impl(s). -pub(crate) fn is_impossible_self_ty(tcx: TyCtxt<'_>, adt_did: LocalDefId) -> bool { - let clauses = tcx.clauses_of(adt_did).clauses; - if clauses.is_empty() { - return false; - } - - // Be conservative in cases where we have `W` and a method like `Self: Sized`, - // since that method *may* have some substitutions where the predicates hold. - // - // This replicates the logic we use in coherence. - let infcx = tcx - .infer_ctxt() - .ignoring_regions() - .with_next_trait_solver(true) - .enable_next_solver_overflow_fcw(false) - .build(TypingMode::Coherence); - let param_env = ty::ParamEnv::empty(); - let args = infcx.fresh_args_for_item(tcx.def_span(adt_did), adt_did.to_def_id()); - - let obligations = clauses.iter().map(|(clause, span)| { - Obligation::new( - tcx, - ObligationCause::dummy_with_span(*span), - param_env, - ty::EarlyBinder::bind(tcx, *clause).instantiate(tcx, args).skip_norm_wip(), - ) - }); - - let ocx = ObligationCtxt::new(&infcx); - ocx.register_obligations(obligations); - ocx.try_evaluate_obligations().has_errors() -} - fn ensure_impl_params_and_item_params_correspond<'tcx>( tcx: TyCtxt<'tcx>, impl_def_id: LocalDefId, @@ -211,7 +173,7 @@ fn ensure_impl_params_and_item_params_correspond<'tcx>( as the {self_descr} definition", ), ); - Err(err.emit()) + Err(err.emit_err()) } fn ensure_all_fields_are_const_destruct<'tcx>( @@ -278,7 +240,7 @@ fn ensure_all_fields_are_const_destruct<'tcx>( None, ); } - Err(diag.emit()) + Err(diag.emit_err()) }) .collect() } @@ -362,7 +324,7 @@ fn ensure_impl_predicates_are_implied_by_item_defn<'tcx>( but the {self_descr} it is implemented for does not", ) .with_span_note(item_span, "the implementor must specify the same requirement") - .emit(), + .emit_err(), ); } } @@ -395,7 +357,7 @@ fn ensure_impl_predicates_are_implied_by_item_defn<'tcx>( but the {self_descr} it is implemented for does not", ) .with_span_note(item_span, "the implementor must specify the same requirement") - .emit(), + .emit_err(), ); } return Err(guar.unwrap()); @@ -442,7 +404,7 @@ fn check_drop_xor_pin_drop<'tcx>( } (Some(span), None) => { if tcx.adt_def(adt_def_id).is_pin_project() { - let pin_v2_span = rustc_hir::find_attr!(tcx, adt_def_id, PinV2(attr) => *attr); + let pin_v2_span = find_attr!(tcx, adt_def_id, PinV2(attr) => *attr); let adt_name = tcx.item_name(adt_def_id); return Err(tcx.dcx().emit_err(crate::diagnostics::PinV2WithoutPinDrop { span, diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 705bb780a3ecf..1e75902884fe1 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -2,14 +2,15 @@ use std::cell::LazyCell; use std::ops::ControlFlow; use rustc_abi::{ExternAbi, FieldIdx, MAX_SIMD_LANES, ScalableElt}; +use rustc_attr_ir::ReprAttr::ReprPacked; +use rustc_attr_ir::find_attr; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::codes::*; use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level, MultiSpan}; use rustc_hir as hir; -use rustc_hir::attrs::ReprAttr::ReprPacked; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def::{CtorKind, DefKind}; -use rustc_hir::{Node, find_attr, intravisit}; +use rustc_hir::{Node, intravisit}; use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::traits::{Obligation, ObligationCauseCode, TraitErrors, WellFormedLoc}; use rustc_lint_defs::builtin::{ @@ -19,6 +20,7 @@ use rustc_macros::Diagnostic; use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_bound_vars::ResolvedArg; use rustc_middle::middle::stability::EvalResult; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::error::TypeErrorToStringExt; use rustc_middle::ty::layout::LayoutError; use rustc_middle::ty::util::Discr; @@ -43,7 +45,7 @@ use crate::check::wfcheck::{ use crate::collect::ItemCtxt; use crate::diagnostics; -fn add_abi_diag_help(abi: ExternAbi, diag: &mut Diag<'_, G>) { +fn add_abi_diag_help(abi: ExternAbi, diag: &mut Diag<'_>) { if let ExternAbi::Cdecl { unwind } = abi { let c_abi = ExternAbi::C { unwind }; diag.help(format!("use `extern {c_abi}` instead",)); @@ -62,8 +64,8 @@ pub fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: ExternAbi abi: ExternAbi, } - impl<'a> Diagnostic<'a, ()> for UnsupportedCallingConventions { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + impl<'a> Diagnostic<'a> for UnsupportedCallingConventions { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let Self { abi } = self; let mut lint = Diag::new( dcx, @@ -557,7 +559,7 @@ fn sanity_check_found_hidden_type<'tcx>( } else { let span = tcx.def_span(key.def_id); let other = ty::ProvisionalHiddenType { ty: hidden_ty, span }; - Err(ty.build_mismatch_error(&other, tcx)?.emit()) + Err(ty.build_mismatch_error(&other, tcx)?.emit_err()) } } @@ -775,9 +777,23 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), if has_default { // need to store default and type of default let ct = tcx.const_param_default(param.def_id).skip_binder(); - if let ty::ConstKind::Alias(_, alias_const) = ct.kind() - && let Some(def_id) = alias_const.kind.opt_def_id() - { + if let ty::ConstKind::Alias(_, alias_const) = ct.kind() { + let def_id = match alias_const.kind { + ty::AliasConstKind::Projection { def_id } => def_id, + ty::AliasConstKind::InherentSelf { def_id } => { + // NOTE: typically, InherentSelf is illegal to pass to type_of, + // because the generic args are incorrect (type_of expects impl-form + // arguments). However, we are just checking ensure_ok().type_of(), + // we are not instantiating the result, so it's OK here. + def_id + } + ty::AliasConstKind::InherentImpl { .. } => span_bug!( + tcx.def_span(param.def_id), + "const_param_default should return an unnormalized constant, which should always be InherentSelf, not InherentImpl" + ), + ty::AliasConstKind::Free { def_id } => def_id, + ty::AliasConstKind::Anon { def_id } => def_id, + }; tcx.ensure_ok().type_of(def_id); } } @@ -1527,6 +1543,8 @@ fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) { match element_ty.kind() { ty::Param(_) => (), // pass struct([T; 4]) through, let monomorphization catch errors ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::RawPtr(_, _) => (), // struct([u8; 4]) is ok + // So we can create structs like; `struct bfloat16x4_t (f16b x 4);` + ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::F16B) => (), _ => { struct_span_code_err!( tcx.dcx(), @@ -1592,6 +1610,8 @@ fn check_scalable_vector(tcx: TyCtxt<'_>, span: Span, def_id: LocalDefId, scalab // bools match element_ty.kind() { ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Bool => (), + // We need to treat a `bfloat` (`f16b`) as a primitive scalar + ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::F16B) => (), _ => { let mut err = tcx.dcx().struct_span_err( span, @@ -2046,7 +2066,7 @@ fn detect_discriminant_duplicate<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) let mut i = 0; while i < discrs.len() { let var_i_idx = discrs[i].0; - let mut error: Option> = None; + let mut error: Option> = None; let mut o = i + 1; while o < discrs.len() { @@ -2290,7 +2310,7 @@ fn opaque_type_cycle_error(tcx: TyCtxt<'_>, opaque_def_id: LocalDefId) -> ErrorG if !label { err.span_label(span, "cannot resolve opaque type"); } - err.emit() + err.emit_err() } pub(super) fn check_coroutine_obligations( diff --git a/compiler/rustc_hir_analysis/src/check/compare_eii.rs b/compiler/rustc_hir_analysis/src/check/compare_eii.rs index aefe53f2f4399..6e22ce3d84b76 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_eii.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_eii.rs @@ -6,12 +6,12 @@ use std::borrow::Cow; use std::iter; +use rustc_attr_ir::{EiiImplResolution, find_attr}; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{Applicability, E0806, struct_span_code_err}; -use rustc_hir::attrs::EiiImplResolution; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::{self as hir, FnSig, HirId, ItemKind, find_attr}; +use rustc_hir::{self as hir, FnSig, HirId, ItemKind}; use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::{ObligationCause, ObligationCauseCode, TraitErrors}; use rustc_middle::ty::error::{ExpectedFound, TypeError}; @@ -201,7 +201,7 @@ pub(crate) fn compare_eii_statics<'tcx>( ); diag.span_note(eii_attr_span, "expected this because of this attribute"); - return Err(diag.emit()); + return Err(diag.emit_err()); } // Check that all obligations are satisfied by the implementation's @@ -349,7 +349,7 @@ fn check_early_region_bounds<'tcx>( }); diag.span_label(eii_attr_span, format!("required because of this attribute")); - return Err(diag.emit()); + return Err(diag.emit_err()); } fn check_number_of_arguments<'tcx>( @@ -447,7 +447,7 @@ fn report_number_of_arguments_mismatch<'tcx>( err.span_label(eii_attr_span, format!("required because of this attribute")); - err.emit() + err.emit_err() } fn report_eii_mismatch<'tcx>( @@ -521,7 +521,7 @@ fn report_eii_mismatch<'tcx>( None, ); - diag.emit() + diag.emit_err() } #[instrument(level = "debug", skip(infcx))] diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 4061d247f59f1..204b6f913cb8b 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -638,7 +638,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( false, None, ); - return Err(diag.emit()); + return Err(diag.emit_err()); } } @@ -950,7 +950,7 @@ impl<'tcx> ty::FallibleTypeFolder> for RemapHiddenTyRegions<'tcx> { "hidden type must only reference lifetimes captured by this impl trait", ) .with_note(format!("hidden type inferred to be `{}`", self.ty)) - .emit() + .emit_err() } None => { // This code path is not reached in any tests, but may be @@ -1066,12 +1066,12 @@ fn report_trait_method_mismatch<'tcx>( }; }; } else if let Some(trait_ty) = trait_sig.inputs().get(*i) { - diag.span_suggestion_verbose( + rustc_middle::ty::print::with_crate_prefix!(diag.span_suggestion_verbose( impl_err_span, "change the parameter type to match the trait", trait_ty, Applicability::MachineApplicable, - ); + )); } } _ => {} @@ -1091,7 +1091,7 @@ fn report_trait_method_mismatch<'tcx>( None, ); - diag.emit() + diag.emit_err() } fn check_region_bounds_on_impl_item<'tcx>( @@ -1134,7 +1134,7 @@ fn check_region_bounds_on_impl_item<'tcx>( bounds_span, where_span, }) - .emit_unless_delay(delay); + .emit_err_unless_delay(delay); Err(reported) } @@ -1435,7 +1435,7 @@ fn check_region_late_boundedness<'tcx>( } } - Some(diag.emit()) + Some(diag.emit_err()) } fn find_region_in_clauses<'tcx>( @@ -1538,7 +1538,7 @@ fn compare_self_type<'tcx>( } else { err.note_trait_signature(trait_m.name(), trait_m.signature(tcx)); } - return Err(err.emit_unless_delay(delay)); + return Err(err.emit_err_unless_delay(delay)); } (true, false) => { @@ -1559,7 +1559,7 @@ fn compare_self_type<'tcx>( err.note_trait_signature(trait_m.name(), trait_m.signature(tcx)); } - return Err(err.emit_unless_delay(delay)); + return Err(err.emit_err_unless_delay(delay)); } } @@ -1719,8 +1719,8 @@ fn compare_number_of_generics<'tcx>( err.span_label(*span, "`impl Trait` introduces an implicit type parameter"); } - let reported = err.emit_unless_delay(delay); - err_occurred = Some(reported); + let guar = err.emit_err_unless_delay(delay); + err_occurred = Some(guar); } } @@ -1901,7 +1901,7 @@ fn compare_number_of_method_arguments<'tcx>( } } - return Err(err.emit_unless_delay(delay)); + return Err(err.emit_err_unless_delay(delay)); } Ok(()) @@ -2028,7 +2028,7 @@ fn compare_synthetic_generics<'tcx>( ); }; } - error_found = Some(err.emit_unless_delay(delay)); + error_found = Some(err.emit_err_unless_delay(delay)); } } if let Some(reported) = error_found { Err(reported) } else { Ok(()) } @@ -2130,8 +2130,8 @@ fn compare_generic_param_kinds<'tcx>( err.span_context(impl_header_span); err.span_label(param_impl_span, make_param_message("found", param_impl)); - let reported = err.emit_unless_delay(delay); - return Err(reported); + let guar = err.emit_err_unless_delay(delay); + return Err(guar); } } @@ -2162,8 +2162,8 @@ pub(super) fn compare_const_directness<'tcx>( if trait_is_gca == impl_is_gca { return Ok(()); } - // feature(generic_const_args) is allowed to impl non-GCA traits with a GCA const - if tcx.features().generic_const_args() && !trait_is_gca && impl_is_gca { + // feature(gca_const_items) is allowed to impl non-GCA traits with a GCA const + if tcx.features().gca_const_items() && !trait_is_gca && impl_is_gca { return Ok(()); } @@ -2171,24 +2171,24 @@ pub(super) fn compare_const_directness<'tcx>( tcx.dcx() .struct_span_err( tcx.def_span(impl_const_item.def_id), - "implementation of a `#[rustc_always_gca]` must have a `direct_const_arg!` RHS", + "implementation of a `#[rustc_always_gca]` must have a `gca!` RHS", ) .with_span_note( tcx.def_span(trait_const_item.def_id), "trait declaration of const is marked as `#[rustc_always_gca]`", ) - .emit() + .emit_err() } else { tcx.dcx() .struct_span_err( tcx.def_span(impl_const_item.def_id), - "implementation of a regular const cannot have a `direct_const_arg!` RHS", + "implementation of a regular const cannot have a `gca!` RHS", ) .with_span_note( tcx.def_span(trait_const_item.def_id), "trait declaration of const is not marked as `#[rustc_always_gca]`", ) - .emit() + .emit_err() }; Err(guar) } @@ -2303,7 +2303,7 @@ fn compare_const_clause_entailment<'tcx>( false, None, ); - return Err(diag.emit()); + return Err(diag.emit_err()); }; // Check that all obligations are satisfied by the implementation's diff --git a/compiler/rustc_hir_analysis/src/check/entry.rs b/compiler/rustc_hir_analysis/src/check/entry.rs index a115ccba9ad08..17123376159de 100644 --- a/compiler/rustc_hir_analysis/src/check/entry.rs +++ b/compiler/rustc_hir_analysis/src/check/entry.rs @@ -1,7 +1,8 @@ use std::ops::Not; +use rustc_attr_ir::find_attr; use rustc_hir as hir; -use rustc_hir::{Node, find_attr}; +use rustc_hir::Node; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::TraitErrors; use rustc_middle::ty::{self, TyCtxt, TypingMode, Unnormalized}; diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index cca93e8aef0ec..86e66f7658d65 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -1,9 +1,10 @@ //! Type-checking for the `#[rustc_intrinsic]` intrinsics that the compiler exposes. +use rustc_attr_ir::lang_items::LangItem; use rustc_errors::DiagMessage; use rustc_hir as hir; -use rustc_hir::attrs::lang_items::LangItem; use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::{self, Const, Ty, TyCtxt}; use rustc_span::def_id::LocalDefId; use rustc_span::{Span, Symbol, sym}; @@ -66,7 +67,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi // safe extern fns are otherwise unprecedented. // tidy-alphabetical-start - | sym::abort + | sym::abort_immediate | sym::add_with_overflow | sym::aggregate_raw_ptr | sym::align_of @@ -150,7 +151,6 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::minimumf128 | sym::mul_with_overflow | sym::needs_drop - | sym::non_exhaustive | sym::offload | sym::offload_get_num_devices | sym::offset_of @@ -207,17 +207,19 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::type_id_function_ptr | sym::type_id_generics | sym::type_id_is_signed + | sym::type_id_non_exhaustive | sym::type_id_points_mutably | sym::type_id_points_to + | sym::type_id_size_of + | sym::type_id_type_of + | sym::type_id_variant_name + | sym::type_id_variant_non_exhaustive | sym::type_id_variants | sym::type_id_vtable | sym::type_name - | sym::type_of | sym::ub_checks | sym::va_copy | sym::variant_count - | sym::variant_name - | sym::variant_non_exhaustive | sym::wrapping_add | sym::wrapping_mul | sym::wrapping_sub @@ -288,7 +290,7 @@ pub(crate) fn check_intrinsic_type( let n_lts = 0; let (n_tps, n_cts, inputs, output) = match intrinsic_name { sym::autodiff => (4, 0, vec![param(0), param(1), param(2)], param(3)), - sym::abort => (0, 0, vec![], tcx.types.never), + sym::abort_immediate => (0, 0, vec![], tcx.types.never), sym::amdgpu_dispatch_ptr => (0, 0, vec![], Ty::new_imm_ptr(tcx, tcx.types.unit)), sym::unreachable => (0, 0, vec![], tcx.types.never), sym::breakpoint => (0, 0, vec![], tcx.types.unit), @@ -296,7 +298,7 @@ pub(crate) fn check_intrinsic_type( sym::size_of_val | sym::align_of_val => { (1, 0, vec![Ty::new_imm_ptr(tcx, param(0))], tcx.types.usize) } - sym::size_of_type_id => (0, 0, vec![type_id_ty()], Ty::new_option(tcx, tcx.types.usize)), + sym::type_id_size_of => (0, 0, vec![type_id_ty()], Ty::new_option(tcx, tcx.types.usize)), sym::offset_of => (1, 0, vec![tcx.types.u32, tcx.types.u32], tcx.types.usize), sym::field_offset => (1, 0, vec![], tcx.types.usize), sym::rustc_peek => (1, 0, vec![param(0)], param(0)), @@ -339,8 +341,12 @@ pub(crate) fn check_intrinsic_type( sym::type_id_points_mutably => (0, 0, vec![type_id_ty()], tcx.types.bool), sym::type_id_points_to => (0, 0, vec![type_id_ty()], Ty::new_option(tcx, type_id_ty())), sym::type_id_variants => (0, 0, vec![type_id_ty()], tcx.types.usize), - sym::variant_name => (0, 0, vec![type_id_ty(), tcx.types.usize], Ty::new_static_str(tcx)), - sym::variant_non_exhaustive => (0, 0, vec![type_id_ty(), tcx.types.usize], tcx.types.bool), + sym::type_id_variant_name => { + (0, 0, vec![type_id_ty(), tcx.types.usize], Ty::new_static_str(tcx)) + } + sym::type_id_variant_non_exhaustive => { + (0, 0, vec![type_id_ty(), tcx.types.usize], tcx.types.bool) + } sym::type_id_vtable => { let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, span); let dyn_metadata_adt_ref = tcx.adt_def(dyn_metadata); @@ -355,7 +361,7 @@ pub(crate) fn check_intrinsic_type( (0, 0, vec![type_id_ty(); 2], ret_ty) } - sym::type_of => ( + sym::type_id_type_of => ( 0, 0, vec![type_id_ty()], @@ -364,7 +370,7 @@ pub(crate) fn check_intrinsic_type( sym::field_representing_type_actual_type_id => (0, 0, vec![type_id_ty()], type_id_ty()), sym::field_representing_type_name => (0, 0, vec![type_id_ty()], Ty::new_static_str(tcx)), sym::field_representing_type_offset => (0, 0, vec![type_id_ty()], tcx.types.usize), - sym::non_exhaustive => (0, 0, vec![type_id_ty()], tcx.types.bool), + sym::type_id_non_exhaustive => (0, 0, vec![type_id_ty()], tcx.types.bool), sym::type_id_generics => ( 0, 0, diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 8d936b2cd3498..0a57a177be985 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -76,9 +76,9 @@ use std::num::NonZero; pub use check::check_abi; use rustc_abi::VariantIdx; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::{ErrorGuaranteed, pluralize, struct_span_code_err}; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; use rustc_index::bit_set::DenseBitSet; @@ -125,11 +125,7 @@ pub(super) fn provide(providers: &mut Providers) { } fn adt_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option { - let dtor = tcx.calculate_dtor( - def_id, - always_applicable::check_drop_impl, - always_applicable::is_impossible_self_ty, - ); + let dtor = tcx.calculate_dtor(def_id, always_applicable::check_drop_impl); if dtor.is_none() && tcx.features().async_drop() { if let Some(async_dtor) = adt_async_destructor(tcx, def_id) { // When type has AsyncDrop impl, but doesn't have Drop impl, generate error @@ -141,11 +137,7 @@ fn adt_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option } fn adt_async_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option { - let result = tcx.calculate_async_dtor( - def_id, - always_applicable::check_drop_impl, - always_applicable::is_impossible_self_ty, - ); + let result = tcx.calculate_async_dtor(def_id, always_applicable::check_drop_impl); // Async drop in libstd/libcore would become insta-stable — catch that mistake. if result.is_some() && tcx.features().staged_api() { span_bug!(tcx.def_span(def_id), "don't use async drop in libstd, it becomes insta-stable"); @@ -724,7 +716,7 @@ pub fn check_function_signature<'tcx>( false, None, ); - return Err(diag.emit()); + return Err(diag.emit_err()); } } diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 30947ce0f4f98..dc4b6a6fd319c 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -4,16 +4,16 @@ use std::ops::{ControlFlow, Deref}; use hir::intravisit::{self, Visitor}; use rustc_abi::{ExternAbi, ScalableElt}; use rustc_ast as ast; +use rustc_attr_ir::lang_items::LangItem; +use rustc_attr_ir::{EiiDecl, EiiImpl, EiiImplResolution, find_attr}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::transitive_relation::TransitiveRelationBuilder; use rustc_errors::codes::*; use rustc_errors::{Applicability, ErrorGuaranteed, msg, pluralize, struct_span_code_err}; use rustc_hir as hir; -use rustc_hir::attrs::lang_items::LangItem; -use rustc_hir::attrs::{EiiDecl, EiiImpl, EiiImplResolution}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::{AmbigArg, ItemKind, find_attr}; +use rustc_hir::{AmbigArg, ItemKind}; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{BoundRegionConversionTime, SolverRegionConstraint, TyCtxtInferExt}; use rustc_infer::traits::{PredicateObligations, TraitErrors}; @@ -292,7 +292,7 @@ pub(super) fn check_item<'tcx>( .struct_span_err(sp, "impls of auto traits cannot be default") .with_span_labels(of_trait.defaultness_span, "default because of this") .with_span_label(sp, "auto trait") - .emit()); + .emit_err()); } match header.polarity { ty::ImplPolarity::Positive => { @@ -312,7 +312,7 @@ pub(super) fn check_item<'tcx>( E0750, "negative impls cannot be default impls" ) - .emit()); + .emit_err()); } } } @@ -905,7 +905,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &ty::GenericParamDef) -> Result<(), Er tcx.disabled_nightly_features(&mut diag, features); } - Err(diag.emit()) + Err(diag.emit_err()) } } } @@ -1289,7 +1289,7 @@ pub(super) fn check_const_item<'tcx>( tcx.require_lang_item(LangItem::ConstParamTy, span), ); } - // FIXME(min_generic_const_args): We *might* want to move this check to `type_of`, so we can + // FIXME(gca_min_const_items): We *might* want to move this check to `type_of`, so we can // return `ty::Error` if it references invalid params. However, doing so is hard, because // `type_of` doesn't know if it's a direct const - `const_of_item` determines that, and // `const_of_item` calls `type_of`. @@ -1780,7 +1780,7 @@ fn check_method_receiver<'tcx>( ), ) .with_help(msg!("consider changing to `self`, `&self`, `&mut self`, or a type implementing `Receiver` such as `self: Box`, `self: Rc`, or `self: Arc`")) - .emit() + .emit_err() } None | Some(ArbitrarySelfTypesLevel::Basic) if receiver_is_valid( @@ -1804,7 +1804,7 @@ fn check_method_receiver<'tcx>( ), ) .with_help(msg!("consider changing to `self`, `&self`, `&mut self`, or a type implementing `Receiver` such as `self: Box`, `self: Rc`, or `self: Arc`")) - .emit() + .emit_err() } _ => // Report error; would not have worked with `arbitrary_self_types[_pointers]`. @@ -2213,7 +2213,7 @@ fn report_bivariance<'tcx>( // Silence potentially redundant error, as the item had a parse error. diag.delay_as_bug() } else { - diag.emit() + diag.emit_err() } } @@ -2403,7 +2403,7 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { ); err.note(format!("it is a {ty}")); err.note(format!("and here it is `Debug`ged :3 {ty:?}")); - r = Err(err.emit()); + r = Err(err.emit_err()); } } } @@ -2433,7 +2433,7 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { body.type_outlives, builder.freeze(), ); - self.infcx.insert_placeholder_assumptions(u, Some(assumptions)); + self.infcx.insert_placeholder_assumptions(u, assumptions); self.check_test_binder_body(body.value); let solver_region_constraint = self.infcx.get_solver_region_constraint(); let constraint = ty::region_constraint::eagerly_handle_placeholders_in_universe( diff --git a/compiler/rustc_hir_analysis/src/check_unused.rs b/compiler/rustc_hir_analysis/src/check_unused.rs index 3c8bd2af9b4c1..d8dafd509cf24 100644 --- a/compiler/rustc_hir_analysis/src/check_unused.rs +++ b/compiler/rustc_hir_analysis/src/check_unused.rs @@ -12,8 +12,8 @@ struct UnusedImport<'tcx> { span: Span, } -impl<'a, 'tcx> Diagnostic<'a, ()> for UnusedImport<'tcx> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { +impl<'a, 'tcx> Diagnostic<'a> for UnusedImport<'tcx> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let Self { tcx, span } = self; if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(span) { Diag::new(dcx, level, format!("unused import: `{snippet}`")) @@ -44,16 +44,15 @@ pub(super) fn check_unused_traits(tcx: TyCtxt<'_>, (): ()) { if used_trait_imports.contains(&id) { continue; } - let item = tcx.hir_expect_item(id); - if item.span.is_dummy() { + let span = tcx.def_span(id); + if span.is_dummy() { continue; } - let (path, _) = item.expect_use(); tcx.emit_node_span_lint( UNUSED_IMPORTS, - item.hir_id(), - path.span, - UnusedImport { tcx, span: path.span }, + tcx.local_def_id_to_hir_id(id), + span, + UnusedImport { tcx, span }, ); } } diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index f7faefa240abe..e7f20766bc8eb 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -3,11 +3,11 @@ use std::collections::BTreeMap; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; use rustc_hir::ItemKind; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::infer::{self, InferCtxt, RegionResolutionError, SubregionOrigin, TyCtxtInferExt}; use rustc_infer::traits::{Obligation, TraitErrors}; @@ -115,7 +115,11 @@ fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaran Err(CopyImplementationError::HasDestructor(did)) => { let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span; let impl_ = tcx.def_span(did); - Err(tcx.dcx().emit_err(diagnostics::CopyImplOnTypeWithDtor { span, impl_ })) + Err(tcx.dcx().emit_err(diagnostics::TraitImplOnTypeWithDtor { + span, + impl_, + trait_name: sym::Copy, + })) } Err(CopyImplementationError::HasUnsafeFields) => { let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span; @@ -537,6 +541,15 @@ pub(crate) fn reborrow_info<'tcx>( assert_field_type_is_copy(tcx, &infcx, impl_did, param_env, field.ty, field.span)?; } + if let Some(did) = def.destructor(tcx).map(|dtor| dtor.did) { + let impl_ = tcx.def_span(did); + return Err(tcx.dcx().emit_err(diagnostics::TraitImplOnTypeWithDtor { + span, + impl_, + trait_name: sym::Reborrow, + })); + } + Ok(()) } @@ -1003,7 +1016,7 @@ fn infringing_fields_error<'tcx>( None, ); - err.emit() + err.emit_err() } fn visit_implementation_of_coerce_pointee_validity( diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin/coerce_shared.rs b/compiler/rustc_hir_analysis/src/coherence/builtin/coerce_shared.rs index bd7130ee48bc8..4d685ec3c6810 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin/coerce_shared.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin/coerce_shared.rs @@ -1,11 +1,10 @@ +use rustc_attr_ir::lang_items::LangItem; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; use rustc_hir::ItemKind; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; +use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::{Obligation, TraitErrors}; -use rustc_middle::ty::relate::solver_relating::RelateExt; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypingMode, Unnormalized}; use rustc_span::Span; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; @@ -131,14 +130,14 @@ fn first_explicit_lifetime_span_in_ambig_ty(ty: &hir::Ty<'_, hir::AmbigArg>) -> fn first_explicit_lifetime_span_in_ty(ty: &hir::Ty<'_>) -> Option { match ty.kind { - hir::TyKind::Ref(lifetime, mut_ty) => first_explicit_lifetime_span(lifetime) - .or_else(|| first_explicit_lifetime_span_in_ty(mut_ty.ty)), + hir::TyKind::Ref(lifetime, ty, _) => first_explicit_lifetime_span(lifetime) + .or_else(|| first_explicit_lifetime_span_in_ty(ty)), hir::TyKind::Slice(ty) | hir::TyKind::Array(ty, _) | hir::TyKind::Pat(ty, _) | hir::TyKind::FieldOf(ty, _) | hir::TyKind::View(ty, _) => first_explicit_lifetime_span_in_ty(ty), - hir::TyKind::Ptr(mut_ty) => first_explicit_lifetime_span_in_ty(mut_ty.ty), + hir::TyKind::Ptr(ty, _) => first_explicit_lifetime_span_in_ty(ty), hir::TyKind::Tup(tys) => tys.iter().find_map(first_explicit_lifetime_span_in_ty), hir::TyKind::Path(qpath) => first_explicit_lifetime_span_in_qpath(qpath), hir::TyKind::TraitObject(bounds, lifetime) => bounds @@ -400,29 +399,20 @@ fn validate_reborrow_field_access( ) -> Result<(), ErrorGuaranteed> { let module = tcx.parent_module_from_def_id(impl_did); let variant = def.non_enum_variant(); - if variant.field_list_has_applicable_non_exhaustive() { - return Err(tcx.dcx().emit_err(diagnostics::CoerceSharedInaccessibleField { + + if variant.field_list_has_applicable_non_exhaustive() + || variant.fields.iter().any(|f| !f.vis.is_accessible_from(module, tcx)) + { + Err(tcx.dcx().emit_err(diagnostics::CoerceSharedInaccessibleField { span: diagnostic_context.impl_span, type_span: role.type_span(diagnostic_context), trait_name, role: role.as_str(), type_name: tcx.item_name(def.did()), - })); - } - - for field in &variant.fields { - if !field.vis.is_accessible_from(module, tcx) { - return Err(tcx.dcx().emit_err(diagnostics::CoerceSharedInaccessibleField { - span: diagnostic_context.impl_span, - type_span: role.type_span(diagnostic_context), - trait_name, - role: role.as_str(), - type_name: tcx.item_name(def.did()), - })); - } + })) + } else { + Ok(()) } - - Ok(()) } fn validate_coerce_shared_fields<'tcx>( @@ -760,6 +750,9 @@ enum FieldRelation { // instantiating a field can expose projections. Each candidate relation uses a // fresh inference context, so failed checks cannot affect the next one; this // intentionally normalizes the fields for each check. +// +// FIXME(field_projections): This function should take `Unnormalized>` for +// `source_ty` and `target_ty`. fn field_tys_satisfy_relation_after_normalization_and_resolution<'tcx>( tcx: TyCtxt<'tcx>, impl_did: LocalDefId, @@ -785,7 +778,10 @@ fn field_tys_satisfy_relation_after_normalization_and_resolution<'tcx>( match relation { FieldRelation::Equal => { - if infcx.relate(param_env, source_ty, ty::Variance::Invariant, target_ty, span).is_err() + if infcx + .at(&cause, param_env) + .relate(DefineOpaqueTypes::Yes, source_ty, ty::Variance::Invariant, target_ty) + .is_err() { return false; } diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs index 94234cd185a02..591ff35becf81 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs @@ -7,10 +7,10 @@ //! `tcx.inherent_impls(def_id)`). That value, however, //! is computed by selecting an idea from this table. +use rustc_attr_ir::find_attr; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::find_attr; use rustc_middle::ty::fast_reject::{SimplifiedType, TreatParams, simplify_type}; use rustc_middle::ty::{self, CrateInherentImpls, Ty, TyCtxt}; use rustc_span::{ErrorGuaranteed, bug}; diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs index 9052f11e109b6..05ceb049e0dba 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs @@ -91,7 +91,7 @@ impl<'tcx> InherentOverlapChecker<'tcx> { ) .with_span_label(span, format!("duplicate definitions for `{ident}`")) .with_span_label(*former, format!("other definition for `{ident}`")) - .emit()); + .emit_err()); } IndexEntry::Vacant(entry) => { entry.insert(span); @@ -142,7 +142,7 @@ impl<'tcx> InherentOverlapChecker<'tcx> { traits::add_placeholder_note(&mut err); } - res = Err(err.emit()); + res = Err(err.emit_err()); } } res diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs index 894cd584ae41d..c1ca62d333e64 100644 --- a/compiler/rustc_hir_analysis/src/coherence/mod.rs +++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs @@ -5,9 +5,9 @@ // done by the orphan and overlap modules. Then we build up various // mappings. That mapping code resides here. +use rustc_attr_ir::lang_items::LangItem; use rustc_errors::codes::*; use rustc_errors::struct_span_code_err; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::query::Providers; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt, elaborate}; @@ -87,7 +87,7 @@ fn enforce_trait_manually_implementable( err.code(E0328); } - return Err(err.emit()); + return Err(err.emit_err()); } if let ty::trait_def::TraitSpecializationKind::AlwaysApplicable = trait_def.specialization_kind @@ -135,7 +135,7 @@ fn enforce_empty_impls_for_marker_traits( E0715, "impls for marker traits cannot contain items" ) - .emit()) + .emit_err()) } /// Adds query implementations to the [Providers] vtable, see [`rustc_middle::query`]. @@ -239,7 +239,7 @@ fn check_object_overlap<'tcx>( tcx.def_path_str(trait_def_id) ), ) - .emit()); + .emit_err()); } } } diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index 44a1211687c29..d2c3a08eb714f 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -321,7 +321,7 @@ fn orphan_check<'tcx>( // (1) Instantiate all generic params with fresh inference vars. let infcx = tcx .infer_ctxt() - .with_next_trait_solver(tcx.next_trait_solver_in_coherence()) + .with_next_trait_solver(true) .enable_next_solver_overflow_fcw(false) .build(TypingMode::Coherence); let cause = traits::ObligationCause::dummy(); @@ -487,7 +487,7 @@ fn emit_orphan_check_error<'tcx>( } } - diag.emit() + diag.emit_err() } traits::OrphanCheckErr::UncoveredTyParams(UncoveredTyParams { uncovered, local_ty }) => { let mut guar = None; diff --git a/compiler/rustc_hir_analysis/src/coherence/unsafety.rs b/compiler/rustc_hir_analysis/src/coherence/unsafety.rs index ce0171a4d2d7d..cfca879abe8f1 100644 --- a/compiler/rustc_hir_analysis/src/coherence/unsafety.rs +++ b/compiler/rustc_hir_analysis/src/coherence/unsafety.rs @@ -1,10 +1,10 @@ //! Unsafety checker: every impl either implements a trait defined in this //! crate or pertains to a type defined in this crate. +use rustc_attr_ir::lang_items::LangItem; use rustc_errors::codes::*; use rustc_errors::struct_span_code_err; use rustc_hir::Safety; -use rustc_hir::attrs::lang_items::LangItem; use rustc_middle::ty::ImplPolarity::*; use rustc_middle::ty::print::PrintTraitRefExt as _; use rustc_middle::ty::{ImplTraitHeader, TraitDef, TyCtxt}; @@ -49,7 +49,7 @@ pub(super) fn check_item( "", rustc_errors::Applicability::MachineApplicable, ) - .emit()); + .emit_err()); } (Safety::Unsafe, _, Safety::Safe, Positive) => { @@ -83,7 +83,7 @@ pub(super) fn check_item( "unsafe ", rustc_errors::Applicability::MaybeIncorrect, ) - .emit()); + .emit_err()); } (Safety::Safe, Some(attr_name), Safety::Safe, Positive) => { @@ -107,7 +107,7 @@ pub(super) fn check_item( "unsafe ", rustc_errors::Applicability::MaybeIncorrect, ) - .emit()); + .emit_err()); } (_, _, Safety::Unsafe, Negative) => { diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index e581774747601..1cd1fc356ca7d 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -19,6 +19,7 @@ use std::{assert_matches, debug_assert_matches, iter}; use rustc_abi::{ExternAbi, Size}; use rustc_ast::Recovered; +use rustc_attr_ir::find_attr; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::thin_vec::{ThinVec, thin_vec}; use rustc_errors::{ @@ -27,7 +28,7 @@ use rustc_errors::{ use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{InferKind, Visitor}; -use rustc_hir::{self as hir, GenericParamKind, HirId, Node, PreciseCapturingArgKind, find_attr}; +use rustc_hir::{self as hir, GenericParamKind, HirId, Node, PreciseCapturingArgKind}; use rustc_infer::infer::{InferCtxt, SolverRegionConstraint, TyCtxtInferExt}; use rustc_infer::traits::{DynCompatibilityViolation, ObligationCause}; use rustc_lint_defs::builtin::REPR_C_ENUMS_LARGER_THAN_INT; @@ -37,6 +38,7 @@ use rustc_middle::ty::{ self, AdtKind, Const, IsSuggestable, Ty, TyCtxt, TypeVisitableExt, TypingMode, Unnormalized, fold_regions, }; +use rustc_span::def_id::LocalModId; use rustc_span::{DUMMY_SP, Ident, Span, Symbol, bug, kw, span_bug, sym}; use rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamName; use rustc_trait_selection::infer::InferCtxtExt; @@ -238,7 +240,7 @@ fn bad_placeholder<'cx, 'tcx>( ) -> Diag<'cx> { let kind = if kind.ends_with('s') { format!("{kind}es") } else { format!("{kind}s") }; - spans.sort(); + spans.sort_by_key(|span| span.lo_hi()); cx.dcx().create_err(diagnostics::PlaceholderNotAllowedItemSignatures { spans, kind }) } @@ -318,7 +320,7 @@ impl<'tcx> ItemCtxt<'tcx> { ); } - diag.emit() + diag.emit_err() } #[instrument(level = "debug", skip(self), ret)] @@ -497,6 +499,10 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { self.item_def_id } + fn mod_id(&self) -> LocalModId { + self.tcx.parent_module_from_def_id(self.item_def_id) + } + fn re_infer(&self, span: Span, reason: RegionInferReason<'_>) -> ty::Region<'tcx> { if let RegionInferReason::ObjectLifetimeDefault(sugg_sp) = reason { // FIXME: Account for trailing plus `dyn Trait+`, the need of parens in @@ -514,7 +520,7 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { " + /* 'a */", Applicability::HasPlaceholders, ) - .emit(); + .emit_err(); ty::Region::new_error(self.tcx(), guar) } else { // If we found elided lifetime during lowering of delegation parent or child @@ -807,8 +813,8 @@ pub(super) fn check_enum_variant_types(tcx: TyCtxt<'_>, def_id: LocalDefId) { msg: &'static str, } - impl<'a> Diagnostic<'a, ()> for ReprCIssue { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + impl<'a> Diagnostic<'a> for ReprCIssue { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let Self { msg } = self; Diag::new(dcx, level, msg) .with_note("`repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C") @@ -1412,7 +1418,7 @@ fn recover_infer_ret_ty<'tcx>( https://doc.rust-lang.org/book/ch13-01-closures.html", ); } - let guar = diag.emit(); + let guar = diag.emit_err(); // If we return a dummy binder here, we can ICE later in borrowck when it encounters // `ReLateParam` regions (e.g. in a local type annotation) which weren't registered via the @@ -1797,7 +1803,7 @@ fn anon_const_kind<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> ty::AnonConstKin debug_assert_matches!(const_arg.kind, hir::ConstArgKind::Anon(hir::AnonConst { def_id, .. }) if *def_id == def); if tcx.features().generic_const_exprs() { ty::AnonConstKind::GCE - } else if tcx.features().min_generic_const_args() { + } else if tcx.features().gca() { ty::AnonConstKind::MCG } else if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Repeat(_, repeat_count), diff --git a/compiler/rustc_hir_analysis/src/collect/clauses_of.rs b/compiler/rustc_hir_analysis/src/collect/clauses_of.rs index 78884bab033bf..ae384a67ee6aa 100644 --- a/compiler/rustc_hir_analysis/src/collect/clauses_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/clauses_of.rs @@ -1,11 +1,11 @@ use std::assert_matches; use hir::Node; +use rustc_attr_ir::find_attr; use rustc_data_structures::fx::FxIndexSet; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::find_attr; use rustc_middle::ty::{ self, GenericClauses, ImplTraitInTraitData, Ty, TyCtxt, TypeVisitable, TypeVisitor, Upcast, }; diff --git a/compiler/rustc_hir_analysis/src/collect/dump.rs b/compiler/rustc_hir_analysis/src/collect/dump.rs index 772bbe4a6b579..9bf62e2846b9d 100644 --- a/compiler/rustc_hir_analysis/src/collect/dump.rs +++ b/compiler/rustc_hir_analysis/src/collect/dump.rs @@ -1,7 +1,8 @@ +use rustc_attr_ir::find_attr; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; -use rustc_hir::{find_attr, intravisit}; +use rustc_hir::intravisit; use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt, Unnormalized}; @@ -103,7 +104,7 @@ pub(crate) fn clauses_and_item_bounds(tcx: TyCtxt<'_>) { for bound in bounds { diag.note(format!("{bound:?}")); } - diag.emit() + diag.emit_err() } kind => tcx.dcx().span_delayed_bug( tcx.def_span(id), diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index c9deedb33ffc7..4d43a9ca5700e 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -21,8 +21,8 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { msg: &'static str, } - impl<'a> Diagnostic<'a, ()> for GenericParametersForbiddenHere { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + impl<'a> Diagnostic<'a> for GenericParametersForbiddenHere { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let Self { msg } = self; Diag::new(dcx, level, msg) } @@ -224,7 +224,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { // Node::AnonConst, or whether it will be represented directly, so it must generate a // DefId. If it ends up being direct, this DefId is then attached to the top-level // ConstArg, which is what we are seeing here. - debug_assert!(tcx.features().min_generic_const_args()); + debug_assert!(tcx.features().gca()); // Forward to the real parent. Some(tcx.local_parent(def_id)) } diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 1f35c302cc9f1..1dc6c1c96e93e 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -808,13 +808,13 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { LifetimeKind::Error(..) => {} } } - hir::TyKind::Ref(lifetime_ref, ref mt) => { + hir::TyKind::Ref(lifetime_ref, ref inner_ty, _) => { self.visit_lifetime(lifetime_ref); let scope = Scope::ObjectLifetimeDefault { lifetime: self.rbv.defs.get(&lifetime_ref.hir_id.local_id).copied(), s: self.scope, }; - self.with(scope, |this| this.visit_ty_unambig(mt.ty)); + self.with(scope, |this| this.visit_ty_unambig(inner_ty)); } hir::TyKind::TraitAscription(bounds) => { let scope = Scope::TraitRefBoundary { s: self.scope }; @@ -2827,7 +2827,7 @@ fn deny_non_region_late_bound( format!("late-bound {what} parameter not allowed on {where_}"), ); - let guar = diag.emit_unless_delay(!tcx.features().non_lifetime_binders() || !first); + let guar = diag.emit_err_unless_delay(!tcx.features().non_lifetime_binders() || !first); first = false; *arg = ResolvedArg::Error(guar); diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 4c7b307c4e146..8754430aaa99e 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -256,10 +256,8 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_ GenericParamKind::Const { ty, .. } => { let lowered_ty = icx.lower_ty(ty); if !tcx.features().generic_const_parameter_types() && lowered_ty.has_param() { - let guar = tcx - .dcx() - .create_err(ParamInTyOfConstParam { span: ty.span, ty: lowered_ty }) - .emit(); + let guar = + tcx.dcx().emit_err(ParamInTyOfConstParam { span: ty.span, ty: lowered_ty }); Ty::new_error(tcx, guar) } else { lowered_ty @@ -543,7 +541,7 @@ fn infer_placeholder_type<'tcx>( } } - diag.emit() + diag.emit_err() }); Ty::new_error(tcx, guar) } diff --git a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs index 66167e0042982..5ffbe693180f0 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs @@ -119,7 +119,7 @@ impl<'tcx> TaitConstraintLocator<'tcx> { if let Some(prev) = &mut self.found { if hidden_ty.ty != prev.ty { let (Ok(guar) | Err(guar)) = - prev.build_mismatch_error(&hidden_ty, self.tcx).map(|d| d.emit()); + prev.build_mismatch_error(&hidden_ty, self.tcx).map(|d| d.emit_err()); *prev = ty::DefinitionSiteHiddenType::new_error(self.tcx, guar); } } else { diff --git a/compiler/rustc_hir_analysis/src/diagnostics.rs b/compiler/rustc_hir_analysis/src/diagnostics.rs index 1f0c493b881f1..b003a358f9589 100644 --- a/compiler/rustc_hir_analysis/src/diagnostics.rs +++ b/compiler/rustc_hir_analysis/src/diagnostics.rs @@ -40,6 +40,10 @@ pub(crate) struct AssocKindMismatch { #[primary_span] #[label("unexpected {$got}")] pub span: Span, + #[context] + pub item_span: Span, + #[context] + pub enclosing_span: Option, pub expected: &'static str, pub got: &'static str, #[label("expected a {$expected} because of this associated {$expected}")] @@ -292,13 +296,14 @@ pub(crate) struct FieldAlreadyDeclaredNestedHelp { } #[derive(Diagnostic)] -#[diag("the trait `Copy` cannot be implemented for this type; the type has a destructor", code = E0184)] -pub(crate) struct CopyImplOnTypeWithDtor { +#[diag("the trait `{$trait_name}` cannot be implemented for this type; the type has a destructor", code = E0184)] +pub(crate) struct TraitImplOnTypeWithDtor { #[primary_span] - #[label("`Copy` not allowed on types with destructors")] + #[label("`{$trait_name}` not allowed on types with destructors")] pub span: Span, #[note("destructor declared here")] pub impl_: Span, + pub trait_name: Symbol, } #[derive(Diagnostic)] @@ -467,9 +472,9 @@ pub(crate) struct MissingGenericParams { } // FIXME: This doesn't need to be a manual impl! -impl<'a, G> Diagnostic<'a, G> for MissingGenericParams { +impl<'a> Diagnostic<'a> for MissingGenericParams { #[track_caller] - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let mut err = Diag::new( dcx, level, @@ -1736,7 +1741,7 @@ pub(crate) struct UnusedGenericParameter { pub span: Span, pub param_name: Ident, pub param_def_kind: &'static str, - #[label("`{$param_name}` is named here, but is likely unused in the containing type")] + #[label("`{$param_name}` is named here, but is not used in the type that wraps it")] pub usage_spans: Vec, #[subdiagnostic] pub help: UnusedGenericParameterHelp, @@ -2069,8 +2074,8 @@ pub(crate) struct UncoveredTyParam<'tcx> { pub(crate) local_ty: Option>, } -impl Diagnostic<'_, G> for UncoveredTyParam<'_> { - fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { +impl Diagnostic<'_> for UncoveredTyParam<'_> { + fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_> { let Self { param, local_ty } = self; let mut diag = Diag::new(dcx, level, "") diff --git a/compiler/rustc_hir_analysis/src/diagnostics/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/diagnostics/wrong_number_of_generic_args.rs index e995d4fdf697b..f2b166e0b2a01 100644 --- a/compiler/rustc_hir_analysis/src/diagnostics/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/diagnostics/wrong_number_of_generic_args.rs @@ -3,7 +3,7 @@ use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, Diagnostic, MultiSpan, pluralize}; use rustc_hir as hir; use rustc_middle::ty::{self as ty, AssocItem, AssocItems, TyCtxt}; -use rustc_span::def_id::DefId; +use rustc_span::def_id::{DefId, LocalDefId}; use tracing::debug; /// Handles the `wrong number of type / lifetime / ... arguments` family of error messages. @@ -30,6 +30,9 @@ pub(crate) struct WrongNumberOfGenericArgs<'a, 'tcx> { /// DefId of the generic type pub(crate) def_id: DefId, + + /// DefId of the type that contains the generic + pub(crate) cx_def_id: LocalDefId, } // Provides information about the kind of arguments that were provided for @@ -94,6 +97,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { params_offset: usize, gen_args: &'a hir::GenericArgs<'a>, def_id: DefId, + cx_def_id: LocalDefId, ) -> Self { let angle_brackets = if gen_args.span_ext().is_none() { if gen_args.is_empty() { AngleBrackets::Missing } else { AngleBrackets::Implied } @@ -110,6 +114,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { params_offset, gen_args, def_id, + cx_def_id, } } @@ -543,7 +548,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { } /// Builds the `expected 1 type argument / supplied 2 type arguments` message. - fn notify(&self, err: &mut Diag<'_, G>) { + fn notify(&self, err: &mut Diag<'_>) { let (quantifier, bound) = self.get_quantifier_and_bound(); let provided_args = self.num_provided_args(); @@ -595,7 +600,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { } } - fn suggest(&self, err: &mut Diag<'_, G>) { + fn suggest(&self, err: &mut Diag<'_>) { debug!( "suggest(self.provided {:?}, self.gen_args.span(): {:?})", self.num_provided_args(), @@ -623,7 +628,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { /// ```text /// type Map = HashMap; /// ``` - fn suggest_adding_args(&self, err: &mut Diag<'_, G>) { + fn suggest_adding_args(&self, err: &mut Diag<'_>) { if self.gen_args.parenthesized != hir::GenericArgsParentheses::No { return; } @@ -650,7 +655,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { } } - fn suggest_adding_lifetime_args(&self, err: &mut Diag<'_, G>) { + fn suggest_adding_lifetime_args(&self, err: &mut Diag<'_>) { debug!("suggest_adding_lifetime_args(path_segment: {:?})", self.path_segment); let num_missing_args = self.num_missing_lifetime_args(); let num_params_to_take = num_missing_args; @@ -704,7 +709,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { } } - fn suggest_adding_type_and_const_args(&self, err: &mut Diag<'_, G>) { + fn suggest_adding_type_and_const_args(&self, err: &mut Diag<'_>) { let num_missing_args = self.num_missing_type_or_const_args(); let msg = format!("add missing {} argument{}", self.kind(), pluralize!(num_missing_args)); @@ -764,7 +769,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { /// ```compile_fail /// Into::into::>(42) // suggests considering `Into::>::into(42)` /// ``` - fn suggest_moving_args_from_assoc_fn_to_trait(&self, err: &mut Diag<'_, G>) { + fn suggest_moving_args_from_assoc_fn_to_trait(&self, err: &mut Diag<'_>) { let Some(trait_) = self.tcx.trait_of_assoc(self.def_id) else { return; }; @@ -817,9 +822,9 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { } } - fn suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path( + fn suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path( &self, - err: &mut Diag<'_, G>, + err: &mut Diag<'_>, qpath: &'tcx hir::QPath<'tcx>, msg: String, num_assoc_fn_excess_args: usize, @@ -848,9 +853,9 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { } } - fn suggest_moving_args_from_assoc_fn_to_trait_for_method_call( + fn suggest_moving_args_from_assoc_fn_to_trait_for_method_call( &self, - err: &mut Diag<'_, G>, + err: &mut Diag<'_>, trait_def_id: DefId, expr: &'tcx hir::Expr<'tcx>, msg: String, @@ -904,7 +909,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { /// ```text /// type Map = HashMap; /// ``` - fn suggest_removing_args_or_generics(&self, err: &mut Diag<'_, G>) { + fn suggest_removing_args_or_generics(&self, err: &mut Diag<'_>) { let num_provided_lt_args = self.num_provided_lifetime_args(); let num_provided_type_const_args = self.num_provided_type_or_const_args(); let unbound_assoc_items = self.get_unbound_associated_item(); @@ -920,7 +925,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { let remove_entire_generics = num_redundant_args >= self.gen_args.args.len(); - let remove_lifetime_args = |err: &mut Diag<'_, _>| { + let remove_lifetime_args = |err: &mut Diag<'_>| { let mut lt_arg_spans = Vec::new(); let mut found_redundant = false; for arg in self.gen_args.args { @@ -955,7 +960,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { let msg_lifetimes = format!("remove the lifetime argument{s}", s = pluralize!(num_redundant_lt_args)); - err.span_suggestion( + err.span_suggestion_short( span_redundant_lt_args, msg_lifetimes, "", @@ -963,7 +968,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { ); }; - let remove_type_or_const_args = |err: &mut Diag<'_, _>| { + let remove_type_or_const_args = |err: &mut Diag<'_>| { let mut gen_arg_spans = Vec::new(); let mut found_redundant = false; for arg in self.gen_args.args { @@ -1004,7 +1009,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { s = pluralize!(num_redundant_gen_args), ); - err.span_suggestion( + err.span_suggestion_short( span_redundant_type_or_const_args, msg_types_or_consts, "", @@ -1082,7 +1087,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { // no code, which would trigger an `assert!` later. Ideally, we would do something a // bit more principled. See closed PR #109082. } else { - err.span_suggestion(span, msg, "", Applicability::MaybeIncorrect); + err.span_suggestion_short(span, msg, "", Applicability::MaybeIncorrect); } } else if redundant_lifetime_args && redundant_type_or_const_args { remove_lifetime_args(err); @@ -1096,7 +1101,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { } /// Builds the `type defined here` message. - fn show_definition(&self, err: &mut Diag<'_, G>) { + fn show_definition(&self, err: &mut Diag<'_>) { let Some(def_span) = self.tcx.def_ident_span(self.def_id) else { return }; if !self.tcx.sess.source_map().is_span_accessible(def_span) { return; @@ -1143,7 +1148,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { } /// Add note if `impl Trait` is explicitly specified. - fn note_synth_provided(&self, err: &mut Diag<'_, G>) { + fn note_synth_provided(&self, err: &mut Diag<'_>) { if !self.is_synth_provided() { return; } @@ -1152,21 +1157,31 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { } } -impl<'a, G> Diagnostic<'a, G> for WrongNumberOfGenericArgs<'_, '_> { +impl<'a> Diagnostic<'a> for WrongNumberOfGenericArgs<'_, '_> { fn into_diag( self, dcx: rustc_errors::DiagCtxtHandle<'a>, level: rustc_errors::Level, - ) -> Diag<'a, G> { + ) -> Diag<'a> { let msg = self.create_error_message(); let mut err = Diag::new(dcx, level, msg); err.code(E0107); err.span(self.path_segment.ident.span); - self.notify(&mut err); - self.suggest(&mut err); - self.show_definition(&mut err); - self.note_synth_provided(&mut err); + let cx_span = self.tcx.def_span(self.cx_def_id); + if cx_span.in_derive_expansion() { + // Very likely this is a botched `derive` which passes the iten name straight + // through, but doesn't support type parameters. + err.span_label( + cx_span.ctxt().outer_expn_data().call_site, + "this derive macro might not support items with generic parameters", + ); + } else { + self.notify(&mut err); + self.suggest(&mut err); + self.show_definition(&mut err); + self.note_synth_provided(&mut err); + } err } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index 7f0c6910cc425..0b7df89551da8 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -1,13 +1,14 @@ use std::ops::ControlFlow; +use rustc_attr_ir::find_attr; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::struct_span_code_err; use rustc_hir as hir; -use rustc_hir::attrs::lang_items::LangItem; +use rustc_hir::PolyTraitRef; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{PolyTraitRef, find_attr}; use rustc_middle::ty::{ self as ty, IsSuggestable, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, Upcast, @@ -557,14 +558,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { if let ty::AssocTag::Const = assoc_tag && !self.tcx().is_direct_const(assoc_item.def_id) - && !tcx.features().generic_const_args() + && !tcx.features().gca_const_items() { - if tcx.features().min_generic_const_args() { + if tcx.features().gca_min_const_items() { let err = self.dcx().struct_span_err( constraint.span, "use of trait associated const not defined as `#[rustc_always_gca]`", ); - return Err(err.emit()); + return Err(err.emit_err()); } else { let err = self.dcx().span_delayed_bug( constraint.span, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs index beacb3f188cd9..9123afd5986f5 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs @@ -1,11 +1,8 @@ use rustc_ast::TraitObjectSyntax; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; -use rustc_errors::{ - Applicability, Diag, DiagCtxtHandle, Diagnostic, Level, StashKey, Suggestions, - struct_span_code_err, -}; -use rustc_hir::attrs::lang_items::LangItem; +use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, Level, struct_span_code_err}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, HirId}; @@ -17,6 +14,7 @@ use rustc_middle::ty::{ }; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::{ErrorGuaranteed, Span}; +use rustc_trait_selection::diagnostics::AssocTypeWithSameName; use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility; use rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamName; use rustc_trait_selection::traits; @@ -138,7 +136,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { trait_pred.def_id(), &violations, ) - .emit(); + .emit_err(); return Ty::new_error(tcx, reported); } } @@ -578,8 +576,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { this: &'a dyn HirTyLowerer<'tcx>, } - impl<'a, 'b, 'tcx> Diagnostic<'a, ()> for TraitObjectWithoutDyn<'b, 'tcx> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + impl<'a, 'b, 'tcx> Diagnostic<'a> for TraitObjectWithoutDyn<'b, 'tcx> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let Self { span, hir_id, sugg, this } = self; let mut lint = Diag::new(dcx, level, "trait objects without an explicit `dyn` are deprecated"); @@ -655,17 +653,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { poly_trait_ref.trait_ref.trait_def_id(), &mut diag, ); - // In case there is an associated type with the same name - // Add the suggestion to this error - if let Some(mut sugg) = - self.dcx().steal_non_err(span, StashKey::AssociatedTypeSuggestion) - && let Suggestions::Enabled(ref mut s1) = diag.suggestions - && let Suggestions::Enabled(ref mut s2) = sugg.suggestions + // If there is an associated type with the same name, add the suggestion to this error. + if self + .tcx() + .resolutions(()) + .paths_matching_assoc_types + .contains(&span.with_parent(None)) { - s1.append(s2); - sugg.cancel(); + diag.subdiagnostic(AssocTypeWithSameName { span: span.shrink_to_lo() }); } - Some(diag.emit()) + Some(diag.emit_err()) } else { tcx.emit_node_span_lint( BARE_TRAIT_OBJECTS, @@ -739,11 +736,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } /// Make sure that we are in the condition to suggest the blanket implementation. - fn maybe_suggest_blanket_trait_impl( + fn maybe_suggest_blanket_trait_impl( &self, span: Span, hir_id: hir::HirId, - diag: &mut Diag<'_, G>, + diag: &mut Diag<'_>, ) { let tcx = self.tcx(); let parent_id = tcx.hir_get_parent_item(hir_id).def_id; @@ -1085,6 +1082,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { typo, Applicability::MaybeIncorrect, ); + } else { + diag.span_label( + segment.ident.span, + format!( + "not an associated item of trait `{trait_name}`, so `{trait_name}` is \ + interpreted as a type", + trait_name = tcx.item_name(trait_def_id), + ), + ); } } } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 2a3728573f31e..b21f80925aa05 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -18,7 +18,7 @@ use rustc_middle::ty::{ }; use rustc_session::diagnostics::feature_err; use rustc_span::edit_distance::find_best_match_for_name; -use rustc_span::{BytePos, DUMMY_SP, Ident, Span, Symbol, bug, kw, sym}; +use rustc_span::{BytePos, DUMMY_SP, Ident, OrdSpan, Span, Symbol, bug, kw, sym}; use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility; use rustc_trait_selection::traits::{ FulfillmentError, dyn_compatibility_violations_for_assoc_item, @@ -96,7 +96,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // Do not suggest the other syntax if we are in trait impl: // the desugaring would contain an associated type constraint. if !is_impl { - err.span_suggestion( + err.span_suggestion_verbose( span, "use parenthetical notation instead", fn_trait_to_string(self.tcx(), trait_segment, true), @@ -199,8 +199,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .visible_traits() .filter(|trait_def_id| { let viz = tcx.visibility(*trait_def_id); - let def_id = self.item_def_id(); - viz.is_accessible_from(def_id, tcx) + viz.is_accessible_from(self.mod_id(), tcx) }) .collect(); @@ -315,7 +314,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Applicability::MaybeIncorrect, ); } - return err.emit(); + return err.emit_err(); } } return self.dcx().emit_err(err); @@ -371,7 +370,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { && let Some(hir_ty) = constraint.ty() && let ty = self.lower_ty(hir_ty) && (ty.is_enum() || ty.references_error()) - && tcx.features().min_generic_const_args() + && tcx.features().gca() { Some(diagnostics::AssocKindMismatchWrapInBracesSugg { lo: hir_ty.span.shrink_to_lo(), @@ -395,8 +394,31 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { (ident.span, None, assoc_tag, assoc_item.tag()) }; + let def_kind = tcx.def_kind(self.item_def_id()); + let item_span = tcx.def_span(self.item_def_id()).shrink_to_lo(); + let (item_span, enclosing_span) = match def_kind { + DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy => ( + item_span, + Some(tcx.def_span(tcx.parent(self.item_def_id().into())).shrink_to_lo()), + ), + DefKind::OpaqueTy => { + let item_span = tcx.def_span(tcx.parent(self.item_def_id().into())).shrink_to_lo(); + let enclosing_span = + if let DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy = + tcx.def_kind(tcx.parent(self.item_def_id().into())) + { + Some(tcx.def_span(tcx.parent(self.item_def_id().into())).shrink_to_lo()) + } else { + None + }; + (item_span, enclosing_span) + } + _ => (item_span, None), + }; self.dcx().emit_err(diagnostics::AssocKindMismatch { span, + item_span, + enclosing_span, expected: assoc_tag_str(expected), got: assoc_tag_str(got), expected_because_label, @@ -535,7 +557,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { where_bounds.join(",\n"), )); } - err.emit() + err.emit_err() } pub(crate) fn report_missing_self_ty_for_resolved_path( @@ -568,7 +590,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .map(|impl_def_id| tcx.impl_trait_header(impl_def_id)) .filter(|header| { // Consider only accessible traits - tcx.visibility(trait_def_id).is_accessible_from(self.item_def_id(), tcx) + tcx.visibility(trait_def_id).is_accessible_from(self.mod_id(), tcx) && header.polarity != ty::ImplPolarity::Negative }) .map(|header| header.trait_ref.instantiate_identity().skip_norm_wip().self_ty()) @@ -682,7 +704,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { err.span_label(sp, format!("variant `{ident}` not found here")); } - err.emit() + err.emit_err() } else if let Err(reported) = self_ty.error_reported() { reported } else { @@ -814,7 +836,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } } - err.emit() + err.emit_err() } pub(crate) fn report_ambiguous_inherent_assoc_item( @@ -831,7 +853,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ); err.span_label(name.span, format!("multiple `{name}` found")); self.note_ambiguous_inherent_assoc_item(&mut err, candidates, span); - err.emit() + err.emit_err() } // FIXME(fmease): Heavily adapted from `rustc_hir_typeck::method::suggest`. Deduplicate. @@ -941,25 +963,25 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { "the associated {assoc_tag_str} was found for\n{type_candidates}{additional_types}", )); add_def_label(&mut err); - return err.emit(); + return err.emit_err(); } - let mut bound_spans: SortedMap> = Default::default(); + let mut bound_spans: SortedMap> = Default::default(); let mut bound_span_label = |self_ty: Ty<'_>, obligation: &str, quiet: &str| { let msg = format!("`{}`", if obligation.len() > 50 { quiet } else { obligation }); match self_ty.kind() { // Point at the type that couldn't satisfy the bound. - ty::Adt(def, _) => { - bound_spans.get_mut_or_insert_default(tcx.def_span(def.did())).push(msg) - } + ty::Adt(def, _) => bound_spans + .get_mut_or_insert_default(OrdSpan(tcx.def_span(def.did()))) + .push(msg), // Point at the trait object that couldn't satisfy the bound. ty::Dynamic(preds, _) => { for pred in preds.iter() { match pred.skip_binder() { ty::ExistentialPredicate::Trait(tr) => { bound_spans - .get_mut_or_insert_default(tcx.def_span(tr.def_id)) + .get_mut_or_insert_default(OrdSpan(tcx.def_span(tr.def_id))) .push(msg.clone()); } ty::ExistentialPredicate::Projection(_) @@ -970,7 +992,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // Point at the closure that couldn't satisfy the bound. ty::Closure(def_id, _) => { bound_spans - .get_mut_or_insert_default(tcx.def_span(*def_id)) + .get_mut_or_insert_default(OrdSpan(tcx.def_span(*def_id))) .push(format!("`{quiet}`")); } _ => {} @@ -1035,7 +1057,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ); for (span, mut bounds) in bound_spans { - if !tcx.sess.source_map().is_span_accessible(span) { + if !tcx.sess.source_map().is_span_accessible(span.0) { continue; } bounds.sort(); @@ -1046,10 +1068,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { [bounds @ .., last] => format!("doesn't satisfy {} or {last}", bounds.join(", ")), [] => unreachable!(), }; - err.span_label(span, msg); + err.span_label(span.0, msg); } add_def_label(&mut err); - err.emit() + err.emit_err() } /// If there are any missing associated items, emit an error instructing the user to provide @@ -1101,7 +1123,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { trait_ref.def_id(), &violations, ) - .emit()); + .emit_err()); } names.entry(trait_ref).or_default().push(assoc_item.name()); @@ -1316,7 +1338,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ); } } - suggestions.sort_by_key(|&(span, _)| span); + suggestions.sort_by_key(|&(span, _)| span.lo_hi()); // There are cases where one bound points to a span within another bound's span, like when // you have code like the following (#115019), so we skip providing a suggestion in those // cases to avoid having a malformed suggestion. @@ -1341,7 +1363,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - Err(err.emit()) + Err(err.emit_err()) } /// On ambiguous associated type, look for an associated function whose name matches the @@ -1393,7 +1415,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { name, Applicability::MaybeIncorrect, ) - .emit()) + .emit_err()) } else { Ok(()) } @@ -1478,7 +1500,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { err.span_label(span, format!("not allowed on {what}")); } generics_args_err_extend(self.tcx(), segments.into_iter(), &mut err, err_extend); - err.emit() + err.emit_err() } pub fn report_trait_object_addition_traits( @@ -1521,7 +1543,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { for more information on them, visit \ ", ); - err.emit() + err.emit_err() } pub fn report_trait_object_with_no_traits( @@ -1716,7 +1738,7 @@ pub fn prohibit_assoc_item_constraint( } } - err.emit() + err.emit_err() } pub(crate) fn fn_trait_to_string( @@ -2005,12 +2027,12 @@ pub(super) struct AmbiguityBetweenVariantAndAssocItem<'tcx> { pub(super) mode: super::LowerTypeRelativePathMode, } -impl<'a, 'tcx> rustc_errors::Diagnostic<'a, ()> for AmbiguityBetweenVariantAndAssocItem<'tcx> { +impl<'a, 'tcx> rustc_errors::Diagnostic<'a> for AmbiguityBetweenVariantAndAssocItem<'tcx> { fn into_diag( self, dcx: rustc_errors::DiagCtxtHandle<'a>, level: rustc_errors::Level, - ) -> Diag<'a, ()> { + ) -> Diag<'a> { let Self { variant_def_id, item_def_id, @@ -2036,7 +2058,7 @@ impl<'a, 'tcx> rustc_errors::Diagnostic<'a, ()> for AmbiguityBetweenVariantAndAs could_refer_to(DefKind::Variant, variant_def_id, ""); could_refer_to(mode.def_kind_for_diagnostics(), item_def_id, " also"); - lint.span_suggestion( + lint.span_suggestion_verbose( span, "use fully-qualified syntax", format!("<{} as {}>::{}", self_ty, tcx.item_name(bound_def_id), segment_ident), diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs index 4a35bcb5ef28f..9eda20fa0d9c4 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs @@ -70,7 +70,7 @@ fn generic_arg_mismatch_err( add_braces_suggestion(arg, &mut err); return err .with_primary_message("unresolved item provided when a constant was expected") - .emit(); + .emit_err(); } Res::Def(DefKind::TyParam, src_def_id) => { if let Some(param_local_id) = param.def_id.as_local() { @@ -97,12 +97,24 @@ fn generic_arg_mismatch_err( GenericArg::Type(hir::Ty { kind: hir::TyKind::Array(_, len), .. }), GenericParamDefKind::Const { .. }, ) if tcx.type_of(param.def_id).skip_binder() == tcx.types.usize => { + err.span_label(arg.span(), "array type provided where a `usize` was expected"); let snippet = sess.source_map().span_to_snippet(tcx.hir_span(len.hir_id)); if let Ok(snippet) = snippet { - err.span_suggestion( + let sugg = if let hir::ConstArgKind::Anon(hir::AnonConst { body, .. }) = len.kind + && let hir::ExprKind::Lit(..) = tcx.hir_body(*body).value.kind + { + // We don't need to surround literals in braces when used as consts + snippet + } else if let hir::ConstArgKind::Literal { .. } = len.kind { + // We don't need to surround literals in braces when used as consts + snippet + } else { + format!("{{ {snippet} }}") + }; + err.span_suggestion_verbose( arg.span(), - "array type provided where a `usize` was expected, try", - format!("{{ {snippet} }}"), + format!("you might have meant to use the array's length's value"), + sugg, Applicability::MaybeIncorrect, ); } @@ -145,7 +157,7 @@ fn generic_arg_mismatch_err( } } - err.emit() + err.emit_err() } /// Lower generic arguments from the HIR to the [`rustc_middle::ty`] representation. @@ -491,6 +503,7 @@ pub(crate) fn check_generic_arg_count( has_self as usize, gen_args, def_id, + cx.item_def_id(), )); Err(reported) @@ -605,8 +618,9 @@ pub(crate) fn check_generic_arg_count( params_offset, gen_args, def_id, + cx.item_def_id(), )) - .emit_unless_delay(all_params_are_binded || has_invalid_bound) + .emit_err_unless_delay(all_params_are_binded || has_invalid_bound) }); Err(reported) @@ -655,8 +669,8 @@ pub(crate) fn prohibit_explicit_late_bound_lifetimes( msg: &'static str, } - impl<'a> Diagnostic<'a, ()> for LifetimeArgsIssue { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + impl<'a> Diagnostic<'a> for LifetimeArgsIssue { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let Self { msg } = self; Diag::new(dcx, level, msg) } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index e89caa6aeff8c..4fa02926f15d8 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -25,6 +25,7 @@ use std::{assert_matches, slice}; use rustc_abi::FIRST_VARIANT; use rustc_ast::LitKind; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::sso::SsoHashSet; use rustc_data_structures::thin_vec::ThinVec; @@ -33,7 +34,6 @@ use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, FatalError, StashKey, struct_span_code_err, }; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{self as hir, AnonConst, GenericArg, GenericArgs, HirId}; @@ -42,13 +42,14 @@ use rustc_infer::traits::DynCompatibilityViolation; use rustc_lint_defs::builtin::AMBIGUOUS_ASSOCIATED_ITEMS; use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::middle::stability::AllowUnstable; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::{ self, Const, FnSigKind, GenericArgKind, GenericArgsRef, GenericParamDefKind, LitToConstInput, Ty, TyCtxt, TypeSuperFoldable, TypeVisitableExt, TypingMode, Unnormalized, Upcast, const_lit_matches_ty, fold_regions, }; use rustc_session::diagnostics::feature_err; -use rustc_span::def_id::ModId; +use rustc_span::def_id::{LocalModId, ModId}; use rustc_span::{DUMMY_SP, Ident, Span, bug, kw, span_bug, sym}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{self, FulfillmentError}; @@ -142,6 +143,9 @@ pub trait HirTyLowerer<'tcx> { /// Returns the [`LocalDefId`] of the overarching item whose constituents get lowered. fn item_def_id(&self) -> LocalDefId; + /// Returns the containing module. + fn mod_id(&self) -> LocalModId; + /// Returns the region to use when a lifetime is omitted (and not elided). fn re_infer(&self, span: Span, reason: RegionInferReason<'_>) -> ty::Region<'tcx>; @@ -415,7 +419,7 @@ impl<'tcx> ForbidParamUsesFolder<'tcx> { "generic `Self` types are currently not permitted in anonymous constants" } ForbidParamContext::ConstArgument => { - if self.tcx.features().generic_const_args() { + if self.tcx.features().gca_const_items() { "generic parameters in const blocks are not allowed; use a named `const` item instead" } else { "generic parameters may not be used in const operations" @@ -438,18 +442,18 @@ impl<'tcx> ForbidParamUsesFolder<'tcx> { } } if matches!(self.context, ForbidParamContext::ConstArgument) { - if self.tcx.features().generic_const_args() { + if self.tcx.features().gca_const_items() { diag.help("consider factoring the expression into a `type const` item and use it as the const argument instead"); - } else if self.tcx.features().min_generic_const_args() { - diag.help("add `#![feature(generic_const_args)]` and extract the expression into a `type const` item"); + } else if self.tcx.features().gca_min_const_items() { + diag.help("add `#![feature(gca_const_items)]` and extract the expression into a `type const` item"); } else if self.tcx.sess.is_nightly_build() { diag.help( "add `#![feature(generic_const_exprs)]` to allow generic const expressions", ); - diag.help("alternatively, you can use `#![feature(generic_const_args)]` and extract the expression into a `type const` item"); + diag.help("alternatively, you can use `#![feature(gca_const_items)]` and extract the expression into a `type const` item"); } } - diag.emit() + diag.emit_err() } } @@ -1480,9 +1484,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { )? { TypeRelativePath::AssocItem(alias_term) => { let alias_ct = alias_term.expect_ct(); - if let Some(def_id) = alias_ct.kind.opt_def_id() { - self.check_const_item_in_type_system(def_id, span)?; - } + self.check_const_item_in_type_system(alias_ct.kind, span)?; let ct = Const::new_alias(tcx, ty::IsRigid::No, alias_ct); let ct = self.check_param_uses_if_mcg(ct, span, false); Ok(ct) @@ -1717,7 +1719,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { span, "inherent associated types are unstable", ) - .emit()); + .emit_err()); } ty::AssocTag::Fn => unreachable!(), } @@ -1816,7 +1818,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ) -> Option<(ty::AssocItem, /*scope*/ ModId)> { let tcx = self.tcx(); - let (ident, def_scope) = tcx.adjust_ident_and_get_scope(ident, scope, self.item_def_id()); + let (ident, def_scope) = tcx.adjust_ident_and_get_scope(ident, scope, self.mod_id()); // We have already adjusted the item name above, so compare with `.normalize_to_macros_2_0()` // instead of calling `filter_by_name_and_kind` which would needlessly normalize the // `ident` again and again. @@ -1881,7 +1883,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }) // Consider only accessible traits && tcx.visibility(*trait_def_id) - .is_accessible_from(self.item_def_id(), tcx) + .is_accessible_from(self.mod_id(), tcx) && tcx.all_impls(*trait_def_id) .any(|impl_def_id| { let header = tcx.impl_trait_header(impl_def_id); @@ -1948,13 +1950,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { item_segment, ty::AssocTag::Const, )?; - self.check_const_item_in_type_system(item_def_id, span)?; - let alias_const = ty::AliasConst::new( - tcx, - ty::AliasConstKind::Projection { def_id: item_def_id }, - item_args, - ); - Ok(Const::new_alias(tcx, ty::IsRigid::No, alias_const)) + let kind = ty::AliasConstKind::Projection { def_id: item_def_id }; + self.check_const_item_in_type_system(kind, span)?; + let alias = ty::AliasConst::new(tcx, kind, item_args); + Ok(Const::new_alias(tcx, ty::IsRigid::No, alias)) } /// Lower a [resolved][hir::QPath::Resolved] (type-level) associated item path. @@ -2769,7 +2768,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { format!("`{}` does not have this field", variant_def.name), ); } - return ty::Const::new_error(tcx, err.emit()); + return ty::Const::new_error(tcx, err.emit_err()); } } @@ -2879,7 +2878,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.lower_const_param(def_id, hir_id) } Res::Def(DefKind::Const, did) => { - if let Err(guar) = self.check_const_item_in_type_system(did, span) { + let kind = ty::AliasConstKind::Free { def_id: did }; + if let Err(guar) = self.check_const_item_in_type_system(kind, span) { return Const::new_error(self.tcx(), guar); } @@ -2888,11 +2888,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let _ = self .prohibit_generic_args(leading_segments.iter(), GenericsArgsErrExtend::None); let args = self.lower_generic_args_of_path_segment(span, did, segment); - ty::Const::new_alias( - tcx, - ty::IsRigid::No, - ty::AliasConst::new(tcx, ty::AliasConstKind::Free { def_id: did }, args), - ) + let alias = ty::AliasConst::new(tcx, kind, args); + ty::Const::new_alias(tcx, ty::IsRigid::No, alias) } Res::Def(kind @ DefKind::Ctor(ctor_of, CtorKind::Const), did) => { assert_eq!(opt_self_ty, None); @@ -2981,7 +2978,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let guar = self .dcx() .struct_span_err(span, "function items cannot be used as const args") - .emit(); + .emit_err(); Const::new_error(tcx, guar) } // Exhaustive match to be clear about what exactly we're considering to be @@ -3123,35 +3120,42 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }) } - /// `def_id` is a const item used in the type system. Checks if that's OK. + /// `alias_const` is a const item used in the type system. Checks if that's OK. fn check_const_item_in_type_system( &self, - def_id: DefId, + alias_const: ty::AliasConstKind<'tcx>, span: Span, ) -> Result<(), ErrorGuaranteed> { let tcx = self.tcx(); - if tcx.features().generic_const_args() || tcx.is_direct_const(def_id) { + if tcx.features().gca_const_items() || alias_const.is_direct_const(tcx) { Ok(()) } else { let mut err = self .dcx() .struct_span_err(span, "use of `const` in the type system not marked as direct"); - if let Some(local_def_id) = def_id.as_local() { - if let Some(body_id) = tcx.hir_node_by_def_id(local_def_id).body_id() { + let hir_node = match alias_const { + ty::AliasConstKind::Projection { def_id } + | ty::AliasConstKind::InherentSelf { def_id } + | ty::AliasConstKind::InherentImpl { def_id } + | ty::AliasConstKind::Free { def_id } + | ty::AliasConstKind::Anon { def_id } => { + def_id.as_local().map(|id| tcx.hir_node_by_def_id(id)) + } + }; + if let Some(hir_node) = hir_node { + if let Some(body_id) = hir_node.body_id() { let body_span = tcx.hir_body(body_id).value.span; err.multipart_suggestion( - "add direct_const_arg!() to the right-hand side of the constant", + "add gca!() to the right-hand side of the constant", vec![ - (body_span.shrink_to_lo(), String::from("core::direct_const_arg!(")), + (body_span.shrink_to_lo(), String::from("core::gca!(")), (body_span.shrink_to_hi(), String::from(")")), ], Applicability::MaybeIncorrect, ); - } else if let DefKind::AssocConst = tcx.def_kind(def_id) - && let DefKind::Trait = tcx.def_kind(tcx.parent(def_id)) - { - let node = tcx.hir_node_by_def_id(local_def_id).expect_trait_item(); + } else if let ty::AliasConstKind::Projection { .. } = alias_const { + let node = hir_node.expect_trait_item(); let sp = node.span.shrink_to_lo(); err.span_suggestion_verbose( sp, @@ -3161,11 +3165,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ); } } else { - err.note( - "only consts with a `direct_const_arg!` right-hand side may be used in types", - ); + err.note("only consts with a `gca!` right-hand side may be used in types"); } - Err(err.emit()) + Err(err.emit_err()) } } @@ -3193,12 +3195,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let result_ty = match &hir_ty.kind { hir::TyKind::InferDelegation(infer) => self.lower_delegation_ty(*infer), hir::TyKind::Slice(ty) => Ty::new_slice(tcx, self.lower_ty(ty)), - hir::TyKind::Ptr(mt) => Ty::new_ptr(tcx, self.lower_ty(mt.ty), mt.mutbl), - hir::TyKind::Ref(region, mt) => { + hir::TyKind::Ptr(ty, mutbl) => Ty::new_ptr(tcx, self.lower_ty(ty), *mutbl), + hir::TyKind::Ref(region, ty, mutbl) => { let r = self.lower_lifetime(region, RegionInferReason::Reference); debug!(?r); - let t = self.lower_ty(mt.ty); - Ty::new_ref(tcx, r, t, mt.mutbl) + let t = self.lower_ty(ty); + Ty::new_ref(tcx, r, t, *mutbl) } hir::TyKind::Never => tcx.types.never, hir::TyKind::Tup(fields) => { @@ -3424,7 +3426,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } hir::TyKind::FieldOf(ty, hir::TyFieldPath { variant, field }) => self.lower_field_of( self.lower_ty(ty), - self.item_def_id(), + self.mod_id(), ty.span, hir_ty.hir_id, *variant, @@ -3478,7 +3480,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { fn lower_field_of( &self, ty: Ty<'tcx>, - item_def_id: LocalDefId, + mod_id: LocalModId, ty_span: Span, hir_id: HirId, variant: Option, @@ -3498,7 +3500,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { field.span.shrink_to_lo(), "you might be missing a variant here: `Variant.`", ) - .emit(); + .emit_err(); return Ty::new_error(tcx, err); }; @@ -3509,9 +3511,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { { res } else { - let err = dcx - .create_err(NoVariantNamed { span: variant.span, ident: variant, ty }) - .emit(); + let err = + dcx.emit_err(NoVariantNamed { span: variant.span, ident: variant, ty }); return Ty::new_error(tcx, err); } } else { @@ -3528,8 +3529,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } (FIRST_VARIANT, def.non_enum_variant()) }; - let (ident, def_scope) = - tcx.adjust_ident_and_get_scope(field, def.did(), item_def_id); + let (ident, def_scope) = tcx.adjust_ident_and_get_scope(field, def.did(), mod_id); if let Some((field_idx, field)) = variant .fields .iter_enumerated() @@ -3550,8 +3550,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } Ty::new_field_representing_type(tcx, ty, variant_idx, field_idx) } else { - let err = - dcx.create_err(NoFieldOnType { span: ident.span, field: ident, ty }).emit(); + let err = dcx.emit_err(NoFieldOnType { span: ident.span, field: ident, ty }); Ty::new_error(tcx, err) } } @@ -3559,8 +3558,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let index = match field.as_str().parse::() { Ok(idx) => idx, Err(_) => { - let err = - dcx.create_err(NoFieldOnType { span: field.span, field, ty }).emit(); + let err = dcx.emit_err(NoFieldOnType { span: field.span, field, ty }); return Ty::new_error(tcx, err); } }; @@ -3570,7 +3568,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { if tys.get(index).is_some() { Ty::new_field_representing_type(tcx, ty, FIRST_VARIANT, index.into()) } else { - let err = dcx.create_err(NoFieldOnType { span: field.span, field, ty }).emit(); + let err = dcx.emit_err(NoFieldOnType { span: field.span, field, ty }); Ty::new_error(tcx, err) } } diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check.rs b/compiler/rustc_hir_analysis/src/impl_wf_check.rs index e1a91292abe21..4615f1ae4fe58 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check.rs @@ -179,7 +179,7 @@ pub(crate) fn enforce_impl_lifetime_params_are_constrained( } } suggest_to_remove_or_use_generic(tcx, &mut diag, impl_def_id, param, true); - res = Err(diag.emit()); + res = Err(diag.emit_err()); } } ty::GenericParamDefKind::Type { .. } | ty::GenericParamDefKind::Const { .. } => { @@ -245,7 +245,7 @@ pub(crate) fn enforce_impl_non_lifetime_params_are_constrained( }); diag.code(E0207); suggest_to_remove_or_use_generic(tcx, &mut diag, impl_def_id, ¶m, false); - res = Err(diag.emit()); + res = Err(diag.emit_err()); } } res diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index d56c094f9f5c3..fdc6357bb9bbe 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -281,7 +281,7 @@ fn check_duplicate_params<'tcx>( return Err(tcx .dcx() .struct_span_err(span, format!("specializing impl repeats parameter `{param}`")) - .emit()); + .emit_err()); } Ok(()) } @@ -430,7 +430,7 @@ fn check_specialization_on<'tcx>( tcx.def_path_str(trait_ref.def_id), ), ) - .emit()) + .emit_err()) } } ty::ClauseKind::Projection(ty::ProjectionClause { projection_term, term }) => Err(tcx @@ -439,7 +439,7 @@ fn check_specialization_on<'tcx>( span, format!("cannot specialize on associated type `{projection_term} == {term}`",), ) - .emit()), + .emit_err()), ty::ClauseKind::ConstArgHasType(..) => { // FIXME(min_specialization), FIXME(const_generics): // It probably isn't right to allow _every_ `ConstArgHasType` but I am somewhat unsure @@ -454,7 +454,7 @@ fn check_specialization_on<'tcx>( _ => Err(tcx .dcx() .struct_span_err(span, format!("cannot specialize on predicate `{clause}`")) - .emit()), + .emit_err()), } } diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index a01ea8271570f..1cd9d008194fc 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -56,13 +56,12 @@ This API is completely unstable and subject to change. */ // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(never_type))] +#![cfg_attr(bootstrap, feature(unwrap_infallible))] #![feature(default_field_values)] #![feature(gen_blocks)] #![feature(iter_intersperse)] #![feature(slice_partition_dedup)] #![feature(try_blocks)] -#![feature(unwrap_infallible)] // tidy-alphabetical-end // These are used by Clippy. @@ -104,12 +103,10 @@ fn check_c_variadic_abi(tcx: TyCtxt<'_>, decl: &hir::FnDecl<'_>, abi: ExternAbi, match abi.supports_c_variadic() { CVariadicStatus::Stable => {} CVariadicStatus::NotSupported => { - tcx.dcx() - .create_err(diagnostics::VariadicFunctionCompatibleConvention { - span, - convention: &format!("{abi}"), - }) - .emit(); + tcx.dcx().emit_err(diagnostics::VariadicFunctionCompatibleConvention { + span, + convention: &format!("{abi}"), + }); } CVariadicStatus::Unstable { feature } => { if !tcx.features().enabled(feature) { diff --git a/compiler/rustc_hir_analysis/src/outlives/dump.rs b/compiler/rustc_hir_analysis/src/outlives/dump.rs index c0314c378fbdd..6cb585c6c97c5 100644 --- a/compiler/rustc_hir_analysis/src/outlives/dump.rs +++ b/compiler/rustc_hir_analysis/src/outlives/dump.rs @@ -1,4 +1,4 @@ -use rustc_hir::find_attr; +use rustc_attr_ir::find_attr; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::{bug, sym}; diff --git a/compiler/rustc_hir_analysis/src/variance/dump.rs b/compiler/rustc_hir_analysis/src/variance/dump.rs index 7963f5da0e870..aa05e512dc2af 100644 --- a/compiler/rustc_hir_analysis/src/variance/dump.rs +++ b/compiler/rustc_hir_analysis/src/variance/dump.rs @@ -1,8 +1,8 @@ use std::fmt::Write; +use rustc_attr_ir::find_attr; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; -use rustc_hir::find_attr; use rustc_middle::ty::{GenericArgs, TyCtxt}; fn format_variances(tcx: TyCtxt<'_>, def_id: LocalDefId) -> String { diff --git a/compiler/rustc_hir_analysis/src/variance/solve.rs b/compiler/rustc_hir_analysis/src/variance/solve.rs index eef26d783e7b7..06e8bc84e4474 100644 --- a/compiler/rustc_hir_analysis/src/variance/solve.rs +++ b/compiler/rustc_hir_analysis/src/variance/solve.rs @@ -5,6 +5,7 @@ //! optimal solution to the constraints. The final variance for each //! inferred is then written into the `variance_map` in the tcx. +use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::DefIdMap; use rustc_middle::ty; use tracing::debug; @@ -43,16 +44,26 @@ struct SolveContext<'a, 'tcx> { pub(crate) fn solve_constraints<'tcx>( constraints_cx: ConstraintContext<'_, 'tcx>, ) -> ty::CrateVariancesMap<'tcx> { - let ConstraintContext { terms_cx, constraints, .. } = constraints_cx; + let ConstraintContext { terms_cx, mut constraints, .. } = constraints_cx; + let mut overridden_inferreds = FxHashSet::default(); let mut solutions = vec![ty::Bivariant; terms_cx.inferred_terms.len()]; + // prime the solutions for certain lang items which have hard-coded variance for (id, variances) in &terms_cx.lang_items { let InferredIndex(start) = terms_cx.inferred_starts[id]; for (i, &variance) in variances.iter().enumerate() { solutions[start + i] = variance; + overridden_inferreds.insert(start + i); } } + // ensure the solutions for overridden inferreds are never constrained by anything else + if !overridden_inferreds.is_empty() { + constraints.retain(|Constraint { inferred: InferredIndex(inferred), .. }| { + !overridden_inferreds.contains(inferred) + }); + } + let mut solutions_cx = SolveContext { terms_cx, constraints, solutions }; solutions_cx.solve(); let variances = solutions_cx.create_map(); diff --git a/compiler/rustc_hir_id/src/definitions.rs b/compiler/rustc_hir_id/src/definitions.rs index 50f7a15b19adf..426a4e0109153 100644 --- a/compiler/rustc_hir_id/src/definitions.rs +++ b/compiler/rustc_hir_id/src/definitions.rs @@ -8,6 +8,7 @@ use std::fmt::{self, Write}; use std::hash::Hash; use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::stable_hash::StableHasher; use rustc_hashes::Hash64; use rustc_index::IndexVec; @@ -46,13 +47,55 @@ impl LocalDefIdMap { } } +#[derive(Debug, Default)] +pub struct DefPathToIndexMap { + /// Stores mapping from local hash to def indices which are allocated when the order + /// of their allocation is deterministic (see where `commit_end_of_determinism` is called, + /// at the moment of writing it is after prefetch of `hir_crate_items` in `run_required_analysis`). + pub before_parallel_alloc: DefPathHashMap, + + /// Stores the same mapping as previous map but is used after the allocation order of def indices + /// is not deterministic, so when serializing metadata we have a ready to use sorted by stable local + /// hash (which does not change between compiler invocations) mapping. We use it for relatively small number + /// of definitions, so the majority of them would be stored in `DefPathHashMap`, which makes insertion + /// and serialization costs of `SortedMap` acceptable. + pub after_parallel_alloc: Option>, +} + +impl DefPathToIndexMap { + #[inline] + pub fn get(&self, hash: Hash64) -> Option { + self.before_parallel_alloc + .get(&hash) + .or_else(|| self.after_parallel_alloc.as_ref().and_then(|map| map.get(&hash).copied())) + } + + /// This insert function does not behave like regular `insert` of a `HashMap`, + /// as the return value is used only for printing information about existing + /// def index for local hash before panicking. So we can do not actually insert + /// def index into `det_part` when we are in non-deterministic mode. + #[inline] + pub fn insert(&mut self, hash: Hash64, index: DefIndex) -> Option { + match self.after_parallel_alloc.as_mut() { + None => self.before_parallel_alloc.insert(&hash, &index), + Some(map) => { + if let Some(existing) = self.before_parallel_alloc.get(&hash) { + return Some(existing); + } + + map.insert(hash, index) + } + } + } +} + #[derive(Debug)] pub struct Definitions { stable_crate_id: StableCrateId, def_id_to_key: IndexVec, // We do only store the local hash, as all the definitions are from the current crate. def_path_hashes: IndexVec, - def_path_hash_to_index: DefPathHashMap, + def_path_hash_to_index: DefPathToIndexMap, } /// A unique identifier that we can use to lookup a definition @@ -249,6 +292,15 @@ pub enum DefPathData { } impl Definitions { + /// This function indicates that the order of def id allocations + /// may be non-deterministic after it was called. + pub fn commit_end_of_determinism(&mut self) { + assert!( + self.def_path_hash_to_index.after_parallel_alloc.replace(Default::default()).is_none(), + "this function should be called only once" + ) + } + #[inline(always)] pub fn def_key(&self, id: LocalDefId) -> DefKey { self.def_id_to_key[id] @@ -323,7 +375,7 @@ impl Definitions { // Check for hash collisions of DefPathHashes. These should be // exceedingly rare. if let Some(existing) = - self.def_path_hash_to_index.insert(&local_hash, &def_id.local_def_index) + self.def_path_hash_to_index.insert(local_hash, def_id.local_def_index) { let def_path1 = self.def_path(LocalDefId { local_def_index: existing }); let def_path2 = self.def_path(def_id); @@ -411,11 +463,11 @@ impl Definitions { pub fn local_def_path_hash_to_def_id(&self, hash: DefPathHash) -> Option { debug_assert!(hash.stable_crate_id() == self.stable_crate_id); self.def_path_hash_to_index - .get(&hash.local_hash()) + .get(hash.local_hash()) .map(|local_def_index| LocalDefId { local_def_index }) } - pub fn def_path_hash_to_def_index_map(&self) -> &DefPathHashMap { + pub fn def_path_hash_to_def_index_map(&self) -> &DefPathToIndexMap { &self.def_path_hash_to_index } diff --git a/compiler/rustc_hir_id/src/lib.rs b/compiler/rustc_hir_id/src/lib.rs index dce7e7fd31a36..0e01600f9cb3d 100644 --- a/compiler/rustc_hir_id/src/lib.rs +++ b/compiler/rustc_hir_id/src/lib.rs @@ -16,7 +16,7 @@ use rustc_data_structures::stable_hash::{ StableHash, StableHashCtxt, StableHasher, StableOrd, ToStableHashKey, }; use rustc_macros::{Decodable, Encodable, StableHash}; -use rustc_span::def_id::{CRATE_DEF_ID, DefId, DefIndex, DefPathHash, LocalDefId}; +use rustc_span::def_id::{CRATE_DEF_ID, DefId, DefIndex, DefPathHash, LocalDefId, LocalModId}; #[derive(Copy, Clone, PartialEq, Eq, Hash, Encodable, Decodable)] pub struct OwnerId { @@ -42,6 +42,12 @@ impl From for DefId { } } +impl From for OwnerId { + fn from(value: LocalModId) -> Self { + OwnerId { def_id: value.to_local_def_id() } + } +} + impl OwnerId { #[inline] pub fn to_def_id(self) -> DefId { @@ -141,6 +147,12 @@ impl fmt::Display for HirId { } } +impl From for HirId { + fn from(id: LocalModId) -> Self { + HirId::make_owner(id.to_local_def_id()) + } +} + rustc_data_structures::define_stable_id_collections!(HirIdMap, HirIdSet, HirIdMapEntry, HirId); rustc_data_structures::define_id_collections!( ItemLocalMap, diff --git a/compiler/rustc_hir_pretty/Cargo.toml b/compiler/rustc_hir_pretty/Cargo.toml index f5d7dbd3f96e4..8279d9c6d620a 100644 --- a/compiler/rustc_hir_pretty/Cargo.toml +++ b/compiler/rustc_hir_pretty/Cargo.toml @@ -8,6 +8,7 @@ edition = "2024" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } +rustc_attr_ir = { path = "../rustc_attr_ir" } rustc_hir = { path = "../rustc_hir" } rustc_span = { path = "../rustc_span" } # tidy-alphabetical-end diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 6d1ae563a9fa2..d8c57bfbdb56b 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -16,8 +16,8 @@ use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent}; use rustc_ast_pretty::pp::{self, BoxMarker, Breaks}; use rustc_ast_pretty::pprust::state::MacHeader; use rustc_ast_pretty::pprust::{Comments, PrintState}; +use rustc_attr_ir::{AttrArgs, AttrItem, Attribute, AttributeKind, PrintAttribute}; use rustc_hir as hir; -use rustc_hir::attrs::{AttributeKind, PrintAttribute}; use rustc_hir::{ BindingMode, ByRef, ConstArg, ConstArgExprField, ConstArgKind, GenericArg, GenericBound, GenericParam, GenericParamKind, HirId, ImplicitSelfKind, LifetimeParamKind, Node, PatKind, @@ -72,12 +72,12 @@ impl PpAnn for &dyn rustc_hir::intravisit::HirTyCtxt<'_> { pub struct State<'a> { pub s: pp::Printer, comments: Option>, - attrs: &'a dyn Fn(HirId) -> &'a [hir::Attribute], + attrs: &'a dyn Fn(HirId) -> &'a [Attribute], ann: &'a (dyn PpAnn + 'a), } impl<'a> State<'a> { - fn attrs(&self, id: HirId) -> &'a [hir::Attribute] { + fn attrs(&self, id: HirId) -> &'a [Attribute] { (self.attrs)(id) } @@ -86,7 +86,7 @@ impl<'a> State<'a> { expr.precedence(&has_attr) } - fn print_attrs(&mut self, attrs: &[hir::Attribute]) { + fn print_attrs(&mut self, attrs: &[Attribute]) { if attrs.is_empty() { return; } @@ -99,9 +99,9 @@ impl<'a> State<'a> { /// Print a single attribute as if it has style `style`, disregarding the /// actual style of the attribute. - fn print_attribute_as_style(&mut self, attr: &hir::Attribute, style: ast::AttrStyle) { + fn print_attribute_as_style(&mut self, attr: &Attribute, style: ast::AttrStyle) { match &attr { - hir::Attribute::Unparsed(unparsed) => { + Attribute::Unparsed(unparsed) => { self.maybe_print_comment(unparsed.span.lo()); match style { ast::AttrStyle::Inner => self.word("#!["), @@ -111,13 +111,13 @@ impl<'a> State<'a> { self.word("]"); self.hardbreak() } - hir::Attribute::Parsed(AttributeKind::DocComment { kind, comment, .. }) => { + Attribute::Parsed(AttributeKind::DocComment { kind, comment, .. }) => { self.word(rustc_ast_pretty::pprust::state::doc_comment_to_string( *kind, style, *comment, )); self.hardbreak() } - hir::Attribute::Parsed(pa) => { + Attribute::Parsed(pa) => { match style { ast::AttrStyle::Inner => self.word("#![attr = "), ast::AttrStyle::Outer => self.word("#[attr = "), @@ -129,7 +129,7 @@ impl<'a> State<'a> { } } - fn print_attr_item(&mut self, item: &hir::AttrItem, span: Span) { + fn print_attr_item(&mut self, item: &AttrItem, span: Span) { let ib = self.ibox(0); let path = ast::Path { span, @@ -146,21 +146,20 @@ impl<'a> State<'a> { }; match &item.args { - hir::AttrArgs::Delimited(DelimArgs { dspan: _, delim, tokens }) => self - .print_mac_common( - Some(MacHeader::Path(&path)), - false, - None, - *delim, - None, - &tokens, - true, - span, - ), - hir::AttrArgs::Empty => { + AttrArgs::Delimited(DelimArgs { dspan: _, delim, tokens }) => self.print_mac_common( + Some(MacHeader::Path(&path)), + false, + None, + *delim, + None, + &tokens, + true, + span, + ), + AttrArgs::Empty => { PrintState::print_path(self, &path, false, 0); } - hir::AttrArgs::Eq { eq_span: _, expr } => { + AttrArgs::Eq { eq_span: _, expr } => { PrintState::print_path(self, &path, false, 0); self.space(); self.word_space("="); @@ -216,6 +215,7 @@ impl<'a> State<'a> { Node::LetStmt(a) => self.print_local_decl(a), Node::Crate(..) => panic!("cannot print Crate"), Node::WherePredicate(pred) => self.print_where_predicate(pred), + Node::NestedUseTree(tree) => self.print_use_tree(tree), Node::TestBinderForall(_) => panic!("cannot print Node::TestBinderForall"), Node::TestBinderExists(_) => panic!("cannot print Node::TestBinderExists"), Node::TestBinderBoundTypeConstraint(_) => { @@ -277,7 +277,7 @@ pub fn print_crate<'a>( krate: &hir::Mod<'_>, filename: FileName, input: String, - attrs: &'a dyn Fn(HirId) -> &'a [hir::Attribute], + attrs: &'a dyn Fn(HirId) -> &'a [Attribute], ann: &'a dyn PpAnn, ) -> String { let mut s = State { @@ -311,7 +311,7 @@ where printer.s.eof() } -pub fn attribute_to_string(ann: &dyn PpAnn, attr: &hir::Attribute) -> String { +pub fn attribute_to_string(ann: &dyn PpAnn, attr: &Attribute) -> String { to_string(ann, |s| s.print_attribute_as_style(attr, ast::AttrStyle::Outer)) } @@ -396,14 +396,16 @@ impl<'a> State<'a> { self.print_type(ty); self.word("]"); } - hir::TyKind::Ptr(ref mt) => { + hir::TyKind::Ptr(ref ty, mutbl) => { self.word("*"); - self.print_mt(mt, true); + self.print_mutability(mutbl, true); + self.print_type(ty); } - hir::TyKind::Ref(lifetime, ref mt) => { + hir::TyKind::Ref(lifetime, ref ty, mutbl) => { self.word("&"); self.print_opt_lifetime(lifetime); - self.print_mt(mt, false); + self.print_mutability(mutbl, false); + self.print_type(ty); } hir::TyKind::Never => { self.word("!"); @@ -609,22 +611,11 @@ impl<'a> State<'a> { self.end(ib); self.end(cb); } - hir::ItemKind::Use(path, kind) => { + hir::ItemKind::Use(ref tree) => { let (cb, ib) = self.head("use"); - self.print_path(path, false); - match kind { - hir::UseKind::Single(ident) => { - if path.segments.last().unwrap().ident != ident { - self.space(); - self.word_space("as"); - self.print_ident(ident); - } - self.word(";"); - } - hir::UseKind::Glob => self.word("::*;"), - hir::UseKind::ListStem => self.word("::{};"), - } + self.print_use_tree(tree); + self.word(";"); self.end(ib); self.end(cb); } @@ -819,6 +810,29 @@ impl<'a> State<'a> { self.ann.post(self, AnnNode::Item(item)) } + fn print_use_tree(&mut self, tree: &hir::UseTree<'_>) { + let hir::UseTree { prefix, kind } = *tree; + self.print_path(prefix, false); + match kind { + hir::UseKind::Single(ident) => { + if tree.prefix.segments.last().unwrap().ident != ident { + self.space(); + self.word_space("as"); + self.print_ident(ident); + } + } + hir::UseKind::Glob => self.word("::*"), + hir::UseKind::Nested { items } => { + self.word("::{"); + for (item, _, _) in items { + self.print_use_tree(item); + self.word(","); + } + self.word("}"); + } + } + } + fn print_trait_ref(&mut self, t: &hir::TraitRef<'_>) { self.print_path(t.path, false); } @@ -2556,11 +2570,6 @@ impl<'a> State<'a> { } } - fn print_mt(&mut self, mt: &hir::MutTy<'_>, print_const: bool) { - self.print_mutability(mt.mutbl, print_const); - self.print_type(mt.ty); - } - fn print_fn_output(&mut self, decl: &hir::FnDecl<'_>) { match decl.output { hir::FnRetTy::Return(ty) => { diff --git a/compiler/rustc_hir_typeck/Cargo.toml b/compiler/rustc_hir_typeck/Cargo.toml index 0e00615170aa2..840441ed3bbfa 100644 --- a/compiler/rustc_hir_typeck/Cargo.toml +++ b/compiler/rustc_hir_typeck/Cargo.toml @@ -8,6 +8,7 @@ edition = "2024" itertools = "0.15" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } +rustc_attr_ir = { path = "../rustc_attr_ir" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_hir = { path = "../rustc_hir" } diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 8a919ede26605..025c64896edb7 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -2,12 +2,13 @@ use std::iter; use rustc_abi::{CanonAbi, ExternAbi}; use rustc_ast::util::parser::ExprPrecedence; +use rustc_attr_ir::find_attr; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_errors::{Applicability, Diag, ErrorGuaranteed, StashKey, msg}; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def::{self, CtorKind, Namespace, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, HirId, find_attr}; +use rustc_hir::{self as hir, HirId}; use rustc_hir_analysis::autoderef::Autoderef; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes}; use rustc_infer::traits::{Obligation, ObligationCause, ObligationCauseCode}; @@ -127,7 +128,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty: autoderef.final_ty(), }); err.span_label(callee_expr.span, "you can create scalable vectors using intrinsics"); - Ty::new_error(self.tcx, err.emit()); + Ty::new_error(self.tcx, err.emit_err()); } self.register_predicates(autoderef.into_obligations()); @@ -1069,7 +1070,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label(span, label); } } - err.emit() + err.emit_err() } fn confirm_deferred_closure_call( diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index a82290d395de3..14cb73e28c871 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -39,7 +39,7 @@ use rustc_lint_defs::builtin::{TRIVIAL_CASTS, TRIVIAL_NUMERIC_CASTS}; use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::AllowTwoPhase; -use rustc_middle::ty::cast::{CastKind, CastTy}; +use rustc_middle::ty::cast::CastTy; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::{ self, Ty, TyCtxt, TypeAndMut, TypeVisitableExt, Unnormalized, VariantDef, elaborate, @@ -52,6 +52,22 @@ use tracing::{debug, instrument}; use super::FnCtxt; use crate::{diagnostics, type_error_struct}; +/// Cast Kind. See [RFC 401](https://rust-lang.github.io/rfcs/0401-coercions.html) +/// (or rustc_hir_typeck/src/cast.rs). +#[derive(Copy, Clone, Debug)] +pub enum CastKind { + PtrPtrCast, + PtrAddrCast, + AddrPtrCast, + NumericCast, + EnumCast, + PrimIntCast, + U8CharCast, + ArrayPtrCast, + FnPtrPtrCast, + FnPtrAddrCast, +} + /// Reifies a cast check to be checked once we have full type information for /// a function context. #[derive(Debug)] @@ -733,7 +749,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { err.span_help(self.expr_span, "consider using a box or reference as appropriate"); } } - err.emit() + err.emit_err() } fn trivial_cast_lint(&self, fcx: &FnCtxt<'a, 'tcx>) { @@ -912,7 +928,6 @@ impl<'a, 'tcx> CastCheck<'tcx> { /// directly. coercion-cast is handled in check instead of here. fn do_check(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result> { use rustc_middle::ty::cast::CastTy::*; - use rustc_middle::ty::cast::IntTy::*; let (t_from, t_cast) = match (CastTy::from_ty(self.expr_ty), CastTy::from_ty(self.cast_ty)) { @@ -947,7 +962,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { // a cast. ty::Ref(_, inner_ty, mutbl) => { return match t_cast { - Int(_) | Float => match *inner_ty.kind() { + Int | CEnum | Bool | Char | Float => match *inner_ty.kind() { ty::Int(_) | ty::Uint(_) | ty::Float(_) @@ -979,19 +994,21 @@ impl<'a, 'tcx> CastCheck<'tcx> { } match (t_from, t_cast) { // These types have invariants! can't cast into them. - (_, Int(CEnum) | FnPtr) => Err(CastError::NonScalar), + (_, CEnum | FnPtr) => Err(CastError::NonScalar), // * -> Bool - (_, Int(Bool)) => Err(CastError::CastToBool), + (_, Bool) => Err(CastError::CastToBool), // * -> Char - (Int(U(ty::UintTy::U8)), Int(Char)) => Ok(CastKind::U8CharCast), // u8-char-cast - (_, Int(Char)) => Err(CastError::CastToChar), + (Int, Char) if self.expr_ty == fcx.tcx.types.u8 => { + Ok(CastKind::U8CharCast) // u8-char-cast + } + (_, Char) => Err(CastError::CastToChar), // prim -> float,ptr - (Int(Bool) | Int(CEnum) | Int(Char), Float) => Err(CastError::NeedViaInt), + (Bool | CEnum | Char, Float) => Err(CastError::NeedViaInt), - (Int(Bool) | Int(CEnum) | Int(Char) | Float, Ptr(_)) | (Ptr(_) | FnPtr, Float) => { + (Bool | CEnum | Char | Float, Ptr(_)) | (Ptr(_) | FnPtr, Float) => { Err(CastError::IllegalCast) } @@ -999,24 +1016,24 @@ impl<'a, 'tcx> CastCheck<'tcx> { (Ptr(m_e), Ptr(m_c)) => self.check_ptr_ptr_cast(fcx, m_e, m_c), // ptr-ptr-cast // ptr-addr-cast - (Ptr(m_expr), Int(_)) => self.check_ptr_addr_cast(fcx, m_expr), + (Ptr(m_expr), Int) => self.check_ptr_addr_cast(fcx, m_expr), - (FnPtr, Int(_)) => { + (FnPtr, Int) => { // FIXME(#95489): there should eventually be a lint for these casts Ok(CastKind::FnPtrAddrCast) } // addr-ptr-cast - (Int(_), Ptr(mt)) => self.check_addr_ptr_cast(fcx, mt), + (Int, Ptr(mt)) => self.check_addr_ptr_cast(fcx, mt), // fn-ptr-cast (FnPtr, Ptr(mt)) => self.check_fptr_ptr_cast(fcx, mt), // enum -> int - (Int(CEnum), Int(_)) => self.check_enum_cast(fcx), + (CEnum, Int) => self.check_enum_cast(fcx), // prim -> prim - (Int(Char) | Int(Bool), Int(_)) => Ok(CastKind::PrimIntCast), + (Char | Bool, Int) => Ok(CastKind::PrimIntCast), - (Int(_) | Float, Int(_) | Float) => Ok(CastKind::NumericCast), + (Int | Float, Int | Float) => Ok(CastKind::NumericCast), } } diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index 60fc0b8f9b214..b1b7aa5727097 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -1,11 +1,12 @@ use std::cell::RefCell; +use rustc_attr_ir::lang_items::LangItem; use rustc_hir as hir; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def::DefKind; use rustc_hir_analysis::check::check_function_signature; use rustc_infer::infer::RegionVariableOrigin; use rustc_infer::traits::WellFormedLoc; +use rustc_lint_defs::builtin::UNSAFE_PANIC_HANDLERS; use rustc_middle::ty::{self, Binder, Ty, TyCtxt}; use rustc_span::def_id::LocalDefId; use rustc_span::sym; @@ -13,6 +14,7 @@ use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode}; use tracing::{debug, instrument}; use crate::coercion::CoerceMany; +use crate::diagnostics::UnsafePanicHandlers; use crate::gather_locals::GatherLocalsVisitor; use crate::{CoroutineTypes, Diverges, FnCtxt}; @@ -63,7 +65,6 @@ pub(super) fn check_fn<'a, 'tcx>( }); // Add formal parameters. - let inputs_hir = tcx.hir_fn_decl_by_hir_id(fn_id).map(|decl| &decl.inputs); let inputs_fn = fn_sig.inputs().iter().copied(); for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() { // We checked the root's signature during wfcheck, but not the child. @@ -79,7 +80,7 @@ pub(super) fn check_fn<'a, 'tcx>( } // Check the pattern. - let ty: Option<&hir::Ty<'_>> = inputs_hir.and_then(|h| h.get(idx)); + let ty: Option<&hir::Ty<'_>> = decl.inputs.get(idx); let ty_span = ty.map(|ty| ty.span); fcx.check_pat_top(param.pat, param_ty, ty_span, None, None); if param.pat.is_never_pattern() { @@ -177,6 +178,21 @@ fn check_panic_info_fn(tcx: TyCtxt<'_>, fn_id: LocalDefId, fn_sig: ty::FnSig<'_> tcx.dcx().span_err(span, "should have no const parameters"); } + if fn_sig.safety().is_unsafe() { + let hir_id = tcx.local_def_id_to_hir_id(fn_id); + let span = tcx.def_span(fn_id); + // If the function is implicitly "unsafe" because it has a `#[target_feature]` attribute, + // then we should already have emitted an error. Don't also emit a warning. + // See `tests/ui/panic-handler/panic-handler-with-target-feature.rs` + if let Some(hir_fn_sig) = tcx.hir_fn_sig_by_hir_id(hir_id) + && hir_fn_sig.header.safety == hir::HeaderSafety::SafeTargetFeatures + { + tcx.dcx().span_delayed_bug(span, "`unsafe_panic_handlers` lint suppressed because there should already be an error for `#[target_feature]`"); + } else { + tcx.emit_node_span_lint(UNSAFE_PANIC_HANDLERS, hir_id, span, UnsafePanicHandlers); + } + } + let panic_info_did = tcx.require_lang_item(LangItem::PanicInfo, span); // build type `for<'a, 'b> fn(&'a PanicInfo<'b>) -> !` diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 22349259b32a6..79b4eff9153b0 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -4,9 +4,9 @@ use std::iter; use std::ops::ControlFlow; use rustc_abi::ExternAbi; +use rustc_attr_ir::lang_items::LangItem; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk, InferResult}; use rustc_infer::traits::{ObligationCauseCode, PredicateObligations}; @@ -385,44 +385,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - // Don't infer a closure signature from a goal that names the closure type as this will - // (almost always) lead to occurs check errors later in type checking. - if self.next_trait_solver() - && let Some(inferred_sig) = inferred_sig - { - // In the new solver it is difficult to explicitly normalize the inferred signature as we - // would have to manually handle universes and rewriting bound vars and placeholders back - // and forth. - // - // Instead we take advantage of the fact that we relating an inference variable with an alias - // will only instantiate the variable if the alias is rigid(*not quite). Concretely we: - // - Create some new variable `?sig` - // - Equate `?sig` with the unnormalized signature, e.g. `fn( as Trait>::Assoc)` - // - Depending on whether ` as Trait>::Assoc` is rigid, ambiguous or normalizeable, - // we will either wind up with `?sig= as Trait>::Assoc/?y/ConcreteTy` respectively. - // - // *: In cases where there are ambiguous aliases in the signature that make use of bound vars - // they will wind up present in `?sig` even though they are non-rigid. - // - // This is a bit weird and means we may wind up discarding the goal due to it naming `expected_ty` - // even though the normalized form may not name `expected_ty`. However, this matches the existing - // behaviour of the old solver and would be technically a breaking change to fix. - let generalized_fnptr_sig = self.next_ty_var(span); - let inferred_fnptr_sig = Ty::new_fn_ptr(self.tcx, inferred_sig.sig); - self.demand_eqtype(span, inferred_fnptr_sig, generalized_fnptr_sig); - - let resolved_sig = self.deeply_resolve_ignoring_regions(generalized_fnptr_sig); - - if resolved_sig.visit_with(&mut MentionsTy { expected_ty }).is_continue() { - expected_sig = Some(ExpectedSig { - cause_span: inferred_sig.cause_span, - sig: resolved_sig.fn_sig(self.tcx), - }); - } - } else { - if inferred_sig.visit_with(&mut MentionsTy { expected_ty }).is_continue() { - expected_sig = inferred_sig; - } + if inferred_sig.visit_with(&mut MentionsTy { expected_ty }).is_continue() { + expected_sig = inferred_sig; } } @@ -778,7 +742,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { true, closure_arg_span, ) - .emit(); + .emit_err(); let error_sig = self.error_sig_of_closure(decl, guar); diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 8a4228ebe3124..af00194a95bf0 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -37,11 +37,11 @@ use std::ops::{ControlFlow, Deref}; +use rustc_attr_ir::InlineAttr; +use rustc_attr_ir::lang_items::LangItem; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, struct_span_code_err}; use rustc_hir as hir; -use rustc_hir::attrs::InlineAttr; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_infer::infer::relate::RelateResult; @@ -1913,9 +1913,9 @@ impl<'tcx> CoerceMany<'tcx> { ); } - let reported = err.emit_unless_delay(unsized_return); + let guar = err.emit_err_unless_delay(unsized_return); - self.final_ty = Some(Ty::new_error(fcx.tcx, reported)); + self.final_ty = Some(Ty::new_error(fcx.tcx, guar)); } } } diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index d18745e24fd84..e1ad43f0c00c9 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -1,7 +1,8 @@ -use rustc_errors::{Applicability, Diag, MultiSpan, listify}; -use rustc_hir::def::Res; +use rustc_attr_ir::find_attr; +use rustc_errors::{Applicability, Diag, MultiSpan, listify, pluralize}; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::Visitor; -use rustc_hir::{self as hir, find_attr}; use rustc_infer::infer::DefineOpaqueTypes; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::error::{ExpectedFound, TypeError}; @@ -28,7 +29,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if expr_ty == expected { return; } - self.annotate_alternative_method_deref(err, expr, error); + self.annotate_alternative_method_deref_for_unop(err, expr, error); self.explain_self_literal(err, expr, expected, expr_ty); // Use `||` to give these suggestions a precedence @@ -734,7 +735,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match (parent, error) { (hir::Node::LetStmt(hir::LetStmt { ty: Some(ty), init: Some(init), .. }), _) - if init.hir_id == current_hir_id && !ty.span.source_equal(init.span) => + if init.hir_id == current_hir_id && ty.span.lo_hi() != init.span.lo_hi() => { // Point at `let` assignment type. err.span_label(ty.span, "expected due to this"); @@ -752,11 +753,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::ExprKind::Path(hir::QPath::Resolved( None, hir::Path { - res: - hir::def::Res::Def( - hir::def::DefKind::Static { .. } | hir::def::DefKind::Const, - def_id, - ), + res: hir::def::Res::Def(DefKind::Static { .. } | DefKind::Const, def_id), .. }, )) => { @@ -897,18 +894,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .inputs .iter() .filter_map(|ty| match ty.kind { - hir::TyKind::Ref(lt, mut_ty) if ty.span == *ty_span => Some((lt, mut_ty)), + hir::TyKind::Ref(lt, inner_ty, mutbl) if ty.span == *ty_span => Some((lt, inner_ty, mutbl)), _ => None, }) .next() { - let mut sugg = if ty_ref.1.mutbl.is_mut() { + let mut sugg = if ty_ref.2.is_mut() { // Leave `&'name mut Ty` and `&mut Ty` as they are (#136028). vec![] } else { // `&'name Ty` -> `&'name mut Ty` or `&Ty` -> `&mut Ty` vec![( - ty_ref.1.ty.span.shrink_to_lo(), + ty_ref.1.span.shrink_to_lo(), format!("{}mut ", if ty_ref.0.ident.span.is_empty() { "" } else { " " },), )] }; @@ -929,7 +926,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } - fn annotate_alternative_method_deref( + fn annotate_alternative_method_deref_for_unop( &self, err: &mut Diag<'_>, expr: &hir::Expr<'_>, @@ -949,7 +946,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let hir::ExprKind::Unary(hir::UnOp::Deref, deref) = lhs.kind else { return; }; - let hir::ExprKind::MethodCall(path, base, args, _) = deref.kind else { + self.annotate_alternative_method_deref(err, deref, Some(expected)) + } + + #[tracing::instrument(skip(self, err), level = "debug")] + pub(crate) fn annotate_alternative_method_deref( + &self, + err: &mut Diag<'_>, + expr: &hir::Expr<'_>, + expected: Option>, + ) { + let hir::ExprKind::MethodCall(path, base, args, _) = expr.kind else { return; }; let Some(self_ty) = self.typeck_results.borrow().expr_ty_adjusted_opt(base) else { @@ -959,7 +966,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let Ok(pick) = self.lookup_probe_for_diagnostic( path.ident, self_ty, - deref, + expr, probe::ProbeScope::TraitsInScope, None, ) else { @@ -969,10 +976,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let Ok(in_scope_methods) = self.probe_for_name_many( probe::Mode::MethodCall, path.ident, - Some(expected), + expected, probe::IsSuggestion(true), self_ty, - deref.hir_id, + expr.hir_id, probe::ProbeScope::TraitsInScope, ) else { return; @@ -984,10 +991,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let Ok(all_methods) = self.probe_for_name_many( probe::Mode::MethodCall, path.ident, - Some(expected), + expected, probe::IsSuggestion(true), self_ty, - deref.hir_id, + expr.hir_id, probe::ProbeScope::AllTraits, ) else { return; @@ -995,34 +1002,51 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let suggestions: Vec<_> = all_methods .into_iter() - .filter(|c| c.item.def_id != pick.item.def_id) - .map(|c| { + .filter_map(|c| { + if c.item.def_id == pick.item.def_id { + return None; + } let m = c.item; let generic_args = ty::GenericArgs::for_item(self.tcx, m.def_id, |param, _| { - self.var_for_def(deref.span, param) + self.var_for_def(expr.span, param) }); - let mutability = - match self.tcx.fn_sig(m.def_id).skip_binder().input(0).skip_binder().kind() { - ty::Ref(_, _, hir::Mutability::Mut) => "&mut ", - ty::Ref(_, _, _) => "&", - _ => "", - }; - vec![ - ( - deref.span.until(base.span), - format!( - "{}({}", - with_no_trimmed_paths!( - self.tcx.def_path_str_with_args(m.def_id, generic_args,) - ), - mutability, - ), - ), + let fn_sig = self.tcx.fn_sig(m.def_id); + if fn_sig.skip_binder().inputs().skip_binder().len() != args.len() + 1 { + return None; + } + let rcvr_ty = fn_sig.skip_binder().input(0).skip_binder(); + let (mutability, ty) = match rcvr_ty.kind() { + ty::Ref(_, ty, hir::Mutability::Mut) => ("&mut ", ty), + ty::Ref(_, ty, _) => ("&", ty), + _ => ("", &rcvr_ty), + }; + let path = match self.tcx.assoc_parent(m.def_id) { + Some((_, DefKind::Impl { of_trait: true })) => { + // We have `impl Trait for T {}`, suggest `::method`. + self.tcx.def_path_str_with_args(m.def_id, generic_args).to_string() + } + Some((_, DefKind::Impl { of_trait: false })) => { + if let ty::Adt(def, _) = ty.kind() { + // We have `impl T {}`, suggest `T::method`. + format!("{}::{}", self.tcx.def_path_str(def.did()), path.ident) + } else { + // This should be unreachable, as `impl &'a T {}` is invalid. + format!("{ty}::{}", path.ident) + } + } + // Fallback for arbitrary self types. + _ => with_no_trimmed_paths!( + self.tcx.def_path_str_with_args(m.def_id, generic_args) + ) + .to_string(), + }; + Some(vec![ + (expr.span.until(base.span), format!("{path}({}", mutability)), match &args { - [] => (base.span.shrink_to_hi().with_hi(deref.span.hi()), ")".to_string()), + [] => (base.span.shrink_to_hi().with_hi(expr.span.hi()), ")".to_string()), [first, ..] => (base.span.between(first.span), ", ".to_string()), }, - ] + ]) }) .collect(); if suggestions.is_empty() { @@ -1076,9 +1100,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), ); if suggestions.len() > other_methods_in_scope.len() { + let n = suggestions.len() - other_methods_in_scope.len(); err.note(format!( - "additionally, there are {} other available methods that aren't in scope", - suggestions.len() - other_methods_in_scope.len() + "additionally, there {are} {n} other available method{s} that {are}n't in scope", + are = pluralize!("is", n), + s = pluralize!(n), )); } err.multipart_suggestions( @@ -1293,7 +1319,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let hir::def::Res::Def(kind, def_id) = path.res else { return; }; - let callable_kind = if matches!(kind, hir::def::DefKind::Ctor(_, _)) { + let callable_kind = if matches!(kind, DefKind::Ctor(_, _)) { CallableKind::Constructor } else { CallableKind::Function diff --git a/compiler/rustc_hir_typeck/src/diagnostics.rs b/compiler/rustc_hir_typeck/src/diagnostics.rs index 3b7de7790ac02..12e1006cc734f 100644 --- a/compiler/rustc_hir_typeck/src/diagnostics.rs +++ b/compiler/rustc_hir_typeck/src/diagnostics.rs @@ -275,7 +275,7 @@ pub(crate) struct SuggestAnnotations { pub suggestions: Vec, } impl Subdiagnostic for SuggestAnnotations { - fn add_to_diag(self, diag: &mut Diag<'_, G>) { + fn add_to_diag(self, diag: &mut Diag<'_>) { if self.suggestions.is_empty() { return; } @@ -338,7 +338,7 @@ pub(crate) struct TypeMismatchFruTypo { } impl Subdiagnostic for TypeMismatchFruTypo { - fn add_to_diag(self, diag: &mut Diag<'_, G>) { + fn add_to_diag(self, diag: &mut Diag<'_>) { diag.arg("expr", self.expr.as_deref().unwrap_or("NONE")); // Only explain that `a ..b` is a range if it's split up @@ -360,7 +360,7 @@ impl Subdiagnostic for TypeMismatchFruTypo { ); } - diag.span_suggestion( + diag.span_suggestion_verbose( self.expr_span.shrink_to_hi(), msg!( "to set the remaining fields{$expr -> @@ -561,7 +561,7 @@ pub(crate) struct RemoveSemiForCoerce { } impl Subdiagnostic for RemoveSemiForCoerce { - fn add_to_diag(self, diag: &mut Diag<'_, G>) { + fn add_to_diag(self, diag: &mut Diag<'_>) { let mut multispan: MultiSpan = self.semi.into(); multispan.push_span_label( self.expr, @@ -584,6 +584,14 @@ impl Subdiagnostic for RemoveSemiForCoerce { } } +#[derive(Diagnostic)] +#[diag("runtime values cannot be referenced in patterns", code = E0080)] +pub(crate) struct NonConstPathInPattern { + #[primary_span] + #[label("references a runtime value")] + pub spans: Vec, +} + #[derive(Diagnostic)] #[diag("union patterns should have exactly one field")] pub(crate) struct UnionPatMultipleFields { @@ -704,9 +712,9 @@ pub(crate) struct BreakNonLoop<'a> { pub break_expr_span: Span, } -impl<'a, G> Diagnostic<'_, G> for BreakNonLoop<'a> { +impl<'a> Diagnostic<'_> for BreakNonLoop<'a> { #[track_caller] - fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_> { let mut diag = Diag::new(dcx, level, msg!("`break` with value from a `{$kind}` loop")); diag.span(self.span); diag.code(E0571); @@ -718,7 +726,7 @@ impl<'a, G> Diagnostic<'_, G> for BreakNonLoop<'a> { if let Some(head) = self.head { diag.span_label(head, msg!("you can't `break` with a value in a `{$kind}` loop")); } - diag.span_suggestion( + diag.span_suggestion_verbose( self.span, msg!("use `break` on its own without a value inside this `{$kind}` loop"), self.suggestion, @@ -736,7 +744,7 @@ impl<'a, G> Diagnostic<'_, G> for BreakNonLoop<'a> { diag.downgrade_to_delayed_bug(); } _ => { - diag.span_suggestion( + diag.span_suggestion_verbose( self.break_expr_span, msg!("alternatively, you might have meant to use the available loop label"), label.ident, @@ -906,7 +914,7 @@ pub(crate) enum CastUnknownPointerSub { } impl rustc_errors::Subdiagnostic for CastUnknownPointerSub { - fn add_to_diag(self, diag: &mut Diag<'_, G>) { + fn add_to_diag(self, diag: &mut Diag<'_>) { match self { CastUnknownPointerSub::To(span) => { let msg = msg!("needs more type information"); @@ -1202,9 +1210,9 @@ pub(crate) struct NakedFunctionsAsmBlock { pub non_asms: Vec, } -impl Diagnostic<'_, G> for NakedFunctionsAsmBlock { +impl Diagnostic<'_> for NakedFunctionsAsmBlock { #[track_caller] - fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_> { let mut diag = Diag::new( dcx, level, @@ -1249,7 +1257,7 @@ pub(crate) fn maybe_emit_plus_equals_diagnostic<'a>( err.span_label(assign_op.span, "cannot use `+=` in a let chain"); - err.span_suggestion( + err.span_suggestion_short( assign_op.span, "you might have meant to compare with `==` instead of assigning with `+=`", "==", @@ -1337,3 +1345,7 @@ impl ExprParenthesesNeeded { ExprParenthesesNeeded { left: s.shrink_to_lo(), right: s.shrink_to_hi() } } } + +#[derive(Diagnostic)] +#[diag("`#[panic_handler]` functions can't be `unsafe`")] +pub(crate) struct UnsafePanicHandlers; diff --git a/compiler/rustc_hir_typeck/src/diverges.rs b/compiler/rustc_hir_typeck/src/diverges.rs index 1a10e19d0a5e9..15ce40d417af2 100644 --- a/compiler/rustc_hir_typeck/src/diverges.rs +++ b/compiler/rustc_hir_typeck/src/diverges.rs @@ -1,13 +1,13 @@ -use std::{cmp, ops}; +use std::ops; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::Span; /// Tracks whether executing a node may exit normally (versus /// return/break/panic, which "diverge", leaving dead code in their /// wake). Tracked semi-automatically (through type variables marked /// as diverging), with some manual adjustments for control-flow /// primitives (approximating a CFG). -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub(crate) enum Diverges { /// Potentially unknown, some cases converge, /// others require a CFG to determine them. @@ -40,14 +40,28 @@ pub(crate) enum Diverges { impl ops::BitAnd for Diverges { type Output = Self; fn bitand(self, other: Self) -> Self { - cmp::min(self, other) + match (self, other) { + (x @ Self::Maybe, _) => x, + (_, x @ Self::Maybe) => x, + // Using `span`/`custom_note` from `self` seems to give better error messages. + (x @ Self::Always { .. }, Self::Always { .. }) => x, + (Self::WarnedAlways, x) => x, + (x, Self::WarnedAlways) => x, + } } } impl ops::BitOr for Diverges { type Output = Self; fn bitor(self, other: Self) -> Self { - cmp::max(self, other) + match (self, other) { + (x @ Self::WarnedAlways, _) => x, + (_, x @ Self::WarnedAlways) => x, + // Using `span`/`custom_note` from `self` seems to give better error messages. + (x @ Self::Always { .. }, Self::Always { .. }) => x, + (Self::Maybe, x) => x, + (x, Self::Maybe) => x, + } } } @@ -70,9 +84,6 @@ impl Diverges { } pub(super) fn is_always(self) -> bool { - // Enum comparison ignores the - // contents of fields, so we just - // fill them in with garbage here. - self >= Diverges::Always { span: DUMMY_SP, custom_note: None } + matches!(self, Diverges::WarnedAlways | Diverges::Always { .. }) } } diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index a8898acf3a415..9f0d00d5274fa 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -8,6 +8,8 @@ use rustc_abi::{FIRST_VARIANT, FieldIdx}; use rustc_ast as ast; use rustc_ast::util::parser::ExprPrecedence; +use rustc_attr_ir::find_attr; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::thin_vec::ThinVec; use rustc_data_structures::unord::UnordMap; @@ -17,15 +19,15 @@ use rustc_errors::{ struct_span_code_err, }; use rustc_hir as hir; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{ExprKind, HirId, QPath, find_attr, is_range_literal}; +use rustc_hir::{ExprKind, HirId, QPath, is_range_literal}; use rustc_hir_analysis::diagnostics::{NoFieldOnType, NoVariantNamed}; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer as _; use rustc_infer::infer::{self, DefineOpaqueTypes, InferOk, RegionVariableOrigin}; use rustc_infer::traits::query::NoSolution; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{self, AdtKind, GenericArgsRef, Ty, TypeVisitableExt, Unnormalized}; use rustc_session::diagnostics::feature_err; @@ -55,7 +57,10 @@ use crate::{ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn precedence(&self, expr: &hir::Expr<'_>) -> ExprPrecedence { let has_attr = |id: HirId| -> bool { - self.tcx.hir_attrs(id).iter().any(hir::Attribute::is_prefix_attr_for_suggestions) + self.tcx + .hir_attrs(id) + .iter() + .any(rustc_attr_ir::Attribute::is_prefix_attr_for_suggestions) }; // Special case: range expressions are desugared to struct literals in HIR, @@ -426,7 +431,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut err = self.dcx().create_err(CantDereference { span: expr.span, ty: oprnd_t }); let sp = tcx.sess.source_map().start_point(expr.span).with_parent(None); - if let Some(sp) = tcx.sess.psess.ambiguous_block_expr_parse.borrow().get(&sp) { + if let Some(sp) = + tcx.sess.psess.complete_stmt_exprs_before_bin_op_lookalike.borrow().get(&sp) + { err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp)); } // The operand may be an uncalled function, in which case it is its return type @@ -439,7 +446,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .may_apply() }) }); - Ty::new_error(tcx, err.emit()) + Ty::new_error(tcx, err.emit_err()) }), hir::UnOp::Not => { let result = self.check_user_unop(expr, oprnd_t, unop, expected_inner); @@ -617,6 +624,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr_and_args.map_or(expr.span, |(e, _)| e.span), expr.span, expr.hir_id, + call_expr_and_args.is_some(), ) .0 } @@ -1398,7 +1406,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If the assignment expression itself is ill-formed, don't // bother emitting another error - err.emit_unless_delay(lhs_ty.references_error() || rhs_ty.references_error()) + err.emit_err_unless_delay(lhs_ty.references_error() || rhs_ty.references_error()) } pub(super) fn check_expr_let( @@ -1618,7 +1626,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), ) .with_note("unsafe binders are the only valid output of wrap") - .emit(); + .emit_err(); Ty::new_error(self.tcx, guar) } }; @@ -1656,7 +1664,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), ) .with_note("only an unsafe binder type can be unwrapped") - .emit(); + .emit_err(); Ty::new_error(self.tcx, guar) } } @@ -1700,34 +1708,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.next_ty_var(expr.span) }; let array_len = args.len() as u64; - self.suggest_array_len(expr, array_len); Ty::new_array(self.tcx, element_ty, array_len) } - fn suggest_array_len(&self, expr: &'tcx hir::Expr<'tcx>, array_len: u64) { - let parent_node = self.tcx.hir_parent_iter(expr.hir_id).find(|(_, node)| { - !matches!(node, hir::Node::Expr(hir::Expr { kind: hir::ExprKind::AddrOf(..), .. })) - }); - let Some((_, hir::Node::LetStmt(hir::LetStmt { ty: Some(ty), .. }))) = parent_node else { - return; - }; - if let hir::TyKind::Array(_, ct) = ty.peel_refs().kind { - let span = ct.span; - self.dcx().try_steal_modify_and_emit_err( - span, - StashKey::UnderscoreForArrayLengths, - |err| { - err.span_suggestion( - span, - "consider specifying the array length", - array_len, - Applicability::MaybeIncorrect, - ); - }, - ); - } - } - pub(super) fn check_expr_const_block( &self, block: &'tcx hir::ConstBlock, @@ -1762,10 +1745,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), ); - if let Some(count) = count.try_to_target_usize(tcx) { - self.suggest_array_len(expr, count); - } - let uty = match expected { ExpectHasType(uty) => uty.builtin_index(), _ => None, @@ -2240,9 +2219,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let private_fields: Vec<&ty::FieldDef> = variant .fields .iter() - .filter(|field| { - !field.vis.is_accessible_from(tcx.parent_module(expr.hir_id), tcx) - }) + .filter(|field| !field.vis.is_accessible_from(self.mod_id, tcx)) .collect(); if !private_fields.is_empty() { @@ -2700,7 +2677,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; } } - err.emit() + err.emit_err() } fn available_field_names( @@ -2714,7 +2691,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .iter() .filter(|field| { skip_fields.iter().all(|&skip| skip.ident.name != field.name) - && self.is_field_suggestable(field, expr.hir_id, expr.span) + && self.is_field_suggestable(field, expr.span) }) .map(|field| field.name) .collect() @@ -2790,11 +2767,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return Ty::new_error(self.tcx(), guar); } - let (ident, def_scope) = self.tcx.adjust_ident_and_get_scope( - field, - base_def.did(), - self.body_def_id, - ); + let (ident, def_scope) = + self.tcx.adjust_ident_and_get_scope(field, base_def.did(), self.mod_id); if let Some((idx, field)) = self.find_adt_field(*base_def, ident) { self.write_field_index(expr.hir_id, idx); @@ -2933,7 +2907,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } } - err.emit() + err.emit_err() }; Ty::new_error(self.tcx(), guar) @@ -3044,7 +3018,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { HelpUseLatestEdition::new().add_to_diag(&mut err); } - err.emit() + err.emit_err() } fn ban_private_field_access( @@ -3070,7 +3044,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None, ); } - err.emit() + err.emit_err() } fn ban_take_value_of_method( @@ -3269,7 +3243,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // try to add a suggestion in case the field is a nested field of a field of the Adt - let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id(); let (ty, unwrap) = if let ty::Adt(def, args) = base_ty.kind() && (self.tcx.is_diagnostic_item(sym::Result, def.did()) || self.tcx.is_diagnostic_item(sym::Option, def.did())) @@ -3280,9 +3253,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { (base_ty, "") }; - for found_fields in - self.get_field_candidates_considering_privacy_for_diag(span, ty, mod_id, expr.hir_id) - { + for found_fields in self.get_field_candidates_considering_privacy_for_diag(span, ty) { let field_names = found_fields.iter().map(|field| field.0.name).collect::>(); let mut candidate_fields: Vec<_> = found_fields .into_iter() @@ -3292,8 +3263,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &|candidate_field, _| candidate_field == field, candidate_field, vec![], - mod_id, - expr.hir_id, ) }) .map(|mut field_path| { @@ -3354,8 +3323,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, span: Span, base_ty: Ty<'tcx>, - mod_id: DefId, - hir_id: HirId, ) -> Vec)>> { debug!("get_field_candidates(span: {:?}, base_t: {:?}", span, base_ty); @@ -3379,15 +3346,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Some struct, e.g. some that impl `Deref`, have all private fields // because you're expected to deref them to access the _real_ fields. // This, for example, will help us suggest accessing a field through a `Box`. - if fields.iter().all(|field| !field.vis.is_accessible_from(mod_id, tcx)) { + if fields + .iter() + .all(|field| !field.vis.is_accessible_from(self.mod_id, tcx)) + { return None; } return Some( fields .iter() .filter(move |field| { - field.vis.is_accessible_from(mod_id, tcx) - && self.is_field_suggestable(field, hir_id, span) + field.vis.is_accessible_from(self.mod_id, tcx) + && self.is_field_suggestable(field, span) }) // For compile-time reasons put a limit on number of fields we search .take(100) @@ -3419,15 +3389,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// This method is called after we have encountered a missing field error to recursively /// search for the field - #[instrument(skip(self, matches, mod_id, hir_id), level = "debug")] + #[instrument(skip(self, matches), level = "debug")] pub(crate) fn check_for_nested_field_satisfying_condition_for_diag( &self, span: Span, matches: &impl Fn(Ident, Ty<'tcx>) -> bool, (candidate_name, candidate_ty): (Ident, Ty<'tcx>), mut field_path: Vec, - mod_id: DefId, - hir_id: HirId, ) -> Option> { if field_path.len() > 3 { // For compile-time reasons and to avoid infinite recursion we only check for fields @@ -3438,12 +3406,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if matches(candidate_name, candidate_ty) { return Some(field_path); } - for nested_fields in self.get_field_candidates_considering_privacy_for_diag( - span, - candidate_ty, - mod_id, - hir_id, - ) { + for nested_fields in + self.get_field_candidates_considering_privacy_for_diag(span, candidate_ty) + { // recursively search fields of `candidate_field` if it's a ty::Adt for field in nested_fields { if let Some(field_path) = self.check_for_nested_field_satisfying_condition_for_diag( @@ -3451,8 +3416,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { matches, field, field_path.clone(), - mod_id, - hir_id, ) { return Some(field_path); } @@ -3538,8 +3501,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } - let reported = err.emit(); - Ty::new_error(self.tcx, reported) + let guar = err.emit_err(); + Ty::new_error(self.tcx, guar) } } } @@ -3849,7 +3812,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.dcx() .create_err(NoVariantNamed { span: ident.span, ident, ty: container }) .with_span_label(field.span, "variant not found") - .emit_unless_delay(container.references_error()); + .emit_err_unless_delay(container.references_error()); break; }; let Some(&subfield) = fields.next() else { @@ -3864,11 +3827,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .emit(); break; }; - let (subident, sub_def_scope) = self.tcx.adjust_ident_and_get_scope( - subfield, - variant.def_id, - self.body_def_id, - ); + let (subident, sub_def_scope) = + self.tcx.adjust_ident_and_get_scope(subfield, variant.def_id, self.mod_id); let Some((subindex, field)) = variant .fields @@ -3884,7 +3844,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { enum_span: field.span, field_span: subident.span, }) - .emit_unless_delay(container.references_error()); + .emit_err_unless_delay(container.references_error()); break; }; @@ -3919,7 +3879,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (ident, def_scope) = self.tcx.adjust_ident_and_get_scope( field, container_def.did(), - self.body_def_id, + self.mod_id, ); let fields = &container_def.non_enum_variant().fields; diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index e655e0857d858..1ad6225a8c467 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -2,12 +2,12 @@ use std::collections::hash_map::Entry; use std::slice; use rustc_abi::FieldIdx; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, Diagnostic, ErrorGuaranteed, Level, MultiSpan, }; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; @@ -57,8 +57,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { custom_note: Option<&'b str>, } - impl<'a, 'b, 'c> Diagnostic<'a, ()> for UnreachableItem<'b, 'c> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + impl<'a, 'b, 'c> Diagnostic<'a> for UnreachableItem<'b, 'c> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let Self { kind, span, orig_span, custom_note } = self; let msg = format!("unreachable {kind}"); Diag::new(dcx, level, msg.clone()).with_span_label(span, msg).with_span_label( @@ -1006,6 +1006,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, path_span: Span, hir_id: HirId, + has_args: bool, ) -> (Ty<'tcx>, Res) { let tcx = self.tcx; @@ -1238,7 +1239,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (ctor_kind, ctor_def_id) = adt_def.non_enum_variant().ctor.unwrap(); // Check the visibility of the ctor. let vis = tcx.visibility(ctor_def_id); - if !vis.is_accessible_from(tcx.parent_module(hir_id).to_def_id(), tcx) { + if !vis.is_accessible_from(self.mod_id, tcx) { self.dcx() .emit_err(CtorIsPrivate { span, def: tcx.def_path_str(adt_def.did()) }); } @@ -1253,22 +1254,55 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "the `Self` constructor can only be used with tuple or unit structs", ); if let Some(adt_def) = ty.normalized.ty_adt_def() { - match adt_def.adt_kind() { - AdtKind::Enum => { - err.help("did you mean to use one of the enum's variants?"); - } - AdtKind::Struct | AdtKind::Union => { - err.span_suggestion( - span, - "use curly brackets", - "Self { /* fields */ }", - Applicability::HasPlaceholders, - ); + let def_id = self.body_def_id.to_def_id(); + if !has_args + && let Some(assoc) = tcx.opt_associated_item(def_id) + && assoc.is_method() + { + let self_ty = + tcx.fn_sig(def_id).instantiate_identity().skip_binder().inputs()[0]; + let applicability = if let ty::Adt(..) = self_ty.kind() { + // We're within a method that takes ownership of `Self`, likely a + // builder, so this is most likely a typo. + Applicability::MachineApplicable + } else { + // We still might have meant `self` instead of `Self`. + Applicability::MaybeIncorrect + }; + err.span_suggestion_verbose( + span, + format!( + "you might have meant to refer to the `self` binding of type \ + `{self_ty}`", + ), + "self".to_string(), + applicability, + ); + } else { + match adt_def.adt_kind() { + AdtKind::Enum => { + err.span_help( + tcx.def_span(adt_def.did()), + if adt_def.variants().is_empty() { + "the enum is unconstructable because it has no variants" + } else { + "you might have meant to use one of the enum's variants" + }, + ); + } + AdtKind::Struct | AdtKind::Union => { + err.span_suggestion_verbose( + span, + "use curly brackets", + "Self { /* fields */ }", + Applicability::HasPlaceholders, + ); + } } } } - let reported = err.emit(); - return (Ty::new_error(tcx, reported), res); + let guar = err.emit_err(); + return (Ty::new_error(tcx, guar), res); } } } else { @@ -1529,7 +1563,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { TypeAnnotationNeeded::E0282, true, ) - .emit() + .emit_err() }); let err = Ty::new_error(self.tcx, guar); self.demand_suptype(sp, err, ty); @@ -1555,7 +1589,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { TypeAnnotationNeeded::E0282, true, ) - .emit() + .emit_err() }); // FIXME: Infer `?ct = {const error}`? ty::Const::new_error(self.tcx, e) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 074b95321db3b..937182c2030da 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -3,12 +3,12 @@ use std::{fmt, iter}; use itertools::Itertools; use rustc_ast as ast; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, a_or_an, listify, pluralize}; use rustc_hir as hir; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; @@ -693,7 +693,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { must be a tuple or unit type: {:?}", type_errors, ) - .emit(); + .emit_err(); Ty::new_error(self.tcx, guar) } } else { @@ -758,7 +758,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "cannot use call notation; the first type parameter \ for the function trait is neither a tuple nor unit" ) - .emit(); + .emit_err(); Some(guar) } else if tuple_arguments.is_splatted() { @@ -789,7 +789,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) .kind(), ) - .emit(); + .emit_err(); Some(guar) } else if formal_input_tys.len() != provided_args.len() { @@ -803,7 +803,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { provided_args.len(), if provided_args.len() == 1 { "was" } else { "were" }, ) - .emit(); + .emit_err(); Some(guar) } else { @@ -978,7 +978,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } - err.emit() + err.emit_err() } fn suggest_ptr_null_mut( @@ -1123,7 +1123,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty.normalized.sort_string(self.tcx) ) .with_span_label(path_span, "not a struct") - .emit(), + .emit_err(), }) } } @@ -2363,7 +2363,7 @@ impl<'a, 'tcx> FnCallDiagCtxt<'a, 'tcx> { self.tuple_arguments, ); self.suggest_confusable(&mut err); - Some(err.emit()) + Some(err.emit_err()) } else { None } @@ -2384,7 +2384,7 @@ impl<'a, 'tcx> FnCallDiagCtxt<'a, 'tcx> { span: self.call_metadata.error_span, }); self.arg_matching_ctxt.suggest_confusable(&mut err); - return Some(err.emit()); + return Some(err.emit_err()); } } @@ -2440,7 +2440,7 @@ impl<'a, 'tcx> FnCallDiagCtxt<'a, 'tcx> { *e, ); self.arg_matching_ctxt.suggest_confusable(&mut err); - reported = Some(err.emit()); + reported = Some(err.emit_err()); return false; } true @@ -2522,7 +2522,7 @@ impl<'a, 'tcx> FnCallDiagCtxt<'a, 'tcx> { ); self.arg_matching_ctxt.suggest_confusable(&mut err); self.detect_dotdot(&mut err, provided_ty, self.provided_args[provided_idx]); - return Some(err.emit()); + return Some(err.emit_err()); } None @@ -3186,6 +3186,8 @@ impl<'a, 'tcx> ArgMatchingCtxt<'a, 'tcx> { ); return; } + + self.annotate_alternative_method_deref(err, self.call_expr, None); } /// A "softer" version of the `demand_compatible`, which checks types without persisting them, @@ -3309,7 +3311,7 @@ impl<'a, 'tcx> ArgsCtxt<'a, 'tcx> { // Sometimes macros mess up the spans, so do not normalize the // arg span to equal the error span, because that's less useful // than pointing out the arg expr in the wrong context. - if normalized_span.source_equal(self.call_metadata.error_span) { + if normalized_span.lo_hi() == self.call_metadata.error_span.lo_hi() { span } else { normalized_span diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 3e2d35da5307d..b2b49dc06b7a6 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -22,6 +22,7 @@ use rustc_middle::ty::{ self, CantBeErased, Const, Flags, Ty, TyCtxt, TypeVisitableExt, TypingMode, Unnormalized, }; use rustc_session::Session; +use rustc_span::def_id::LocalModId; use rustc_span::{self, DUMMY_SP, ErrorGuaranteed, Ident, Span}; use rustc_trait_selection::error_reporting::TypeErrCtxt; use rustc_trait_selection::traits::{ @@ -239,6 +240,10 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> { self.body_def_id } + fn mod_id(&self) -> LocalModId { + self.mod_id + } + fn re_infer(&self, span: Span, reason: RegionInferReason<'_>) -> ty::Region<'tcx> { let v = match reason { RegionInferReason::Param(def) => { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index ab8020a3182e0..116bbf6280642 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -5,9 +5,9 @@ use core::iter; use hir::def_id::LocalDefId; use itertools::Itertools; use rustc_ast::util::parser::ExprPrecedence; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::packed::Pu128; use rustc_errors::{Applicability, Diag, MultiSpan, listify, msg}; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::intravisit::Visitor; use rustc_hir::{ @@ -18,6 +18,7 @@ use rustc_hir::{ use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_hir_analysis::suggest_impl_trait; use rustc_middle::middle::stability::EvalResult; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::print::{with_no_trimmed_paths, with_types_for_suggestion}; use rustc_middle::ty::{ self, Article, Binder, IsSuggestable, Ty, TyCtxt, TypeVisitableExt, Unnormalized, Upcast, @@ -1026,7 +1027,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .segments .last() .and_then(|seg| seg.args) - .map_or(false, |args| !args.constraints.is_empty()) + .is_some_and(|args| !args.constraints.is_empty()) { // Use the path to get the trait name string let trait_name = trait_ref @@ -1466,7 +1467,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &hir::Expr<'_>, ) -> bool { let sp = self.tcx.sess.source_map().start_point(expr.span).with_parent(None); - if let Some(sp) = self.tcx.sess.psess.ambiguous_block_expr_parse.borrow().get(&sp) { + if let Some(sp) = + self.tcx.sess.psess.complete_stmt_exprs_before_bin_op_lookalike.borrow().get(&sp) + { // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }` err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp)); true @@ -1691,12 +1694,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let suggestion = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) { - Some(ident) => format!(": {ident}.is_some()"), - None => ".is_some()".to_string(), + Some(ident) => vec![(expr.span.shrink_to_hi(), format!(": {ident}.is_some()"))], + None if self.precedence(expr) < ExprPrecedence::Unambiguous => { + // Apply the method to the whole expression, e.g. `(*value).is_some()`. + vec![ + (expr.span.shrink_to_lo(), "(".to_string()), + (expr.span.shrink_to_hi(), ").is_some()".to_string()), + ] + } + None => vec![(expr.span.shrink_to_hi(), ".is_some()".to_string())], }; - diag.span_suggestion_verbose( - expr.span.shrink_to_hi(), + diag.multipart_suggestion( "use `Option::is_some` to test if the `Option` has a value", suggestion, Applicability::MachineApplicable, @@ -2285,14 +2294,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - pub(crate) fn is_field_suggestable( - &self, - field: &ty::FieldDef, - hir_id: HirId, - span: Span, - ) -> bool { + pub(crate) fn is_field_suggestable(&self, field: &ty::FieldDef, span: Span) -> bool { // The field must be visible in the containing module. - field.vis.is_accessible_from(self.tcx.parent_module(hir_id), self.tcx) + field.vis.is_accessible_from(self.mod_id, self.tcx) // The field must not be unstable. && !matches!( self.tcx.eval_stability(field.did, None, rustc_span::DUMMY_SP, None), @@ -2860,7 +2864,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { true } - /// Identify some cases where `as_ref()` would be appropriate and suggest it. + /// Identify some cases where `as_ref()` or `as_mut()` would be appropriate and suggest it. /// /// Given the following code: /// ```compile_fail,E0308 @@ -2876,7 +2880,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// ```ignore (illustrative) /// opt.map(|param| { takes_ref(param) }); /// ``` - fn can_use_as_ref(&self, expr: &hir::Expr<'_>) -> Option<(Vec<(Span, String)>, &'static str)> { + fn can_use_as_ref_or_mut( + &self, + expr: &hir::Expr<'_>, + mutability: hir::Mutability, + ) -> Option<(Vec<(Span, String)>, &'static str)> { let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind else { return None; }; @@ -2913,9 +2921,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; }; - let self_ty = self.typeck_results.borrow().expr_ty_opt(receiver)?; + let mut self_ty = self.typeck_results.borrow().expr_ty_opt(receiver)?; + while let ty::Ref(_, inner, ref_mutability) = self_ty.kind() { + // `as_mut()` cannot borrow through a shared reference, + // also we cannot suggest `as_ref()` either when the reference is shared + if mutability.is_mut() && ref_mutability.is_not() { + return None; + } + self_ty = *inner; + } let name = method_path.ident.name; - let is_as_ref_able = match self_ty.peel_refs().kind() { + let can_borrow = match self_ty.kind() { ty::Adt(def, _) => { (self.tcx.is_diagnostic_item(sym::Option, def.did()) || self.tcx.is_diagnostic_item(sym::Result, def.did())) @@ -2923,11 +2939,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } _ => false, }; - if is_as_ref_able { - Some(( - vec![(method_path.ident.span.shrink_to_lo(), "as_ref().".to_string())], - "consider using `as_ref` instead", - )) + if can_borrow { + let (suggestion, message) = match mutability { + hir::Mutability::Not => ("as_ref().", "consider using `as_ref` instead"), + hir::Mutability::Mut => ("as_mut().", "consider using `as_mut` instead"), + }; + Some((vec![(method_path.ident.span.shrink_to_lo(), suggestion.to_string())], message)) } else { None } @@ -3118,7 +3135,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return Some((suggs, help, app, mutref)); } - if let Some((sugg, msg)) = self.can_use_as_ref(expr) { + if let Some((sugg, msg)) = self.can_use_as_ref_or_mut(expr, mutability) { return Some(( sugg, msg.to_string(), diff --git a/compiler/rustc_hir_typeck/src/inline_asm.rs b/compiler/rustc_hir_typeck/src/inline_asm.rs index 2b1d9ed038b87..b3c7071199963 100644 --- a/compiler/rustc_hir_typeck/src/inline_asm.rs +++ b/compiler/rustc_hir_typeck/src/inline_asm.rs @@ -1,19 +1,19 @@ use rustc_abi::FieldIdx; use rustc_ast::InlineAsmTemplatePiece; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level}; use rustc_hir as hir; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def_id::DefId; use rustc_lint_defs::builtin::ASM_SUB_REGISTER; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::{ self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitableExt, UintTy, Unnormalized, }; use rustc_span::def_id::LocalDefId; use rustc_span::{ErrorGuaranteed, Span, Symbol, bug, sym}; use rustc_target::asm::{ - InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmSize, InlineAsmType, - ModifierInfo, + InlineAsmReg, InlineAsmRegClass, InlineAsmSize, InlineAsmType, ModifierInfo, }; use rustc_trait_selection::infer::InferCtxtExt; @@ -190,7 +190,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { fn check_asm_operand_type( &self, idx: usize, - reg: InlineAsmRegOrRegClass, + reg: hir::InlineAsmRegOrRegClass, expr: &'tcx hir::Expr<'tcx>, template: &[InlineAsmTemplatePiece], is_input: bool, @@ -207,8 +207,8 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { default_size: InlineAsmSize, } - impl<'a, 'b> Diagnostic<'a, ()> for FormattingSubRegisterArg<'b> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + impl<'a, 'b> Diagnostic<'a> for FormattingSubRegisterArg<'b> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let Self { expr_span, idx, @@ -476,7 +476,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { if let Some(reg) = op.reg() { // Some explicit registers cannot be used depending on the // target. Reject those here. - if let InlineAsmRegOrRegClass::Reg(reg) = reg { + if let hir::InlineAsmRegOrRegClass::Reg { reg, source_name } = reg { if let InlineAsmReg::Err = reg { // `validate` will panic on `Err`, as an error must // already have been reported. @@ -489,7 +489,11 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { &self.tcx().sess.target, op.is_clobber(), ) { - let msg = format!("cannot use register `{}`: {}", reg.name(), msg); + let reg_name = match source_name { + Some(ref reg_name) => reg_name.as_str(), + None => ®.name(), + }; + let msg = format!("cannot use register `{reg_name}`: {msg}"); self.fcx.dcx().span_err(op_sp, msg); continue; } diff --git a/compiler/rustc_hir_typeck/src/intrinsicck.rs b/compiler/rustc_hir_typeck/src/intrinsicck.rs index 2e52517de07d1..d2432a7e39af6 100644 --- a/compiler/rustc_hir_typeck/src/intrinsicck.rs +++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs @@ -126,11 +126,11 @@ fn check_transmute<'tcx>( ); if from == to { err.note(format!("`{from}` does not have a fixed size")); - Err(err.emit()) + Err(err.emit_err()) } else { err.note(format!("source type: `{}` ({})", from, skeleton_string(from, sk_from))); err.note(format!("target type: `{}` ({})", to, skeleton_string(to, sk_to))); - Err(err.emit()) + Err(err.emit_err()) } } @@ -144,15 +144,15 @@ fn check_offload<'tcx>( ) -> Result<(), ErrorGuaranteed> { let span = tcx.hir_span(hir_id); let ty::FnDef(kernel_def_id, kernel_args) = *kernel_ty.kind() else { - let err = tcx + let guar = tcx .sess .dcx() .struct_span_err( span, format!("expected a function item for the offload kernel, found `{}`", kernel_ty), ) - .emit(); - return Err(err); + .emit_err(); + return Err(guar); }; let kernel_sig = @@ -160,19 +160,19 @@ fn check_offload<'tcx>( let kernel_sig = tcx.instantiate_bound_regions_with_erased(kernel_sig); let ty::Tuple(tuple_fields) = *args_ty.kind() else { - let err = tcx + let guar = tcx .sess .dcx() .struct_span_err( span, format!("expected a tuple for the offload arguments, found `{}`", args_ty), ) - .emit(); - return Err(err); + .emit_err(); + return Err(guar); }; if kernel_sig.inputs().len() != tuple_fields.len() { - let err = tcx + let guar = tcx .sess .dcx() .struct_span_err( @@ -183,8 +183,8 @@ fn check_offload<'tcx>( tuple_fields.len() ), ) - .emit(); - return Err(err); + .emit_err(); + return Err(guar); } let normalize = |ty| { @@ -206,7 +206,7 @@ fn check_offload<'tcx>( let norm_input_ty = normalize(input_ty); let norm_arg_ty = normalize(arg_ty); if norm_input_ty != norm_arg_ty { - let err = tcx + let guar = tcx .sess .dcx() .struct_span_err( @@ -216,22 +216,22 @@ fn check_offload<'tcx>( i, norm_input_ty, norm_arg_ty ), ) - .emit(); - result = Err(err); + .emit_err(); + result = Err(guar); } } let norm_kernel_ret = normalize(kernel_sig.output()); let norm_offload_ret = normalize(ret_ty); if norm_kernel_ret != norm_offload_ret { - let err = tcx.sess.dcx().struct_span_err( + let guar = tcx.sess.dcx().struct_span_err( span, format!( "offload kernel return type mismatch: kernel returns `{}`, but offload call expects `{}`", norm_kernel_ret, norm_offload_ret ) - ).emit(); - result = Err(err); + ).emit_err(); + result = Err(guar); } result diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index dd3b5d4429975..5efb2775c77fe 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -1,6 +1,4 @@ // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(never_type))] -#![cfg_attr(bootstrap, feature(trim_prefix_suffix))] #![feature(deref_patterns)] #![feature(iter_intersperse)] #![feature(iter_order_by)] @@ -607,7 +605,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } _ => err.with_span_label(span, format!("not a {expected}")), } - .emit() + .emit_err() } } @@ -714,7 +712,7 @@ fn fatally_break_rust(tcx: TyCtxt<'_>, span: Span) -> ! { diag.note("some of the compiler flags provided by cargo are hidden"); } } - diag.emit() + diag.emit_bug() } /// Adds query implementations to the [Providers] vtable, see [`rustc_middle::query`] diff --git a/compiler/rustc_hir_typeck/src/loops.rs b/compiler/rustc_hir_typeck/src/loops.rs index 3ce6c34176101..18dfbaffc6082 100644 --- a/compiler/rustc_hir_typeck/src/loops.rs +++ b/compiler/rustc_hir_typeck/src/loops.rs @@ -2,15 +2,16 @@ use std::collections::BTreeMap; use std::fmt; use Context::*; +use rustc_attr_ir::find_attr; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{Destination, Node, find_attr}; +use rustc_hir::{Destination, Node}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; use rustc_span::hygiene::DesugaringKind; -use rustc_span::{BytePos, Span, span_bug}; +use rustc_span::{BytePos, OrdSpan, Span, span_bug}; use crate::diagnostics::{ BreakInsideClosure, BreakInsideCoroutine, BreakNonLoop, ConstContinueBadLabel, @@ -79,7 +80,7 @@ struct CheckLoopVisitor<'tcx> { // such as adding a label for an `if`. // e.g. `if 'foo: {}` would be incorrect. cx_stack: Vec, - block_breaks: BTreeMap, + block_breaks: BTreeMap, } pub(crate) fn check<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &'tcx hir::Body<'tcx>) { @@ -377,12 +378,13 @@ impl<'hir> CheckLoopVisitor<'hir> { UnlabeledBlock { label_span, wrap_end } if br_cx_kind == BreakContextKind::Break && label_span.eq_ctxt(break_span) => { - let block = self.block_breaks.entry(label_span).or_insert_with(|| BlockInfo { - name: br_cx_kind.to_string(), - spans: vec![], - suggs: vec![], - wrap_end, - }); + let block = + self.block_breaks.entry(OrdSpan(label_span)).or_insert_with(|| BlockInfo { + name: br_cx_kind.to_string(), + spans: vec![], + suggs: vec![], + wrap_end, + }); block.spans.push(span); block.suggs.push(break_span); } @@ -423,7 +425,7 @@ impl<'hir> CheckLoopVisitor<'hir> { name: &block.name, is_break: true, suggestion: Some(OutsideLoopSuggestion { - block_span: *s, + block_span: s.0, break_spans: block.suggs.clone(), block_prefix: if block.wrap_end.is_some() { "{ 'block: " } else { "'block: " }, wrap_end: block.wrap_end, diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index c3f18926143b9..4518e419aea0f 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -1,9 +1,8 @@ use std::fmt::Debug; use std::ops::Deref; +use rustc_attr_ir::lang_items::LangItem; use rustc_hir as hir; -use rustc_hir::GenericArg; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def_id::DefId; use rustc_hir_analysis::hir_ty_lowering::generics::{ check_generic_arg_count_for_value_path, lower_generic_args, @@ -450,23 +449,23 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { &mut self, preceding_args: &[ty::GenericArg<'tcx>], param: &ty::GenericParamDef, - arg: &GenericArg<'_>, + arg: &hir::GenericArg<'_>, ) -> ty::GenericArg<'tcx> { match (¶m.kind, arg) { - (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => self + (GenericParamDefKind::Lifetime, hir::GenericArg::Lifetime(lt)) => self .cfcx .fcx .lowerer() .lower_lifetime(lt, RegionInferReason::Param(param)) .into(), - (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { + (GenericParamDefKind::Type { .. }, hir::GenericArg::Type(ty)) => { // We handle the ambig portions of `Ty` in the match arms below self.cfcx.lower_ty(ty.as_unambig_ty()).raw.into() } - (GenericParamDefKind::Type { .. }, GenericArg::Infer(inf)) => { + (GenericParamDefKind::Type { .. }, hir::GenericArg::Infer(inf)) => { self.cfcx.lower_ty(&inf.to_ty()).raw.into() } - (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => self + (GenericParamDefKind::Const { .. }, hir::GenericArg::Const(ct)) => self .cfcx // We handle the ambig portions of `ConstArg` in the match arms below .lower_const_arg( @@ -478,7 +477,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { .skip_norm_wip(), ) .into(), - (GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => { + (GenericParamDefKind::Const { .. }, hir::GenericArg::Infer(inf)) => { self.cfcx.ct_infer(Some(param), inf.span).into() } (kind, arg) => { diff --git a/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs b/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs index d961fdc0885b7..7a60c7472cf45 100644 --- a/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs +++ b/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs @@ -3,9 +3,9 @@ use std::fmt::Write; use hir::def_id::DefId; use hir::{HirId, ItemKind}; use rustc_ast::join_path_idents; +use rustc_attr_ir::lang_items::LangItem; use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, Level}; use rustc_hir as hir; -use rustc_hir::attrs::lang_items::LangItem; use rustc_lint::{ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER}; use rustc_lint_defs::builtin::{RUST_2021_PRELUDE_COLLISIONS, RUST_2024_PRELUDE_COLLISIONS}; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -24,8 +24,8 @@ struct AmbiguousTraitMethodCall<'a, 'tcx> { edition: &'static str, } -impl<'a, 'b, 'tcx> Diagnostic<'a, ()> for AmbiguousTraitMethodCall<'b, 'tcx> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { +impl<'a, 'b, 'tcx> Diagnostic<'a> for AmbiguousTraitMethodCall<'b, 'tcx> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let Self { segment_name, self_expr_span, pick, tcx, edition } = self; let mut lint = Diag::new( dcx, @@ -91,8 +91,8 @@ struct AmbiguousTraitMethod<'a, 'tcx, 'fnctx> { this: &'a FnCtxt<'fnctx, 'tcx>, } -impl<'a, 'c, 'tcx, 'fnctx> Diagnostic<'a, ()> for AmbiguousTraitMethod<'c, 'tcx, 'fnctx> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { +impl<'a, 'c, 'tcx, 'fnctx> Diagnostic<'a> for AmbiguousTraitMethod<'c, 'tcx, 'fnctx> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let Self { segment, call_expr, self_expr, pick, args, edition, span, this } = self; let mut lint = Diag::new( dcx, @@ -289,8 +289,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_ty: Ty<'tcx>, } - impl<'a, 'b, 'fnctx, 'tcx> Diagnostic<'a, ()> for AmbiguousTraitAssocFunc<'b, 'fnctx, 'tcx> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + impl<'a, 'b, 'fnctx, 'tcx> Diagnostic<'a> for AmbiguousTraitAssocFunc<'b, 'fnctx, 'tcx> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let Self { method_name, this, pick, span, expr_id, self_ty_span, self_ty } = self; let mut lint = Diag::new( dcx, @@ -441,23 +441,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Find an identifier with which this trait was imported (note that `_` doesn't count). for item in import_items.iter() { - let (_, kind) = item.expect_use(); - match kind { + match item.expect_use().kind { hir::UseKind::Single(ident) => { if ident.name != kw::Underscore { return Some(format!("{}", ident.name)); } } hir::UseKind::Glob => return None, // Glob import, so just use its name. - hir::UseKind::ListStem => unreachable!(), + hir::UseKind::Nested { .. } => unreachable!(), } } // All that is left is `_`! We need to use the full path. It doesn't matter which one we // pick, so just take the first one. match import_items[0].kind { - ItemKind::Use(path, _) => { - Some(join_path_idents(path.segments.iter().map(|seg| seg.ident))) + ItemKind::Use(tree) => { + Some(join_path_idents(tree.prefix.segments.iter().map(|seg| seg.ident))) } _ => { span_bug!(span, "unexpected item kind, expected a use: {:?}", import_items[0].kind); diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 02b3255e795ac..c74047590331c 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -3,12 +3,13 @@ use std::cmp::max; use std::ops::Deref; use std::{assert_matches, debug_assert_matches}; +use rustc_attr_ir::find_attr; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sso::SsoHashSet; use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, Level}; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def::DefKind; -use rustc_hir::{self as hir, ExprKind, HirId, Node, find_attr}; +use rustc_hir::{self as hir, ExprKind, HirId, Node}; use rustc_hir_analysis::autoderef::{self, Autoderef}; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk, TyCtxtInferExt}; @@ -523,8 +524,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, MethodCallOnDivergingInferenceVariable, ); - let root_ty = Ty::new_var(self.tcx, ty_id); - self.demand_eqtype(span, root_ty, self.tcx.types.never); + self.demand_eqtype(span, ty, self.tcx.types.never); } else { let guar = match *ty.kind() { _ if let Some(guar) = self.tainted_by_errors() => guar, @@ -555,7 +555,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if raw_ptr_call { err.span_label(span, "cannot call a method on a raw pointer with an unknown pointee type"); } - err.emit() + err.emit_err() } ty::Error(guar) => guar, _ => bug!("unexpected bad final type in method autoderef"), @@ -859,8 +859,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let is_accessible = if let Some(name) = self.method_name { let item = candidate.item; let container_id = item.container_id(self.tcx); - let def_scope = - self.tcx.adjust_ident_and_get_scope(name, container_id, self.body_def_id).1; + let def_scope = self.tcx.adjust_ident_and_get_scope(name, container_id, self.mod_id).1; item.visibility(self.tcx).is_accessible_from(def_scope, self.tcx) } else { true @@ -1877,8 +1876,8 @@ impl<'tcx> Pick<'tcx> { span: Span, } - impl<'a, 'b, 'tcx> Diagnostic<'a, ()> for ItemMaybeBeAddedToStd<'b, 'tcx> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + impl<'a, 'b, 'tcx> Diagnostic<'a> for ItemMaybeBeAddedToStd<'b, 'tcx> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let Self { this, tcx, span } = self; let def_kind = this.item.as_def_kind(); let mut lint = Diag::new( @@ -2383,18 +2382,22 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } - // They are all the same, so if any of them is ambiguous, we report the pick as ambiguous. - let is_ambiguously_imported = probes.iter().any(|(p, _)| match p.kind { - TraitCandidate { is_ambiguously_imported, .. } => is_ambiguously_imported, - _ => false, + // We try and find the ambiguous candidate in order to report the pick as ambiguous. + let ambiguous_candidate = probes.iter().find_map(|(p, _)| match p.kind { + TraitCandidate { is_ambiguously_imported: true, .. } => Some(p), + _ => None, }); + // If there isn't any, then we just use the first probe. + let (is_ambiguously_imported, candidate) = + ambiguous_candidate.map(|c| (true, *c)).unwrap_or_else(|| (false, probes[0].0)); + // FIXME: check the return type here somehow. // If so, just use this trait and call it a day. Some(Pick { - item: probes[0].0.item, + item: candidate.item, kind: TraitPick { is_ambiguously_imported }, - import_ids: probes[0].0.import_ids, + import_ids: candidate.import_ids, autoderefs: 0, autoref_or_ptr_adjustment: None, self_ty, diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 07c98f884cebb..53b081962a386 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -9,6 +9,9 @@ use std::path::PathBuf; use hir::Expr; use rustc_ast::ast::Mutability; +use rustc_attr_ir::diagnostic::CustomDiagnostic; +use rustc_attr_ir::find_attr; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::unord::UnordSet; @@ -16,14 +19,10 @@ use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, MultiSpan, StashKey, StringPart, listify, pluralize, struct_span_code_err, }; -use rustc_hir::attrs::diagnostic::CustomDiagnostic; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{ - self as hir, ExprKind, HirId, Node, PathSegment, QPath, find_attr, is_range_literal, -}; +use rustc_hir::{self as hir, ExprKind, HirId, Node, PathSegment, QPath, is_range_literal}; use rustc_infer::infer::{BoundRegionConversionTime, RegionVariableOrigin}; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, simplify_type}; use rustc_middle::ty::print::{ @@ -33,7 +32,7 @@ use rustc_middle::ty::print::{ use rustc_middle::ty::{self, GenericArgKind, IsSuggestable, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::def_id::DefIdSet; use rustc_span::{ - DUMMY_SP, ErrorGuaranteed, ExpnKind, FileName, Ident, MacroKind, Span, Symbol, bug, + DUMMY_SP, ErrorGuaranteed, ExpnKind, FileName, Ident, MacroKind, OrdSpan, Span, Symbol, bug, edit_distance, kw, sym, }; use rustc_trait_selection::error_reporting::traits::DefIdOrName; @@ -352,7 +351,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &mut sources, Some(expr_span), ); - err.emit() + err.emit_err() } MethodError::PrivateMatch(kind, def_id, out_of_scope_traits) => { @@ -374,7 +373,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } self.suggest_valid_traits(&mut err, item_name, out_of_scope_traits, true); self.suggest_unwrapping_inner_self(&mut err, source, rcvr_ty, item_name); - err.emit() + err.emit_err() } MethodError::IllegalSizedBound { candidates, needs_mut, bound_span, self_expr } => { @@ -425,12 +424,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { )); } else { msg += &format!(" but {} not reachable", pluralize!("is", suggs.len())); - err.span_suggestions( - span, - msg, - suggs, - Applicability::MaybeIncorrect, - ); + err.help(format!("{msg}:\n{}", suggs.join("").trim_end())); } }; if accessible_sugg.is_empty() { @@ -462,11 +456,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let hir::Node::Param(p) = self.tcx.parent_hir_node(b.hir_id) && let Some(decl) = self.tcx.parent_hir_node(p.hir_id).fn_decl() && let Some(ty) = decl.inputs.iter().find(|ty| ty.span == p.ty_span) - && let hir::TyKind::Ref(_, mut_ty) = &ty.kind - && let hir::Mutability::Not = mut_ty.mutbl + && let hir::TyKind::Ref(_, inner_ty, hir::Mutability::Not) = &ty.kind { err.span_suggestion_verbose( - mut_ty.ty.span.shrink_to_lo(), + inner_ty.span.shrink_to_lo(), msg, "mut ", Applicability::MachineApplicable, @@ -476,7 +469,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - err.emit() + err.emit_err() } MethodError::ErrorReported(guar) => guar, @@ -895,12 +888,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { source: SelfSource<'tcx>, unsatisfied_predicates: &UnsatisfiedPredicates<'tcx>, static_candidates: &[CandidateSource], - ) -> Result<(bool, bool, bool, bool, SortedMap>), ()> { + ) -> Result<(bool, bool, bool, bool, SortedMap>), ()> { let mut restrict_type_params = false; let mut suggested_derive = false; let mut unsatisfied_bounds = false; let mut custom_span_label = !static_candidates.is_empty(); - let mut bound_spans: SortedMap> = Default::default(); + let mut bound_spans: SortedMap> = Default::default(); let tcx = self.tcx; if item_ident.name == sym::count && self.is_slice_ty(rcvr_ty, span) { @@ -1142,7 +1135,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rcvr_ty: Ty<'tcx>, item_ident: Ident, item_kind: &str, - bound_spans: SortedMap>, + bound_spans: SortedMap>, unsatisfied_predicates: &UnsatisfiedPredicates<'tcx>, ) { let mut ty_span = match rcvr_ty.kind() { @@ -1184,6 +1177,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None }; for (span, mut bounds) in bound_spans { + let span = span.0; if !self.tcx.sess.source_map().is_span_accessible(span) { continue; } @@ -1365,7 +1359,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &static_candidates, ) else { - return err.emit(); + return err.emit_err(); }; let similar_candidate = no_match_data.similar_candidate; @@ -1443,7 +1437,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.note_derefed_ty_has_method(&mut err, source, rcvr_ty, item_ident, expected); self.suggest_bounds_for_range_to_method(&mut err, source, item_ident); - err.emit() + err.emit_err() } fn set_not_found_span_label( @@ -1720,7 +1714,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { suggested_derive: &mut bool, unsatisfied_bounds: &mut bool, custom_span_label: &mut bool, - bound_spans: &mut SortedMap>, + bound_spans: &mut SortedMap>, ) { let tcx = self.tcx; let rcvr_ty_str = self.tcx.short_string(rcvr_ty, err.long_ty_path()); @@ -1805,16 +1799,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let msg = format!("`{}`", if obligation.len() > 50 { quiet } else { obligation }); match self_ty.kind() { // Point at the type that couldn't satisfy the bound. - ty::Adt(def, _) => { - bound_spans.get_mut_or_insert_default(tcx.def_span(def.did())).push(msg) - } + ty::Adt(def, _) => bound_spans + .get_mut_or_insert_default(OrdSpan(tcx.def_span(def.did()))) + .push(msg), // Point at the trait object that couldn't satisfy the bound. ty::Dynamic(preds, _) => { for pred in preds.iter() { match pred.skip_binder() { ty::ExistentialPredicate::Trait(tr) => { bound_spans - .get_mut_or_insert_default(tcx.def_span(tr.def_id)) + .get_mut_or_insert_default(OrdSpan(tcx.def_span(tr.def_id))) .push(msg.clone()); } ty::ExistentialPredicate::Projection(_) @@ -1825,7 +1819,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Point at the closure that couldn't satisfy the bound. ty::Closure(def_id, _) => { bound_spans - .get_mut_or_insert_default(tcx.def_span(*def_id)) + .get_mut_or_insert_default(OrdSpan(tcx.def_span(*def_id))) .push(format!("`{quiet}`")); } _ => {} @@ -2035,7 +2029,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } let mut spanned_predicates: Vec<_> = spanned_predicates.into_iter().collect(); - spanned_predicates.sort_by_key(|(span, _)| *span); + spanned_predicates.sort_by_key(|(span, _)| span.lo_hi()); for (_, (primary_spans, span_labels, predicates)) in spanned_predicates { let mut tracker = TraitBoundDuplicateTracker::new(); let mut all_trait_bounds_for_rcvr = true; @@ -2841,8 +2835,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => None, }); if let Some((field, field_ty)) = field_receiver { - let scope = tcx.parent_module_from_def_id(self.body_def_id); - let is_accessible = field.vis.is_accessible_from(scope, tcx); + let is_accessible = field.vis.is_accessible_from(self.mod_id, tcx); if is_accessible { if let Some((what, _, _)) = self.extract_callable_info(field_ty) { @@ -3129,7 +3122,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } _ => {} } - return Err(err.emit()); + return Err(err.emit_err()); } Ok(()) } @@ -3204,13 +3197,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return_type: Option>, ) { if let SelfSource::MethodCall(expr) = source { - let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id(); - for fields in self.get_field_candidates_considering_privacy_for_diag( - span, - actual, - mod_id, - expr.hir_id, - ) { + for fields in self.get_field_candidates_considering_privacy_for_diag(span, actual) { let call_expr = self.tcx.hir_expect_expr(self.tcx.parent_hir_id(expr.hir_id)); let lang_items = self.tcx.lang_items(); @@ -3245,8 +3232,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, candidate_field, vec![], - mod_id, - expr.hir_id, ) }) .map(|field_path| { @@ -3586,7 +3571,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, trait_pred: ty::TraitClause<'tcx>, adt: ty::AdtDef<'tcx>, - ) -> Option> { + ) -> Option> { let diagnostic_name = self.tcx.get_diagnostic_name(trait_pred.def_id())?; let can_derive = match diagnostic_name { @@ -3624,7 +3609,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut derives = Vec::new(); let self_name = self_ty.to_string(); - let self_span = self.tcx.def_span(adt.did()); + let self_span = OrdSpan(self.tcx.def_span(adt.did())); for super_trait in supertraits(self.tcx, ty::Binder::dummy(trait_pred.trait_ref)) { if let Some(parent_diagnostic_name) = self.tcx.get_diagnostic_name(super_trait.def_id()) @@ -3642,7 +3627,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, err: &mut Diag<'_>, unsatisfied_predicates: &UnsatisfiedPredicates<'tcx>, - ) -> Vec<(String, Span, Symbol)> { + ) -> Vec<(String, OrdSpan, Symbol)> { let mut derives = Vec::new(); let mut traits = Vec::new(); for (pred, _, _) in unsatisfied_predicates { @@ -3706,7 +3691,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { continue; } } - derives_grouped.push((self_name, self_span, trait_name.to_string())); + derives_grouped.push((self_name, self_span.0, trait_name.to_string())); } for (self_name, self_span, traits) in &derives_grouped { @@ -3996,11 +3981,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { let parent_map = self.tcx.visible_parent_map(()); - let scope = self.tcx.parent_module_from_def_id(self.body_def_id); let (accessible_candidates, inaccessible_candidates): (Vec<_>, Vec<_>) = candidates.into_iter().partition(|id| { let vis = self.tcx.visibility(*id); - vis.is_accessible_from(scope, self.tcx) + vis.is_accessible_from(self.mod_id, self.tcx) + // Visibility alone does not make `fn_name::Trait` an importable path. + // We need to make sure all parent are modules, otherwise the path is not importable. + && std::iter::successors(self.tcx.opt_parent(*id), |&id| self.tcx.opt_parent(id)) + .all(|id| self.tcx.def_kind(id) == DefKind::Mod) }); let sugg = |candidates: Vec<_>, visible| { @@ -4054,7 +4042,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let accessible_sugg = sugg(accessible_candidates, true); let inaccessible_sugg = sugg(inaccessible_candidates, false); - let (module, _, _) = self.tcx.hir_get_module(scope); + let (module, _) = self.tcx.hir_get_module(self.mod_id); let span = module.spans.inject_use_span; handle_candidates(accessible_sugg, inaccessible_sugg, span); } @@ -4114,7 +4102,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if suggs.len() == 1 { err.help(msg); } else { - err.span_suggestions(span, msg, suggs, Applicability::MaybeIncorrect); + err.help(format!("{msg}:\n{}", suggs.join("").trim_end())); } }; if accessible_sugg.is_empty() { diff --git a/compiler/rustc_hir_typeck/src/naked_functions.rs b/compiler/rustc_hir_typeck/src/naked_functions.rs index e241cd0797cc9..ce4893ce94b1f 100644 --- a/compiler/rustc_hir_typeck/src/naked_functions.rs +++ b/compiler/rustc_hir_typeck/src/naked_functions.rs @@ -1,9 +1,10 @@ //! Checks validity of naked functions. +use rustc_attr_ir::find_attr; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::Visitor; -use rustc_hir::{ExprKind, HirIdSet, StmtKind, find_attr}; +use rustc_hir::{ExprKind, HirIdSet, StmtKind}; use rustc_middle::ty::TyCtxt; use rustc_span::{Span, span_bug}; diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index c5d082fba8016..526f29cb3753b 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -557,7 +557,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &mut err, ); - Ty::new_error(self.tcx, err.emit()) + Ty::new_error(self.tcx, err.emit_err()) } fn suggest_deref_or_call_for_binop_error( @@ -1010,8 +1010,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let sp = self.tcx.sess.source_map().start_point(ex.span).with_parent(None); - if let Some(sp) = - self.tcx.sess.psess.ambiguous_block_expr_parse.borrow().get(&sp) + if let Some(sp) = self + .tcx + .sess + .psess + .complete_stmt_exprs_before_bin_op_lookalike + .borrow() + .get(&sp) { // If the previous expression was a block expression, suggest parentheses // (turning this into a binary subtraction operation instead.) @@ -1060,7 +1065,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - err.emit() + err.emit_err() }); Ty::new_error(self.tcx, guar) } diff --git a/compiler/rustc_hir_typeck/src/opaque_types.rs b/compiler/rustc_hir_typeck/src/opaque_types.rs index 2fc2c0d80a255..c96144d849944 100644 --- a/compiler/rustc_hir_typeck/src/opaque_types.rs +++ b/compiler/rustc_hir_typeck/src/opaque_types.rs @@ -202,7 +202,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { // hint that the ambiguity is in an opaque's hidden type rather than an // ordinary local inference failure. .with_note("cannot infer type of hidden type of opaque") - .emit() + .emit_err() } } }; diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index caffef6a217a8..16c51ca625dcd 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -3,13 +3,13 @@ use std::{assert_matches, cmp}; use rustc_abi::FieldIdx; use rustc_ast as ast; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::fx::FxHashMap; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, Diagnostic, ErrorGuaranteed, Level, MultiSpan, pluralize, struct_span_code_err, }; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::pat_util::EnumerateAndAdjustIterator; @@ -21,6 +21,7 @@ use rustc_hir_analysis::autoderef::report_autoderef_recursion_limit_error; use rustc_infer::infer::RegionVariableOrigin; use rustc_lint_defs::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_middle::traits::PatternOriginExpr; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::{self, Pinnedness, Ty, TypeVisitableExt, Unnormalized}; use rustc_session::diagnostics::feature_err; use rustc_span::edit_distance::find_best_match_for_name; @@ -156,7 +157,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { actual: Ty<'tcx>, ti: &TopInfo<'tcx>, ) -> Result<(), ErrorGuaranteed> { - self.demand_eqtype_pat_diag(cause_span, expected, actual, ti).map_err(|err| err.emit()) + self.demand_eqtype_pat_diag(cause_span, expected, actual, ti).map_err(|err| err.emit_err()) } } @@ -911,7 +912,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rustc_hir::PatExprKind::Path(qpath) => { let (res, opt_ty, segments) = self.resolve_ty_and_res_fully_qualified_call(qpath, lt.hir_id, lt.span); - self.instantiate_value_path(segments, opt_ty, res, lt.span, lt.span, lt.hir_id).0 + self.instantiate_value_path( + segments, opt_ty, res, lt.span, lt.span, lt.hir_id, false, + ) + .0 } }; self.write_ty(lt.hir_id, ty); @@ -1036,13 +1040,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some((fail, ty, expr.span)) } }; + let endpoints = [lhs, rhs]; let mut lhs = calc_side(lhs); let mut rhs = calc_side(rhs); if let (Some((true, ..)), _) | (_, Some((true, ..))) = (lhs, rhs) { // There exists a side that didn't meet our criteria that the end-point // be of a numeric or char type, as checked in `calc_side` above. - let guar = self.emit_err_pat_range(span, lhs, rhs); + let guar = self.emit_err_pat_range(span, lhs, rhs, endpoints); return Ty::new_error(self.tcx, guar); } @@ -1078,7 +1083,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some((ref mut fail, _, _)) = rhs { *fail = true; } - let guar = self.emit_err_pat_range(span, lhs, rhs); + let guar = self.emit_err_pat_range(span, lhs, rhs, endpoints); return Ty::new_error(self.tcx, guar); } ty @@ -1095,7 +1100,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, lhs: Option<(bool, Ty<'tcx>, Span)>, rhs: Option<(bool, Ty<'tcx>, Span)>, + endpoints: [Option<&hir::PatExpr<'tcx>>; 2], ) -> ErrorGuaranteed { + if !(lhs, rhs).references_error() { + // Range endpoints must resolve to constants, not local variables. + // Label both runtime endpoints before the type error. + let mut spans = Vec::new(); + for expr in endpoints.into_iter().flatten() { + if let hir::PatExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind + && matches!(path.res, Res::Local(_)) + { + spans.push(expr.span); + } + } + if !spans.is_empty() { + return self.dcx().emit_err(diagnostics::NonConstPathInPattern { spans }); + } + } + let span = match (lhs, rhs) { (Some((true, ..)), Some((true, ..))) => span, (Some((true, _, sp)), _) => sp, @@ -1140,7 +1162,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { type between two end-points, you can use a guard.", ); } - err.emit() + err.emit_err() } fn check_pat_ident( @@ -1227,7 +1249,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); if let Some(span) = and_pat_span { - err.span_suggestion( + err.span_suggestion_short( span, "replace this `&` with `&mut`", "&mut ", @@ -1459,7 +1481,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } _ if let Some((sp, msg, sugg)) = mut_var_suggestion => { - err.span_suggestion(sp, msg, sugg, Applicability::MachineApplicable); + err.span_suggestion_verbose(sp, msg, sugg, Applicability::MachineApplicable); } _ => {} // don't provide suggestions in other cases #55175 } @@ -1490,7 +1512,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if self.tcx.sess.teach(err.code.unwrap()) { err.note(CANNOT_IMPLICITLY_DEREF_POINTER_TRAIT_OBJ); } - return Err(err.emit()); + return Err(err.emit_err()); } Ok(()) } @@ -1624,7 +1646,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Find the type of the path pattern, for later checking. let (pat_ty, pat_res) = - self.instantiate_value_path(segments, opt_ty, res, span, span, path_id); + self.instantiate_value_path(segments, opt_ty, res, span, span, path_id, false); Ok(ResolvedPat { ty: pat_ty, kind: ResolvedPatKind::Path { res, pat_res, segments } }) } @@ -1784,8 +1806,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // Type-check the path. - let (pat_ty, res) = - self.instantiate_value_path(segments, opt_ty, res, pat.span, pat.span, pat.hir_id); + let (pat_ty, res) = self + .instantiate_value_path(segments, opt_ty, res, pat.span, pat.span, pat.hir_id, false); if !pat_ty.is_fn() { return report_unexpected_res(res); } @@ -2025,7 +2047,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - err.emit() + err.emit_err() } fn check_pat_tuple( @@ -2163,7 +2185,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let accessible_unmentioned_fields: Vec<_> = unmentioned_fields .iter() .copied() - .filter(|(field, _)| self.is_field_suggestable(field, pat.hir_id, pat.span)) + .filter(|(field, _)| self.is_field_suggestable(field, pat.span)) .collect(); if !has_rest_pat { @@ -2195,7 +2217,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Err(e) } else { i.emit(); - Err(u.emit()) + Err(u.emit_err()) } } (None, Some(u)) => { @@ -2203,10 +2225,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { u.delay_as_bug(); Err(e) } else { - Err(u.emit()) + Err(u.emit_err()) } } - (Some(err), None) => Err(err.emit()), + (Some(err), None) => Err(err.emit_err()), (None, None) => { self.error_tuple_variant_index_shorthand(variant, pat, fields)?; result @@ -2241,7 +2263,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("({})", self.get_suggested_tuple_struct_pattern(fields, variant)), Applicability::MaybeIncorrect, ); - return Err(err.emit()); + return Err(err.emit_err()); } } Ok(()) @@ -2284,7 +2306,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) .with_span_label(span, format!("multiple uses of `{ident}` in pattern")) .with_span_label(other_field, format!("first use of `{ident}`")) - .emit() + .emit_err() } fn error_inexistent_fields( @@ -2336,7 +2358,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); if let [(field_def, field)] = unmentioned_fields.as_slice() - && self.is_field_suggestable(field_def, pat.hir_id, pat.span) + && self.is_field_suggestable(field_def, pat.span) { let suggested_name = find_best_match_for_name(&[field.name], pat_field.ident.name, None); @@ -2438,7 +2460,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("({sugg})"), appl, ); - return Err(err.emit()); + return Err(err.emit_err()); } Ok(()) } @@ -2547,8 +2569,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty: Ty<'tcx>, } - impl<'a, 'b, 'c, 'tcx> Diagnostic<'a, ()> for FieldsNotListed<'b, 'c, 'tcx> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + impl<'a, 'b, 'c, 'tcx> Diagnostic<'a> for FieldsNotListed<'b, 'c, 'tcx> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let Self { pat_span, unmentioned_fields, joined_patterns, ty } = self; Diag::new(dcx, level, "some fields are not explicitly listed") .with_span_label(pat_span, format!("field{} {} not listed", rustc_errors::pluralize!(unmentioned_fields.len()), joined_patterns)) @@ -2646,7 +2668,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } }; - err.span_suggestion( + err.span_suggestion_verbose( sp, format!( "include the missing field{} in the pattern{}", @@ -2669,7 +2691,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), Applicability::MachineApplicable, ); - err.span_suggestion( + err.span_suggestion_verbose( sp, format!( "if you don't care about {these} missing field{s}, you can explicitly ignore {them}", @@ -2693,7 +2715,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), Applicability::MachineApplicable, ); - err.span_suggestion( + err.span_suggestion_verbose( sp, "or always ignore missing fields here", format!("{prefix}..{postfix}"), @@ -2994,7 +3016,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { self.dcx().struct_span_err(pat.span, err_msg) }; - err.emit() + err.emit_err() } fn try_resolve_slice_ty_to_array_ty( @@ -3191,7 +3213,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { size, ) .with_span_label(span, format!("expected {} element{}", size, pluralize!(size))) - .emit() + .emit_err() } fn error_scrutinee_with_rest_inconsistent_length( @@ -3213,7 +3235,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, format!("pattern cannot match array of {} element{}", size, pluralize!(size),), ) - .emit() + .emit_err() } fn error_scrutinee_unfixed_length(&self, span: Span) -> ErrorGuaranteed { @@ -3223,7 +3245,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { E0730, "cannot pattern-match on an array without a fixed length", ) - .emit() + .emit_err() } fn error_expected_array_or_slice( diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index 42ca305d3e00d..f3cba3a27d69f 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -92,8 +92,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MachineApplicable, ); } - let reported = err.emit(); - Some((Ty::new_error(self.tcx, reported), Ty::new_error(self.tcx, reported))) + let guar = err.emit_err(); + Some((Ty::new_error(self.tcx, guar), Ty::new_error(self.tcx, guar))) } /// To type-check `base_expr[index_expr]`, we progressively autoderef diff --git a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs index 37b753062c708..1c037cc2e2d7e 100644 --- a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs +++ b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs @@ -5,7 +5,7 @@ use rustc_hir::def_id::LocalDefId; use rustc_hir::{self as hir, HirId, HirIdMap}; use rustc_infer::infer::{InferCtxt, InferOk, OpaqueTypeStorageEntries, TyCtxtInferExt}; use rustc_middle::ty::{self, Ty, TyCtxt, TyVid, TypeVisitableExt, TypingMode}; -use rustc_span::def_id::LocalDefIdMap; +use rustc_span::def_id::{LocalDefIdMap, LocalModId}; use rustc_span::{Span, span_bug}; use rustc_trait_selection::traits::{self, FulfillmentEngine, FulfillmentError, TraitEngine}; use tracing::instrument; @@ -67,6 +67,9 @@ pub(crate) struct TypeckRootCtxt<'tcx> { /// we record that type variable here. This is later used to inform /// fallback. See the `fallback` module for details. pub(super) diverging_type_vars: RefCell>, + + /// Parent module + pub(super) mod_id: LocalModId, } impl<'tcx> Deref for TypeckRootCtxt<'tcx> { @@ -78,6 +81,7 @@ impl<'tcx> Deref for TypeckRootCtxt<'tcx> { impl<'tcx> TypeckRootCtxt<'tcx> { pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self { + let mod_id = tcx.parent_module_from_def_id(def_id); let hir_owner = tcx.local_def_id_to_hir_id(def_id).owner; let infcx = tcx @@ -102,6 +106,7 @@ impl<'tcx> TypeckRootCtxt<'tcx> { deferred_asm_checks: RefCell::new(Vec::new()), deferred_repeat_expr_checks: RefCell::new(Vec::new()), diverging_type_vars: RefCell::new(Default::default()), + mod_id, } } diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index 1497bfeb0f774..98155d9a8d5ef 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -30,16 +30,17 @@ //! then mean that all later passes would have to check for these figments //! and report an error, and it just seems like more mess in the end.) -use std::iter; +use std::{cmp, iter}; use rustc_abi::FIRST_VARIANT; +use rustc_attr_ir::find_attr; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::unord::{ExtendUnord, UnordSet}; use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, Level, MultiSpan}; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{self as hir, HirId, find_attr}; +use rustc_hir::{self as hir, HirId}; use rustc_lint_defs::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES; use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection, ProjectionKind}; use rustc_middle::mir::FakeReadCause; @@ -1085,8 +1086,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { migration_message: String, } - impl<'a, 'b, 'tcx> Diagnostic<'a, ()> for MigrationLint<'b, 'tcx> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + impl<'a, 'b, 'tcx> Diagnostic<'a> for MigrationLint<'b, 'tcx> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let Self { closure_def_id, closure_drop_location_span, @@ -2318,9 +2319,9 @@ fn restrict_precision_for_drop_types<'a, 'tcx>( mut place: Place<'tcx>, mut curr_mode: ty::UpvarCapture, ) -> (Place<'tcx>, ty::UpvarCapture) { - let is_copy_type = fcx.infcx.type_is_copy_modulo_regions(fcx.param_env, place.ty()); - - if let (false, UpvarCapture::ByValue) = (is_copy_type, curr_mode) { + if curr_mode == UpvarCapture::ByValue + && !fcx.infcx.type_is_copy_modulo_regions(fcx.param_env, place.ty()) + { for i in 0..place.projections.len() { match place.ty_before_projection(i).kind() { ty::Adt(def, _) if def.destructor(fcx.tcx).is_some() => { @@ -2608,56 +2609,18 @@ fn determine_capture_info( capture_info_a: ty::CaptureInfo, capture_info_b: ty::CaptureInfo, ) -> ty::CaptureInfo { - // If the capture kind is equivalent then, we don't need to escalate and can compare the - // expressions. - let eq_capture_kind = match (capture_info_a.capture_kind, capture_info_b.capture_kind) { - (ty::UpvarCapture::ByValue, ty::UpvarCapture::ByValue) => true, - (ty::UpvarCapture::ByUse, ty::UpvarCapture::ByUse) => true, - (ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => ref_a == ref_b, - (ty::UpvarCapture::ByValue, _) - | (ty::UpvarCapture::ByUse, _) - | (ty::UpvarCapture::ByRef(_), _) => false, - }; - - if eq_capture_kind { - match (capture_info_a.capture_kind_expr_id, capture_info_b.capture_kind_expr_id) { - (Some(_), _) | (None, None) => capture_info_a, - (None, Some(_)) => capture_info_b, - } - } else { - // We select the CaptureKind which ranks higher based the following priority order: - // (ByUse | ByValue) > MutBorrow > UniqueImmBorrow > ImmBorrow - match (capture_info_a.capture_kind, capture_info_b.capture_kind) { - (ty::UpvarCapture::ByUse, ty::UpvarCapture::ByValue) - | (ty::UpvarCapture::ByValue, ty::UpvarCapture::ByUse) => { - bug!("Same capture can't be ByUse and ByValue at the same time") - } - (ty::UpvarCapture::ByValue, ty::UpvarCapture::ByValue) - | (ty::UpvarCapture::ByUse, ty::UpvarCapture::ByUse) - | (ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse, ty::UpvarCapture::ByRef(_)) => { - capture_info_a - } - (ty::UpvarCapture::ByRef(_), ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse) => { - capture_info_b - } - (ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => { - match (ref_a, ref_b) { - // Take LHS: - (BorrowKind::UniqueImmutable | BorrowKind::Mutable, BorrowKind::Immutable) - | (BorrowKind::Mutable, BorrowKind::UniqueImmutable) => capture_info_a, - - // Take RHS: - (BorrowKind::Immutable, BorrowKind::UniqueImmutable | BorrowKind::Mutable) - | (BorrowKind::UniqueImmutable, BorrowKind::Mutable) => capture_info_b, - - (BorrowKind::Immutable, BorrowKind::Immutable) - | (BorrowKind::UniqueImmutable, BorrowKind::UniqueImmutable) - | (BorrowKind::Mutable, BorrowKind::Mutable) => { - bug!("Expected unequal capture kinds"); - } - } + // We select the CaptureKind which ranks higher based the following priority order: + // (ByUse | ByValue) > MutBorrow > UniqueImmBorrow > ImmBorrow + match capture_info_a.capture_kind.partial_cmp(&capture_info_b.capture_kind) { + Some(cmp::Ordering::Equal) => { + match (capture_info_a.capture_kind_expr_id, capture_info_b.capture_kind_expr_id) { + (Some(_), _) | (None, None) => capture_info_a, + (None, Some(_)) => capture_info_b, } } + Some(cmp::Ordering::Greater) => capture_info_a, + Some(cmp::Ordering::Less) => capture_info_b, + None => bug!("Same capture can't be ByUse and ByValue at the same time"), } } diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index f4fe38924351a..923c0d49936c8 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -11,12 +11,13 @@ use std::mem; use std::ops::ControlFlow; +use rustc_attr_ir::find_attr; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::unord::ExtendUnord; use rustc_errors::{E0720, ErrorGuaranteed}; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, InferKind, Visitor}; -use rustc_hir::{self as hir, AmbigArg, HirId, find_attr}; +use rustc_hir::{self as hir, AmbigArg, HirId}; use rustc_infer::traits::solve::Goal; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCoercion}; @@ -499,7 +500,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } if !errors_buffer.is_empty() { - errors_buffer.sort_by_key(|diag| diag.span.primary_span()); + errors_buffer.sort_by_key(|diag| diag.span.primary_span().map(Span::lo_hi)); for err in errors_buffer { err.emit(); } @@ -619,7 +620,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { guar } else { let (Ok(guar) | Err(guar)) = - prev.build_mismatch_error(&hidden_type, tcx).map(|d| d.emit()); + prev.build_mismatch_error(&hidden_type, tcx).map(|d| d.emit_err()); guar }; *entry = DefinitionSiteHiddenType::new_error(tcx, guar); @@ -656,7 +657,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { .dcx() .struct_span_err(span, "cannot resolve opaque type") .with_code(E0720) - .emit(); + .emit_err(); self.typeck_results .hidden_types .insert(def_id, DefinitionSiteHiddenType::new_error(tcx, guar)); @@ -947,7 +948,7 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { TypeAnnotationNeeded::E0282, false, ) - .emit() + .emit_err() } } diff --git a/compiler/rustc_incremental/Cargo.toml b/compiler/rustc_incremental/Cargo.toml index ac4d7d8e1cbc0..bf331f41b0e09 100644 --- a/compiler/rustc_incremental/Cargo.toml +++ b/compiler/rustc_incremental/Cargo.toml @@ -6,6 +6,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start rand = "0.9.0" +rustc_attr_ir = { path = "../rustc_attr_ir" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fs_util = { path = "../rustc_fs_util" } diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs index be23c3466f184..8147e3c3ae550 100644 --- a/compiler/rustc_incremental/src/assert_dep_graph.rs +++ b/compiler/rustc_incremental/src/assert_dep_graph.rs @@ -37,12 +37,11 @@ use std::env; use std::fs::{self, File}; use std::io::Write; +use rustc_attr_ir::{Attribute, AttributeKind}; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::graph::linked_graph::{Direction, INCOMING, NodeIndex, OUTGOING}; use rustc_graphviz as dot; use rustc_hir as hir; -use rustc_hir::Attribute; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; use rustc_middle::dep_graph::{DepKind, DepNode, DepNodeFilter, EdgeFilter, RetainedDepGraph}; diff --git a/compiler/rustc_incremental/src/diagnostics.rs b/compiler/rustc_incremental/src/diagnostics.rs index 6e291b7ea3abb..b9ac4662dcbdc 100644 --- a/compiler/rustc_incremental/src/diagnostics.rs +++ b/compiler/rustc_incremental/src/diagnostics.rs @@ -169,21 +169,6 @@ pub(crate) struct DeleteLock<'a> { pub err: std::io::Error, } -#[derive(Diagnostic)] -#[diag( - "hard linking files in the incremental compilation cache failed. copying files instead. consider moving the cache directory to a file system which supports hard linking in session dir `{$path}`" -)] -pub(crate) struct HardLinkFailed<'a> { - pub path: &'a Path, -} - -#[derive(Diagnostic)] -#[diag("failed to delete partly initialized session dir `{$path}`: {$err}")] -pub(crate) struct DeletePartial<'a> { - pub path: &'a Path, - pub err: std::io::Error, -} - #[derive(Diagnostic)] #[diag("did not finalize incremental compilation session directory `{$path}`: {$err}")] #[help("the next build will not be able to reuse work from this compilation")] @@ -266,13 +251,6 @@ pub(crate) struct CopyWorkProductToCache<'a> { pub err: std::io::Error, } -#[derive(Diagnostic)] -#[diag("file-system error deleting outdated file `{$path}`: {$err}")] -pub(crate) struct DeleteWorkProduct<'a> { - pub path: &'a Path, - pub err: std::io::Error, -} - #[derive(Diagnostic)] #[diag( "corrupt incremental compilation artifact found at `{$path}`. This file will automatically be ignored and deleted. If you see this message repeatedly or can provoke it without manually manipulating the compiler's artifacts, please file an issue. The incremental compilation system relies on hardlinks and filesystem locks behaving correctly, and may not deal well with OS crashes, so whatever information you can provide about your filesystem or other state may be very relevant" diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs index 83646cb086d8d..b5470d224ffbd 100644 --- a/compiler/rustc_incremental/src/lib.rs +++ b/compiler/rustc_incremental/src/lib.rs @@ -3,6 +3,7 @@ // tidy-alphabetical-start #![deny(missing_docs)] #![feature(file_buffered)] +#![feature(try_blocks)] // tidy-alphabetical-end mod assert_dep_graph; @@ -11,7 +12,7 @@ mod persist; pub use persist::{ copy_cgu_workproduct_to_incr_comp_cache_dir, finalize_session_directory, in_incr_comp_dir_sess, - load_query_result_cache, save_work_product_index, setup_dep_graph, + in_old_incr_comp_dir_sess, load_query_result_cache, save_work_product_index, setup_dep_graph, }; use rustc_middle::util::Providers; diff --git a/compiler/rustc_incremental/src/persist/clean.rs b/compiler/rustc_incremental/src/persist/clean.rs index a311832e62d96..b6402181fba12 100644 --- a/compiler/rustc_incremental/src/persist/clean.rs +++ b/compiler/rustc_incremental/src/persist/clean.rs @@ -19,14 +19,11 @@ //! Errors are reported if we are in the suitable configuration but //! the required condition is not met. +use rustc_attr_ir::{Attribute, AttributeKind, RustcCleanAttribute, find_attr}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::unord::UnordSet; -use rustc_hir::attrs::{AttributeKind, RustcCleanAttribute}; use rustc_hir::def_id::LocalDefId; -use rustc_hir::{ - Attribute, ImplItemKind, ItemKind as HirItem, Node as HirNode, TraitItemKind, find_attr, - intravisit, -}; +use rustc_hir::{ImplItemKind, ItemKind as HirItem, Node as HirNode, TraitItemKind, intravisit}; use rustc_middle::dep_graph::{DepKind, DepNode, dep_kind_from_label}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index 504e93cf2aaef..3f896584eaef2 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -1,14 +1,14 @@ //! This module manages how the incremental compilation cache is represented in //! the file system. //! -//! Incremental compilation caches are managed according to a copy-on-write -//! strategy: Once a complete, consistent cache version is finalized, it is -//! never modified. Instead, when a subsequent compilation session is started, -//! the compiler will allocate a new version of the cache that starts out as -//! a copy of the previous version. Then only this new copy is modified and it -//! will not be visible to other processes until it is finalized. This ensures -//! that multiple compiler processes can be executed concurrently for the same -//! crate without interfering with each other or blocking each other. +//! Incremental compilation caches are managed according to a rebuild from +//! scratch strategy: Once a complete, consistent cache version is finalized, it +//! is never modified. Instead, when a subsequent compilation session is started, +//! the compiler will allocate a new version of the cache that starts out empty. +//! Then only this new directory is written to and it will not be visible to +//! other processes until it is finalized. This ensures that multiple compiler +//! processes can be executed concurrently for the same crate without +//! interfering with each other or blocking each other. //! //! More concretely this is implemented via the following protocol: //! @@ -22,12 +22,12 @@ //! considered finalized if the "-working" suffix in the directory name has //! been replaced by the SVH of the crate. //! 3. Once the compiler has found a valid, finalized session directory, it will -//! hard-link/copy its contents into the new "-working" directory. If all -//! goes well, it will have its own, private copy of the source directory and -//! subsequently not have to worry about synchronizing with other compiler -//! processes. +//! obtain a shared lock on the directory. If this succeeds, it will have +//! read-only access to the old session directory without having to worry +//! about synchronizing with other compiler processes. //! 4. Now the compiler can do its normal compilation process, which involves -//! reading and updating its private session directory. +//! writing to its private session directory. Possibly by hardlinking +//! existing files from the old session directory if they haven't changed. //! 5. When compilation finishes without errors, the private session directory //! will be in a state where it can be used as input for other compilation //! sessions. That is, it will contain a dependency graph and cache artifacts @@ -71,23 +71,15 @@ //! //! Another case that has to be considered is what happens if one process //! deletes a finalized session directory that another process is currently -//! trying to copy from. This case is also handled via the lock file. Before -//! a process starts copying a finalized session directory, it will acquire a -//! shared lock on the directory's lock file. Any garbage collecting process, -//! on the other hand, will acquire an exclusive lock on the lock file. -//! Thus, if a directory is being collected, any reader process will fail -//! acquiring the shared lock and will leave the directory alone. Conversely, -//! if a collecting process can't acquire the exclusive lock because the -//! directory is currently being read from, it will leave collecting that -//! directory to another process at a later point in time. -//! The exact same scheme is also used when reading the metadata hashes file -//! from an extern crate. When a crate is compiled, the hash values of its -//! metadata are stored in a file in its session directory. When the -//! compilation session of another crate imports the first crate's metadata, -//! it also has to read in the accompanying metadata hashes. It thus will access -//! the finalized session directory of all crates it links to and while doing -//! so, it will also place a read lock on that the respective session directory -//! so that it won't be deleted while the metadata hashes are loaded. +//! reading from. This case is also handled via the lock file. Before a process +//! starts reading from a finalized session directory, it will acquire a shared +//! lock on the directory's lock file. Any garbage collecting process, on the +//! other hand, will acquire an exclusive lock on the lock file. Thus, if a +//! directory is being collected, any reader process will fail acquiring the +//! shared lock and will leave the directory alone. Conversely, if a collecting +//! process can't acquire the exclusive lock because the directory is currently +//! being read from, it will leave collecting that directory to another process +//! at a later point in time. //! //! ## Preconditions //! @@ -110,11 +102,11 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH}; use rand::{RngCore, rng}; use rustc_data_structures::base_n::{BaseNString, CASE_INSENSITIVE, ToBaseN}; -use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; +use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::svh::Svh; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_data_structures::{base_n, flock}; -use rustc_fs_util::{LinkOrCopy, link_or_copy, try_canonicalize}; +use rustc_fs_util::try_canonicalize; use rustc_middle::dep_graph::WorkProduct; use rustc_session::config::OutputType; use rustc_session::{IncrCompSession, Session, StableCrateId}; @@ -138,6 +130,11 @@ const QUERY_CACHE_FILENAME: &str = "query-cache.bin"; // case-sensitive (as opposed to base64, for example). const INT_ENCODE_BASE: usize = base_n::CASE_INSENSITIVE; +/// Returns the path to a previous session's dependency graph. +pub(crate) fn old_dep_graph_path(incr_comp_session: &IncrCompSession) -> Option { + in_old_incr_comp_dir_sess(incr_comp_session, DEP_GRAPH_FILENAME) +} + /// Returns the path to a session's dependency graph. pub(crate) fn dep_graph_path(incr_comp_session: &IncrCompSession) -> PathBuf { in_incr_comp_dir_sess(incr_comp_session, DEP_GRAPH_FILENAME) @@ -151,10 +148,19 @@ pub(crate) fn staging_dep_graph_path(incr_comp_session: &IncrCompSession) -> Pat in_incr_comp_dir_sess(incr_comp_session, STAGING_DEP_GRAPH_FILENAME) } +pub(crate) fn old_work_products_path(incr_comp_session: &IncrCompSession) -> Option { + in_old_incr_comp_dir_sess(incr_comp_session, WORK_PRODUCTS_FILENAME) +} + pub(crate) fn work_products_path(incr_comp_session: &IncrCompSession) -> PathBuf { in_incr_comp_dir_sess(incr_comp_session, WORK_PRODUCTS_FILENAME) } +/// Returns the path to a previous session's query cache. +pub(crate) fn old_query_cache_path(incr_comp_session: &IncrCompSession) -> Option { + in_old_incr_comp_dir_sess(incr_comp_session, QUERY_CACHE_FILENAME) +} + /// Returns the path to a session's query cache. pub(crate) fn query_cache_path(incr_comp_session: &IncrCompSession) -> PathBuf { in_incr_comp_dir_sess(incr_comp_session, QUERY_CACHE_FILENAME) @@ -182,10 +188,19 @@ fn lock_file_path(session_dir: &Path) -> PathBuf { crate_dir.join(&directory_name[0..dash_indices[2]]).with_extension(&LOCK_FILE_EXT[1..]) } +/// Returns the path for a given filename within the incremental compilation directory +/// in the previous session. +pub fn in_old_incr_comp_dir_sess( + incr_comp_session: &IncrCompSession, + file_name: &str, +) -> Option { + incr_comp_session.old_session_directory.as_ref().map(|dir| dir.join(file_name)) +} + /// Returns the path for a given filename within the incremental compilation directory /// in the current session. pub fn in_incr_comp_dir_sess(incr_comp_session: &IncrCompSession, file_name: &str) -> PathBuf { - incr_comp_session.session_directory.join(file_name) + incr_comp_session.new_session_directory.join(file_name) } /// Allocates the private session directory. @@ -230,65 +245,30 @@ pub(crate) fn prepare_session_directory( } }; - let mut source_directories_already_tried = FxHashSet::default(); - - loop { - // Generate a session directory of the form: - // - // {incr-comp-dir}/{crate-name-and-disambiguator}/s-{timestamp}-{random}-working - let session_dir = generate_session_dir_path(&crate_dir); - debug!("session-dir: {}", session_dir.display()); - - // Lock the new session directory. If this fails, return an - // error without retrying - let (directory_lock, lock_file_path) = lock_directory(sess, &session_dir); - - // Now that we have the lock, we can actually create the session - // directory - create_dir(sess, &session_dir, "session"); - - // Find a suitable source directory to copy from. Ignore those that we - // have already tried before. - let source_directory = find_source_directory(&crate_dir, &source_directories_already_tried); - - let Some(source_directory) = source_directory else { - // There's nowhere to copy from, we're done - debug!( - "no source directory found. Continuing with empty session \ - directory." - ); - - return IncrCompSession { session_directory: session_dir, _lock_file: directory_lock }; - }; - - debug!("attempting to copy data from source: {}", source_directory.display()); + // Generate a session directory of the form: + // + // {incr-comp-dir}/{crate-name-and-disambiguator}/s-{timestamp}-{random}-working + let new_session_dir = generate_session_dir_path(&crate_dir); + debug!("session-dir: {}", new_session_dir.display()); - // Try copying over all files from the source directory - if let Ok(allows_links) = copy_files(sess, &session_dir, &source_directory) { - debug!("successfully copied data from: {}", source_directory.display()); + // Lock the new session directory. If this fails, return an + // error without retrying + let new_session_directory = lock_directory(sess, &new_session_dir, true /* new_session */) + .expect("should emit fatal error on lock fail"); - if !allows_links { - sess.dcx().emit_warn(diagnostics::HardLinkFailed { path: &session_dir }); - } + // Find a suitable source directory to copy from. Ignore those that we + // have already tried before. + let old_source_directory = find_source_directory(sess, &crate_dir); - return IncrCompSession { session_directory: session_dir, _lock_file: directory_lock }; - } else { - debug!("copying failed - trying next directory"); - - // Something went wrong while trying to copy/link files from the - // source directory. Try again with a different one. - source_directories_already_tried.insert(source_directory); - - // Try to remove the session directory we just allocated. We don't - // know if there's any garbage in it from the failed copy action. - if let Err(err) = std_fs::remove_dir_all(&session_dir) { - sess.dcx().emit_warn(diagnostics::DeletePartial { path: &session_dir, err }); - } + let old_session_directory = if let Some(old_source_directory) = old_source_directory { + debug!("attempting to use: {}", old_source_directory.display()); + Some(old_source_directory) + } else { + debug!("no source directory found. Continuing with empty session directory."); + None + }; - delete_session_dir_lock_file(sess, &lock_file_path); - drop(directory_lock); - } - } + IncrCompSession { old_session_directory, new_session_directory } } /// This function finalizes and thus 'publishes' the session directory by @@ -304,13 +284,13 @@ pub fn finalize_session_directory( if sess.opts.incremental.is_none() { return; } - let incr_comp_session = incr_comp_session.unwrap(); + let mut incr_comp_session = incr_comp_session.unwrap(); // The svh is always produced when incr. comp. is enabled. let svh = svh.unwrap(); let _timer = sess.timer("incr_comp_finalize_session_directory"); - let incr_comp_session_dir = incr_comp_session.session_directory.clone(); + let incr_comp_session_dir = &*incr_comp_session.new_session_directory; debug!("finalize_session_directory() - session directory: {}", incr_comp_session_dir.display()); @@ -334,7 +314,7 @@ pub fn finalize_session_directory( let new_path = incr_comp_session_dir.parent().unwrap().join(&*sub_dir_name); debug!("finalize_session_directory() - new path: {}", new_path.display()); - let result = std_fs::rename(&*incr_comp_session_dir, &new_path).or_else(|e| { + let result = std_fs::rename(incr_comp_session_dir, &new_path).or_else(|e| { if !cfg!(windows) || e.kind() != ErrorKind::PermissionDenied { return Err(e); } @@ -355,7 +335,7 @@ pub fn finalize_session_directory( debug!("finalize_session_directory() - error replacing hard link with copy: {}", err); } - rename_path_with_retry(&*incr_comp_session_dir, &new_path, 3) + rename_path_with_retry(incr_comp_session_dir, &new_path, 3) }); match result { @@ -364,81 +344,34 @@ pub fn finalize_session_directory( } Err(e) => { // Warn about the error. However, no need to abort compilation now. - sess.dcx().emit_note(diagnostics::Finalize { path: &incr_comp_session_dir, err: e }); + sess.dcx().emit_note(diagnostics::Finalize { path: incr_comp_session_dir, err: e }); debug!("finalize_session_directory() - error"); } } - drop(incr_comp_session); // Unlock incr comp session dir - - let _ = garbage_collect_session_directories(sess, &new_path); -} + // Unlock the old session directory now that we will no longer read from it. + incr_comp_session.old_session_directory = None; -pub(crate) fn delete_all_session_dir_contents( - incr_comp_session: &IncrCompSession, -) -> io::Result<()> { - let sess_dir_iterator = incr_comp_session.session_directory.read_dir()?; - for entry in sess_dir_iterator { - let entry = entry?; - safe_remove_file(&entry.path())? - } - Ok(()) + let _ = garbage_collect_session_directories(sess, &incr_comp_session); } -fn copy_files(sess: &Session, target_dir: &Path, source_dir: &Path) -> Result { - // We acquire a shared lock on the lock file of the directory, so that - // nobody deletes it out from under us while we are reading from it. - let lock_file_path = lock_file_path(source_dir); - - // not exclusive - let Ok(_lock) = flock::Lock::try_lock( - &lock_file_path, - false, // don't create - false, - ) else { - // Could not acquire the lock, don't try to copy from here - return Err(()); - }; - - let Ok(source_dir_iterator) = source_dir.read_dir() else { - return Err(()); - }; - - let mut files_linked = 0; - let mut files_copied = 0; - - for entry in source_dir_iterator { - match entry { - Ok(entry) => { - let file_name = entry.file_name(); - - let target_file_path = target_dir.join(file_name); - let source_path = entry.path(); - - debug!("copying into session dir: {}", source_path.display()); - match link_or_copy(source_path, target_file_path) { - Ok(LinkOrCopy::Link) => files_linked += 1, - Ok(LinkOrCopy::Copy) => files_copied += 1, - Err(_) => return Err(()), - } +pub(crate) fn invalidate_old_session_dir(sess: &Session, incr_comp_session: &mut IncrCompSession) { + if let Some(old_incr_comp_session_dir) = incr_comp_session.old_session_directory.take() { + let res = try { + let sess_dir_iterator = old_incr_comp_session_dir.read_dir()?; + for entry in sess_dir_iterator { + let entry = entry?; + safe_remove_file(&entry.path())? } - Err(_) => return Err(()), + }; + if let Err(err) = res { + sess.dcx().emit_err(diagnostics::DeleteIncompatible { + path: (*old_incr_comp_session_dir).to_owned(), + err, + }); } } - - if sess.opts.unstable_opts.incremental_info { - eprintln!( - "[incremental] session directory: \ - {files_linked} files hard-linked" - ); - eprintln!( - "[incremental] session directory: \ - {files_copied} files copied" - ); - } - - Ok(files_linked > 0 || files_copied == 0) } /// Generates unique directory path of the form: @@ -471,26 +404,44 @@ fn create_dir(sess: &Session, path: &Path, dir_tag: &str) { } } -/// Allocate the lock-file and lock it. -fn lock_directory(sess: &Session, session_dir: &Path) -> (flock::Lock, PathBuf) { +/// Allocate the lock-file, lock it and create the session directory if requested. +fn lock_directory( + sess: &Session, + session_dir: &Path, + new_session: bool, +) -> Option { let lock_file_path = lock_file_path(session_dir); debug!("lock_directory() - lock_file: {}", lock_file_path.display()); - match flock::Lock::try_lock( + match flock::LockedDir::try_lock( + session_dir.to_owned(), &lock_file_path, - true, // create the lock file - true, + new_session, // create + new_session, // exclusive ) { - // the lock should be exclusive - Ok(lock) => (lock, lock_file_path), + Ok(lock) => { + // Now that we have the lock, we can actually create the session + // directory + if new_session { + create_dir(sess, &session_dir, "session"); + } + + Some(lock) + } Err(lock_err) => { let is_unsupported_lock = flock::Lock::error_unsupported(&lock_err); - sess.dcx().emit_fatal(diagnostics::CreateLock { + let diag = diagnostics::CreateLock { lock_err, session_dir, is_unsupported_lock, is_cargo: rustc_session::utils::was_invoked_from_cargo(), - }); + }; + if new_session { + sess.dcx().emit_fatal(diag); + } else { + sess.dcx().emit_warn(diag); + None + } } } } @@ -501,24 +452,18 @@ fn delete_session_dir_lock_file(sess: &Session, lock_file_path: &Path) { } } -/// Finds the most recent published session directory that is not in the -/// ignore-list. -fn find_source_directory( - crate_dir: &Path, - source_directories_already_tried: &FxHashSet, -) -> Option { +/// Finds the most recent published session directory. +fn find_source_directory(sess: &Session, crate_dir: &Path) -> Option { let iter = crate_dir .read_dir() .unwrap() // FIXME .filter_map(|e| e.ok().map(|e| e.path())); - find_source_directory_in_iter(iter, source_directories_already_tried) + find_source_directory_in_iter(iter) + .and_then(|session_dir| lock_directory(sess, &session_dir, false /* new_session */)) } -fn find_source_directory_in_iter( - iter: I, - source_directories_already_tried: &FxHashSet, -) -> Option +fn find_source_directory_in_iter(iter: I) -> Option where I: Iterator, { @@ -532,10 +477,7 @@ where continue; }; - if source_directories_already_tried.contains(&session_dir) - || !is_session_directory(&directory_name) - || !is_finalized(&directory_name) - { + if !is_session_directory(&directory_name) || !is_finalized(&directory_name) { debug!("find_source_directory_in_iter - ignoring"); continue; } @@ -612,10 +554,12 @@ fn is_old_enough_to_be_collected(timestamp: SystemTime) -> bool { /// Runs garbage collection for the current session. pub(crate) fn garbage_collect_session_directories( sess: &Session, - session_directory: &Path, + incr_comp_session: &IncrCompSession, ) -> io::Result<()> { debug!("garbage_collect_session_directories() - begin"); + let session_directory = &*incr_comp_session.new_session_directory; + debug!( "garbage_collect_session_directories() - session directory: {}", session_directory.display() @@ -717,9 +661,6 @@ pub(crate) fn garbage_collect_session_directories( } } - let current_session_directory_name = - session_directory.file_name().expect("session directory is not `..`"); - // Now garbage collect the valid session directories. let deletion_candidates = lock_file_to_session_dir.items().filter_map(|(lock_file_name, directory_name)| { @@ -754,10 +695,7 @@ pub(crate) fn garbage_collect_session_directories( ); // Note that we are holding on to the lock - return Some(( - (timestamp, crate_directory.join(directory_name)), - Some(lock), - )); + return Some((crate_directory.join(directory_name), lock)); } Err(_) => { debug!( @@ -767,31 +705,6 @@ pub(crate) fn garbage_collect_session_directories( } } } else if is_old_enough_to_be_collected(timestamp) { - if directory_name.as_str() == current_session_directory_name { - // Skipping our own active directory is important for correctness. - // - // To summarize #147821: we will try to lock directories before deciding they can be - // garbage collected, but the ability of `flock::Lock` to detect a lock held *by the - // same process* varies across file locking APIs. Then, if our own session directory - // has become old enough to be eligible for GC, we are beholden to platform-specific - // details about detecting the our own lock on the session directory. - // - // POSIX `fcntl(F_SETLK)`-style file locks are maintained across a process. On - // systems where this is the mechanism for `flock::Lock`, there is no way to - // discover if an `flock::Lock` has been created in the same process on the same - // file. Attempting to set a lock on the lockfile again will succeed, even if the - // lock was set by another thread, on another file descriptor. Then we would - // garbage collect our own live directory, unable to tell it was locked perhaps by - // this same thread. - // - // It's not clear that `flock::Lock` can be fixed for this in general, and our own - // incremental session directory is the only one which this process may own, so skip - // it here and avoid the problem. We know it's not garbage anyway: we're using it. - // Once finalized, its lock is released. Include it in collection so we keep - // the newest completed session. - return None; - } - // When cleaning out "-working" session directories, i.e. // session directories that might still be in use by another // compiler instance, we only look a directories that are @@ -837,14 +750,9 @@ pub(crate) fn garbage_collect_session_directories( } None }); - let deletion_candidates = deletion_candidates.into(); // Delete all but the most recent of the candidates - all_except_most_recent(deletion_candidates).into_items().all(|(path, lock)| { - if path.file_name() == Some(current_session_directory_name) { - return true; - } - + deletion_candidates.all(|(path, lock)| { debug!("garbage_collect_session_directories() - deleting `{}`", path.display()); if let Err(err) = std_fs::remove_dir_all(&path) { @@ -872,22 +780,6 @@ fn delete_old(sess: &Session, path: &Path) { } } -fn all_except_most_recent( - deletion_candidates: UnordMap<(SystemTime, PathBuf), Option>, -) -> UnordMap> { - let most_recent = deletion_candidates.items().map(|(&(timestamp, _), _)| timestamp).max(); - - if let Some(most_recent) = most_recent { - deletion_candidates - .into_items() - .filter(|&((timestamp, _), _)| timestamp != most_recent) - .map(|((_, path), lock)| (path, lock)) - .collect() - } else { - UnordMap::default() - } -} - fn safe_remove_file(p: &Path) -> io::Result<()> { match std_fs::remove_file(p) { Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(()), diff --git a/compiler/rustc_incremental/src/persist/fs/tests.rs b/compiler/rustc_incremental/src/persist/fs/tests.rs index 3652656b7c48f..112afd8c7bd1d 100644 --- a/compiler/rustc_incremental/src/persist/fs/tests.rs +++ b/compiler/rustc_incremental/src/persist/fs/tests.rs @@ -1,22 +1,5 @@ use super::*; -#[test] -fn test_all_except_most_recent() { - let input: UnordMap<_, Option> = UnordMap::from_iter([ - ((UNIX_EPOCH + Duration::new(4, 0), PathBuf::from("4")), None), - ((UNIX_EPOCH + Duration::new(1, 0), PathBuf::from("1")), None), - ((UNIX_EPOCH + Duration::new(5, 0), PathBuf::from("5")), None), - ((UNIX_EPOCH + Duration::new(3, 0), PathBuf::from("3")), None), - ((UNIX_EPOCH + Duration::new(2, 0), PathBuf::from("2")), None), - ]); - assert_eq!( - all_except_most_recent(input).into_items().map(|(path, _)| path).into_sorted_stable_ord(), - vec![PathBuf::from("1"), PathBuf::from("2"), PathBuf::from("3"), PathBuf::from("4")] - ); - - assert!(all_except_most_recent(UnordMap::default()).is_empty()); -} - #[test] fn test_timestamp_serialization() { for i in 0..1_000u64 { @@ -28,8 +11,6 @@ fn test_timestamp_serialization() { #[test] fn test_find_source_directory_in_iter() { - let already_visited = FxHashSet::default(); - // Find newest assert_eq!( find_source_directory_in_iter( @@ -39,7 +20,6 @@ fn test_find_source_directory_in_iter() { PathBuf::from("crate-dir/s-1234-0000-svh") ] .into_iter(), - &already_visited ), Some(PathBuf::from("crate-dir/s-3234-0000-svh")) ); @@ -53,13 +33,12 @@ fn test_find_source_directory_in_iter() { PathBuf::from("crate-dir/s-1234-0000-svh") ] .into_iter(), - &already_visited ), Some(PathBuf::from("crate-dir/s-2234-0000-svh")) ); // Handle empty - assert_eq!(find_source_directory_in_iter([].into_iter(), &already_visited), None); + assert_eq!(find_source_directory_in_iter([].into_iter()), None); // Handle only working assert_eq!( @@ -70,7 +49,6 @@ fn test_find_source_directory_in_iter() { PathBuf::from("crate-dir/s-1234-0000-working") ] .into_iter(), - &already_visited ), None ); diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index 3cf08961ed7b3..76e0bf92c6f0f 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs @@ -16,8 +16,8 @@ use rustc_span::Symbol; use tracing::{debug, warn}; use super::data::*; +use super::file_format; use super::fs::*; -use super::{file_format, work_product}; use crate::diagnostics; use crate::persist::file_format::{OpenFile, OpenFileError}; @@ -32,15 +32,6 @@ enum LoadResult { IoError { path: PathBuf, err: io::Error }, } -fn delete_dirty_work_product( - sess: &Session, - incr_comp_session: &IncrCompSession, - swp: SerializedWorkProduct, -) { - debug!("delete_dirty_work_product({:?})", swp); - work_product::delete_workproduct_files(sess, incr_comp_session, &swp.work_product); -} - fn load_dep_graph(sess: &Session, incr_comp_session: &IncrCompSession) -> LoadResult { assert!(sess.opts.incremental.is_some()); @@ -48,12 +39,16 @@ fn load_dep_graph(sess: &Session, incr_comp_session: &IncrCompSession) -> LoadRe // Calling `sess.incr_comp_session_dir()` will panic if `sess.opts.incremental.is_none()`. // Fortunately, we just checked that this isn't the case. - let path = dep_graph_path(incr_comp_session); + let Some(path) = old_dep_graph_path(incr_comp_session) else { + return LoadResult::DataOutOfDate; + }; let expected_hash = sess.opts.dep_tracking_hash(false); let mut prev_work_products = UnordMap::default(); - let work_products_path = work_products_path(incr_comp_session); + let Some(work_products_path) = old_work_products_path(incr_comp_session) else { + return LoadResult::DataOutOfDate; + }; if let Ok(OpenFile { mmap, start_pos }) = file_format::open_incremental_file(sess, &work_products_path) @@ -68,7 +63,7 @@ fn load_dep_graph(sess: &Session, incr_comp_session: &IncrCompSession) -> LoadRe for swp in work_products { let all_files_exist = swp.work_product.saved_files.items().all(|(_, path)| { - let exists = in_incr_comp_dir_sess(incr_comp_session, path).exists(); + let exists = in_old_incr_comp_dir_sess(incr_comp_session, path).unwrap().exists(); if !exists && sess.opts.unstable_opts.incremental_info { eprintln!("incremental: could not find file for work product: {path}",); } @@ -80,7 +75,7 @@ fn load_dep_graph(sess: &Session, incr_comp_session: &IncrCompSession) -> LoadRe prev_work_products.insert(swp.id, swp.work_product); } else { debug!("reconcile_work_products: some file for {:?} does not exist", swp); - delete_dirty_work_product(sess, incr_comp_session, swp); + return LoadResult::DataOutOfDate; } } } @@ -134,7 +129,9 @@ pub fn load_query_result_cache( let _prof_timer = sess.prof.generic_activity("incr_comp_load_query_result_cache"); - let path = query_cache_path(incr_comp_session); + let Some(path) = old_query_cache_path(incr_comp_session) else { + return Some(OnDiskCache::new_empty()); + }; match file_format::open_incremental_file(sess, &path) { Ok(OpenFile { mmap, start_pos }) => { let cache = OnDiskCache::new(sess, mmap, start_pos).unwrap_or_else(|()| { @@ -190,14 +187,12 @@ pub fn setup_dep_graph( } // `load_dep_graph` can only be called after `prepare_session_directory`. - let incr_comp_session = prepare_session_directory(sess, crate_name, stable_crate_id); + let mut incr_comp_session = prepare_session_directory(sess, crate_name, stable_crate_id); // Try to load the previous session's dep graph and work products. let load_result = load_dep_graph(sess, &incr_comp_session); sess.time("incr_comp_garbage_collect_session_directories", || { - if let Err(e) = - garbage_collect_session_directories(sess, &incr_comp_session.session_directory) - { + if let Err(e) = garbage_collect_session_directories(sess, &incr_comp_session) { warn!( "Error while trying to garbage collect incremental compilation \ cache directory: {e}", @@ -211,15 +206,11 @@ pub fn setup_dep_graph( let (prev_graph, prev_work_products) = match load_result { LoadResult::IoError { path, err } => { sess.dcx().emit_warn(diagnostics::LoadDepGraph { path, err }); + invalidate_old_session_dir(sess, &mut incr_comp_session); Default::default() } LoadResult::DataOutOfDate => { - if let Err(err) = delete_all_session_dir_contents(&incr_comp_session) { - sess.dcx().emit_err(diagnostics::DeleteIncompatible { - path: dep_graph_path(&incr_comp_session), - err, - }); - } + invalidate_old_session_dir(sess, &mut incr_comp_session); Default::default() } LoadResult::Ok { prev_graph, prev_work_products } => (prev_graph, prev_work_products), diff --git a/compiler/rustc_incremental/src/persist/mod.rs b/compiler/rustc_incremental/src/persist/mod.rs index 7d486cc394b80..fb318357b26cb 100644 --- a/compiler/rustc_incremental/src/persist/mod.rs +++ b/compiler/rustc_incremental/src/persist/mod.rs @@ -10,7 +10,7 @@ mod load; mod save; mod work_product; -pub use fs::{finalize_session_directory, in_incr_comp_dir_sess}; +pub use fs::{finalize_session_directory, in_incr_comp_dir_sess, in_old_incr_comp_dir_sess}; pub use load::{load_query_result_cache, setup_dep_graph}; pub(crate) use save::save_dep_graph; pub use save::save_work_product_index; diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs index 12f674fe2a859..46f47d6c8623c 100644 --- a/compiler/rustc_incremental/src/persist/save.rs +++ b/compiler/rustc_incremental/src/persist/save.rs @@ -11,7 +11,7 @@ use tracing::debug; use super::data::*; use super::fs::*; -use super::{clean, file_format, work_product}; +use super::{clean, file_format}; use crate::assert_dep_graph::assert_dep_graph; use crate::diagnostics; @@ -112,23 +112,6 @@ pub fn save_work_product_index( e.finish() }); - // We also need to clean out old work-products, as not all of them are - // deleted during invalidation. Some object files don't change their - // content, they are just not needed anymore. - let previous_work_products = dep_graph.previous_work_products(); - for (id, wp) in previous_work_products.to_sorted_stable_ord() { - if !new_work_products.contains_key(id) { - work_product::delete_workproduct_files(sess, incr_comp_session.unwrap(), wp); - debug_assert!( - !wp.saved_files.items().all(|(_, path)| in_incr_comp_dir_sess( - incr_comp_session.unwrap(), - path - ) - .exists()) - ); - } - } - // Check that we did not delete one of the current work-products: debug_assert!({ new_work_products.items().all(|(_, wp)| { diff --git a/compiler/rustc_incremental/src/persist/work_product.rs b/compiler/rustc_incremental/src/persist/work_product.rs index 7bb66fee4d1a3..0aaa9aa8e96c2 100644 --- a/compiler/rustc_incremental/src/persist/work_product.rs +++ b/compiler/rustc_incremental/src/persist/work_product.rs @@ -1,9 +1,8 @@ -//! Functions for saving and removing intermediate [work products]. +//! Function for saving intermediate [work products]. //! //! [work products]: WorkProduct -use std::fs as std_fs; -use std::path::{Path, PathBuf}; +use std::path::Path; use rustc_data_structures::unord::UnordMap; use rustc_fs_util::link_or_copy; @@ -23,7 +22,6 @@ pub fn copy_cgu_workproduct_to_incr_comp_cache_dir( incr_comp_session: &IncrCompSession, cgu_name: &str, files: &[(&'static str, &Path)], - known_links: &[PathBuf], ) -> (WorkProductId, WorkProduct) { debug!(?cgu_name, ?files); assert!(sess.opts.incremental.is_some()); @@ -32,10 +30,6 @@ pub fn copy_cgu_workproduct_to_incr_comp_cache_dir( for (ext, path) in files { let file_name = format!("{cgu_name}.{ext}"); let path_in_incr_dir = in_incr_comp_dir_sess(incr_comp_session, &file_name); - if known_links.contains(&path_in_incr_dir) { - let _ = saved_files.insert(ext.to_string(), file_name); - continue; - } match link_or_copy(path, &path_in_incr_dir) { Ok(_) => { let _ = saved_files.insert(ext.to_string(), file_name); @@ -55,17 +49,3 @@ pub fn copy_cgu_workproduct_to_incr_comp_cache_dir( let work_product_id = WorkProductId::from_cgu_name(cgu_name); (work_product_id, work_product) } - -/// Removes files for a given work product. -pub(crate) fn delete_workproduct_files( - sess: &Session, - incr_comp_session: &IncrCompSession, - work_product: &WorkProduct, -) { - for (_, path) in work_product.saved_files.items().into_sorted_stable_ord() { - let path = in_incr_comp_dir_sess(incr_comp_session, path); - if let Err(err) = std_fs::remove_file(&path) { - sess.dcx().emit_warn(diagnostics::DeleteWorkProduct { path: &path, err }); - } - } -} diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index 4d90e60736995..38dab6ec4875d 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -1,4 +1,6 @@ //! Definition of `InferCtxtLike` from the librarified type layer. +use std::range::RangeInclusive; + use rustc_data_structures::sso::SsoHashMap; use rustc_hir::def_id::DefId; use rustc_middle::traits::ObligationCause; @@ -49,7 +51,7 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { fn insert_placeholder_assumptions( &self, u: ty::UniverseIndex, - assumptions: Option>>, + assumptions: rustc_type_ir::region_constraint::Assumptions>, ) { self.insert_placeholder_assumptions(u, assumptions); } @@ -57,7 +59,7 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { fn get_placeholder_assumptions( &self, u: ty::UniverseIndex, - ) -> Option>> { + ) -> rustc_type_ir::region_constraint::Assumptions> { self.get_placeholder_assumptions(u) } @@ -67,6 +69,10 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { self.get_solver_region_constraint().without_spans() } + fn has_placeholder_assumptions(&self, range: RangeInclusive) -> bool { + self.has_placeholder_assumptions(range) + } + fn overwrite_solver_region_constraint( &self, constraint: rustc_type_ir::region_constraint::RegionConstraint>, @@ -189,7 +195,7 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { let u = self.universe(); self.placeholder_assumptions_for_next_solver .borrow_mut() - .insert(u, Some(rustc_type_ir::region_constraint::Assumptions::empty())); + .insert(u, rustc_type_ir::region_constraint::Assumptions::empty()); f(value) }) } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 267c36652656d..a7ce6a4353b59 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1,5 +1,6 @@ use std::cell::{Cell, RefCell}; use std::fmt; +use std::range::RangeInclusive; pub use at::DefineOpaqueTypes; use free_regions::RegionRelations; @@ -335,10 +336,7 @@ pub struct InferCtxt<'tcx> { // FIXME(-Zassumptions-on-binders): This and `universe` should probably be // in `InferCtxtInner` so they can participate in rollbacks and whatnot placeholder_assumptions_for_next_solver: RefCell< - FxIndexMap< - ty::UniverseIndex, - Option>>, - >, + FxIndexMap>>, >, next_trait_solver: bool, @@ -465,7 +463,7 @@ pub enum SubregionOrigin<'tcx> { trait_item_def_id: DefId, }, - AscribeUserTypeProvePredicate(Span), + AscribeUserTypeProvePredicate(Span, DefId), // FIXME(-Zassumptions-on-binders): this is a temporary hack until we support // proper diagnostics for solver region constraints. @@ -480,7 +478,9 @@ impl<'tcx> SubregionOrigin<'tcx> { pub fn to_constraint_category(&self) -> ConstraintCategory<'tcx> { match self { Self::Subtype(type_trace) => type_trace.cause.to_constraint_category(), - Self::AscribeUserTypeProvePredicate(span) => ConstraintCategory::Predicate(*span), + Self::AscribeUserTypeProvePredicate(span, def_id) => { + ConstraintCategory::Predicate(*span, *def_id) + } Self::SolverRegionConstraint(span) => ConstraintCategory::SolverRegionConstraint(*span), _ => ConstraintCategory::BoringNoLocation, } @@ -1579,28 +1579,34 @@ impl<'tcx> InferCtxt<'tcx> { pub fn insert_placeholder_assumptions( &self, u: ty::UniverseIndex, - assumptions: Option>>, + assumptions: rustc_type_ir::region_constraint::Assumptions>, ) { - if let Some(assumptions) = &assumptions { - assert!( - !assumptions.type_outlives.has_escaping_bound_vars(), - "assumptions has escaping bound vars, which is indicative of a bug in how assumptions are handled: {:?}", - assumptions.type_outlives - ); - assert!( - assumptions.region_outlives.base_edges().all(|r| !r.has_escaping_bound_vars()), - "assumptions has escaping bound vars, which is indicative of a bug in how assumptions are handled: {:?}", - assumptions.region_outlives - ); - } + assert!( + !assumptions.type_outlives.has_escaping_bound_vars(), + "assumptions has escaping bound vars, which is indicative of a bug in how assumptions are handled: {:?}", + assumptions.type_outlives + ); + assert!( + assumptions.region_outlives.base_edges().all(|r| !r.has_escaping_bound_vars()), + "assumptions has escaping bound vars, which is indicative of a bug in how assumptions are handled: {:?}", + assumptions.region_outlives + ); self.placeholder_assumptions_for_next_solver.borrow_mut().insert(u, assumptions); } pub fn get_placeholder_assumptions( &self, u: ty::UniverseIndex, - ) -> Option>> { - self.placeholder_assumptions_for_next_solver.borrow().get(&u).unwrap().as_ref().cloned() + ) -> rustc_type_ir::region_constraint::Assumptions> { + self.placeholder_assumptions_for_next_solver.borrow().get(&u).unwrap().clone() + } + + pub fn has_placeholder_assumptions(&self, range: RangeInclusive) -> bool { + let assumptions = self.placeholder_assumptions_for_next_solver.borrow(); + // Walk around that `Step` trait is nightly only. + (range.start.index()..=range.last.index()) + .map(ty::UniverseIndex::from_usize) + .all(|u| assumptions.get(&u).is_some()) } pub fn get_solver_region_constraint(&self) -> SolverRegionConstraint<'tcx> { @@ -1836,7 +1842,7 @@ impl<'tcx> SubregionOrigin<'tcx> { SubregionOrigin::Reborrow(a) => a, SubregionOrigin::ReferenceOutlivesReferent(_, a) => a, SubregionOrigin::CompareImplItemObligation { span, .. } => span, - SubregionOrigin::AscribeUserTypeProvePredicate(span) => span, + SubregionOrigin::AscribeUserTypeProvePredicate(span, _) => span, SubregionOrigin::CheckAssociatedTypeBounds { ref parent, .. } => parent.span(), SubregionOrigin::SolverRegionConstraint(a) => a, } @@ -1870,8 +1876,8 @@ impl<'tcx> SubregionOrigin<'tcx> { parent: Box::new(default()), }, - traits::ObligationCauseCode::AscribeUserTypeProvePredicate(span) => { - SubregionOrigin::AscribeUserTypeProvePredicate(span) + traits::ObligationCauseCode::AscribeUserTypeProvePredicate(span, def_id) => { + SubregionOrigin::AscribeUserTypeProvePredicate(span, def_id) } traits::ObligationCauseCode::ObjectTypeBound(ty, _reg) => { diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 14f534a3e7eb0..4b833da8eeb59 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -287,8 +287,6 @@ impl<'tcx> InferCtxt<'tcx> { &assumptions, ); debug!(?constraint); - let constraint = region_constraint::propagate_ambiguity(constraint); - debug!(?constraint); // FIXME(-Zassumptions-on-binders): actually implement OR as an OR for c in constraint.and_constraint.0.into_iter().chain( diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index 69ced9d3acb40..a81ec3d527a6e 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -3,7 +3,7 @@ use std::mem; use rustc_data_structures::sso::SsoHashMap; use rustc_hir::def_id::DefId; use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::{self, InferConst, Term, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, InferConst, Term, Ty, TyCtxt, TypeFlags, TypeVisitableExt}; use rustc_span::{Span, bug}; use tracing::{debug, instrument, warn}; @@ -356,6 +356,15 @@ struct Generalizer<'me, 'tcx> { cache: SsoHashMap<(Ty<'tcx>, ty::Variance, bool), Ty<'tcx>>, } +/// Do we need to generalize this type? +const NEEDS_GENERALIZATION: TypeFlags = TypeFlags::from_bits( + TypeFlags::HAS_FREE_REGIONS.bits() + | TypeFlags::HAS_INFER.bits() + | TypeFlags::HAS_PLACEHOLDER.bits() + | TypeFlags::HAS_NON_RIGID_ALIAS.bits(), +) +.unwrap(); + impl<'tcx> Generalizer<'_, 'tcx> { /// Create an error that corresponds to the term kind in `root_term` fn cyclic_term_error(&self) -> TypeError<'tcx> { @@ -484,6 +493,10 @@ impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { fn tys(&mut self, t: Ty<'tcx>, t2: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { assert_eq!(t, t2); // we are misusing TypeRelation here; both LHS and RHS ought to be == + if !t.has_type_flags(NEEDS_GENERALIZATION) { + return Ok(t); + } + if let Some(&result) = self.cache.get(&(t, self.ambient_variance, self.in_alias)) { return Ok(result); } diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index cbf961f4cc58b..aea4456792836 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -10,6 +10,7 @@ rustc_ast = { path = "../rustc_ast" } rustc_ast_lowering = { path = "../rustc_ast_lowering" } rustc_ast_passes = { path = "../rustc_ast_passes" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } +rustc_attr_ir = { path = "../rustc_attr_ir" } rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_borrowck = { path = "../rustc_borrowck" } rustc_builtin_macros = { path = "../rustc_builtin_macros" } @@ -33,6 +34,7 @@ rustc_macros = { path = "../rustc_macros" } rustc_metadata = { path = "../rustc_metadata" } rustc_middle = { path = "../rustc_middle" } rustc_mir_build = { path = "../rustc_mir_build" } +rustc_mir_dataflow = { path = "../rustc_mir_dataflow" } rustc_mir_transform = { path = "../rustc_mir_transform" } rustc_monomorphize = { path = "../rustc_monomorphize" } rustc_parse = { path = "../rustc_parse" } diff --git a/compiler/rustc_interface/src/callbacks.rs b/compiler/rustc_interface/src/callbacks.rs index 0d8b565b4e384..2b9170c80de21 100644 --- a/compiler/rustc_interface/src/callbacks.rs +++ b/compiler/rustc_interface/src/callbacks.rs @@ -13,7 +13,7 @@ use std::fmt; use std::fmt::Arguments; use std::panic::Location; -use rustc_errors::DiagInner; +use rustc_errors::{DiagInner, DiagLocation, Level}; use rustc_middle::dep_graph::{QuerySideEffect, TaskDepsRef}; use rustc_middle::ty::tls; use rustc_span::{Span, Symbol}; @@ -86,16 +86,26 @@ fn def_id_debug(def_id: rustc_hir::def_id::DefId, f: &mut fmt::Formatter<'_>) -> write!(f, ")") } -fn emit_bug_diagnostic(span: Option, args: Arguments<'_>, location: &Location<'_>) { +/// Returns true if it printed the diagnostic, which happens if a `tcx` is available. +fn emit_bug_diagnostic( + span: Option, + args: Arguments<'_>, + location: &'static Location<'static>, +) -> bool { tls::with_opt(move |tcx| { if let Some(tcx) = tcx { - let message = format!("{location}: {args}"); + let mut diag = DiagInner::new(Level::Bug, format!("{location}: {args}")); if let Some(span) = span { - tcx.dcx().struct_span_bug(span, message) - } else { - tcx.dcx().struct_bug(message) + diag.span = span.into(); } - .emit_producing_nothing(); + diag.emitted_at = DiagLocation::from_location(location); + // Emit the bug without aborting. We let `bug_impl` do the abort because it has + // `#[track_caller]` which gives a better location. (`#[track_caller]` doesn't work + // here because this function is called via a function pointer.) + tcx.dcx().emit_diagnostic(diag); + true + } else { + false } }) } diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 1950a29661249..7a6a49f37278a 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -135,7 +135,7 @@ pub(crate) fn parse_check_cfg(sess: &Session, specs: Vec) -> CheckCfg { sess.dcx().struct_fatal(format!("invalid `--check-cfg` argument: `{s}`")); diag.note($reason); diag.note(VISIT); - diag.emit() + diag.emit_fatal() }}; (in $arg:expr, $reason:expr) => {{ let mut diag = @@ -154,7 +154,7 @@ pub(crate) fn parse_check_cfg(sess: &Session, specs: Vec) -> CheckCfg { diag.note($reason); diag.note(VISIT); - diag.emit() + diag.emit_fatal() }}; } @@ -450,6 +450,9 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se config.using_internal_features, ); + // STACK_SIZE is setup in run_in_thread_pool_with_globals, before arriving here. + sess.opts.recommended_stack_size = *util::STACK_SIZE.get().unwrap(); + let target_config = codegen_backend.target_config(&sess); // Store all of the target features in the session. diff --git a/compiler/rustc_interface/src/limits.rs b/compiler/rustc_interface/src/limits.rs index 8f116cfdf6a86..a0791bb22df6d 100644 --- a/compiler/rustc_interface/src/limits.rs +++ b/compiler/rustc_interface/src/limits.rs @@ -8,7 +8,7 @@ //! Users can override these limits via an attribute on the crate like //! `#![recursion_limit="22"]`. This pass just looks for those attributes. -use rustc_hir::{Attribute, find_attr}; +use rustc_attr_ir::{Attribute, find_attr}; use rustc_middle::query::Providers; use rustc_session::{Limits, Session}; use rustc_structures::Limit; diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 2316e80b6e945..8975f01de5216 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -6,6 +6,7 @@ use std::sync::{Arc, LazyLock, OnceLock}; use std::{env, fs, iter}; use rustc_ast as ast; +use rustc_attr_ir::{Attribute, AttributeKind, find_attr}; use rustc_attr_parsing::{AttributeParser, ShouldEmit}; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_codegen_ssa::{CompiledModules, CrateInfo}; @@ -21,10 +22,8 @@ use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level}; use rustc_expand::base::{ExtCtxt, LintStoreExpand}; use rustc_feature::Features; use rustc_fs_util::try_canonicalize; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::{LOCAL_CRATE, StableCrateId, StableCrateIdMap}; use rustc_hir::definitions::Definitions; -use rustc_hir::{Attribute, find_attr}; use rustc_incremental::setup_dep_graph; use rustc_lint::{BufferedEarlyLint, EarlyCheckNode, LintStore, unerased_lint_store}; use rustc_metadata::EncodedMetadata; @@ -72,7 +71,7 @@ pub fn parse<'a>(sess: &'a Session) -> ast::Crate { parser.parse_crate_mod() }) .unwrap_or_else(|parse_error| { - let guar: ErrorGuaranteed = parse_error.emit(); + let guar: ErrorGuaranteed = parse_error.emit_err(); guar.raise_fatal(); }); @@ -434,7 +433,7 @@ fn early_lint_checks(tcx: TyCtxt<'_>, (): ()) { // Gate identifiers containing invalid Unicode codepoints that were recovered during lexing. sess.psess.bad_unicode_identifiers.with_lock(|identifiers| { for (ident, mut spans) in identifiers.drain(..) { - spans.sort(); + spans.sort_by_key(|span| span.lo_hi()); if ident == sym::ferris { enum FerrisFix { SnakeCase, @@ -907,6 +906,7 @@ pub static DEFAULT_QUERY_PROVIDERS: LazyLock = LazyLock::new(|| { rustc_borrowck::provide(&mut providers.queries); rustc_incremental::provide(providers); rustc_mir_build::provide(providers); + rustc_mir_dataflow::provide(providers); rustc_mir_transform::provide(providers); rustc_monomorphize::provide(providers); rustc_privacy::provide(&mut providers.queries); @@ -960,8 +960,13 @@ pub fn create_and_enter_global_ctxt FnOnce(TyCtxt<'tcx>) -> T>( let definitions = FreezeLock::new(Definitions::new(stable_crate_id)); let stable_crate_ids = FreezeLock::new(StableCrateIdMap::default()); - let untracked = - Untracked { cstore, source_span: AppendOnlyIndexVec::new(), definitions, stable_crate_ids }; + let untracked = Untracked { + cstore, + source_span: AppendOnlyIndexVec::new(), + definitions, + stable_crate_ids, + local_crate_hash: OnceLock::new(), + }; // We're constructing the HIR here; we don't care what we will // read, since we haven't even constructed the *input* to @@ -1057,13 +1062,13 @@ pub fn create_and_enter_global_ctxt FnOnce(TyCtxt<'tcx>) -> T>( struct DiagCallback<'tcx> { callback: Box< - dyn for<'b> FnOnce(DiagCtxtHandle<'b>, Level, &dyn Any) -> Diag<'b, ()> + DynSend + DynSync, + dyn for<'b> FnOnce(DiagCtxtHandle<'b>, Level, &dyn Any) -> Diag<'b> + DynSend + DynSync, >, tcx: TyCtxt<'tcx>, } -impl<'a, 'tcx> Diagnostic<'a, ()> for DiagCallback<'tcx> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { +impl<'a, 'tcx> Diagnostic<'a> for DiagCallback<'tcx> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { (self.callback)(dcx, level, self.tcx.sess) } } @@ -1099,6 +1104,8 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { // to use `hir_crate_items`. tcx.ensure_done().hir_crate_items(()); + tcx.untracked().definitions.write().commit_end_of_determinism(); + rustc_passes::delegation::check_glob_and_list_delegations_target_expr(tcx); let sess = tcx.sess; @@ -1403,7 +1410,7 @@ pub(crate) fn parse_crate_name( attrs: &[ast::Attribute], emit_errors: ShouldEmit, ) -> Option<(Symbol, Span)> { - let rustc_hir::Attribute::Parsed(AttributeKind::CrateName { name, name_span, .. }) = + let Attribute::Parsed(AttributeKind::CrateName { name, name_span, .. }) = AttributeParser::parse_limited_sym_should_emit( sess, attrs, diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 2f196c5e5d609..759297bc69592 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -101,7 +101,6 @@ impl Linker { incr_comp_session.as_ref().unwrap(), WorkProduct::METADATA_WORKPRODUCT_CGU_NAME, &[(OutputType::Metadata.extension(), path)], - &[], ); work_products.insert(id, product); } diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 0d584be4ad0b0..34ac92faef88c 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -42,7 +42,7 @@ where { let mut early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default()); let matches = optgroups().parse(args).unwrap(); - let sessopts = build_session_options(&mut early_dcx, &matches); + let sessopts = build_session_options(&mut early_dcx, &matches, true); let target = rustc_session::config::build_target_config( &early_dcx, &sessopts.target_triple, @@ -851,6 +851,7 @@ fn test_unstable_options_tracking_hash() { tracked!(min_function_alignment, Some(Align::EIGHT)); tracked!(min_recursion_limit, Some(256)); tracked!(mir_enable_passes, vec![("DestProp".to_string(), false)]); + tracked!(mir_move_elimination, true); tracked!(mir_opt_level, Some(4)); tracked!(mir_preserve_ub, true); tracked!(move_size_limit, Some(4096)); @@ -858,7 +859,7 @@ fn test_unstable_options_tracking_hash() { // tidy-alphabetical-end // FIXME(#160895): We don't test this when the next-solver is enabled by default. if option_env!("CFG_DEFAULT_NEXT_SOLVER_GLOBALLY").is_none() { - tracked!(next_solver, NextSolverConfig { coherence: true, globally: true }); + tracked!(next_solver, NextSolverConfig::Globally); } // tidy-alphabetical-start tracked!(no_generate_arange_section, true); @@ -941,33 +942,21 @@ fn test_edition_parsing() { let mut early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default()); let matches = optgroups().parse(&["--edition=2018".to_string()]).unwrap(); - let sessopts = build_session_options(&mut early_dcx, &matches); + let sessopts = build_session_options(&mut early_dcx, &matches, false); assert!(sessopts.edition == Edition::Edition2018) } #[test] fn test_assumptions_on_binders_enables_next_solver_globally() { - let globally = NextSolverConfig { coherence: true, globally: true }; + let globally = NextSolverConfig::Globally; let mut early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default()); // `-Zassumptions-on-binders` alone enables the next solver globally. let matches = optgroups().parse(&["-Zassumptions-on-binders".to_string()]).unwrap(); - let opts = build_session_options(&mut early_dcx, &matches); + let opts = build_session_options(&mut early_dcx, &matches, false); assert!(opts.unstable_opts.assumptions_on_binders); assert_eq!(opts.unstable_opts.next_solver, globally); - // Flag order must not matter when both `-Zassumptions-on-binders` and `-Znext-solver` - // are present. - for args in [ - ["-Zassumptions-on-binders".to_string(), "-Znext-solver=coherence".to_string()], - ["-Znext-solver=coherence".to_string(), "-Zassumptions-on-binders".to_string()], - ] { - let matches = optgroups().parse(&args).unwrap(); - let opts = build_session_options(&mut early_dcx, &matches); - assert!(opts.unstable_opts.assumptions_on_binders); - assert_eq!(opts.unstable_opts.next_solver, globally); - } - // `-Zassumptions-on-binders` overrides `-Znext-solver=no` regardless of order, since // the assumptions implementation requires the next solver. This also emits an early // warning (covered by the UI test `next-solver-no-overridden`). @@ -976,7 +965,7 @@ fn test_assumptions_on_binders_enables_next_solver_globally() { ["-Znext-solver=no".to_string(), "-Zassumptions-on-binders".to_string()], ] { let matches = optgroups().parse(&args).unwrap(); - let opts = build_session_options(&mut early_dcx, &matches); + let opts = build_session_options(&mut early_dcx, &matches, false); assert!(opts.unstable_opts.assumptions_on_binders); assert_eq!(opts.unstable_opts.next_solver, globally); } diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 87b2bb94cd4f9..d58b7bfddfc6d 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -21,6 +21,7 @@ use rustc_metadata::{DylibError, EncodedMetadata, load_symbol_from_dylib}; use rustc_middle::dep_graph::WorkProductMap; use rustc_middle::ty::{CurrentGcx, TyCtxt}; use rustc_query_impl::{CollectActiveJobsKind, collect_active_query_jobs}; +pub use rustc_session::config::DEFAULT_STACK_SIZE; use rustc_session::config::{ Cfg, Jobs, OutFileName, OutputFilenames, OutputTypes, Sysroot, host_tuple, }; @@ -74,6 +75,9 @@ pub(crate) fn add_configuration( if target_config.has_reliable_f16_math { cfg.insert((sym::target_has_reliable_f16_math, None)); } + if target_config.has_reliable_f16b { + cfg.insert((sym::target_has_reliable_f16b, None)); + } if target_config.has_reliable_f128 { cfg.insert((sym::target_has_reliable_f128, None)); } @@ -139,7 +143,6 @@ pub(crate) fn check_abi_required_features(sess: &Session) { } pub static STACK_SIZE: OnceLock = OnceLock::new(); -pub const DEFAULT_STACK_SIZE: usize = 17 * 1024 * 1024; fn init_stack_size(early_dcx: &EarlyDiagCtxt) -> usize { // Obey the environment setting or default @@ -161,7 +164,7 @@ fn init_stack_size(early_dcx: &EarlyDiagCtxt) -> usize { r#"`RUST_MIN_STACK` should be a number of bytes, but was "{s}""#, )); err.note("you can also unset `RUST_MIN_STACK` to use the default stack size"); - err.emit() + err.emit_fatal() }) }) // otherwise pick a consistent default @@ -338,7 +341,7 @@ internal compiler error: query cycle handler thread panicked, aborting process"; diag.help( "try lowering `-Z threads` or checking the operating system's resource limits", ); - diag.emit() + diag.emit_fatal() }) }) }) @@ -419,6 +422,7 @@ impl CodegenBackend for DummyCodegenBackend { internal_target_features, has_reliable_f16: true, has_reliable_f16_math: true, + has_reliable_f16b: true, has_reliable_f128: true, has_reliable_f128_math: true, } diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index dc6e3b1f358dc..043b7ad59d0a8 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -101,6 +101,9 @@ pub enum TokenKind { /// A raw identifier, e.g. "r#ident". RawIdent, + /// A forced keyword like `k#fn`. + ForcedKeywordIdent, + /// An unknown literal prefix, like `foo#`, `foo'`, `foo"`. Excludes /// literal prefixes that contain emoji, which are considered "invalid". /// @@ -574,7 +577,11 @@ impl<'a> Cursor<'a> { // Raw identifier, raw string literal or identifier. 'r' => match (self.first(), self.second()) { - ('#', c1) if is_id_start(c1) => self.raw_ident(), + ('#', c1) if is_id_start(c1) => { + self.bump(); // `#` + self.eat_identifier(); + RawIdent + } ('#', _) | ('"', _) => { let res = self.raw_double_quoted_string(1); let suffix_start = self.pos_within_token(); @@ -587,6 +594,16 @@ impl<'a> Cursor<'a> { _ => self.ident_or_unknown_prefix(), }, + // Forced keyword identifier or identifier. + 'k' => match (self.first(), self.second()) { + ('#', c1) if is_id_start(c1) => { + self.bump(); // `#` + self.eat_identifier(); + ForcedKeywordIdent + } + _ => self.ident_or_unknown_prefix(), + }, + // Byte literal, byte string literal, raw byte string literal or identifier. 'b' => self.c_or_byte_string( |terminated| ByteStr { terminated }, @@ -823,15 +840,6 @@ impl<'a> Cursor<'a> { Whitespace } - fn raw_ident(&mut self) -> TokenKind { - debug_assert!(self.prev() == 'r' && self.first() == '#' && is_id_start(self.second())); - // Eat "#" symbol. - self.bump(); - // Eat the identifier part of RawIdent. - self.eat_identifier(); - RawIdent - } - fn ident_or_unknown_prefix(&mut self) -> TokenKind { debug_assert!(is_id_start(self.prev())); // Start is already eaten, eat the rest of identifier. diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml index a672f242449e4..0024d8b26f1fb 100644 --- a/compiler/rustc_lint/Cargo.toml +++ b/compiler/rustc_lint/Cargo.toml @@ -10,6 +10,7 @@ rustc_abi = { path = "../rustc_abi" } rustc_apfloat = "0.2.0" rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } +rustc_attr_ir = { path = "../rustc_attr_ir" } rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_lint/src/autorefs.rs b/compiler/rustc_lint/src/autorefs.rs index 728937b762110..0064e8c6b3a23 100644 --- a/compiler/rustc_lint/src/autorefs.rs +++ b/compiler/rustc_lint/src/autorefs.rs @@ -1,5 +1,6 @@ use rustc_ast::{BorrowKind, UnOp}; -use rustc_hir::{Expr, ExprKind, Mutability, find_attr}; +use rustc_attr_ir::find_attr; +use rustc_hir::{Expr, ExprKind, Mutability}; use rustc_lint_defs::{declare_lint, declare_lint_pass}; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AutoBorrow, DerefAdjustKind, OverloadedDeref, diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 2d571d8b8e13d..2c8c6a3ce7f17 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -21,18 +21,19 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_ast::visit::{FnCtxt, FnKind}; use rustc_ast::{self as ast, *}; use rustc_ast_pretty::pprust::expr_to_string; +use rustc_attr_ir::lang_items::LangItem; +use rustc_attr_ir::{AttributeKind, DocAttribute, find_attr}; use rustc_attr_parsing::AttributeParser; use rustc_errors::{Applicability, Diagnostic, msg}; use rustc_feature::GateIssue; -use rustc_hir::attrs::lang_items::LangItem; -use rustc_hir::attrs::{AttributeKind, DocAttribute}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId}; use rustc_hir::intravisit::FnKind as HirFnKind; -use rustc_hir::{self as hir, Body, FnDecl, ImplItemImplKind, PatKind, PredicateOrigin, find_attr}; +use rustc_hir::{self as hir, Body, FnDecl, ImplItemImplKind, PatKind, PredicateOrigin}; // Lints from rustc_lint_defs pub use rustc_lint_defs::builtin::*; use rustc_lint_defs::{declare_lint, declare_lint_pass, fcw, impl_lint_pass}; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{ @@ -148,10 +149,7 @@ declare_lint_pass!(NonShorthandFieldPatterns => [NON_SHORTHAND_FIELD_PATTERNS]); impl<'tcx> LateLintPass<'tcx> for NonShorthandFieldPatterns { fn check_pat(&mut self, cx: &LateContext<'_>, pat: &hir::Pat<'_>) { - // The result shouldn't be tainted, otherwise it will cause ICE. - if let PatKind::Struct(ref qpath, field_pats, _) = pat.kind - && cx.typeck_results().tainted_by_errors.is_none() - { + if let PatKind::Struct(ref qpath, field_pats, _) = pat.kind { let variant = cx .typeck_results() .pat_ty(pat) @@ -195,7 +193,7 @@ impl UnsafeCode { &self, cx: &EarlyContext<'_>, span: Span, - decorate: impl for<'a> Diagnostic<'a, ()>, + decorate: impl for<'a> Diagnostic<'a>, ) { // This comes from a macro that has `#[allow_internal_unsafe]`. if span.allows_unsafe() { @@ -241,13 +239,13 @@ impl EarlyLintPass for UnsafeCode { } ast::ItemKind::MacroDef(..) => { - if let Some(hir::Attribute::Parsed(AttributeKind::AllowInternalUnsafe(span))) = - AttributeParser::parse_limited_sym( - cx.builder.sess(), - &it.attrs, - &[sym::allow_internal_unsafe], - ) - { + if let Some(rustc_attr_ir::Attribute::Parsed(AttributeKind::AllowInternalUnsafe( + span, + ))) = AttributeParser::parse_limited_sym( + cx.builder.sess(), + &it.attrs, + &[sym::allow_internal_unsafe], + ) { self.report_unsafe(cx, span, BuiltinUnsafe::AllowInternalUnsafe); } } @@ -309,12 +307,12 @@ pub struct MissingDoc; impl_lint_pass!(MissingDoc => [MISSING_DOCS]); -fn has_doc(attr: &hir::Attribute) -> bool { - if matches!(attr, hir::Attribute::Parsed(AttributeKind::DocComment { .. })) { +fn has_doc(attr: &rustc_attr_ir::Attribute) -> bool { + if matches!(attr, rustc_attr_ir::Attribute::Parsed(AttributeKind::DocComment { .. })) { return true; } - if let hir::Attribute::Parsed(AttributeKind::Doc(d)) = attr + if let rustc_attr_ir::Attribute::Parsed(AttributeKind::Doc(d)) = attr && matches!(d.as_ref(), DocAttribute { hidden: Some(..), .. }) { return true; @@ -1032,7 +1030,7 @@ declare_lint_pass!( ); impl<'tcx> LateLintPass<'tcx> for UnstableFeatures { - fn check_attributes(&mut self, cx: &LateContext<'_>, attrs: &[hir::Attribute]) { + fn check_attributes(&mut self, cx: &LateContext<'_>, attrs: &[rustc_attr_ir::Attribute]) { if let Some(features) = find_attr!(attrs, Feature(features, _) => features) { for feature in features { cx.emit_span_lint(UNSTABLE_FEATURES, feature.span, BuiltinUnstableFeatures); @@ -1150,49 +1148,46 @@ impl UnreachablePub { exportable: bool, ) { let mut applicability = Applicability::MachineApplicable; - if cx.tcx.visibility(def_id).is_public() && !cx.effective_visibilities.is_reachable(def_id) + if !cx.tcx.visibility(def_id).is_public() || cx.effective_visibilities.is_reachable(def_id) { - // prefer suggesting `pub(super)` instead of `pub(crate)` when possible, - // except when `pub(super) == pub(crate)` - let new_vis = if let Some(ty::Visibility::Restricted(restricted_did)) = - cx.effective_visibilities.effective_vis(def_id).map(|effective_vis| { - effective_vis.at_level(rustc_middle::middle::privacy::Level::Reachable) - }) - && let parent_parent = cx - .tcx - .parent_module_from_def_id(cx.tcx.parent_module_from_def_id(def_id).into()) - && *restricted_did == parent_parent - && !restricted_did.to_def_id().is_crate_root() - { - "pub(super)" - } else { - "pub(crate)" - }; + return; + } - if vis_span.from_expansion() { - applicability = Applicability::MaybeIncorrect; - } - let def_span = cx.tcx.def_span(def_id); - cx.emit_span_lint( - UNREACHABLE_PUB, - def_span, - BuiltinUnreachablePub { - what, - new_vis, - suggestion: (vis_span, applicability), - help: exportable, - }, - ); + // prefer suggesting `pub(super)` instead of `pub(crate)` when possible, + // except when `pub(super) == pub(crate)` + let new_vis = if let Some(ty::Visibility::Restricted(restricted_did)) = + cx.effective_visibilities.effective_vis(def_id).map(|effective_vis| { + effective_vis.at_level(rustc_middle::middle::privacy::Level::Reachable) + }) + && let parent_parent = + cx.tcx.parent_module_from_def_id(cx.tcx.parent_module_from_def_id(def_id).into()) + && *restricted_did == parent_parent + && !restricted_did.to_def_id().is_crate_root() + { + "pub(super)" + } else { + "pub(crate)" + }; + + if vis_span.from_expansion() { + applicability = Applicability::MaybeIncorrect; } + let def_span = cx.tcx.def_span(def_id); + cx.emit_span_lint( + UNREACHABLE_PUB, + def_span, + BuiltinUnreachablePub { + what, + new_vis, + suggestion: (vis_span, applicability), + help: exportable, + }, + ); } } impl<'tcx> LateLintPass<'tcx> for UnreachablePub { fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { - // Do not warn for fake `use` statements. - if let hir::ItemKind::Use(_, hir::UseKind::ListStem) = &item.kind { - return; - } self.perform_lint(cx, "item", item.owner_id.def_id, item.vis_span, true); } @@ -1757,11 +1752,11 @@ impl KeywordIdents { match tt { // Only report non-raw idents. TokenTree::Token(token, _) => { - if let Some((ident, token::IdentIsRaw::No)) = token.ident() { + if let Some((ident, token::IdentKind::Normal)) = token.ident() { if !prev_dollar { self.check_ident_token(cx, UnderMacro(true), ident, ""); } - } else if let Some((ident, token::IdentIsRaw::No)) = token.lifetime() { + } else if let Some((ident, token::IdentKind::Normal)) = token.lifetime() { self.check_ident_token( cx, UnderMacro(true), @@ -2165,8 +2160,8 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { // Due to macros, there might be several predicates with the same span // and we only want to suggest removing them once. - lint_spans.sort_unstable(); - lint_spans.dedup(); + lint_spans.sort_unstable_by_key(|span| span.lo_hi()); + lint_spans.dedup_by_key(|span| span.lo_hi()); cx.emit_span_lint( EXPLICIT_OUTLIVES_REQUIREMENTS, @@ -2654,7 +2649,7 @@ impl<'tcx> LateLintPass<'tcx> for DerefNullPtr { match &expr.kind { hir::ExprKind::Cast(expr, ty) => { - if let hir::TyKind::Ptr(_) = ty.kind { + if let hir::TyKind::Ptr(..) = ty.kind { return is_zero(expr) || is_null_ptr(cx, expr); } } diff --git a/compiler/rustc_lint/src/c_void_returns.rs b/compiler/rustc_lint/src/c_void_returns.rs index 1358dcea262e8..da2c52d019923 100644 --- a/compiler/rustc_lint/src/c_void_returns.rs +++ b/compiler/rustc_lint/src/c_void_returns.rs @@ -1,6 +1,6 @@ use rustc_abi::ExternAbi; +use rustc_attr_ir::lang_items::LangItem; use rustc_hir as hir; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def::Res; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::FnKind; diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index bd8e059bf0f95..15c60c920ce37 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -521,7 +521,7 @@ pub trait LintContext { &self, lint: &'static Lint, span: Option, - decorate: impl for<'a> Diagnostic<'a, ()>, + decorate: impl for<'a> Diagnostic<'a>, ); /// Emit a lint at `span` from a lint struct (some type that implements `Diagnostic`, @@ -531,7 +531,7 @@ pub trait LintContext { &self, lint: &'static Lint, span: S, - decorator: impl for<'a> Diagnostic<'a, ()>, + decorator: impl for<'a> Diagnostic<'a>, ) { self.opt_span_lint(lint, Some(span), decorator); } @@ -547,17 +547,7 @@ pub trait LintContext { /// retrieved from the current lint pass. Buffered or manually created ids can /// cause ICEs. fn fulfill_expectation(&self, expectation: Self::LintExpectationId) { - // We need to make sure that submitted expectation ids are correctly fulfilled suppressed - // and stored between compilation sessions. To not manually do these steps, we simply create - // a dummy diagnostic and emit it as usual, which will be suppressed and stored like a - // normal expected lint diagnostic. - self.sess() - .dcx() - .struct_expect( - "this is a dummy diagnostic, to submit and store an expectation", - expectation.into(), - ) - .emit(); + self.sess().dcx().fulfill_expectation(expectation); } } @@ -595,7 +585,7 @@ impl<'tcx> LintContext for LateContext<'tcx> { &self, lint: &'static Lint, span: Option, - decorate: impl for<'a> Diagnostic<'a, ()>, + decorate: impl for<'a> Diagnostic<'a>, ) { let hir_id = self.last_node_with_lint_attrs; @@ -622,7 +612,7 @@ impl LintContext for EarlyContext<'_> { &self, lint: &'static Lint, span: Option, - decorator: impl for<'a> Diagnostic<'a, ()>, + decorator: impl for<'a> Diagnostic<'a>, ) { self.builder.opt_span_lint(lint, span.map(|s| s.into()), decorator) } @@ -842,7 +832,10 @@ impl<'tcx> LateContext<'tcx> { /// be used for pretty-printing HIR by rustc_hir_pretty. pub fn precedence(&self, expr: &hir::Expr<'_>) -> ExprPrecedence { let has_attr = |id: hir::HirId| -> bool { - self.tcx.hir_attrs(id).iter().any(hir::Attribute::is_prefix_attr_for_suggestions) + self.tcx + .hir_attrs(id) + .iter() + .any(rustc_attr_ir::Attribute::is_prefix_attr_for_suggestions) }; expr.precedence(&has_attr) } diff --git a/compiler/rustc_lint/src/dangling.rs b/compiler/rustc_lint/src/dangling.rs index c272dd4496984..dbe20dbef3c87 100644 --- a/compiler/rustc_lint/src/dangling.rs +++ b/compiler/rustc_lint/src/dangling.rs @@ -1,9 +1,10 @@ use rustc_ast::visit::{visit_opt, walk_list}; -use rustc_hir::attrs::lang_items::LangItem; +use rustc_attr_ir::find_attr; +use rustc_attr_ir::lang_items::LangItem; use rustc_hir::def::Res; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; -use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, TyKind, find_attr}; +use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, TyKind}; use rustc_lint_defs::{declare_lint, impl_lint_pass}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::{Span, sym}; @@ -100,7 +101,7 @@ impl<'tcx> LateLintPass<'tcx> for DanglingPointers { DanglingPointerSearcher { cx, inside_call_args: false }.visit_body(body); if let FnRetTy::Return(ret_ty) = &fn_decl.output - && let TyKind::Ptr(_) = ret_ty.kind + && let TyKind::Ptr(..) = ret_ty.kind { // get the return type of the function or closure let ty = match cx.tcx.type_of(def_id).instantiate_identity().skip_norm_wip().kind() { diff --git a/compiler/rustc_lint/src/default_could_be_derived.rs b/compiler/rustc_lint/src/default_could_be_derived.rs index eba5efa25546b..6b20d6ffc41fe 100644 --- a/compiler/rustc_lint/src/default_could_be_derived.rs +++ b/compiler/rustc_lint/src/default_could_be_derived.rs @@ -164,8 +164,8 @@ struct WrongDefaultImpl<'a, 'tcx> { impl_span: Span, } -impl<'a, 'b, 'tcx> Diagnostic<'a, ()> for WrongDefaultImpl<'b, 'tcx> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { +impl<'a, 'b, 'tcx> Diagnostic<'a> for WrongDefaultImpl<'b, 'tcx> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let Self { tcx, type_def_id, orig_fields, fields, impl_span } = self; let mut diag = Diag::new(dcx, level, "`Default` impl doesn't use the declared default field values"); diff --git a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs index 165c8f58a09f9..506ab027d8771 100644 --- a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs +++ b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs @@ -1,5 +1,5 @@ +use rustc_attr_ir::lang_items::LangItem; use rustc_hir as hir; -use rustc_hir::attrs::lang_items::LangItem; use rustc_lint_defs::{declare_lint, declare_lint_pass}; use rustc_middle::ty; use rustc_span::{Ident, sym}; diff --git a/compiler/rustc_lint/src/diagnostics.rs b/compiler/rustc_lint/src/diagnostics.rs index d96cadaca87eb..d9fc1703f8c03 100644 --- a/compiler/rustc_lint/src/diagnostics.rs +++ b/compiler/rustc_lint/src/diagnostics.rs @@ -42,7 +42,7 @@ pub(crate) enum OverruledAttributeSub { } impl Subdiagnostic for OverruledAttributeSub { - fn add_to_diag(self, diag: &mut Diag<'_, G>) { + fn add_to_diag(self, diag: &mut Diag<'_>) { match self { OverruledAttributeSub::DefaultSource { id } => { diag.note(msg!("`forbid` lint level is the default for {$id}")); @@ -308,8 +308,8 @@ pub(crate) struct BuiltinMissingDebugImpl<'a> { } // Needed for def_path_str -impl<'a> Diagnostic<'a, ()> for BuiltinMissingDebugImpl<'_> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { +impl<'a> Diagnostic<'a> for BuiltinMissingDebugImpl<'_> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let Self { tcx, def_id } = self; Diag::new( dcx, @@ -368,8 +368,8 @@ pub(crate) struct BuiltinUngatedAsyncFnTrackCaller<'a> { pub session: &'a Session, } -impl<'a> Diagnostic<'a, ()> for BuiltinUngatedAsyncFnTrackCaller<'_> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { +impl<'a> Diagnostic<'a> for BuiltinUngatedAsyncFnTrackCaller<'_> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let mut diag = Diag::new(dcx, level, "`#[track_caller]` on async functions is a no-op") .with_span_label(self.label, "this function will not propagate the caller location"); rustc_session::diagnostics::add_feature_diagnostics( @@ -412,8 +412,8 @@ pub(crate) struct BuiltinTypeAliasBounds<'hir> { pub ty: Option<&'hir hir::Ty<'hir>>, } -impl<'a> Diagnostic<'a, ()> for BuiltinTypeAliasBounds<'_> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { +impl<'a> Diagnostic<'a> for BuiltinTypeAliasBounds<'_> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let mut diag = Diag::new(dcx, level, if self.in_where_clause { msg!("where clauses on type aliases are not enforced") } else { @@ -615,8 +615,8 @@ pub(crate) struct BuiltinUnpermittedTypeInit<'a> { pub tcx: TyCtxt<'a>, } -impl<'a> Diagnostic<'a, ()> for BuiltinUnpermittedTypeInit<'_> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { +impl<'a> Diagnostic<'a> for BuiltinUnpermittedTypeInit<'_> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let mut diag = Diag::new(dcx, level, self.msg) .with_arg("ty", self.ty) .with_span_label(self.label, msg!("this code causes undefined behavior when executed")); @@ -638,7 +638,7 @@ pub(crate) struct BuiltinUnpermittedTypeInitSub { } impl Subdiagnostic for BuiltinUnpermittedTypeInitSub { - fn add_to_diag(self, diag: &mut Diag<'_, G>) { + fn add_to_diag(self, diag: &mut Diag<'_>) { let mut err = self.err; loop { if let Some(span) = err.span { @@ -689,7 +689,7 @@ pub(crate) struct BuiltinClashingExternSub<'a> { } impl Subdiagnostic for BuiltinClashingExternSub<'_> { - fn add_to_diag(self, diag: &mut Diag<'_, G>) { + fn add_to_diag(self, diag: &mut Diag<'_>) { let mut expected_str = DiagStyledString::new(); expected_str.push(self.expected.fn_sig(self.tcx).to_string(), false); let mut found_str = DiagStyledString::new(); @@ -944,8 +944,7 @@ pub(crate) enum RedefiningRuntimeSymbolsDiag<'tcx> { "invalid definition of the runtime `{$symbol_name}` symbol used by the standard library" )] #[note( - "expected `{$expected_fn_sig}` (for the current target) - found `{$found_fn_sig}`" + "expected `{$expected_fn_sig}` (for the current target)\n{\" \"}found `{$found_fn_sig}`" )] #[help( "either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = \"{$symbol_name}\")]`, or `#[link_name = \"{$symbol_name}\"]`" @@ -955,8 +954,7 @@ pub(crate) enum RedefiningRuntimeSymbolsDiag<'tcx> { "suspicious definition of the runtime `{$symbol_name}` symbol used by the standard library" )] #[note( - "expected `{$expected_fn_sig}` (for the current target) - found `{$found_fn_sig}`" + "expected `{$expected_fn_sig}` (for the current target)\n{\" \"}found `{$found_fn_sig}`" )] #[help( "either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = \"{$symbol_name}\")]`, or `#[link_name = \"{$symbol_name}\"]`" @@ -1040,9 +1038,10 @@ pub(crate) struct ForgetCopyDiag<'a> { #[derive(Diagnostic)] #[diag( - "calls to `std::mem::drop` with `std::mem::ManuallyDrop` instead of the inner value does nothing" + "calls to `{$krate}::mem::drop` with `{$krate}::mem::ManuallyDrop` instead of the inner value does nothing" )] pub(crate) struct UndroppedManuallyDropsDiag<'a> { + pub krate: &'static str, pub arg_ty: Ty<'a>, #[label("argument has type `{$arg_ty}`")] pub label: Span, @@ -1052,11 +1051,12 @@ pub(crate) struct UndroppedManuallyDropsDiag<'a> { #[derive(Subdiagnostic)] #[multipart_suggestion( - "use `std::mem::ManuallyDrop::into_inner` to get the inner value", + "use `{$krate}::mem::ManuallyDrop::into_inner` to get the inner value", applicability = "machine-applicable" )] pub(crate) struct UndroppedManuallyDropsSuggestion { - #[suggestion_part(code = "std::mem::ManuallyDrop::into_inner(")] + pub krate: &'static str, + #[suggestion_part(code = "{krate}::mem::ManuallyDrop::into_inner(")] pub start_span: Span, #[suggestion_part(code = ")")] pub end_span: Span, @@ -1064,9 +1064,10 @@ pub(crate) struct UndroppedManuallyDropsSuggestion { #[derive(Diagnostic)] #[diag( - "calls to `drop_in_place` with a pointer to a `std::mem::ManuallyDrop` instead of the inner value does nothing" + "calls to `drop_in_place` with a pointer to a `{$krate}::mem::ManuallyDrop` instead of the inner value does nothing" )] pub(crate) struct UndroppedManuallyDropsInPlaceDiag<'a> { + pub krate: &'static str, pub arg_ty: Ty<'a>, #[label("argument has type `{$arg_ty}`")] pub label: Span, @@ -1076,11 +1077,12 @@ pub(crate) struct UndroppedManuallyDropsInPlaceDiag<'a> { #[derive(Subdiagnostic)] #[multipart_suggestion( - "use `std::mem::ManuallyDrop::drop` to drop the inner value", + "use `{$krate}::mem::ManuallyDrop::drop` to drop the inner value", applicability = "maybe-incorrect" )] pub(crate) struct UndroppedManuallyDropsInPlaceSuggestion { - #[suggestion_part(code = "std::mem::ManuallyDrop::drop(&mut *")] + pub krate: &'static str, + #[suggestion_part(code = "{krate}::mem::ManuallyDrop::drop(&mut *")] pub start_span: Span, #[suggestion_part(code = ")")] pub end_span: Span, @@ -1234,9 +1236,11 @@ pub(crate) struct QueryUntracked { pub(crate) struct SpanUseEqCtxtDiag; #[derive(Diagnostic)] -#[diag("using `Symbol::intern` on a string literal")] +#[diag("using `{$fn_name}` on a string literal")] #[help("consider adding the symbol to `compiler/rustc_span/src/symbol.rs`")] -pub(crate) struct SymbolInternStringLiteralDiag; +pub(crate) struct SymbolInternStringLiteralDiag { + pub fn_name: &'static str, +} #[derive(Diagnostic)] #[diag("usage of `ty::TyKind::`")] @@ -1363,7 +1367,7 @@ pub(crate) struct NonBindingLetSub { } impl Subdiagnostic for NonBindingLetSub { - fn add_to_diag(self, diag: &mut Diag<'_, G>) { + fn add_to_diag(self, diag: &mut Diag<'_>) { let can_suggest_binding = self.drop_fn_start_end.is_some() || !self.is_assign_desugar; if can_suggest_binding { @@ -1639,8 +1643,8 @@ pub(crate) struct NonFmtPanicUnused { } // Used because of two suggestions based on one Option -impl<'a> Diagnostic<'a, ()> for NonFmtPanicUnused { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { +impl<'a> Diagnostic<'a> for NonFmtPanicUnused { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let mut diag = Diag::new(dcx, level, msg!( "panic message contains {$count -> [one] an unused @@ -1653,7 +1657,7 @@ impl<'a> Diagnostic<'a, ()> for NonFmtPanicUnused { .with_arg("count", self.count) .with_note(msg!("this message is not used as a format string when given without arguments, but will be in Rust 2021")); if let Some(span) = self.suggestion { - diag.span_suggestion( + diag.span_suggestion_verbose( span.shrink_to_hi(), msg!( "add the missing {$count -> @@ -1664,7 +1668,7 @@ impl<'a> Diagnostic<'a, ()> for NonFmtPanicUnused { ", ...", Applicability::HasPlaceholders, ); - diag.span_suggestion( + diag.span_suggestion_verbose( span.shrink_to_lo(), msg!(r#"or add a "{"{"}{"}"}" format string to use the message literally"#), "\"{}\", ", @@ -1740,7 +1744,7 @@ pub(crate) enum NonSnakeCaseDiagSub { } impl Subdiagnostic for NonSnakeCaseDiagSub { - fn add_to_diag(self, diag: &mut Diag<'_, G>) { + fn add_to_diag(self, diag: &mut Diag<'_>) { match self { NonSnakeCaseDiagSub::Label { span } => { diag.span_label(span, msg!("should have a snake_case name")); @@ -1876,8 +1880,8 @@ pub(crate) enum NonLocalDefinitionsDiag { }, } -impl<'a> Diagnostic<'a, ()> for NonLocalDefinitionsDiag { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { +impl<'a> Diagnostic<'a> for NonLocalDefinitionsDiag { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let mut diag = Diag::new(dcx, level, ""); match self { NonLocalDefinitionsDiag::Impl { @@ -1912,7 +1916,7 @@ impl<'a> Diagnostic<'a, ()> for NonLocalDefinitionsDiag { if let Some(const_anon) = const_anon { diag.note(msg!("items in an anonymous const item (`const _: () = {\"{\"} ... {\"}\"}`) are treated as in the same scope as the anonymous const's declaration for the purpose of this lint")); if let Some(const_anon) = const_anon { - diag.span_suggestion( + diag.span_suggestion_verbose( const_anon, msg!("use a const-anon item to suppress this lint"), "_", @@ -2046,8 +2050,8 @@ pub(crate) struct DropTraitConstraintsDiag<'a> { } // Needed for def_path_str -impl<'a> Diagnostic<'a, ()> for DropTraitConstraintsDiag<'_> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { +impl<'a> Diagnostic<'a> for DropTraitConstraintsDiag<'_> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { Diag::new(dcx, level, msg!("bounds on `{$clause}` are most likely incorrect, consider instead using `{$needs_drop}` to detect whether a type can be trivially dropped")) .with_arg("clause", self.clause) .with_arg("needs_drop", self.tcx.def_path_str(self.def_id)) @@ -2060,8 +2064,8 @@ pub(crate) struct DropGlue<'a> { } // Needed for def_path_str -impl<'a> Diagnostic<'a, ()> for DropGlue<'_> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { +impl<'a> Diagnostic<'a> for DropGlue<'_> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { Diag::new(dcx, level, msg!("types that do not implement `Drop` can still have drop glue, consider instead using `{$needs_drop}` to detect whether a type is trivially dropped")) .with_arg("needs_drop", self.tcx.def_path_str(self.def_id)) } @@ -2074,9 +2078,6 @@ impl<'a> Diagnostic<'a, ()> for DropGlue<'_> { #[note( "exposed provenance semantics can be used to create a pointer based on some previously exposed provenance" )] -#[help( - "if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut`" -)] #[help( "for more information about transmute, see " )] @@ -2084,34 +2085,46 @@ impl<'a> Diagnostic<'a, ()> for DropGlue<'_> { "for more information about exposed provenance, see " )] pub(crate) struct IntegerToPtrTransmutes<'tcx> { + #[subdiagnostic] + pub without_prov: Option, #[subdiagnostic] pub suggestion: Option>, } +#[derive(Subdiagnostic)] +#[help( + "if you truly mean to create a pointer without provenance, use `{$krate}::ptr::without_provenance_mut`" +)] +pub(crate) struct IntegerToPtrWithoutProvHelp { + pub krate: &'static str, +} + #[derive(Subdiagnostic)] pub(crate) enum IntegerToPtrTransmutesSuggestion<'tcx> { #[multipart_suggestion( - "use `std::ptr::with_exposed_provenance{$suffix}` instead to use a previously exposed provenance", + "use `{$krate}::ptr::with_exposed_provenance{$suffix}` instead to use a previously exposed provenance", applicability = "machine-applicable", style = "verbose" )] ToPtr { + krate: &'static str, dst: Ty<'tcx>, suffix: &'static str, - #[suggestion_part(code = "std::ptr::with_exposed_provenance{suffix}::<{dst}>(")] + #[suggestion_part(code = "{krate}::ptr::with_exposed_provenance{suffix}::<{dst}>(")] start_call: Span, }, #[multipart_suggestion( - "use `std::ptr::with_exposed_provenance{$suffix}` instead to use a previously exposed provenance", + "use `{$krate}::ptr::with_exposed_provenance{$suffix}` instead to use a previously exposed provenance", applicability = "machine-applicable", style = "verbose" )] ToRef { + krate: &'static str, dst: Ty<'tcx>, suffix: &'static str, ref_mutbl: &'static str, #[suggestion_part( - code = "&{ref_mutbl}*std::ptr::with_exposed_provenance{suffix}::<{dst}>(" + code = "&{ref_mutbl}*{krate}::ptr::with_exposed_provenance{suffix}::<{dst}>(" )] start_call: Span, }, @@ -2348,25 +2361,30 @@ pub(crate) enum AmbiguousWidePointerComparisons<'a> { #[diag( "ambiguous wide pointer comparison, the comparison includes metadata which may not be expected" )] - #[help("use explicit `std::ptr::eq` method to compare metadata and addresses")] - #[help("use `std::ptr::addr_eq` or untyped pointers to only compare their addresses")] - Spanless, + #[help("use explicit `{$krate}::ptr::eq` method to compare metadata and addresses")] + #[help("use `{$krate}::ptr::addr_eq` or untyped pointers to only compare their addresses")] + Spanless { krate: &'static str }, + #[diag( + "ambiguous wide pointer comparison, the comparison includes metadata which may not be expected" + )] + Warn, } #[derive(Subdiagnostic)] #[multipart_suggestion( - "use explicit `std::ptr::eq` method to compare metadata and addresses", + "use explicit `{$krate}::ptr::eq` method to compare metadata and addresses", style = "verbose", // FIXME(#53934): make machine-applicable again applicability = "maybe-incorrect" )] pub(crate) struct AmbiguousWidePointerComparisonsAddrMetadataSuggestion<'a> { + pub krate: &'static str, pub ne: &'a str, pub deref_left: &'a str, pub deref_right: &'a str, pub l_modifiers: &'a str, pub r_modifiers: &'a str, - #[suggestion_part(code = "{ne}std::ptr::eq({deref_left}")] + #[suggestion_part(code = "{ne}{krate}::ptr::eq({deref_left}")] pub left: Span, #[suggestion_part(code = "{l_modifiers}, {deref_right}")] pub middle: Span, @@ -2376,18 +2394,19 @@ pub(crate) struct AmbiguousWidePointerComparisonsAddrMetadataSuggestion<'a> { #[derive(Subdiagnostic)] #[multipart_suggestion( - "use `std::ptr::addr_eq` or untyped pointers to only compare their addresses", + "use `{$krate}::ptr::addr_eq` or untyped pointers to only compare their addresses", style = "verbose", // FIXME(#53934): make machine-applicable again applicability = "maybe-incorrect" )] pub(crate) struct AmbiguousWidePointerComparisonsAddrSuggestion<'a> { + pub(crate) krate: &'static str, pub(crate) ne: &'a str, pub(crate) deref_left: &'a str, pub(crate) deref_right: &'a str, pub(crate) l_modifiers: &'a str, pub(crate) r_modifiers: &'a str, - #[suggestion_part(code = "{ne}std::ptr::addr_eq({deref_left}")] + #[suggestion_part(code = "{ne}{krate}::ptr::addr_eq({deref_left}")] pub(crate) left: Span, #[suggestion_part(code = "{l_modifiers}, {deref_right}")] pub(crate) middle: Span, @@ -2470,15 +2489,16 @@ pub(crate) enum UnpredictableFunctionPointerComparisons<'a, 'tcx> { #[derive(Subdiagnostic)] pub(crate) enum UnpredictableFunctionPointerComparisonsSuggestion<'a, 'tcx> { #[multipart_suggestion( - "refactor your code, or use `std::ptr::fn_addr_eq` to suppress the lint", + "refactor your code, or use `{$krate}::ptr::fn_addr_eq` to suppress the lint", style = "verbose", applicability = "maybe-incorrect" )] FnAddrEq { + krate: &'static str, ne: &'a str, deref_left: &'a str, deref_right: &'a str, - #[suggestion_part(code = "{ne}std::ptr::fn_addr_eq({deref_left}")] + #[suggestion_part(code = "{ne}{krate}::ptr::fn_addr_eq({deref_left}")] left: Span, #[suggestion_part(code = ", {deref_right}")] middle: Span, @@ -2486,16 +2506,17 @@ pub(crate) enum UnpredictableFunctionPointerComparisonsSuggestion<'a, 'tcx> { right: Span, }, #[multipart_suggestion( - "refactor your code, or use `std::ptr::fn_addr_eq` to suppress the lint", + "refactor your code, or use `{$krate}::ptr::fn_addr_eq` to suppress the lint", style = "verbose", applicability = "maybe-incorrect" )] FnAddrEqWithCast { + krate: &'static str, ne: &'a str, deref_left: &'a str, deref_right: &'a str, fn_sig: rustc_middle::ty::PolyFnSig<'tcx>, - #[suggestion_part(code = "{ne}std::ptr::fn_addr_eq({deref_left}")] + #[suggestion_part(code = "{ne}{krate}::ptr::fn_addr_eq({deref_left}")] left: Span, #[suggestion_part(code = ", {deref_right}")] middle: Span, @@ -2514,8 +2535,8 @@ pub(crate) struct ImproperCTypes<'a> { } // Used because of the complexity of Option, DiagMessage, and Option -impl<'a> Diagnostic<'a, ()> for ImproperCTypes<'_> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { +impl<'a> Diagnostic<'a> for ImproperCTypes<'_> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let mut diag = Diag::new( dcx, level, @@ -2688,11 +2709,32 @@ pub(crate) enum UnusedDefSuggestion { #[suggestion_part(code = ";")] after_span: Span, }, + #[suggestion( + "use `let _unused = ...` to ignore the resulting value without dropping it immediately", + style = "verbose", + code = "let _unused = ", + applicability = "maybe-incorrect" + )] + BindingExpr { + #[primary_span] + span: Span, + }, + #[multipart_suggestion( + "use `let _unused = ...` to ignore the resulting value without dropping it immediately", + style = "verbose", + applicability = "maybe-incorrect" + )] + BindingBlockTailExpr { + #[suggestion_part(code = "let _unused = ")] + before_span: Span, + #[suggestion_part(code = ";")] + after_span: Span, + }, } // Needed because of def_path_str -impl<'a> Diagnostic<'a, ()> for UnusedDef<'_, '_> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { +impl<'a> Diagnostic<'a> for UnusedDef<'_, '_> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let mut diag = Diag::new(dcx, level, msg!("unused {$pre}`{$def}`{$post} that must be used")) .with_arg("pre", self.pre) @@ -2778,8 +2820,8 @@ pub(crate) struct AsyncFnInTraitDiag { pub sugg: Option>, } -impl<'a> Diagnostic<'a, ()> for AsyncFnInTraitDiag { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { +impl<'a> Diagnostic<'a> for AsyncFnInTraitDiag { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let mut diag = Diag::new( dcx, level, @@ -2924,8 +2966,8 @@ pub(crate) struct MismatchedLifetimeSyntaxes { pub suggestions: Vec, } -impl<'a, G> Diagnostic<'a, G> for MismatchedLifetimeSyntaxes { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { +impl<'a> Diagnostic<'a> for MismatchedLifetimeSyntaxes { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let counts = self.inputs.len() + self.outputs.len(); let message = match counts { LifetimeSyntaxCategories { hidden: 0, elided: 0, named: 0 } => { @@ -3037,7 +3079,7 @@ impl MismatchedLifetimeSyntaxesSuggestion { } impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion { - fn add_to_diag(self, diag: &mut Diag<'_, G>) { + fn add_to_diag(self, diag: &mut Diag<'_>) { use MismatchedLifetimeSyntaxesSuggestion::*; let style = |optional_alternative| { diff --git a/compiler/rustc_lint/src/disallowed_pass_by_ref.rs b/compiler/rustc_lint/src/disallowed_pass_by_ref.rs index 92350ecf60a21..71d81e4512ca2 100644 --- a/compiler/rustc_lint/src/disallowed_pass_by_ref.rs +++ b/compiler/rustc_lint/src/disallowed_pass_by_ref.rs @@ -1,5 +1,6 @@ +use rustc_attr_ir::find_attr; use rustc_hir::def::Res; -use rustc_hir::{self as hir, AmbigArg, GenericArg, PathSegment, QPath, TyKind, find_attr}; +use rustc_hir::{self as hir, AmbigArg, GenericArg, PathSegment, QPath, TyKind}; use rustc_lint_defs::{declare_lint_pass, declare_tool_lint}; use rustc_middle::ty; @@ -22,7 +23,7 @@ declare_lint_pass!(DisallowedPassByRef => [DISALLOWED_PASS_BY_REF]); impl<'tcx> LateLintPass<'tcx> for DisallowedPassByRef { fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx, AmbigArg>) { match &ty.kind { - TyKind::Ref(_, hir::MutTy { ty: inner_ty, mutbl: hir::Mutability::Not }) => { + TyKind::Ref(_, inner_ty, hir::Mutability::Not) => { if cx.tcx.trait_impl_of_assoc(ty.hir_id.owner.to_def_id()).is_some() { return; } diff --git a/compiler/rustc_lint/src/drop_forget_useless.rs b/compiler/rustc_lint/src/drop_forget_useless.rs index b952b62ada314..9689f6a053275 100644 --- a/compiler/rustc_lint/src/drop_forget_useless.rs +++ b/compiler/rustc_lint/src/drop_forget_useless.rs @@ -9,6 +9,7 @@ use crate::diagnostics::{ UndroppedManuallyDropsInPlaceSuggestion, UndroppedManuallyDropsSuggestion, UseLetUnderscoreIgnoreSuggestion, }; +use crate::utils::std_or_core; use crate::{LateContext, LateLintPass, LintContext}; declare_lint! { @@ -252,15 +253,18 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetUseless { } sym::mem_drop if let ty::Adt(adt, _) = arg_ty.kind() - && adt.is_manually_drop() => + && adt.is_manually_drop() + && let Some(krate) = std_or_core(cx) => { cx.emit_span_lint( UNDROPPED_MANUALLY_DROPS, expr.span, UndroppedManuallyDropsDiag { + krate, arg_ty, label: arg.span, suggestion: UndroppedManuallyDropsSuggestion { + krate, start_span: arg.span.shrink_to_lo(), end_span: arg.span.shrink_to_hi(), }, @@ -270,15 +274,18 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetUseless { sym::ptr_drop_in_place | sym::ptr_drop_in_place_self if let &ty::RawPtr(inner_ty, _mutbl) = arg_ty.kind() && let ty::Adt(adt, _) = inner_ty.kind() - && adt.is_manually_drop() => + && adt.is_manually_drop() + && let Some(krate) = std_or_core(cx) => { cx.emit_span_lint( UNDROPPED_MANUALLY_DROPS, expr.span, UndroppedManuallyDropsInPlaceDiag { + krate, arg_ty, label: arg.span, suggestion: UndroppedManuallyDropsInPlaceSuggestion { + krate, start_span: expr.span.shrink_to_lo().until(arg.span.shrink_to_lo()), end_span: arg.span.shrink_to_hi().until(expr.span.shrink_to_hi()), }, diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 91259b010178d..95a925d1c1455 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -6,13 +6,13 @@ use rustc_session::Session; pub struct DiagAndSess<'sess> { pub callback: Box< - dyn for<'b> FnOnce(DiagCtxtHandle<'b>, Level, &dyn Any) -> Diag<'b, ()> + DynSend + 'static, + dyn for<'b> FnOnce(DiagCtxtHandle<'b>, Level, &dyn Any) -> Diag<'b> + DynSend + 'static, >, pub sess: &'sess Session, } -impl<'a> Diagnostic<'a, ()> for DiagAndSess<'_> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { +impl<'a> Diagnostic<'a> for DiagAndSess<'_> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { (self.callback)(dcx, level, self.sess) } } diff --git a/compiler/rustc_lint/src/for_loops_over_fallibles.rs b/compiler/rustc_lint/src/for_loops_over_fallibles.rs index df0455470feb7..bb0f2a2793ab8 100644 --- a/compiler/rustc_lint/src/for_loops_over_fallibles.rs +++ b/compiler/rustc_lint/src/for_loops_over_fallibles.rs @@ -1,6 +1,6 @@ use hir::{Expr, Pat}; +use rustc_attr_ir::lang_items::LangItem; use rustc_hir as hir; -use rustc_hir::attrs::lang_items::LangItem; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::ObligationCause; use rustc_lint_defs::{declare_lint, declare_lint_pass}; diff --git a/compiler/rustc_lint/src/foreign_modules.rs b/compiler/rustc_lint/src/foreign_modules.rs index 7dae504804752..e6092a1497a0f 100644 --- a/compiler/rustc_lint/src/foreign_modules.rs +++ b/compiler/rustc_lint/src/foreign_modules.rs @@ -1,8 +1,8 @@ use rustc_abi::FIRST_VARIANT; +use rustc_attr_ir::find_attr; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_hir as hir; use rustc_hir::def::DefKind; -use rustc_hir::find_attr; use rustc_lint_defs::declare_lint; use rustc_middle::query::Providers; use rustc_middle::ty::{self, AdtDef, Instance, Ty, TyCtxt}; diff --git a/compiler/rustc_lint/src/gpukernel_abi.rs b/compiler/rustc_lint/src/gpukernel_abi.rs index 64f0744dffed4..05f41cd6fdf48 100644 --- a/compiler/rustc_lint/src/gpukernel_abi.rs +++ b/compiler/rustc_lint/src/gpukernel_abi.rs @@ -1,7 +1,8 @@ use std::iter; use rustc_abi::ExternAbi; -use rustc_hir::{self as hir, find_attr}; +use rustc_attr_ir::find_attr; +use rustc_hir as hir; use rustc_lint_defs::{declare_lint, declare_lint_pass}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_span::Span; diff --git a/compiler/rustc_lint/src/if_let_rescope.rs b/compiler/rustc_lint/src/if_let_rescope.rs index 1cd701db7d10e..c29cff23acee1 100644 --- a/compiler/rustc_lint/src/if_let_rescope.rs +++ b/compiler/rustc_lint/src/if_let_rescope.rs @@ -324,7 +324,7 @@ struct IfLetRescopeRewrite { } impl Subdiagnostic for IfLetRescopeRewrite { - fn add_to_diag(self, diag: &mut Diag<'_, G>) { + fn add_to_diag(self, diag: &mut Diag<'_>) { let mut suggestions = vec![]; for match_head in self.match_heads { match match_head { diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs index f8e4c70d193d6..9d7d0a0df475f 100644 --- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs +++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs @@ -452,12 +452,12 @@ struct ImplTraitOvercapturesLint<'tcx> { suggestion: Option, } -impl<'a> Diagnostic<'a, ()> for ImplTraitOvercapturesLint<'_> { +impl<'a> Diagnostic<'a> for ImplTraitOvercapturesLint<'_> { fn into_diag( self, dcx: rustc_errors::DiagCtxtHandle<'a>, level: rustc_errors::Level, - ) -> rustc_errors::Diag<'a, ()> { + ) -> rustc_errors::Diag<'a> { let mut diag = rustc_errors::Diag::new( dcx, level, diff --git a/compiler/rustc_lint/src/interior_mutable_consts.rs b/compiler/rustc_lint/src/interior_mutable_consts.rs index ff48749a2e6a9..4d0557d5e9b54 100644 --- a/compiler/rustc_lint/src/interior_mutable_consts.rs +++ b/compiler/rustc_lint/src/interior_mutable_consts.rs @@ -1,5 +1,6 @@ +use rustc_attr_ir::find_attr; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Expr, ExprKind, ItemKind, Node, find_attr}; +use rustc_hir::{Expr, ExprKind, ItemKind, Node}; use rustc_lint_defs::{declare_lint, declare_lint_pass}; use rustc_middle::ty::adjustment::Adjust; diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 9746b52dda41e..b32fa2515238a 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -3,10 +3,11 @@ use rustc_ast as ast; use rustc_ast::{Pat, PatKind, Path}; +use rustc_attr_ir::find_attr; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::def_id::DefId; -use rustc_hir::{Expr, ExprKind, HirId, find_attr}; +use rustc_hir::{Expr, ExprKind, HirId}; use rustc_lint_defs::{declare_lint_pass, declare_tool_lint}; use rustc_middle::ty::{self, ClausePolarity, GenericArgsRef}; use rustc_span::hygiene::{ExpnKind, MacroKind}; @@ -41,6 +42,7 @@ impl LateLintPass<'_> for DefaultHashTypes { if matches!( cx.tcx.hir_node(hir_id), hir::Node::Item(hir::Item { kind: hir::ItemKind::Use(..), .. }) + | hir::Node::NestedUseTree(_) ) { // Don't lint imports, only actual usages. return; @@ -407,51 +409,79 @@ impl<'tcx> LateLintPass<'tcx> for TypeIr { } fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { - let rustc_hir::ItemKind::Use(path, kind) = item.kind else { return }; - - let is_mod_inherent = |res: Res| { - res.opt_def_id() - .is_some_and(|def_id| cx.tcx.is_diagnostic_item(sym::type_ir_inherent, def_id)) + let rustc_hir::ItemKind::Use(tree) = item.kind else { + return; }; - // Path segments except for the final. - if let Some(seg) = path.segments.iter().find(|seg| is_mod_inherent(seg.res)) { - cx.emit_span_lint(USAGE_OF_TYPE_IR_INHERENT, seg.ident.span, TypeIrInherentUsage); - } - // Final path resolutions, like `use rustc_type_ir::inherent` - else if let Some(type_ns) = path.res.type_ns - && is_mod_inherent(type_ns) - { - cx.emit_span_lint( - USAGE_OF_TYPE_IR_INHERENT, - path.segments.last().unwrap().ident.span, - TypeIrInherentUsage, - ); - } + check_use(cx, tree, vec![]); + + fn check_use<'tcx>( + cx: &LateContext<'_>, + tree: hir::UseTree<'tcx>, + mut prefix: Vec<&'tcx hir::PathSegment<'tcx>>, + ) { + let is_mod_inherent = |res: Res| { + res.opt_def_id() + .is_some_and(|def_id| cx.tcx.is_diagnostic_item(sym::type_ir_inherent, def_id)) + }; + + prefix.extend(tree.prefix.segments); + + match tree.kind { + rustc_hir::UseKind::Single(_) | rustc_hir::UseKind::Glob => {} + rustc_hir::UseKind::Nested { items } => { + for (nested, _, _) in items { + check_use(cx, *nested, prefix.clone()) + } + } + } - let (lo, hi, snippet) = match path.segments { - [.., penultimate, segment] if is_mod_inherent(penultimate.res) => { - (segment.ident.span, item.kind.ident().unwrap().span, "*") + // Path segments except for the final. + if let Some(seg) = prefix.iter().find(|seg| is_mod_inherent(seg.res)) { + cx.emit_span_lint(USAGE_OF_TYPE_IR_INHERENT, seg.ident.span, TypeIrInherentUsage); } - [.., segment] - if let Some(type_ns) = path.res.type_ns - && is_mod_inherent(type_ns) - && let rustc_hir::UseKind::Single(ident) = kind => + // Final path resolutions, like `use rustc_type_ir::inherent` + else if let Some(type_ns) = tree.prefix.res.type_ns + && is_mod_inherent(type_ns) { - let (lo, snippet) = - match cx.tcx.sess.source_map().span_to_snippet(path.span).as_deref() { - Ok("self") => (path.span, "*"), - _ => (segment.ident.span.shrink_to_hi(), "::*"), - }; - (lo, if segment.ident == ident { lo } else { ident.span }, snippet) + cx.emit_span_lint( + USAGE_OF_TYPE_IR_INHERENT, + prefix.last().unwrap().ident.span, + TypeIrInherentUsage, + ); } - _ => return, - }; - cx.emit_span_lint( - NON_GLOB_IMPORT_OF_TYPE_IR_INHERENT, - path.span, - NonGlobImportTypeIrInherent { suggestion: lo.eq_ctxt(hi).then(|| lo.to(hi)), snippet }, - ); + + let (lo, hi, snippet) = match prefix { + [.., penultimate, segment] + if is_mod_inherent(penultimate.res) + && let rustc_hir::UseKind::Single(ident) = tree.kind => + { + (segment.ident.span, ident.span, "*") + } + [.., segment] + if let Some(type_ns) = tree.prefix.res.type_ns + && is_mod_inherent(type_ns) + && let rustc_hir::UseKind::Single(ident) = tree.kind => + { + let (lo, snippet) = + match cx.tcx.sess.source_map().span_to_snippet(tree.prefix.span).as_deref() + { + Ok("self") => (tree.prefix.span, "*"), + _ => (segment.ident.span.shrink_to_hi(), "::*"), + }; + (lo, if segment.ident == ident { lo } else { ident.span }, snippet) + } + _ => return, + }; + cx.emit_span_lint( + NON_GLOB_IMPORT_OF_TYPE_IR_INHERENT, + tree.prefix.span, + NonGlobImportTypeIrInherent { + suggestion: lo.eq_ctxt(hi).then(|| lo.to(hi)), + snippet, + }, + ); + } } fn check_path( @@ -573,10 +603,10 @@ fn is_span_ctxt_call(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { } declare_tool_lint! { - /// The `symbol_intern_string_literal` detects `Symbol::intern` being called on a string literal + /// The `symbol_intern_string_literal` lint detects `Symbol::intern` or `Ident::from_str_and_span` being called on a string literal pub rustc::SYMBOL_INTERN_STRING_LITERAL, Allow, - "Forbid uses of string literals in `Symbol::intern`, suggesting preinterning instead", + "Forbid uses of string literals in `Symbol::intern` or `Ident::from_str_and_span`, suggesting preinterning instead", report_in_external_macro: true } @@ -584,18 +614,30 @@ declare_lint_pass!(SymbolInternStringLiteral => [SYMBOL_INTERN_STRING_LITERAL]); impl<'tcx> LateLintPass<'tcx> for SymbolInternStringLiteral { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { - if let hir::ExprKind::Call(path, [arg]) = expr.kind + if let hir::ExprKind::Call(path, args) = expr.kind && let hir::ExprKind::Path(ref qpath) = path.kind && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() - && cx.tcx.is_diagnostic_item(sym::SymbolIntern, def_id) - && let hir::ExprKind::Lit(kind) = arg.kind - && let rustc_ast::LitKind::Str(_, _) = kind.node { - cx.emit_span_lint( - SYMBOL_INTERN_STRING_LITERAL, - kind.span, - SymbolInternStringLiteralDiag, - ); + let (arg, fn_name) = if cx.tcx.is_diagnostic_item(sym::SymbolIntern, def_id) + && let [arg] = args + { + (arg, "Symbol::intern") + } else if cx.tcx.is_diagnostic_item(sym::ident_from_str_and_span, def_id) + && let [arg, _] = args + { + (arg, "Ident::from_str_and_span") + } else { + return; + }; + if let hir::ExprKind::Lit(kind) = arg.kind + && let rustc_ast::LitKind::Str(_, _) = kind.node + { + cx.emit_span_lint( + SYMBOL_INTERN_STRING_LITERAL, + kind.span, + SymbolInternStringLiteralDiag { fn_name }, + ); + } } } } diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index 0679d8abe3d78..93ce4118c0ab7 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -13,6 +13,7 @@ use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::Session; use rustc_span::Span; +use rustc_span::def_id::CRATE_MOD_ID; use tracing::debug; use crate::passes::LateLintPassObject; @@ -75,9 +76,9 @@ impl<'tcx, T: LateLintPass<'tcx>> LateContextAndPass<'tcx, T> { self.context.param_env = old_param_env; } - fn process_mod(&mut self, m: &'tcx hir::Mod<'tcx>, n: HirId) { - lint_callback!(self, check_mod, m, n); - hir_visit::walk_mod(self, m); + fn process_mod(&mut self, module: &'tcx hir::Mod<'tcx>, id: LocalModId) { + lint_callback!(self, check_mod, module, id); + hir_visit::walk_mod(self, module); } } @@ -98,7 +99,15 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas // The body and typeck results are also set in `visit_fn`. // Only fetch the results if this is for a new body. if old_enclosing_body != Some(body_id) && !self.actually_rustdoc { - self.context.typeck_results = Some(self.context.tcx.typeck_body(body_id)); + let typeck_results = self.context.tcx.typeck_body(body_id); + + // To avoid ICE, skip visiting the body if the typeck_results is tainted. + if typeck_results.tainted_by_errors.is_some() { + self.context.enclosing_body = old_enclosing_body; + self.context.typeck_results = old_typeck_results; + return; + } + self.context.typeck_results = Some(typeck_results); } let body = self.context.tcx.hir_body(body_id); @@ -188,7 +197,16 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas let old_enclosing_body = self.context.enclosing_body.replace(body_id); let old_typeck_results = self.context.typeck_results; if !self.actually_rustdoc { - self.context.typeck_results = Some(self.context.tcx.typeck_body(body_id)); + let typeck_results = self.context.tcx.typeck_body(body_id); + + // To avoid ICE, skip visiting the body if the typeck_results is tainted. + if typeck_results.tainted_by_errors.is_some() { + self.context.enclosing_body = old_enclosing_body; + self.context.typeck_results = old_typeck_results; + return; + } + + self.context.typeck_results = Some(typeck_results); } let body = self.context.tcx.hir_body(body_id); lint_callback!(self, check_fn, fk, decl, body, span, id); @@ -220,9 +238,9 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas hir_visit::walk_ty(self, t); } - fn visit_mod(&mut self, m: &'tcx hir::Mod<'tcx>, _: Span, n: HirId) { + fn visit_mod(&mut self, m: &'tcx hir::Mod<'tcx>, _: Span, id: LocalModId) { if !self.context.only_module { - self.process_mod(m, n); + self.process_mod(m, id); } } @@ -385,17 +403,17 @@ fn late_lint_mod_inner<'tcx, T: LateLintPass<'tcx>>( actually_rustdoc: tcx.sess.opts.actually_rustdoc, }; - let (module, _span, hir_id) = tcx.hir_get_module(mod_id); + let (module, _span) = tcx.hir_get_module(mod_id); - cx.with_lint_attrs(hir_id, |cx| { + cx.with_lint_attrs(mod_id.into(), |cx| { // There is no module lint that will have the crate itself as an item, so check it here. - if hir_id == hir::CRATE_HIR_ID { + if mod_id == CRATE_MOD_ID { lint_callback!(cx, check_crate,); } - cx.process_mod(module, hir_id); + cx.process_mod(module, mod_id); - if hir_id == hir::CRATE_HIR_ID { + if mod_id == CRATE_MOD_ID { lint_callback!(cx, check_crate_post,); } }); diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 472835388620c..f847d4c6ae904 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -26,6 +26,7 @@ use rustc_middle::lint::{ use rustc_middle::query::Providers; use rustc_middle::ty::{RegisteredTools, TyCtxt}; use rustc_session::Session; +use rustc_span::def_id::CRATE_MOD_ID; use rustc_span::{AttrId, DUMMY_SP, Span, Symbol, sym}; use tracing::{debug, instrument}; @@ -190,7 +191,7 @@ fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLe hir::OwnerNode::ImplItem(item) => levels.visit_impl_item(item), hir::OwnerNode::Crate(mod_) => { levels.add_id(hir::CRATE_HIR_ID); - levels.visit_mod(mod_, mod_.spans.inner_span, hir::CRATE_HIR_ID) + levels.visit_mod(mod_, mod_.spans.inner_span, CRATE_MOD_ID) } hir::OwnerNode::Synthetic => unreachable!(), }, @@ -959,12 +960,8 @@ where lint_from_cli: bool, } - impl<'a, 'b> Diagnostic<'a, ()> for UnknownLint<'b> { - fn into_diag( - self, - dcx: DiagCtxtHandle<'a>, - level: rustc_errors::Level, - ) -> Diag<'a, ()> { + impl<'a, 'b> Diagnostic<'a> for UnknownLint<'b> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: rustc_errors::Level) -> Diag<'a> { let Self { sess, lint_id, feature, lint_from_cli } = self; let mut lint = Diag::new(dcx, level, msg!("unknown lint: `{$name}`")) .with_arg("name", lint_id.lint.name_lower()) @@ -1012,7 +1009,7 @@ where &self, lint: &'static Lint, span: Option, - decorator: impl for<'a> Diagnostic<'a, ()>, + decorator: impl for<'a> Diagnostic<'a>, ) { let level_spec = self.lint_level_spec(lint); emit_lint_base(self.sess, lint, level_spec, span, decorator) @@ -1023,14 +1020,14 @@ where &self, lint: &'static Lint, span: MultiSpan, - decorator: impl for<'a> Diagnostic<'a, ()>, + decorator: impl for<'a> Diagnostic<'a>, ) { let level_spec = self.lint_level_spec(lint); emit_lint_base(self.sess, lint, level_spec, Some(span), decorator); } #[track_caller] - pub fn emit_lint(&self, lint: &'static Lint, decorator: impl for<'a> Diagnostic<'a, ()>) { + pub fn emit_lint(&self, lint: &'static Lint, decorator: impl for<'a> Diagnostic<'a>) { let level_spec = self.lint_level_spec(lint); emit_lint_base(self.sess, lint, level_spec, None, decorator); } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 95033707cf110..deea9e1bbc13c 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -148,6 +148,7 @@ pub use rustc_lint_defs::{ Applicability, FutureIncompatibleInfo, Lint, LintId, LintPass, LintVec, declare_lint, declare_lint_pass, declare_tool_lint, impl_lint_pass, }; +pub use utils::std_or_core; pub fn provide(providers: &mut Providers) { levels::provide(providers); diff --git a/compiler/rustc_lint/src/lifetime_syntax.rs b/compiler/rustc_lint/src/lifetime_syntax.rs index 208965ede2c73..ea09ce79fd49d 100644 --- a/compiler/rustc_lint/src/lifetime_syntax.rs +++ b/compiler/rustc_lint/src/lifetime_syntax.rs @@ -564,8 +564,8 @@ impl<'tcx> Info<'tcx> { // FIXME: Ideally, we'd also remove the lifetime declaration. fn removing_span(&self) -> Span { let mut span = self.lifetime.ident.span; - if let hir::TyKind::Ref(_, mut_ty) = self.ty.kind { - span = span.until(mut_ty.ty.span); + if let hir::TyKind::Ref(_, ty, _) = self.ty.kind { + span = span.until(ty.span); } span } diff --git a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs index ae07295db512f..75552ab759fd5 100644 --- a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs +++ b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs @@ -1,5 +1,5 @@ +use rustc_attr_ir::lang_items::LangItem; use rustc_hir as hir; -use rustc_hir::attrs::lang_items::LangItem; use rustc_lint_defs::{declare_lint, declare_lint_pass}; use rustc_middle::ty::Unnormalized; diff --git a/compiler/rustc_lint/src/non_ascii_idents.rs b/compiler/rustc_lint/src/non_ascii_idents.rs index 416b8717a7dc3..588a9e1130f76 100644 --- a/compiler/rustc_lint/src/non_ascii_idents.rs +++ b/compiler/rustc_lint/src/non_ascii_idents.rs @@ -2,7 +2,7 @@ use rustc_ast as ast; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::unord::UnordMap; use rustc_lint_defs::{declare_lint, declare_lint_pass}; -use rustc_span::Symbol; +use rustc_span::{OrdSpan, Symbol}; use unicode_security::general_security_profile::IdentifierType; use crate::diagnostics::{ @@ -180,7 +180,7 @@ impl EarlyLintPass for NonAsciiIdents { // We will soon sort, so the initial order does not matter. #[allow(rustc::potential_query_instability)] let mut symbols: Vec<_> = symbols.iter().collect(); - symbols.sort_by_key(|k| k.1); + symbols.sort_by_key(|k| k.1.lo_hi()); for &(ref symbol, &sp) in symbols.iter() { let symbol_str = symbol.as_str(); if symbol_str.is_ascii() { @@ -342,7 +342,7 @@ impl EarlyLintPass for NonAsciiIdents { .collect::>(); // we're sorting the output here. - let mut lint_reports: BTreeMap<(Span, Vec), AugmentedScriptSet> = + let mut lint_reports: BTreeMap<(OrdSpan, Vec), AugmentedScriptSet> = BTreeMap::new(); // The end result is put in `lint_reports` which is sorted. @@ -368,7 +368,7 @@ impl EarlyLintPass for NonAsciiIdents { // We sort primitive chars here and can use unstable sort ch_list.sort_unstable(); ch_list.dedup(); - lint_reports.insert((sp, ch_list), augment_script_set); + lint_reports.insert((OrdSpan(sp), ch_list), augment_script_set); } for ((sp, ch_list), script_set) in lint_reports { @@ -382,7 +382,7 @@ impl EarlyLintPass for NonAsciiIdents { } cx.emit_span_lint( MIXED_SCRIPT_CONFUSABLES, - sp, + sp.0, MixedScriptConfusables { set: script_set.to_string(), includes }, ); } diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs index 991b1083b3272..6fd65af794ac6 100644 --- a/compiler/rustc_lint/src/non_fmt_panic.rs +++ b/compiler/rustc_lint/src/non_fmt_panic.rs @@ -1,7 +1,7 @@ use rustc_ast as ast; +use rustc_attr_ir::lang_items::LangItem; use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, Level, msg}; use rustc_hir as hir; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def_id::DefId; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint_defs::{declare_lint, declare_lint_pass, fcw}; @@ -96,8 +96,8 @@ struct PanicMessageNotLiteral<'a, 'tcx> { panic: Option, } -impl<'a, 'b, 'tcx> Diagnostic<'a, ()> for PanicMessageNotLiteral<'b, 'tcx> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { +impl<'a, 'b, 'tcx> Diagnostic<'a> for PanicMessageNotLiteral<'b, 'tcx> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let Self { arg_span, symbol, span, arg_macro, cx, arg, panic } = self; let mut lint = Diag::new(dcx, level, "panic message is not a string literal") .with_arg("name", symbol) @@ -143,7 +143,14 @@ impl<'a, 'b, 'tcx> Diagnostic<'a, ()> for PanicMessageNotLiteral<'b, 'tcx> { .get_diagnostic_item(sym::Debug) .is_some_and(|t| infcx.type_implements_trait(t, [ty], param_env).may_apply()); - let suggest_panic_any = !is_str && panic == Some(sym::std_panic_macro); + let suggest_panic_any = !is_str + && panic == Some(sym::std_panic_macro) + && cx + .tcx + .all_diagnostic_items(()) + .name_to_id + .keys() + .any(|name| name.as_str() == "panic_any"); let fmt_applicability = if suggest_panic_any { // If we can use panic_any, use that as the MachineApplicable suggestion. @@ -360,5 +367,5 @@ fn is_arg_inside_call(arg: Span, call: Span) -> bool { // panic call in the source file, to avoid invalid suggestions when macros are involved. // We specifically check for the spans to not be identical, as that happens sometimes when // proc_macros lie about spans and apply the same span to all the tokens they produce. - call.contains(arg) && !call.source_equal(arg) + call.contains(arg) && call.lo_hi() != arg.lo_hi() } diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index 01ddc172f274d..e4f082d49cba7 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -1,7 +1,8 @@ +use rustc_attr_ir::find_attr; use rustc_errors::{MultiSpan, msg}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{Body, HirId, Item, ItemKind, Node, Path, TyKind, find_attr}; +use rustc_hir::{Body, HirId, Item, ItemKind, Node, Path, TyKind}; use rustc_lint_defs::{declare_lint, impl_lint_pass}; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::{DefId, LOCAL_CRATE}; diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index a0db07e187cba..66478f052d68b 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -1,17 +1,17 @@ use rustc_abi::ExternAbi; use rustc_ast as ast; +use rustc_attr_ir::{Attribute, AttributeKind, ReprAttr, find_attr}; use rustc_attr_parsing::AttributeParser; use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, Level}; use rustc_hir as hir; -use rustc_hir::attrs::{AttributeKind, ReprAttr}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{FnKind, Visitor}; -use rustc_hir::{Attribute, GenericParamKind, PatExprKind, PatKind, find_attr}; +use rustc_hir::{GenericParamKind, PatExprKind, PatKind}; use rustc_lint_defs::{declare_lint, declare_lint_pass}; use rustc_middle::hir::nested_filter::All; use rustc_middle::ty::AssocContainer; -use rustc_span::def_id::LocalDefId; +use rustc_span::def_id::{CRATE_MOD_ID, LocalDefId, LocalModId}; use rustc_span::{BytePos, Ident, Span, sym}; use rustc_structures::CrateType; @@ -328,8 +328,8 @@ impl NonSnakeCase { } impl<'tcx> LateLintPass<'tcx> for NonSnakeCase { - fn check_mod(&mut self, cx: &LateContext<'_>, _: &'tcx hir::Mod<'tcx>, id: hir::HirId) { - if id != hir::CRATE_HIR_ID { + fn check_mod(&mut self, cx: &LateContext<'_>, _: &'tcx hir::Mod<'tcx>, id: LocalModId) { + if id != CRATE_MOD_ID { return; } @@ -484,10 +484,10 @@ struct NonUpperCaseGlobalGenerator<'a, F: FnOnce() -> NonUpperCaseGlobal<'a>> { callback: F, } -impl<'a, 'b, F: FnOnce() -> NonUpperCaseGlobal<'b>> Diagnostic<'a, ()> +impl<'a, 'b, F: FnOnce() -> NonUpperCaseGlobal<'b>> Diagnostic<'a> for NonUpperCaseGlobalGenerator<'b, F> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let Self { callback } = self; callback().into_diag(dcx, level) } diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs index f870bce669e58..98ab44c37e26a 100644 --- a/compiler/rustc_lint/src/passes.rs +++ b/compiler/rustc_lint/src/passes.rs @@ -11,7 +11,7 @@ macro_rules! late_lint_methods { fn check_body_post(a: &rustc_hir::Body<'tcx>); fn check_crate(); fn check_crate_post(); - fn check_mod(a: &'tcx rustc_hir::Mod<'tcx>, b: rustc_hir::HirId); + fn check_mod(a: &'tcx rustc_hir::Mod<'tcx>, b: rustc_span::def_id::LocalModId); fn check_foreign_item(a: &'tcx rustc_hir::ForeignItem<'tcx>); fn check_item(a: &'tcx rustc_hir::Item<'tcx>); fn check_item_post(a: &'tcx rustc_hir::Item<'tcx>); @@ -40,9 +40,9 @@ macro_rules! late_lint_methods { fn check_field_def(a: &'tcx rustc_hir::FieldDef<'tcx>); fn check_variant(a: &'tcx rustc_hir::Variant<'tcx>); fn check_path(a: &rustc_hir::Path<'tcx>, b: rustc_hir::HirId); - fn check_attribute(a: &'tcx rustc_hir::Attribute); - fn check_attributes(a: &'tcx [rustc_hir::Attribute]); - fn check_attributes_post(a: &'tcx [rustc_hir::Attribute]); + fn check_attribute(a: &'tcx rustc_attr_ir::Attribute); + fn check_attributes(a: &'tcx [rustc_attr_ir::Attribute]); + fn check_attributes_post(a: &'tcx [rustc_attr_ir::Attribute]); ]); ) } diff --git a/compiler/rustc_lint/src/ptr_nulls.rs b/compiler/rustc_lint/src/ptr_nulls.rs index 19c1a8a1b665c..c231bd4cc3b30 100644 --- a/compiler/rustc_lint/src/ptr_nulls.rs +++ b/compiler/rustc_lint/src/ptr_nulls.rs @@ -1,5 +1,6 @@ use rustc_ast::LitKind; -use rustc_hir::{BinOpKind, Expr, ExprKind, TyKind, find_attr}; +use rustc_attr_ir::find_attr; +use rustc_hir::{BinOpKind, Expr, ExprKind, TyKind}; use rustc_lint_defs::{declare_lint, declare_lint_pass}; use rustc_middle::ty::RawPtr; use rustc_span::{Span, sym}; @@ -85,7 +86,7 @@ fn useless_check<'a, 'tcx: 'a>( return Some(UselessPtrNullChecksDiag::FnRet { fn_name }); } e = if let ExprKind::Cast(expr, t) = e.kind - && let TyKind::Ptr(_) = t.kind + && let TyKind::Ptr(..) = t.kind { had_at_least_one_cast = true; expr diff --git a/compiler/rustc_lint/src/raw_borrows_via_references.rs b/compiler/rustc_lint/src/raw_borrows_via_references.rs index 2d17d6c301c3d..74ab9ee39fadc 100644 --- a/compiler/rustc_lint/src/raw_borrows_via_references.rs +++ b/compiler/rustc_lint/src/raw_borrows_via_references.rs @@ -54,18 +54,19 @@ impl<'tcx> LateLintPass<'tcx> for RawBorrowsViaReferences { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { if let ExprKind::Cast(exp, ty) = expr.kind && let ExprKind::AddrOf(BorrowKind::Ref, mutbl, addr_of_exp) = exp.kind - && let TyKind::Ptr(_) = ty.kind + && let TyKind::Ptr(..) = ty.kind && addr_of_exp.is_syntactic_place_expr() { - let suggestion = if let Some(addr_of_span) = - addr_of_exp.span.find_ancestor_in_same_ctxt(expr.span) + let suggestion = if let Some(borrow_span) = + exp.span.find_ancestor_inside_same_ctxt(expr.span) + && let Some(addr_of_span) = addr_of_exp.span.find_ancestor_in_same_ctxt(expr.span) && let Some(ty_span) = ty.span.find_ancestor_in_same_ctxt(expr.span) - && expr.span.can_be_used_for_suggestions() + && borrow_span.can_be_used_for_suggestions() && addr_of_span.can_be_used_for_suggestions() && ty_span.can_be_used_for_suggestions() { RawBorrowViaReferenceSuggestion::Spanful { - left: expr.span.until(addr_of_span), + left: borrow_span.until(addr_of_span), right: addr_of_span.shrink_to_hi().until(ty_span.shrink_to_hi()), mutbl: mutbl.ptr_str(), } diff --git a/compiler/rustc_lint/src/runtime_symbols.rs b/compiler/rustc_lint/src/runtime_symbols.rs index df52150799522..f0e186c34ca0f 100644 --- a/compiler/rustc_lint/src/runtime_symbols.rs +++ b/compiler/rustc_lint/src/runtime_symbols.rs @@ -1,4 +1,4 @@ -use rustc_hir::attrs::CanonicalSymbol; +use rustc_attr_ir::CanonicalSymbol; use rustc_hir::def_id::LocalDefId; use rustc_hir::{self as hir, FnSig, ForeignItemKind}; use rustc_infer::infer::DefineOpaqueTypes; diff --git a/compiler/rustc_lint/src/shadowed_into_iter.rs b/compiler/rustc_lint/src/shadowed_into_iter.rs index 0468142a987ca..021499f6b712c 100644 --- a/compiler/rustc_lint/src/shadowed_into_iter.rs +++ b/compiler/rustc_lint/src/shadowed_into_iter.rs @@ -1,5 +1,5 @@ +use rustc_attr_ir::lang_items::LangItem; use rustc_hir as hir; -use rustc_hir::attrs::lang_items::LangItem; use rustc_lint_defs::{declare_lint, fcw, impl_lint_pass}; use rustc_middle::ty::{self, Ty}; diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs index 2d6d83d4e0776..1e2c012ee337a 100644 --- a/compiler/rustc_lint/src/traits.rs +++ b/compiler/rustc_lint/src/traits.rs @@ -1,4 +1,4 @@ -use rustc_hir::attrs::lang_items::LangItem; +use rustc_attr_ir::lang_items::LangItem; use rustc_hir::{self as hir, AmbigArg}; use rustc_lint_defs::{declare_lint, declare_lint_pass}; use rustc_span::sym; diff --git a/compiler/rustc_lint/src/transmute.rs b/compiler/rustc_lint/src/transmute.rs index 3a4092a46a6b6..3b269a215cb6b 100644 --- a/compiler/rustc_lint/src/transmute.rs +++ b/compiler/rustc_lint/src/transmute.rs @@ -8,7 +8,10 @@ use rustc_macros::Diagnostic; use rustc_middle::ty::{self, Ty}; use rustc_span::sym; -use crate::diagnostics::{IntegerToPtrTransmutes, IntegerToPtrTransmutesSuggestion}; +use crate::diagnostics::{ + IntegerToPtrTransmutes, IntegerToPtrTransmutesSuggestion, IntegerToPtrWithoutProvHelp, +}; +use crate::utils::std_or_core; use crate::{LateContext, LateLintPass}; declare_lint! { @@ -166,30 +169,30 @@ fn check_int_to_ptr_transmute<'tcx>( } let suffix = if mutbl.is_mut() { "_mut" } else { "" }; + let krate = std_or_core(cx); cx.tcx.emit_node_span_lint( INTEGER_TO_PTR_TRANSMUTES, expr.hir_id, expr.span, IntegerToPtrTransmutes { - suggestion: if layout_inner_ty.is_sized() { - Some(if dst.is_ref() { - IntegerToPtrTransmutesSuggestion::ToRef { + without_prov: krate.map(|krate| IntegerToPtrWithoutProvHelp { krate }), + suggestion: match (krate, layout_inner_ty.is_sized()) { + (Some(krate), true) if dst.is_ref() => { + Some(IntegerToPtrTransmutesSuggestion::ToRef { + krate, dst: *inner_ty, suffix, ref_mutbl: mutbl.prefix_str(), start_call: expr.span.shrink_to_lo().until(arg.span), - } - } else { - IntegerToPtrTransmutesSuggestion::ToPtr { - dst: *inner_ty, - suffix, - start_call: expr.span.shrink_to_lo().until(arg.span), - } - }) - } else { - // We can't suggest using `with_exposed_provenance` for unsized type - // so don't suggest anything. - None + }) + } + (Some(krate), true) => Some(IntegerToPtrTransmutesSuggestion::ToPtr { + krate, + dst: *inner_ty, + suffix, + start_call: expr.span.shrink_to_lo().until(arg.span), + }), + _ => None, }, }, ); diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 4d741e06198b5..9897e9cbb4f07 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -2,10 +2,12 @@ use std::iter; use rustc_abi::{BackendRepr, TagEncoding, Variants, WrappingRange}; use rustc_ast as ast; +use rustc_attr_ir::find_attr; +use rustc_attr_ir::lang_items::LangItem; use rustc_hir as hir; -use rustc_hir::attrs::lang_items::LangItem; -use rustc_hir::{Expr, ExprKind, HirId, find_attr}; +use rustc_hir::{Expr, ExprKind, HirId}; use rustc_lint_defs::{declare_lint, declare_lint_pass, impl_lint_pass}; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::layout::{LayoutOf, SizeSkeleton}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, Unnormalized}; use rustc_span::{DUMMY_SP, Span, Symbol, bug, sym}; @@ -23,6 +25,7 @@ use crate::diagnostics::{ UnpredictableFunctionPointerComparisonsSuggestion, UnusedComparisons, VariantSizeDifferencesDiag, }; +use crate::utils::std_or_core; use crate::{LateContext, LateLintPass, LintContext}; mod literal; @@ -346,13 +349,21 @@ fn lint_wide_pointer<'tcx>( return; }; + let Some(krate) = std_or_core(cx) else { + return cx.emit_span_lint( + AMBIGUOUS_WIDE_POINTER_COMPARISONS, + e.span, + AmbiguousWidePointerComparisons::Warn, + ); + }; + let (Some(l_span), Some(r_span)) = (l.span.find_ancestor_inside(e.span), r.span.find_ancestor_inside(e.span)) else { return cx.emit_span_lint( AMBIGUOUS_WIDE_POINTER_COMPARISONS, e.span, - AmbiguousWidePointerComparisons::Spanless, + AmbiguousWidePointerComparisons::Spanless { krate }, ); }; @@ -378,6 +389,7 @@ fn lint_wide_pointer<'tcx>( AmbiguousWidePointerComparisons::SpanfulEq { addr_metadata_suggestion: (!is_dyn_comparison).then(|| { AmbiguousWidePointerComparisonsAddrMetadataSuggestion { + krate, ne, deref_left, deref_right, @@ -389,6 +401,7 @@ fn lint_wide_pointer<'tcx>( } }), addr_suggestion: AmbiguousWidePointerComparisonsAddrSuggestion { + krate, ne, deref_left, deref_right, @@ -510,6 +523,14 @@ fn lint_fn_pointer<'tcx>( let middle = l_span.shrink_to_hi().until(r_span.shrink_to_lo()); let right = r_span.shrink_to_hi().until(e.span.shrink_to_hi()); + let Some(krate) = std_or_core(cx) else { + return cx.emit_span_lint( + UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS, + e.span, + UnpredictableFunctionPointerComparisons::Warn, + ); + }; + let sugg = // We only check for a right cast as `FnDef` == `FnPtr` is not possible, // only `FnPtr == FnDef` is possible. @@ -517,6 +538,7 @@ fn lint_fn_pointer<'tcx>( let fn_sig = r_ty.fn_sig(cx.tcx); UnpredictableFunctionPointerComparisonsSuggestion::FnAddrEqWithCast { + krate, ne, fn_sig, deref_left, @@ -527,6 +549,7 @@ fn lint_fn_pointer<'tcx>( } } else { UnpredictableFunctionPointerComparisonsSuggestion::FnAddrEq { + krate, ne, deref_left, deref_right, diff --git a/compiler/rustc_lint/src/types/literal.rs b/compiler/rustc_lint/src/types/literal.rs index f01d0190fb812..40681664e4437 100644 --- a/compiler/rustc_lint/src/types/literal.rs +++ b/compiler/rustc_lint/src/types/literal.rs @@ -3,8 +3,9 @@ use rustc_abi::{Integer, Size}; use rustc_apfloat::Float; use rustc_apfloat::ieee::{DoubleS, HalfS, IeeeFloat, QuadS, Semantics, SingleS}; use rustc_ast as ast; +use rustc_attr_ir as attrs; use rustc_hir as hir; -use rustc_hir::{HirId, attrs}; +use rustc_hir::HirId; use rustc_middle::ty; use rustc_middle::ty::Ty; use rustc_middle::ty::layout::IntegerExt; diff --git a/compiler/rustc_lint/src/unqualified_local_imports.rs b/compiler/rustc_lint/src/unqualified_local_imports.rs index 0bfca85b59b34..73fecba6d02f2 100644 --- a/compiler/rustc_lint/src/unqualified_local_imports.rs +++ b/compiler/rustc_lint/src/unqualified_local_imports.rs @@ -45,7 +45,7 @@ declare_lint_pass!(UnqualifiedLocalImports => [UNQUALIFIED_LOCAL_IMPORTS]); impl<'tcx> LateLintPass<'tcx> for UnqualifiedLocalImports { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { - let hir::ItemKind::Use(path, _kind) = item.kind else { return }; + let hir::ItemKind::Use(hir::UseTree { prefix: path, .. }) = item.kind else { return }; // Check the type and value namespace resolutions for a local crate. let is_local_import = matches!( path.res.type_ns, diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 17c078615c411..361d1768087ea 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -198,8 +198,8 @@ trait UnusedDelimLint { | Paren(_) | Become(_) => true, Call(..) | MethodCall(_) | Let(..) | Field(..) | MacCall(_) | FormatArgs(_) => false, - // `direct_const_arg!()` is invalid in function/method argument position. - DirectConstArg(_) => false, + // `gca!()` is invalid in function/method argument position. + GcaMacro(_) => false, // don't lint for placeholder/error-recovery Underscore | Err(_) | Dummy => false, } @@ -783,6 +783,14 @@ impl UnusedParens { } } + /// A pattern that a `macro_rules!` metavariable brings in from the call site keeps the call + /// site's syntax context, while the pattern's surroundings keep the macro's. Parentheses in + /// such a pattern can be required by the matcher even when they are redundant in the + /// expansion, so removing them would break the macro call. (Issue #86959) + fn pat_is_macro_arg(pat_span: Span, parent_span: Span) -> bool { + !pat_span.eq_ctxt(parent_span) + } + fn cast_followed_by_lt(&self, expr: &ast::Expr) -> Option { if let ExprKind::Binary(op, lhs, _rhs) = &expr.kind && (op.node == ast::BinOpKind::Lt || op.node == ast::BinOpKind::Shl) @@ -811,7 +819,8 @@ impl EarlyLintPass for UnusedParens { match e.kind { ExprKind::Let(ref pat, _, _, _) | ExprKind::ForLoop(ForLoop { ref pat, .. }) => { - self.check_unused_parens_pat(cx, pat, false, false, (true, true)); + let avoid_or = Self::pat_is_macro_arg(pat.span, e.span); + self.check_unused_parens_pat(cx, pat, avoid_or, false, (true, true)); } // We ignore parens in cases like `if (((let Some(0) = Some(1))))` because we already // handle a hard error for them during AST lowering in `lower_expr_mut`, but we still @@ -890,13 +899,15 @@ impl EarlyLintPass for UnusedParens { | Err(_) => {} // These are list-like patterns; parens can always be removed. TupleStruct(_, _, ps) | Tuple(ps) | Slice(ps) | Or(ps) => { - for p in ps { - self.check_unused_parens_pat(cx, p, false, false, keep_space); + for sub in ps { + let avoid_or = Self::pat_is_macro_arg(sub.span, p.span); + self.check_unused_parens_pat(cx, sub, avoid_or, false, keep_space); } } Struct(_, _, fps, _) => { for f in fps { - self.check_unused_parens_pat(cx, &f.pat, false, false, keep_space); + let avoid_or = Self::pat_is_macro_arg(f.pat.span, p.span); + self.check_unused_parens_pat(cx, &f.pat, avoid_or, false, keep_space); } } // Avoid linting on `i @ (p0 | .. | pn)`, #64106. @@ -938,7 +949,8 @@ impl EarlyLintPass for UnusedParens { } fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &ast::Arm) { - self.check_unused_parens_pat(cx, &arm.pat, false, false, (false, false)); + let avoid_or = Self::pat_is_macro_arg(arm.pat.span, arm.span); + self.check_unused_parens_pat(cx, &arm.pat, avoid_or, false, (false, false)); } fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) { @@ -988,7 +1000,7 @@ impl EarlyLintPass for UnusedParens { self.with_self_ty_parens = false; } - ast::TyKind::Ref(_, mut_ty) | ast::TyKind::Ptr(mut_ty) => { + ast::TyKind::Ref(_, inner_ty, _) | ast::TyKind::Ptr(inner_ty, _) => { // If this type itself appears in no-bounds position, we propagate its // potentially tighter constraint or risk a false posive (issue 143653). let own_constraint = self.in_no_bounds_pos.get(&ty.id); @@ -997,7 +1009,7 @@ impl EarlyLintPass for UnusedParens { Some(NoBoundsException::OneBound) => NoBoundsException::OneBound, None => NoBoundsException::OneBound, }; - self.in_no_bounds_pos.insert(mut_ty.ty.id, constraint); + self.in_no_bounds_pos.insert(inner_ty.id, constraint); } ast::TyKind::TraitObject(bounds, _) | ast::TyKind::ImplTrait(_, bounds) => { for i in 0..bounds.len() { diff --git a/compiler/rustc_lint/src/unused/must_use.rs b/compiler/rustc_lint/src/unused/must_use.rs index 981d9df9c0f29..89e3981b965f2 100644 --- a/compiler/rustc_lint/src/unused/must_use.rs +++ b/compiler/rustc_lint/src/unused/must_use.rs @@ -1,12 +1,14 @@ use std::iter; +use rustc_attr_ir::find_attr; +use rustc_attr_ir::lang_items::LangItem; use rustc_errors::pluralize; -use rustc_hir::attrs::lang_items::LangItem; +use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, find_attr}; use rustc_infer::traits::util::elaborate; use rustc_lint_defs::{declare_lint, declare_lint_pass}; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::{self, Ty, Unnormalized}; use rustc_span::{Span, Symbol, sym}; use tracing::instrument; @@ -297,6 +299,9 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { return; } + let ty = cx.typeck_results().expr_ty(expr); + let needs_binding = ty.has_significant_drop(cx.tcx, cx.typing_env()); + if let hir::ExprKind::Match(await_expr, _arms, hir::MatchSource::AwaitDesugar) = expr.kind && let ty = cx.typeck_results().expr_ty(await_expr) && let ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id: future_def_id }, .. }) = ty.kind() @@ -312,6 +317,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { "output of future returned by ", "", expr_is_from_block, + needs_binding, ) { // We have a bare `foo().await;` on an opaque type from an async function that was @@ -319,19 +325,26 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { return; } - let ty = cx.typeck_results().expr_ty(expr); - let must_use_result = is_ty_must_use(cx, ty, expr); let type_lint_emitted_or_trivial = match must_use_result { IsTyMustUse::Yes(path) => { - emit_must_use_untranslated(cx, &path, "", "", 1, false, expr_is_from_block); + emit_must_use_untranslated( + cx, + &path, + "", + "", + 1, + false, + expr_is_from_block, + needs_binding, + ); true } IsTyMustUse::Trivial => true, IsTyMustUse::No => false, }; - let fn_warned = check_fn_must_use(cx, expr, expr_is_from_block); + let fn_warned = check_fn_must_use(cx, expr, expr_is_from_block, needs_binding); if !fn_warned && type_lint_emitted_or_trivial { // We don't warn about unused unit or uninhabited types. @@ -413,7 +426,12 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { /// Checks if `expr` is a \[method\] call expression marked as `#[must_use]` and emits a lint if so. /// Returns `true` if the lint has been emitted. -fn check_fn_must_use(cx: &LateContext<'_>, expr: &hir::Expr<'_>, expr_is_from_block: bool) -> bool { +fn check_fn_must_use( + cx: &LateContext<'_>, + expr: &hir::Expr<'_>, + expr_is_from_block: bool, + needs_binding: bool, +) -> bool { let maybe_def_id = match expr.kind { hir::ExprKind::Call(callee, _) => { if let hir::ExprKind::Path(ref qpath) = callee.kind @@ -432,9 +450,15 @@ fn check_fn_must_use(cx: &LateContext<'_>, expr: &hir::Expr<'_>, expr_is_from_bl }; match maybe_def_id { - Some(def_id) => { - check_must_use_def(cx, def_id, expr.span, "return value of ", "", expr_is_from_block) - } + Some(def_id) => check_must_use_def( + cx, + def_id, + expr.span, + "return value of ", + "", + expr_is_from_block, + needs_binding, + ), None => false, } } @@ -453,6 +477,7 @@ fn check_must_use_def( descr_pre_path: &str, descr_post_path: &str, expr_is_from_block: bool, + needs_binding: bool, ) -> bool { is_def_must_use(cx, def_id, span) .map(|must_use_path| { @@ -464,6 +489,7 @@ fn check_must_use_def( 1, false, expr_is_from_block, + needs_binding, ) }) .is_some() @@ -478,6 +504,7 @@ fn emit_must_use_untranslated( plural_len: usize, is_inner: bool, expr_is_from_block: bool, + needs_binding: bool, ) { let plural_suffix = pluralize!(plural_len); @@ -492,6 +519,7 @@ fn emit_must_use_untranslated( plural_len, true, expr_is_from_block, + needs_binding, ); } MustUsePath::Pinned(path) => { @@ -504,6 +532,7 @@ fn emit_must_use_untranslated( plural_len, true, expr_is_from_block, + needs_binding, ); } MustUsePath::Opaque(path) => { @@ -516,6 +545,7 @@ fn emit_must_use_untranslated( plural_len, true, expr_is_from_block, + needs_binding, ); } MustUsePath::TraitObject(path) => { @@ -528,6 +558,7 @@ fn emit_must_use_untranslated( plural_len, true, expr_is_from_block, + needs_binding, ); } MustUsePath::TupleElement(elems) => { @@ -541,6 +572,7 @@ fn emit_must_use_untranslated( plural_len, true, expr_is_from_block, + needs_binding, ); } } @@ -554,6 +586,7 @@ fn emit_must_use_untranslated( plural_len, true, expr_is_from_block, + needs_binding, ); } MustUsePath::ControlFlow(path) => { @@ -566,6 +599,7 @@ fn emit_must_use_untranslated( plural_len, true, expr_is_from_block, + needs_binding, ); } MustUsePath::Array(path, len) => { @@ -578,6 +612,7 @@ fn emit_must_use_untranslated( plural_len.saturating_add(usize::try_from(*len).unwrap_or(usize::MAX)), true, expr_is_from_block, + needs_binding, ); } MustUsePath::Closure(span) => { @@ -613,14 +648,24 @@ fn emit_must_use_untranslated( cx, def_id: *def_id, note: *reason, - suggestion: (!is_inner).then_some(if expr_is_from_block { - UnusedDefSuggestion::BlockTailExpr { - before_span: suggestion_span.shrink_to_lo(), - after_span: suggestion_span.shrink_to_hi(), - } + suggestion: if is_inner { + None } else { - UnusedDefSuggestion::NormalExpr { span: suggestion_span.shrink_to_lo() } - }), + let lo = suggestion_span.shrink_to_lo(); + let hi = suggestion_span.shrink_to_hi(); + Some(match (needs_binding, expr_is_from_block) { + (true, true) => UnusedDefSuggestion::BindingBlockTailExpr { + before_span: lo, + after_span: hi, + }, + (true, false) => UnusedDefSuggestion::BindingExpr { span: lo }, + (false, true) => UnusedDefSuggestion::BlockTailExpr { + before_span: lo, + after_span: hi, + }, + (false, false) => UnusedDefSuggestion::NormalExpr { span: lo }, + }) + }, }, ); } diff --git a/compiler/rustc_lint/src/utils.rs b/compiler/rustc_lint/src/utils.rs index 5854e89d801de..19b9dcc5a7267 100644 --- a/compiler/rustc_lint/src/utils.rs +++ b/compiler/rustc_lint/src/utils.rs @@ -1,3 +1,4 @@ +use rustc_attr_ir::find_attr; use rustc_hir::{Expr, ExprKind}; use rustc_span::sym; @@ -47,3 +48,14 @@ pub(crate) fn peel_casts<'tcx>( e } + +/// Crate to use for rustfix / diagnostic paths that exist in both `std` and `core`. Returns `None` for `#![no_core]`. +pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> { + if find_attr!(cx.tcx, crate, NoCore) { + None + } else if find_attr!(cx.tcx, crate, NoStd) { + Some("core") + } else { + Some("std") + } +} diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 2a0901e0243b6..31610f558974f 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -137,6 +137,7 @@ pub mod hardwired { UNREACHABLE_PATTERNS, UNSAFE_ATTR_OUTSIDE_UNSAFE, UNSAFE_OP_IN_UNSAFE_FN, + UNSTABLE_IMPORTS, UNSTABLE_NAME_COLLISIONS, UNSTABLE_SYNTAX_PRE_EXPANSION, UNSUPPORTED_CALLING_CONVENTIONS, @@ -2825,8 +2826,7 @@ declare_lint! { /// /// ### Example /// - #[cfg_attr(bootstrap, doc = "```rust,ignore")] - #[cfg_attr(not(bootstrap), doc = "```rust,compile_fail")] + /// ```rust,compile_fail /// #![feature(staged_api)] /// #![stable(feature = "test", since = "1.0.0")] /// @@ -2837,9 +2837,9 @@ declare_lint! { /// pub use self::S as T; /// /// fn main() {} - #[doc = "```"] + /// ``` /// - #[cfg_attr(not(bootstrap), doc = "{{produces}}")] + /// {{produces}} /// /// ### Explanation /// @@ -3024,7 +3024,7 @@ declare_lint! { "trailing semicolon in macro body used as expression", @future_incompatible = FutureIncompatibleInfo { reason: fcw!(FutureReleaseError #79813), - report_in_deps: true, + report_in_deps: false, }; } @@ -5858,3 +5858,67 @@ declare_lint! { "`repr(C, align)` types nested inside `repr(C, packed)` types \ do not always have a C-compatible layout", } + +declare_lint! { + /// The `unstable_imports` lints detects imports that go through unstable modules. + /// + /// ### Example + /// + /// ```rust + /// use core::intrinsics::transmute; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Previous versions of Rust accidentally allowed certain imports through unstable modules + /// because stability information of modules was not correctly accounted for if the imported + /// item was stable itself. + pub UNSTABLE_IMPORTS, + Deny, + "lints on accidentally allowed unstable imports", + @future_incompatible = FutureIncompatibleInfo { + reason: fcw!(FutureReleaseError # 163160), + report_in_deps: true, + }; +} + +declare_lint! { + /// The `unsafe_panic_handlers` lint detects unsafe functions with + /// the `#[panic_handler]` attribute. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![no_std] + /// + /// use core::panic::PanicInfo; + /// + /// #[panic_handler] + /// unsafe fn handle(_: &PanicInfo<'_>) -> ! { + /// loop {} + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Unsafe functions (declared as `unsafe fn`) are functions that can only be called + /// when the caller ensures that their safety requirements are met. On the other hand, + /// `#[panic_handler]` functions get called automatically by the compiler without + /// checking for any preconditions. Therefore, using the `#[panic_handler]` attribute + /// on unsafe functions is either incorrect or a misuse of `unsafe fn`. + /// + /// This is a [future-incompatible] lint to transition this to a hard + /// error in the future. See [issue #163263] for more details. + /// + /// [issue #163263]: https://github.com/rust-lang/rust/issues/163263 + pub UNSAFE_PANIC_HANDLERS, + Warn, + "detects unsafe functions with the `#[panic_handler]` attribute", + @future_incompatible = FutureIncompatibleInfo { + reason: fcw!(FutureReleaseError #163263), + }; +} diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs index 78f3929dbce47..450b98f20cde9 100644 --- a/compiler/rustc_llvm/build.rs +++ b/compiler/rustc_llvm/build.rs @@ -484,9 +484,8 @@ fn main() { let llvm_static_stdcpp = tracked_env_var_os("LLVM_STATIC_STDCPP"); let llvm_use_libcxx = tracked_env_var_os("LLVM_USE_LIBCXX"); - let stdcppname = if target.contains("openbsd") { - if target.contains("sparc64") { "estdc++" } else { "c++" } - } else if target.contains("darwin") + let stdcppname = if target.contains("openbsd") + || target.contains("darwin") || target.contains("freebsd") || target.contains("windows-gnullvm") || target.contains("aix") diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 35d5597795a9e..3873701ae09ac 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -5,10 +5,8 @@ #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Analysis/Lint.h" -#include "llvm/Analysis/TargetLibraryInfo.h" -#if LLVM_VERSION_GE(22, 0) #include "llvm/Analysis/RuntimeLibcallInfo.h" -#endif +#include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Bitcode/BitcodeWriter.h" #include "llvm/Bitcode/BitcodeWriterPass.h" #include "llvm/CodeGen/CommandFlags.h" @@ -24,12 +22,8 @@ #include "llvm/MC/TargetRegistry.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Passes/PassBuilder.h" -#if LLVM_VERSION_GE(22, 0) -#include "llvm/Plugins/PassPlugin.h" -#else -#include "llvm/Passes/PassPlugin.h" -#endif #include "llvm/Passes/StandardInstrumentations.h" +#include "llvm/Plugins/PassPlugin.h" #include "llvm/Support/CBindingWrapping.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Program.h" @@ -100,16 +94,13 @@ LLVMRustCreateMCSubtargetInfo(const char *TripleStr, const char *CPU, return nullptr; } -#if LLVM_VERSION_GE(22, 0) return TheTarget->createMCSubtargetInfo(Trip, CPU, Features); -#else - return TheTarget->createMCSubtargetInfo(Trip.str(), CPU, Features); -#endif } -extern "C" bool LLVMRustMCSubtargetInfoHasFeature(MCSubtargetInfo *MCInfo, - const char *Feature) { - return MCInfo->checkFeatures(std::string("+") + Feature); +extern "C" bool LLVMRustMCSubtargetInfoCheckFeatures(MCSubtargetInfo *MCInfo, + const char *Features, + size_t FeaturesLen) { + return MCInfo->checkFeatures(StringRef{Features, FeaturesLen}); } extern "C" void LLVMRustDisposeMCSubtargetInfo(MCSubtargetInfo *MCInfo) { @@ -446,30 +437,6 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( return wrap(TM); } -// Unfortunately, the LLVM C API doesn't provide a way to create the -// TargetLibraryInfo pass, so we use this method to do so. -extern "C" void LLVMRustAddLibraryInfo(LLVMTargetMachineRef T, - LLVMPassManagerRef PMR, LLVMModuleRef M, - bool DisableSimplifyLibCalls) { - auto TargetTriple = Triple(unwrap(M)->getTargetTriple()); - TargetOptions *Options = &unwrap(T)->Options; - auto TLII = TargetLibraryInfoImpl(TargetTriple); - if (DisableSimplifyLibCalls) - TLII.disableAllFunctions(); - unwrap(PMR)->add(new TargetLibraryInfoWrapperPass(TLII)); -#if LLVM_VERSION_GE(24, 0) - // LLVM 24 removed TargetOptions::EABIVersion and ExceptionModel; the EABI - // version and exception model are now derived from the target triple and - // module flags respectively instead. - unwrap(PMR)->add(new RuntimeLibraryInfoWrapper(Options->MCOptions.ABIName, - Options->VecLib)); -#elif LLVM_VERSION_GE(22, 0) - unwrap(PMR)->add(new RuntimeLibraryInfoWrapper( - TargetTriple, Options->ExceptionModel, Options->FloatABIType, - Options->EABIVersion, Options->MCOptions.ABIName, Options->VecLib)); -#endif -} - extern "C" void LLVMRustSetLLVMOptions(int Argc, char **Argv) { // Initializing the command-line options more than once is not allowed. So, // check if they've already been initialized. (This could happen if we're @@ -499,10 +466,34 @@ static CodeGenFileType fromRust(LLVMRustFileType Type) { } extern "C" LLVMRustResult -LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMPassManagerRef PMR, - LLVMModuleRef M, const char *Path, const char *DwoPath, - LLVMRustFileType RustFileType, bool VerifyIR) { - llvm::legacy::PassManager *PM = unwrap(PMR); +LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMModuleRef M, + const char *Path, const char *DwoPath, + LLVMRustFileType RustFileType, bool VerifyIR, + bool DisableSimplifyLibCalls) { + std::unique_ptr PM = + std::make_unique(); + + PM->add(createTargetTransformInfoWrapperPass( + unwrap(Target)->getTargetIRAnalysis())); + + auto TargetTriple = Triple(unwrap(M)->getTargetTriple()); + TargetOptions *Options = &unwrap(Target)->Options; + auto TLII = TargetLibraryInfoImpl(TargetTriple); + if (DisableSimplifyLibCalls) + TLII.disableAllFunctions(); + PM->add(new TargetLibraryInfoWrapperPass(TLII)); +#if LLVM_VERSION_GE(24, 0) + // LLVM 24 removed TargetOptions::EABIVersion and ExceptionModel; the EABI + // version and exception model are now derived from the target triple and + // module flags respectively instead. + PM->add(new RuntimeLibraryInfoWrapper(Options->MCOptions.ABIName, + Options->VecLib)); +#else + PM->add(new RuntimeLibraryInfoWrapper( + TargetTriple, Options->ExceptionModel, Options->FloatABIType, + Options->EABIVersion, Options->MCOptions.ABIName, Options->VecLib)); +#endif + auto FileType = fromRust(RustFileType); std::string ErrorInfo; @@ -515,6 +506,9 @@ LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMPassManagerRef PMR, return LLVMRustResult::Failure; } + // TargetMachine::addPassesToEmitFile stores pointers to the output streams + // in a couple of places inside of the object. Explicitly delete the PM after + // we call run() to avoid dangling references. auto BOS = buffer_ostream(OS); if (DwoPath) { auto DOS = raw_fd_ostream(DwoPath, EC, sys::fs::OF_None); @@ -528,15 +522,13 @@ LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMPassManagerRef PMR, auto DBOS = buffer_ostream(DOS); unwrap(Target)->addPassesToEmitFile(*PM, BOS, &DBOS, FileType, !VerifyIR); PM->run(*unwrap(M)); + PM.reset(); } else { unwrap(Target)->addPassesToEmitFile(*PM, BOS, nullptr, FileType, !VerifyIR); PM->run(*unwrap(M)); + PM.reset(); } - // Apparently `addPassesToEmitFile` adds a pointer to our on-the-stack output - // stream (OS), so the only real safe place to delete this is here? Don't we - // wish this was written in Rust? - LLVMDisposePassManager(PMR); return LLVMRustResult::Success; } @@ -719,44 +711,25 @@ extern "C" LLVMRustResult LLVMRustOptimize( } std::optional PGOOpt; -#if LLVM_VERSION_LT(22, 0) - auto FS = vfs::getRealFileSystem(); -#endif if (PGOGenPath) { assert(!PGOUsePath && !PGOSampleUsePath); PGOOpt = PGOOptions( -#if LLVM_VERSION_GE(22, 0) PGOGenPath, "", "", "", PGOOptions::IRInstr, PGOOptions::NoCSAction, -#else - PGOGenPath, "", "", "", FS, PGOOptions::IRInstr, PGOOptions::NoCSAction, -#endif PGOOptions::ColdFuncOpt::Default, DebugInfoForProfiling); } else if (PGOUsePath) { assert(!PGOSampleUsePath); PGOOpt = PGOOptions( -#if LLVM_VERSION_GE(22, 0) PGOUsePath, "", "", "", PGOOptions::IRUse, PGOOptions::NoCSAction, -#else - PGOUsePath, "", "", "", FS, PGOOptions::IRUse, PGOOptions::NoCSAction, -#endif PGOOptions::ColdFuncOpt::Default, DebugInfoForProfiling); } else if (PGOSampleUsePath) { PGOOpt = -#if LLVM_VERSION_GE(22, 0) PGOOptions(PGOSampleUsePath, "", "", "", PGOOptions::SampleUse, -#else - PGOOptions(PGOSampleUsePath, "", "", "", FS, PGOOptions::SampleUse, -#endif PGOOptions::NoCSAction, PGOOptions::ColdFuncOpt::Default, DebugInfoForProfiling); } else if (DebugInfoForProfiling) { - PGOOpt = PGOOptions( -#if LLVM_VERSION_GE(22, 0) - "", "", "", "", PGOOptions::NoAction, PGOOptions::NoCSAction, -#else - "", "", "", "", FS, PGOOptions::NoAction, PGOOptions::NoCSAction, -#endif - PGOOptions::ColdFuncOpt::Default, DebugInfoForProfiling); + PGOOpt = + PGOOptions("", "", "", "", PGOOptions::NoAction, PGOOptions::NoCSAction, + PGOOptions::ColdFuncOpt::Default, DebugInfoForProfiling); } auto PB = PassBuilder(TM, PTO, PGOOpt, &PIC); @@ -774,7 +747,11 @@ extern "C" LLVMRustResult LLVMRustOptimize( SmallVector Plugins; PluginsStr.split(Plugins, ',', -1, false); for (auto PluginPath : Plugins) { +#if LLVM_VERSION_GE(24, 0) + auto Plugin = PassPlugin::load(PluginPath); +#else auto Plugin = PassPlugin::Load(PluginPath.str()); +#endif if (!Plugin) { auto Err = Plugin.takeError(); auto ErrMsg = llvm::toString(std::move(Err)); @@ -1383,11 +1360,7 @@ LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules, size_t num_modules, // being lifted from `lib/LTO/LTO.cpp` as well DenseMap PrevailingCopy; for (auto &I : Ret->Index) { -#if LLVM_VERSION_GE(22, 0) const auto &SummaryList = I.second.getSummaryList(); -#else - const auto &SummaryList = I.second.SummaryList; -#endif if (SummaryList.size() > 1) PrevailingCopy[I.first] = getFirstDefinitionForLinker(SummaryList); } @@ -1420,11 +1393,7 @@ LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules, size_t num_modules, // linkage will stay as external, and internal will stay as internal. std::set ExportedGUIDs; for (auto &List : Ret->Index) { -#if LLVM_VERSION_GE(22, 0) const auto &SummaryList = List.second.getSummaryList(); -#else - const auto &SummaryList = List.second.SummaryList; -#endif for (auto &GVS : SummaryList) { if (GlobalValue::isLocalLinkage(GVS->linkage())) continue; diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 161b5bdb952d3..62aa8c5c57aae 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -195,16 +195,6 @@ LLVMRustVerifyFunction(LLVMValueRef Fn, LLVMRustVerifierFailureAction Action) { return LLVMVerifyFunction(Fn, fromRust(Action)); } -extern "C" LLVMValueRef LLVMRustGetOrInsertFunction(LLVMModuleRef M, - const char *Name, - size_t NameLen, - LLVMTypeRef FunctionTy) { - return wrap(unwrap(M) - ->getOrInsertFunction(StringRef(Name, NameLen), - unwrap(FunctionTy)) - .getCallee()); -} - // Get the global variable with the given name if it exists or create a new // external global. extern "C" LLVMValueRef @@ -480,6 +470,11 @@ extern "C" LLVMAttributeRef LLVMRustCreateByValAttr(LLVMContextRef C, return wrap(Attribute::getWithByValType(*unwrap(C), unwrap(Ty))); } +extern "C" LLVMAttributeRef LLVMRustCreateByRefAttr(LLVMContextRef C, + LLVMTypeRef Ty) { + return wrap(Attribute::getWithByRefType(*unwrap(C), unwrap(Ty))); +} + extern "C" LLVMAttributeRef LLVMRustCreateStructRetAttr(LLVMContextRef C, LLVMTypeRef Ty) { return wrap(Attribute::getWithStructRetType(*unwrap(C), unwrap(Ty))); @@ -1007,11 +1002,6 @@ extern "C" void LLVMRustRemoveFnAttribute(LLVMValueRef Fn, const char *Name, } } -extern "C" void LLVMRustGlobalAddMetadata(LLVMValueRef Global, unsigned Kind, - LLVMMetadataRef MD) { - unwrap(Global)->addMetadata(Kind, *unwrap(MD)); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateCompileUnit( LLVMDIBuilderRef Builder, unsigned Lang, LLVMMetadataRef FileRef, const char *Producer, size_t ProducerLen, bool isOptimized, @@ -1159,16 +1149,10 @@ LLVMRustDICreateVectorType(LLVMDIBuilderRef Builder, uint64_t Size, uint32_t AlignInBits, LLVMMetadataRef Type, LLVMMetadataRef Subscripts, LLVMMetadataRef BitStride) { -#if LLVM_VERSION_GE(22, 0) return wrap(unwrap(Builder)->createVectorType( Size, AlignInBits, unwrapDI(Type), DINodeArray(unwrapDI(Subscripts)), unwrapDI(BitStride))); -#else - return wrap(unwrap(Builder)->createVectorType( - Size, AlignInBits, unwrapDI(Type), - DINodeArray(unwrapDI(Subscripts)))); -#endif } extern "C" LLVMMetadataRef @@ -1557,13 +1541,11 @@ extern "C" void LLVMRustContextConfigureDiagnosticHandler( RemarkStreamer(std::move(RemarkStreamer)), LlvmRemarkStreamer(std::move(LlvmRemarkStreamer)) {} -#if LLVM_VERSION_GE(22, 0) ~RustDiagnosticHandler() { if (RemarkStreamer) { RemarkStreamer->releaseSerializer(); } } -#endif virtual bool handleDiagnostics(const DiagnosticInfo &DI) override { // If this diagnostic is one of the optimization remark kinds, we can @@ -1659,14 +1641,8 @@ extern "C" void LLVMRustContextConfigureDiagnosticHandler( // Do not delete the file after we gather remarks RemarkFile->keep(); -#if LLVM_VERSION_GE(22, 0) auto RemarkSerializer = remarks::createRemarkSerializer( llvm::remarks::Format::YAML, RemarkFile->os()); -#else - auto RemarkSerializer = remarks::createRemarkSerializer( - llvm::remarks::Format::YAML, remarks::SerializerMode::Separate, - RemarkFile->os()); -#endif if (Error E = RemarkSerializer.takeError()) { std::string Error = std::string("Cannot create remark serializer: ") + toString(std::move(E)); @@ -1772,16 +1748,12 @@ extern "C" LLVMValueRef LLVMRustConstPtrAuth(LLVMValueRef Ptr, uint32_t Key, AddrDiversity ? dyn_cast(unwrap(AddrDiversity)) : ConstantPointerNull::get(cast(C->getType())); assert(AddrDiv && "Failed to get Address Diversity"); -#if LLVM_VERSION_GE(22, 0) Constant *DeactivationSym = DeactivationSymbol ? dyn_cast(unwrap(DeactivationSymbol)) : ConstantPointerNull::get(PTy); assert(DeactivationSym && "Failed to get Deactivation Symbol"); return wrap(ConstantPtrAuth::get(C, KeyC, DiscC, AddrDiv, DeactivationSym)); -#else - return wrap(ConstantPtrAuth::get(C, KeyC, DiscC, AddrDiv)); -#endif } // Statically assert that the fixed metadata kind IDs declared in @@ -1880,7 +1852,6 @@ class RustSanitizerSpecialCaseList : public llvm::SpecialCaseList { std::vector SanitizerSections; -#if LLVM_VERSION_GE(22, 0) static bool matchSection(const Section &S, llvm::StringRef Name) { return S.matchName(Name); } @@ -1889,24 +1860,9 @@ class RustSanitizerSpecialCaseList : public llvm::SpecialCaseList { return S.getLastMatch(Prefix, Query, Category); } static unsigned getFileIndex(const Section &S) { return S.fileIndex(); } -#else - static bool matchSection(const Section &S, llvm::StringRef Name) { - return S.SectionMatcher && S.SectionMatcher->match(Name) != 0; - } - unsigned getLastMatch(const Section &S, llvm::StringRef Prefix, - llvm::StringRef Query, llvm::StringRef Category) const { - return llvm::SpecialCaseList::inSectionBlame(S.Entries, Prefix, Query, - Category); - } - static unsigned getFileIndex(const Section &S) { return S.FileIdx; } -#endif void createSanitizerSections() { -#if LLVM_VERSION_GE(22, 0) const auto &SecList = sections(); -#else - const auto &SecList = Sections; -#endif for (const auto &S : SecList) { uint32_t Mask = 0; diff --git a/compiler/rustc_llvm/llvm-wrapper/offload/OffloadWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/offload/OffloadWrapper.cpp index bed54da0a7045..ca4d181179ffd 100644 --- a/compiler/rustc_llvm/llvm-wrapper/offload/OffloadWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/offload/OffloadWrapper.cpp @@ -28,7 +28,6 @@ #include "llvm/Target/TargetOptions.h" #include "llvm/TargetParser/Triple.h" #include "llvm/Transforms/Utils/Cloning.h" -#include "llvm/Transforms/Utils/ModuleUtils.h" #include "llvm/Transforms/Utils/ValueMapper.h" #include @@ -88,22 +87,6 @@ extern "C" bool LLVMRustBundleImages(LLVMModuleRef M, TargetMachine &TM, return true; } -extern "C" bool LLVMRustOffloadEmbedBufferInModule(LLVMModuleRef HostM, - const char *HostOutPath) { - auto MBOrErr = MemoryBuffer::getFile(HostOutPath); - if (!MBOrErr) { - auto E = MBOrErr.getError(); - auto _B = errorCodeToError(E); - return false; - } - MemoryBufferRef Buf = (*MBOrErr)->getMemBufferRef(); - Module *M = unwrap(HostM); - StringRef SectionName = ".llvm.offloading"; - Align Alignment = Align(8); - llvm::embedBufferInModule(*M, Buf, SectionName, Alignment); - return true; -} - // Clone OldFn into NewFn, remapping its arguments to RebuiltArgs. // Each arg of OldFn is replaced with the corresponding value in RebuiltArgs. // For scalars, RebuiltArgs contains the value cast and/or truncated to the diff --git a/compiler/rustc_log/src/lib.rs b/compiler/rustc_log/src/lib.rs index 9976690a0c1f1..fdf9c1be22067 100644 --- a/compiler/rustc_log/src/lib.rs +++ b/compiler/rustc_log/src/lib.rs @@ -130,25 +130,10 @@ where Err(VarError::NotUnicode(_value)) => return Err(Error::NonUnicodeColorValue), }; - let verbose_entry_exit = match cfg.verbose_entry_exit { - Ok(v) => &v != "0", - Err(_) => false, - }; - - let verbose_thread_ids = match cfg.verbose_thread_ids { - Ok(v) => &v == "1", - Err(_) => false, - }; - - let lines = match cfg.lines { - Ok(v) => &v == "1", - Err(_) => false, - }; - - let json = match cfg.json { - Ok(v) => &v == "1", - Err(_) => false, - }; + let verbose_entry_exit = cfg.verbose_entry_exit.is_ok_and(|v| v != "0"); + let verbose_thread_ids = cfg.verbose_thread_ids.is_ok_and(|v| v == "1"); + let lines = cfg.lines.is_ok_and(|v| v == "1"); + let json = cfg.json.is_ok_and(|v| v == "1"); let output_target: BoxMakeWriter = match cfg.output_target { Ok(v) => match File::options().create(true).write(true).truncate(true).open(&v) { diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index 72e7423fc2d88..5bc15eb3d2a5c 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -48,13 +48,13 @@ impl<'a> DiagnosticDerive<'a> { // A lifetime of `'a` causes conflicts, but `_sess` is fine. structure.gen_impl(quote! { - gen impl<'_sess, G> rustc_errors::Diagnostic<'_sess, G> for @Self { + gen impl<'_sess> rustc_errors::Diagnostic<'_sess> for @Self { #[track_caller] fn into_diag( self, dcx: rustc_errors::DiagCtxtHandle<'_sess>, level: rustc_errors::Level - ) -> rustc_errors::Diag<'_sess, G> { + ) -> rustc_errors::Diag<'_sess> { #implementation } } diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index cdff1280d69f9..f071028e5937d 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -347,11 +347,16 @@ impl DiagnosticDeriveVariantBuilder { match (&attr.meta, name.as_str()) { (Meta::Path(_), "primary_span") => { report_error_if_not_applied_to_span(attr, &info)?; - return Ok(quote! { diag.span(#binding); }); } + (Meta::Path(_), "context") => { + report_error_if_not_applied_to_span(attr, &info)?; + return Ok(quote! { + diag.span_context(#binding); + }); + } (Meta::Path(_), "subdiagnostic") => { return Ok(quote! { diag.subdiagnostic(#binding); }); } diff --git a/compiler/rustc_macros/src/diagnostics/message.rs b/compiler/rustc_macros/src/diagnostics/message.rs index 8eff9a4fa9e8a..404404bbc1cd0 100644 --- a/compiler/rustc_macros/src/diagnostics/message.rs +++ b/compiler/rustc_macros/src/diagnostics/message.rs @@ -173,6 +173,9 @@ fn verify_message_formatting(attr_span: Span, msg_span: Span, message: &str) { if line.is_empty() { continue; } + if line.trim().starts_with("{") && line.contains(&"found ") { + continue; + } let indent = line.chars().take_while(|c| *c == ' ').count(); if indent < start { span_err( diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index d6b101ca0addf..058d93e83719a 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -94,10 +94,7 @@ impl SubdiagnosticDerive { let ret = structure.gen_impl(quote! { gen impl rustc_errors::Subdiagnostic for @Self { - fn add_to_diag<__G>( - self, - #diag: &mut rustc_errors::Diag<'_, __G>, - ) { + fn add_to_diag(self, #diag: &mut rustc_errors::Diag<'_>) { #implementation } } @@ -318,7 +315,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { let binding = info.binding.binding.clone(); // FIXME(#100717): support `Option` on `primary_span` like in the // diagnostic derive - if !matches!(info.ty, FieldInnerTy::Plain(_)) { + if !matches!(info.ty, FieldInnerTy::Plain(_) | FieldInnerTy::Option(_)) { throw_invalid_attr!(attr, |diag| { let diag = diag.note("there must be exactly one primary span"); diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index f632862dc4627..fb918c2fba487 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -3,7 +3,6 @@ rustc::default_hash_types, reason = "we like performance but can't use `rustc_data_structures`" )] -#![cfg_attr(bootstrap, feature(never_type))] #![deny( rustc::potential_query_instability, reason = "macros shall produce deterministic output/errors" @@ -190,6 +189,7 @@ decl_derive!( // field attributes primary_span, label, + context, subdiagnostic, suggestion)] => #[doc = "See "] @@ -225,7 +225,7 @@ pub fn msg(input: TokenStream) -> TokenStream { decl_derive! { [PrintAttribute] => /// Derives `PrintAttribute` for `AttributeKind`. - /// This macro is pretty specific to `rustc_hir::attrs` and likely not that useful in + /// This macro is pretty specific to `rustc_attr_ir` and likely not that useful in /// other places. It's deriving something close to `Debug` without printing some extraneous /// things like spans. print_attribute::print_attribute diff --git a/compiler/rustc_metadata/Cargo.toml b/compiler/rustc_metadata/Cargo.toml index a2ef8454ed6a6..878957e879324 100644 --- a/compiler/rustc_metadata/Cargo.toml +++ b/compiler/rustc_metadata/Cargo.toml @@ -10,6 +10,7 @@ libloading = "0.9.0" odht = { version = "0.3.1", features = ["nightly"] } rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } +rustc_attr_ir = { path = "../rustc_attr_ir" } rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_crate_store = { path = "../rustc_crate_store" } rustc_data_structures = { path = "../rustc_data_structures" } @@ -17,6 +18,7 @@ rustc_errors = { path = "../rustc_errors" } rustc_expand = { path = "../rustc_expand" } rustc_feature = { path = "../rustc_feature" } rustc_fs_util = { path = "../rustc_fs_util" } +rustc_hashes = { path = "../rustc_hashes" } rustc_hir = { path = "../rustc_hir" } rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_index = { path = "../rustc_index" } @@ -33,6 +35,7 @@ rustc_structures = { path = "../rustc_structures" } rustc_target = { path = "../rustc_target" } tempfile = "3.7.1" tracing = "0.1" +twox-hash = { version = "2", default-features = false, features = ["xxhash3_128", "std"] } # tidy-alphabetical-end [target.'cfg(target_os = "aix")'.dependencies] diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index c828ba13b8af6..10693448ea685 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -14,9 +14,8 @@ use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::{self, FreezeReadGuard, FreezeWriteGuard}; use rustc_data_structures::unord::UnordMap; use rustc_expand::base::SyntaxExtension; -use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE, LocalDefId, StableCrateId}; -use rustc_hir::definitions::Definitions; +use rustc_hir::definitions::{DefPathData, Definitions}; use rustc_index::IndexVec; use rustc_lint_defs as lint; use rustc_lint_defs::builtin::UNUSED_CRATE_DEPENDENCIES; @@ -89,7 +88,7 @@ pub enum LoadedMacro { MacroDef { def: MacroDef, ident: Ident, - attrs: Vec, + attrs: Vec, span: Span, edition: Edition, }, @@ -144,6 +143,10 @@ enum CrateOrigin<'a> { parent_private: bool, /// Dependency info about this crate. dep: &'a CrateDep, + /// The dependency's `-C extra-filename`, used to narrow the search for it on disk. Stored + /// separately from `dep` because it is encoded outside the hashed crate root; see + /// `CrateRootUnhashed::dep_extra_filenames`. + dep_extra_filename: &'a str, }, /// Injected by `rustc`. Injected, @@ -170,6 +173,14 @@ impl<'a> CrateOrigin<'a> { } } + /// Return the dependency's `-C extra-filename`, if any. + fn dep_extra_filename(&self) -> Option<&'a str> { + match self { + CrateOrigin::IndirectDependency { dep_extra_filename, .. } => Some(dep_extra_filename), + _ => None, + } + } + /// `Some(true)` if the dependency is private or its parent is private, `Some(false)` if the /// dependency is not private, `None` if it could not be determined. fn private_dep(&self) -> Option { @@ -583,7 +594,8 @@ impl CStore { let Library { source, metadata } = lib; let crate_root = metadata.get_root(); - let host_hash = host_lib.as_ref().map(|lib| lib.metadata.get_root().hash()); + let unhashed = metadata.get_root_unhashed(); + let host_hash = host_lib.as_ref().map(|lib| lib.metadata.get_crate_hash()); let private_dep = self.is_private_dep(&tcx.sess.opts.externs, name, private_dep); // Claim this crate number and cache it @@ -636,6 +648,7 @@ impl CStore { tcx, metadata, crate_root, + unhashed, raw_proc_macros, cnum, cnum_map, @@ -778,7 +791,7 @@ impl CStore { let dep = origin.dep(); let hash = dep.map(|d| d.hash); let host_hash = dep.map(|d| d.host_hash).flatten(); - let extra_filename = dep.map(|d| &d.extra_filename[..]); + let extra_filename = origin.dep_extra_filename(); let path_kind = if dep.is_some() { PathKind::Dependency } else { PathKind::Crate }; let private_dep = origin.private_dep(); @@ -859,10 +872,11 @@ impl CStore { // against a hash, we could load a crate which has the same hash // as an already loaded crate. If this is the case prevent // duplicates by just using the first crate. - let root = library.metadata.get_root(); + let root_name = library.metadata.get_root().name(); + let root_hash = library.metadata.get_crate_hash(); let mut result = LoadResult::Loaded(library); for (cnum, data) in self.iter_crate_data() { - if data.name() == root.name() && root.hash() == data.hash() { + if data.name() == root_name && data.hash() == root_hash { assert!(locator.hash.is_none()); info!("load success, going to previous cnum: {}", cnum); result = LoadResult::Previous(cnum); @@ -896,15 +910,26 @@ impl CStore { // We map 0 and all other holes in the map to our parent crate. The "additional" // self-dependencies should be harmless. let deps = crate_root.decode_crate_deps(metadata); + // Encoded outside the hashed crate root; see `CrateRootUnhashed::dep_extra_filenames`. + // Holds one entry per dep after the unused `LOCAL_CRATE` slot, so it lines up with `deps` + // once that slot is skipped. + let dep_extra_filenames = metadata.get_dep_extra_filenames(); + assert_eq!( + dep_extra_filenames.len(), + deps.len() + 1, + "expected one dep_extra_filename per crate dep, plus the unused LOCAL_CRATE slot", + ); let mut crate_num_map = CrateNumMap::with_capacity(1 + deps.len()); crate_num_map.push(krate); - for dep in deps { + for (dep, dep_extra_filename) in + deps.zip(dep_extra_filenames.iter().skip(1).map(String::as_str)) + { info!( "resolving dep `{}`->`{}` hash: `{}` extra filename: `{}` private {}", crate_root.name(), dep.name, dep.hash, - dep.extra_filename, + dep_extra_filename, dep.is_private, ); let dep_kind = match dep_kind { @@ -919,6 +944,7 @@ impl CStore { dep_root_for_errors, parent_private: parent_is_private, dep: &dep, + dep_extra_filename, }, )?; crate_num_map.push(cnum); @@ -1332,14 +1358,24 @@ impl CStore { let cnum = self.resolve_crate(tcx, name, item.span, dep_kind, CrateOrigin::Extern)?; - let path_len = definitions.def_path(def_id).data.len(); + let def_path = definitions.def_path(def_id); + // An extern crate is only globally nameable if every segment in its path + // is in the type namespace (i.e., it is purely nested inside modules). + // If any segment is in the value namespace (e.g. inside a fn or const), + // it is unnameable from outside that block. + let is_unnameable = + def_path.data.iter().any(|d| !matches!(d.data, DefPathData::TypeNs(_))); self.update_extern_crate( cnum, name, ExternCrate { - src: ExternCrateSource::Extern(def_id.to_def_id()), + src: if is_unnameable { + ExternCrateSource::Path + } else { + ExternCrateSource::Extern(def_id.to_def_id()) + }, span: item.span, - path_len, + path_len: if is_unnameable { usize::MAX } else { def_path.data.len() }, dependency_of: LOCAL_CRATE, }, ); diff --git a/compiler/rustc_metadata/src/diagnostics.rs b/compiler/rustc_metadata/src/diagnostics.rs index a093e7990ea28..18796f51375a1 100644 --- a/compiler/rustc_metadata/src/diagnostics.rs +++ b/compiler/rustc_metadata/src/diagnostics.rs @@ -305,8 +305,8 @@ pub(crate) struct MultipleCandidates { pub candidates: Vec, } -impl Diagnostic<'_, G> for MultipleCandidates { - fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { +impl Diagnostic<'_> for MultipleCandidates { + fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_> { let mut diag = Diag::new( dcx, level, @@ -418,9 +418,9 @@ pub(crate) struct InvalidMetadataFiles { pub crate_rejections: Vec, } -impl Diagnostic<'_, G> for InvalidMetadataFiles { +impl Diagnostic<'_> for InvalidMetadataFiles { #[track_caller] - fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_> { let mut diag = Diag::new( dcx, level, @@ -450,9 +450,9 @@ pub(crate) struct CannotFindCrate { pub is_tier_3: bool, } -impl Diagnostic<'_, G> for CannotFindCrate { +impl Diagnostic<'_> for CannotFindCrate { #[track_caller] - fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_> { let mut diag = Diag::new(dcx, level, msg!("can't find crate for `{$crate_name}`{$add_info}")); diag.arg("crate_name", self.crate_name); diff --git a/compiler/rustc_metadata/src/eii.rs b/compiler/rustc_metadata/src/eii.rs index 47e982a315395..d7942e2085522 100644 --- a/compiler/rustc_metadata/src/eii.rs +++ b/compiler/rustc_metadata/src/eii.rs @@ -1,7 +1,6 @@ +use rustc_attr_ir::{EiiDecl, EiiImpl, EiiImplResolution, find_attr}; use rustc_data_structures::fx::FxIndexMap; -use rustc_hir::attrs::{EiiDecl, EiiImpl, EiiImplResolution}; use rustc_hir::def_id::DefId; -use rustc_hir::find_attr; use rustc_middle::query::LocalCrate; use rustc_middle::ty::TyCtxt; use rustc_span::bug; diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs index dd963fc8773c7..1706fece29a8b 100644 --- a/compiler/rustc_metadata/src/lib.rs +++ b/compiler/rustc_metadata/src/lib.rs @@ -1,6 +1,5 @@ // tidy-alphabetical-start #![allow(internal_features)] -#![cfg_attr(bootstrap, feature(never_type))] #![feature(error_iter)] #![feature(file_buffered)] #![feature(gen_blocks)] diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index 23b51dfbf62c6..38da6561ecb38 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -724,7 +724,7 @@ impl<'a> CrateLocator<'a> { return None; } - let hash = header.hash; + let hash = metadata.get_crate_hash(); if let Some(expected_hash) = self.hash { if hash != expected_hash { info!("Rejecting via hash: expected {} got {}", expected_hash, hash); diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 1c7d865e41234..6cb6ad0015f92 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -1,12 +1,11 @@ use rustc_abi::ExternAbi; +use rustc_attr_ir::{PeImportNameType, find_attr}; use rustc_attr_parsing::eval_config_entry; use rustc_crate_store::{ DllCallingConvention, DllImport, DllImportSymbolType, ForeignModule, NativeLib, }; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::attrs::PeImportNameType; use rustc_hir::def::DefKind; -use rustc_hir::find_attr; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::query::LocalCrate; use rustc_middle::ty::{self, List, Ty, TyCtxt}; diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index a73e94dd076e7..5348ee0ccaac1 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -7,6 +7,8 @@ use std::{io, mem}; pub(super) use cstore_impl::provide; use rustc_ast as ast; +use rustc_attr_ir::CanonicalSymbols; +use rustc_attr_ir::diagnostic_items::DiagnosticItems; use rustc_crate_store::{CrateSource, ExternCrate}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxIndexMap; @@ -16,8 +18,6 @@ use rustc_data_structures::unhash::UnhashMap; use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind}; use rustc_expand::proc_macro::{AttrProcMacro, BangProcMacro, DeriveProcMacro}; use rustc_hir::Safety; -use rustc_hir::attrs::CanonicalSymbols; -use rustc_hir::attrs::diagnostic_items::DiagnosticItems; use rustc_hir::def::Res; use rustc_hir::def_id::{CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::definitions::{DefPath, DefPathData}; @@ -95,6 +95,8 @@ pub(crate) struct CrateMetadata { // --- Some data pre-decoded from the metadata blob, usually for performance --- /// Data about the top-level items in a crate, as well as various crate-level metadata. root: CrateRoot, + /// Crate-level metadata that is not in the SVH. + unhashed: CrateRootUnhashed, /// Trait impl data. /// FIXME: Used only from queries and can use query cache, /// so pre-decoding can probably be avoided. @@ -187,7 +189,7 @@ pub(super) trait LazyDecoder: BlobDecoder { self.read_lazy_offset_then(|pos| LazyArray::from_position_and_num_elems(pos, len)) } - fn read_lazy_table(&mut self, width: usize, len: usize) -> LazyTable { + fn read_lazy_table(&mut self, width: usize, len: usize) -> LazyTable { self.read_lazy_offset_then(|pos| LazyTable::from_position_and_encoded_size(pos, width, len)) } @@ -665,7 +667,7 @@ impl Decodable for LazyArray { } } -impl Decodable for LazyTable { +impl Decodable for LazyTable { fn decode(decoder: &mut D) -> Self { let width = decoder.read_usize(); let len = decoder.read_usize(); @@ -694,8 +696,7 @@ impl MetadataBlob { } let found_version = - LazyValue::::from_position(NonZero::new(METADATA_HEADER.len() + 8).unwrap()) - .decode(self); + LazyValue::::from_position(NonZero::new(VERSION_OFFSET).unwrap()).decode(self); if rustc_version(cfg_version) != found_version { return Err(Some(found_version)); } @@ -704,8 +705,13 @@ impl MetadataBlob { } fn root_pos(&self) -> NonZero { - let offset = METADATA_HEADER.len(); - let pos_bytes = self[offset..][..8].try_into().unwrap(); + let pos_bytes = self[ROOT_POS_OFFSET..][..8].try_into().unwrap(); + let pos = u64::from_le_bytes(pos_bytes); + NonZero::new(pos as usize).unwrap() + } + + fn unhashed_pos(&self) -> NonZero { + let pos_bytes = self[UNHASHED_POS_OFFSET..][..8].try_into().unwrap(); let pos = u64::from_le_bytes(pos_bytes); NonZero::new(pos as usize).unwrap() } @@ -720,12 +726,28 @@ impl MetadataBlob { LazyValue::::from_position(pos).decode(self) } + pub(crate) fn get_root_unhashed(&self) -> CrateRootUnhashed { + let pos = self.unhashed_pos(); + LazyValue::::from_position(pos).decode(self) + } + + pub(crate) fn get_dep_extra_filenames(&self) -> IndexVec { + self.get_root_unhashed().dep_extra_filenames + } + + pub(crate) fn get_crate_hash(&self) -> Svh { + let bytes: [u8; CRATE_HASH_LEN] = + self[CRATE_HASH_OFFSET..][..CRATE_HASH_LEN].try_into().unwrap(); + Svh::new(Fingerprint::from_le_bytes(bytes)) + } + pub(crate) fn list_crate_metadata( &self, out: &mut dyn io::Write, ls_kinds: &[String], ) -> io::Result<()> { let root = self.get_root(); + let extra_filename = self.get_root_unhashed().extra_filename; let all_ls_kinds = vec![ "root".to_owned(), @@ -740,11 +762,11 @@ impl MetadataBlob { match &**kind { "root" => { writeln!(out, "Crate info:")?; - writeln!(out, "name {}{}", root.name(), root.extra_filename)?; + writeln!(out, "name {}{}", root.name(), extra_filename)?; writeln!( out, "hash {} stable_crate_id {:?}", - root.hash(), + self.get_crate_hash(), root.stable_crate_id )?; writeln!(out, "proc_macro {:?}", root.proc_macro_data.is_some())?; @@ -778,10 +800,15 @@ impl MetadataBlob { writeln!(out, "=External Dependencies=")?; let dylib_dependency_formats = root.dylib_dependency_formats.decode(self).collect::>(); + // `extra_filename` is stored outside the hashed root; see + // `CrateRootUnhashed::dep_extra_filenames`. + let dep_extra_filenames = self.get_dep_extra_filenames(); for (i, dep) in root.crate_deps.decode(self).enumerate() { - let CrateDep { name, extra_filename, hash, host_hash, kind, is_private } = - dep; + let CrateDep { name, hash, host_hash, kind, is_private } = dep; let number = i + 1; + let extra_filename = dep_extra_filenames + .get(CrateNum::new(number)) + .map_or("", |name| name.as_str()); writeln!( out, @@ -952,10 +979,6 @@ impl CrateRoot { self.header.name } - pub(crate) fn hash(&self) -> Svh { - self.header.hash - } - pub(crate) fn stable_crate_id(&self) -> StableCrateId { self.stable_crate_id } @@ -1424,7 +1447,7 @@ impl CrateMetadata { &self, tcx: TyCtxt<'_>, id: DefIndex, - ) -> impl Iterator { + ) -> impl Iterator { self.root .tables .attributes @@ -1436,7 +1459,6 @@ impl CrateMetadata { // Structure and variant constructors don't have any attributes encoded for them, // but we assume that someone passing a constructor ID actually wants to look at // the attributes on the corresponding struct or variant. - assert_eq!(def_key.disambiguated_data.data, DefPathData::Ctor); let parent_id = def_key.parent.expect("no parent for a constructor"); self.root .tables @@ -1522,10 +1544,12 @@ impl CrateMetadata { fn get_proc_macro_quoted_span(&self, tcx: TyCtxt<'_>, index: usize) -> Span { self.root - .tables + .proc_macro_data + .as_ref() + .expect("missing proc macro data") .proc_macro_quoted_spans .get(self, index) - .unwrap_or_else(|| panic!("Missing proc macro quoted span: {index:?}")) + .unwrap_or_else(|| panic!("missing proc macro quoted span: {index:?}")) .decode((self, tcx)) } @@ -1931,6 +1955,7 @@ impl CrateMetadata { tcx: TyCtxt<'_>, blob: MetadataBlob, root: CrateRoot, + unhashed: CrateRootUnhashed, raw_proc_macros: Option<&'static [ProcMacroClient]>, cnum: CrateNum, cnum_map: CrateNumMap, @@ -1954,6 +1979,7 @@ impl CrateMetadata { let mut cdata = CrateMetadata { blob, root, + unhashed, trait_impls, incoherent_impls: Default::default(), raw_proc_macros, @@ -2095,7 +2121,7 @@ impl CrateMetadata { } pub(crate) fn hash(&self) -> Svh { - self.root.header.hash + self.blob.get_crate_hash() } pub(crate) fn has_async_drops(&self) -> bool { diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index f1a4ed09b3516..154cf4f060a9e 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -2,9 +2,9 @@ use std::any::Any; use std::mem; use std::sync::Arc; +use rustc_attr_ir::Deprecation; use rustc_crate_store::{CrateStore, ExternCrate}; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::attrs::Deprecation; use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE}; use rustc_hir::definitions::{DefKey, DefPath, DefPathHash}; @@ -376,12 +376,12 @@ provide! { tcx, def_id, other, cdata, } native_libraries => { cdata.get_native_libraries(tcx).collect() } foreign_modules => { cdata.get_foreign_modules(tcx).map(|m| (m.def_id, m)).collect() } - crate_hash => { cdata.root.header.hash } + crate_hash => { cdata.hash() } crate_host_hash => { cdata.host_hash } crate_name => { cdata.root.header.name } num_extern_def_ids => { cdata.num_def_ids() } - extra_filename => { tcx.arena.alloc_str(&cdata.root.extra_filename) } + extra_filename => { tcx.arena.alloc_str(&cdata.unhashed.extra_filename) } traits => { tcx.arena.alloc_from_iter(cdata.get_traits(tcx)) } trait_impls_in_crate => { tcx.arena.alloc_from_iter(cdata.get_trait_impls(tcx)) } diff --git a/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs b/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs index 949d7630f6736..61ec7ad4cf7b3 100644 --- a/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs +++ b/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs @@ -1,5 +1,8 @@ use rustc_data_structures::owned_slice::OwnedSlice; -use rustc_hir::def_path_hash_map::{Config as HashMapConfig, DefPathHashMap}; +use rustc_data_structures::sorted_map::SortedMap; +use rustc_hashes::Hash64; +use rustc_hir::def_path_hash_map::Config as HashMapConfig; +use rustc_hir::definitions::DefPathToIndexMap; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use rustc_span::def_id::{DefIndex, DefPathHash}; @@ -7,8 +10,8 @@ use crate::rmeta::EncodeContext; use crate::rmeta::decoder::BlobDecodeContext; pub(crate) enum DefPathHashMapRef<'tcx> { - OwnedFromMetadata(odht::HashTable), - BorrowedFromTcx(&'tcx DefPathHashMap), + OwnedFromMetadata(odht::HashTable, SortedMap), + BorrowedFromTcx(&'tcx DefPathToIndexMap), } impl DefPathHashMapRef<'_> { @@ -17,8 +20,11 @@ impl DefPathHashMapRef<'_> { &self, def_path_hash: &DefPathHash, ) -> Option { - match *self { - DefPathHashMapRef::OwnedFromMetadata(ref map) => map.get(&def_path_hash.local_hash()), + match self { + DefPathHashMapRef::OwnedFromMetadata(det_map, non_det_map) => { + let hash = &def_path_hash.local_hash(); + det_map.get(hash).or_else(|| non_det_map.get(hash).copied()) + } DefPathHashMapRef::BorrowedFromTcx(_) => { panic!("DefPathHashMap::BorrowedFromTcx variant only exists for serialization") } @@ -29,12 +35,18 @@ impl DefPathHashMapRef<'_> { impl<'a, 'tcx> Encodable> for DefPathHashMapRef<'tcx> { fn encode(&self, e: &mut EncodeContext<'a, 'tcx>) { match *self { - DefPathHashMapRef::BorrowedFromTcx(def_path_hash_map) => { - let bytes = def_path_hash_map.raw_bytes(); + DefPathHashMapRef::BorrowedFromTcx(map) => { + let bytes = map.before_parallel_alloc.raw_bytes(); e.emit_usize(bytes.len()); e.emit_raw_bytes(bytes); + + map.after_parallel_alloc + .as_ref() + .expect("must be set before metadata encoding") + .range(..) + .encode(e); } - DefPathHashMapRef::OwnedFromMetadata(_) => { + DefPathHashMapRef::OwnedFromMetadata(..) => { panic!("DefPathHashMap::OwnedFromMetadata variant only exists for deserialization") } } @@ -55,6 +67,10 @@ impl<'a> Decodable> for DefPathHashMapRef<'static> { let inner = odht::HashTable::from_raw_bytes(o).unwrap_or_else(|e| { panic!("decode error: {e}"); }); - DefPathHashMapRef::OwnedFromMetadata(inner) + + let elements = Vec::<(Hash64, DefIndex)>::decode(d); + let non_det_map = SortedMap::from_presorted_elements(elements); + + DefPathHashMapRef::OwnedFromMetadata(inner, non_det_map) } } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 8481db9d3c523..03493d0e1b6a9 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -2,23 +2,28 @@ use std::borrow::Borrow; use std::cell::RefCell; use std::collections::hash_map::Entry; use std::fs::File; -use std::io::{Read, Seek, Write}; +use std::io::{Read, Seek, SeekFrom, Write}; +use std::ops::Deref; use std::path::{Path, PathBuf}; use std::rc::Rc; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; +use rustc_attr_ir::{AttributeKind, EncodeCrossCrate, find_attr}; +use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::memmap::{Mmap, MmapMut}; +use rustc_data_structures::owned_slice::slice_owned; +use rustc_data_structures::stable_hash::{StableHash, StableHasher}; use rustc_data_structures::sync::{par_for_each_in, par_join}; use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_data_structures::thousands::usize_with_underscores; use rustc_hir as hir; -use rustc_hir::attrs::{AttributeKind, EncodeCrossCrate}; use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId, LocalDefIdSet}; use rustc_hir::definitions::DefPathData; -use rustc_hir::find_attr; use rustc_hir_pretty::id_to_string; +use rustc_index::IndexVec; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; +use rustc_middle::hir::map::compute_hir_hash; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::mir::interpret; use rustc_middle::query::Providers; @@ -26,7 +31,8 @@ use rustc_middle::traits::specialization_graph; use rustc_middle::ty::AssocContainer; use rustc_middle::ty::codec::TyEncoder; use rustc_middle::ty::fast_reject::{self, TreatParams}; -use rustc_serialize::{Decodable, Decoder, Encodable, Encoder, opaque}; +use rustc_serialize::opaque::FileEncoder; +use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use rustc_session::config::mitigation_coverage::DeniedPartialMitigation; use rustc_session::config::{OptLevel, OutputType, TargetModifier}; use rustc_span::def_id::CRATE_MOD_ID; @@ -37,13 +43,15 @@ use rustc_span::{ }; use rustc_structures::CrateType; use tracing::{debug, instrument, trace}; +use twox_hash::XxHash3_128; use crate::diagnostics::{FailCreateFileEncoder, FailWriteFile}; use crate::eii::EiiMapEncodedKeyValue; use crate::rmeta::*; pub(super) struct EncodeContext<'a, 'tcx> { - opaque: opaque::FileEncoder<'a>, + opaque: FileEncoder<'a>, + metadata_hasher: Arc>, tcx: TyCtxt<'tcx>, feat: &'tcx rustc_feature::Features, tables: TableBuilders, @@ -125,7 +133,7 @@ impl<'a, 'tcx, T> Encodable> for LazyArray { } } -impl<'a, 'tcx, I, T> Encodable> for LazyTable { +impl<'a, 'tcx, Ie, Id, T> Encodable> for LazyTable { fn encode(&self, e: &mut EncodeContext<'a, 'tcx>) { e.emit_usize(self.width); e.emit_usize(self.len); @@ -396,35 +404,42 @@ impl<'a, 'tcx> TyEncoder<'tcx> for EncodeContext<'a, 'tcx> { // Shorthand for `$self.$tables.$table.set_some($def_id.index, $self.lazy($value))`, which would // normally need extra variables to avoid errors about multiple mutable borrows. -macro_rules! record { +// Also those macros greatly simplify refactorings and modifications of writing to tables. +macro_rules! record_some_lazy { ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr) => {{ - { - let value = $value; - let lazy = $self.lazy(value); - $self.$tables.$table.set_some($def_id.index, lazy); - } + let value = $value; + let lazy = $self.lazy(value); + $self.$tables.$table.set_some($def_id.index, lazy); }}; } +macro_rules! record_some { + ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr) => { + $self.$tables.$table.set_some($def_id.index, $value) + }; +} + +macro_rules! record_value { + ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr) => { + $self.$tables.$table.set($def_id.index, $value) + }; +} + // Shorthand for `$self.$tables.$table.set_some($def_id.index, $self.lazy_array($value))`, which would // normally need extra variables to avoid errors about multiple mutable borrows. macro_rules! record_array { ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr) => {{ - { - let value = $value; - let lazy = $self.lazy_array(value); - $self.$tables.$table.set_some($def_id.index, lazy); - } + let value = $value; + let lazy = $self.lazy_array(value); + $self.$tables.$table.set_some($def_id.index, lazy); }}; } macro_rules! record_defaulted_array { ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr) => {{ - { - let value = $value; - let lazy = $self.lazy_array(value); - $self.$tables.$table.set($def_id.index, lazy); - } + let value = $value; + let lazy = $self.lazy_array(value); + $self.$tables.$table.set($def_id.index, lazy); }}; } @@ -519,18 +534,18 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { for def_id in std::iter::once(CRATE_DEF_ID) .chain(self.tcx.resolutions(()).proc_macros.iter().copied()) { - let def_key = self.lazy(defs.def_key(def_id)); + let def_key = defs.def_key(def_id); let def_path_hash = defs.def_path_hash(def_id); - self.tables.def_keys.set_some(def_id.local_def_index, def_key); - self.tables - .def_path_hashes - .set(def_id.local_def_index, def_path_hash.local_hash().as_u64()); + let def_id = def_id.to_def_id(); + + record_some_lazy!(self.tables.def_keys[def_id] <- def_key); + record_value!(self.tables.def_path_hashes[def_id] <- def_path_hash.local_hash().as_u64()) } } else { for (def_index, def_key, def_path_hash) in defs.enumerated_keys_and_path_hashes() { - let def_key = self.lazy(def_key); - self.tables.def_keys.set_some(def_index, def_key); - self.tables.def_path_hashes.set(def_index, def_path_hash.local_hash().as_u64()); + let def_id = LocalDefId { local_def_index: def_index }.to_def_id(); + record_some_lazy!(self.tables.def_keys[def_id] <- def_key); + record_value!(self.tables.def_path_hashes[def_id] <- def_path_hash.local_hash().as_u64()) } } } @@ -539,7 +554,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.lazy(DefPathHashMapRef::BorrowedFromTcx(self.tcx.def_path_hash_to_def_index_map())) } - fn encode_source_map(&mut self) -> LazyTable>> { + fn encode_source_map( + &mut self, + ) -> LazyTable>> { let source_map = self.tcx.sess.source_map(); let all_source_files = source_map.files(); @@ -607,7 +624,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { adapted.encode(&mut self.opaque) } - fn encode_crate_root(&mut self) -> LazyValue { + fn encode_crate_root(&mut self) -> (LazyValue, LazyValue) { let tcx = self.tcx; let mut stats: Vec<(&'static str, usize)> = Vec::with_capacity(32); @@ -728,17 +745,16 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let denied_partial_mitigations = stat!("denied-partial-mitigations", || self .encode_enabled_denied_partial_mitigations()); - let root = stat!("final", || { + let root = stat!("crate-root", || { let attrs = tcx.hir_krate_attrs(); self.lazy(CrateRoot { header: CrateHeader { name: tcx.crate_name(LOCAL_CRATE), triple: tcx.sess.opts.target_triple.clone(), - hash: tcx.crate_hash(LOCAL_CRATE), is_proc_macro_crate: proc_macro_data.is_some(), is_stub: false, }, - extra_filename: tcx.sess.opts.cg.extra_filename.clone(), + stable_crate_id: tcx.stable_crate_id(LOCAL_CRATE), required_panic_strategy: tcx.required_panic_strategy(LOCAL_CRATE), panic_in_drop_strategy: tcx.sess.opts.unstable_opts.panic_in_drop, @@ -790,6 +806,36 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { }) }); + // By default the crate hash (SVH) is derived from the encoded metadata bytes. With + // `-Z metadata-crate-hash=no` we instead store the legacy HIR-based hash, which the + // `crate_hash` query computes directly without consulting `local_crate_hash`. + let hash = if tcx.sess.opts.unstable_opts.metadata_crate_hash { + self.opaque.flush(); + Svh::new(Fingerprint::from_le_bytes( + self.metadata_hasher.lock().unwrap().finish_128().to_le_bytes(), + )) + } else { + tcx.crate_hash(LOCAL_CRATE) + }; + tcx.untracked().local_crate_hash.set(hash).expect("local_crate_hash set twice"); + + let unhashed = stat!("final", || { + // Indexed by dependency `CrateNum`, matching the numbering `encode_crate_deps` uses. + // Slot 0 (`LOCAL_CRATE`) is filler; this crate's own value is `extra_filename`. + let mut dep_extra_filenames = IndexVec::from_elem_n(String::new(), 1); + if !self.is_proc_macro { + for &cnum in self.tcx.crates(()).iter() { + let idx = dep_extra_filenames.push(self.tcx.extra_filename(cnum).to_owned()); + assert_eq!(idx, cnum, "dep_extra_filenames must be indexed by CrateNum"); + } + } + + self.lazy(CrateRootUnhashed { + extra_filename: self.tcx.sess.opts.cg.extra_filename.clone(), + dep_extra_filenames, + }) + }); + let total_bytes = self.position(); let computed_total_bytes: usize = stats.iter().map(|(_, size)| size).sum(); @@ -853,7 +899,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { eprint!("{s}"); } - root + (root, unhashed) } } @@ -872,9 +918,9 @@ struct AnalyzeAttrState { /// visibility: this is a piece of data that can be computed once per defid, and not once per /// attribute. Some attributes would only be usable downstream if they are public. #[inline] -fn analyze_attr(attr: &hir::Attribute, state: &mut AnalyzeAttrState) -> bool { +fn analyze_attr(attr: &rustc_attr_ir::Attribute, state: &mut AnalyzeAttrState) -> bool { let mut should_encode = false; - if let hir::Attribute::Parsed(p) = attr + if let rustc_attr_ir::Attribute::Parsed(p) = attr && p.encode_cross_crate() == EncodeCrossCrate::No { // Attributes not marked encode-cross-crate don't need to be encoded for downstream crates. @@ -883,14 +929,14 @@ fn analyze_attr(attr: &hir::Attribute, state: &mut AnalyzeAttrState) -> bool { { // Lint attributes don't need to be encoded for downstream crates. // FIXME remove this when #152369 is re-merged - } else if let hir::Attribute::Parsed(AttributeKind::DocComment { .. }) = attr { + } else if let rustc_attr_ir::Attribute::Parsed(AttributeKind::DocComment { .. }) = attr { // We keep all doc comments reachable to rustdoc because they might be "imported" into // downstream crates if they use `#[doc(inline)]` to copy an item's documentation into // their own. if state.is_exported { should_encode = true; } - } else if let hir::Attribute::Parsed(AttributeKind::Doc(d)) = attr { + } else if let rustc_attr_ir::Attribute::Parsed(AttributeKind::Doc(d)) = attr { should_encode = true; if d.hidden.is_some() { state.is_doc_hidden = true; @@ -1405,7 +1451,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if state.is_doc_hidden { attr_flags |= AttrFlags::IS_DOC_HIDDEN; } - self.tables.attr_flags.set(def_id.local_def_index, attr_flags); + + record_value!(self.tables.attr_flags[def_id.to_def_id()] <- attr_flags) } fn encode_def_ids(&mut self) { @@ -1422,7 +1469,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { for local_id in tcx.iter_local_def_id() { let def_id = local_id.to_def_id(); let def_kind = tcx.def_kind(local_id); - self.tables.def_kind.set_some(def_id.index, def_kind); + record_some!(self.tables.def_kind[def_id] <- def_kind); // The `DefCollector` will sometimes create unnecessary `DefId`s // for trivial const arguments which are directly lowered to @@ -1443,33 +1490,33 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { && let hir::Node::Field(field) = tcx.hir_node_by_def_id(local_id) && let Some(anon) = field.default { - record!(self.tables.default_fields[def_id] <- anon.def_id.to_def_id()); + record_some_lazy!(self.tables.default_fields[def_id] <- anon.def_id.to_def_id()); } if should_encode_span(def_kind) { let def_span = tcx.def_span(local_id); - record!(self.tables.def_span[def_id] <- def_span); + record_some_lazy!(self.tables.def_span[def_id] <- def_span); } if should_encode_attrs(def_kind) { self.encode_attrs(local_id); } if should_encode_expn_that_defined(def_kind) { - record!(self.tables.expn_that_defined[def_id] <- self.tcx.expn_that_defined(def_id)); + record_some_lazy!(self.tables.expn_that_defined[def_id] <- self.tcx.expn_that_defined(def_id)); } if should_encode_span(def_kind) && let Some(ident_span) = tcx.def_ident_span(def_id) { - record!(self.tables.def_ident_span[def_id] <- ident_span); + record_some_lazy!(self.tables.def_ident_span[def_id] <- ident_span); } if def_kind.has_codegen_attrs() { - record!(self.tables.codegen_fn_attrs[def_id] <- self.tcx.codegen_fn_attrs(def_id)); + record_some_lazy!(self.tables.codegen_fn_attrs[def_id] <- self.tcx.codegen_fn_attrs(def_id)); } if should_encode_visibility(def_kind) { let vis = self .tcx .local_visibility(local_id) .map_id(|mod_id| mod_id.to_local_def_id().local_def_index); - record!(self.tables.visibility[def_id] <- vis); + record_some_lazy!(self.tables.visibility[def_id] <- vis); } if should_encode_stability(def_kind) { self.encode_stability(def_id); @@ -1482,46 +1529,46 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { record_array!(self.tables.variances_of[def_id] <- v); } if should_encode_fn_sig(def_kind) { - record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id)); + record_some_lazy!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id)); } if should_encode_generics(def_kind) { let g = tcx.generics_of(def_id); - record!(self.tables.generics_of[def_id] <- g); - record!(self.tables.explicit_clauses_of[def_id] <- self.tcx.explicit_clauses_of(def_id)); + record_some_lazy!(self.tables.generics_of[def_id] <- g); + record_some_lazy!(self.tables.explicit_clauses_of[def_id] <- self.tcx.explicit_clauses_of(def_id)); let inferred_outlives = self.tcx.inferred_outlives_of(def_id); record_defaulted_array!(self.tables.inferred_outlives_of[def_id] <- inferred_outlives); for param in &g.own_params { if let ty::GenericParamDefKind::Const { has_default: true, .. } = param.kind { let default = self.tcx.const_param_default(param.def_id); - record!(self.tables.const_param_default[param.def_id] <- default); + record_some_lazy!(self.tables.const_param_default[param.def_id] <- default); } } } if tcx.is_conditionally_const(def_id) { - record!(self.tables.const_conditions[def_id] <- self.tcx.const_conditions(def_id)); + record_some_lazy!(self.tables.const_conditions[def_id] <- self.tcx.const_conditions(def_id)); } if should_encode_type(tcx, local_id, def_kind) { - record!(self.tables.type_of[def_id] <- self.tcx.type_of(def_id)); + record_some_lazy!(self.tables.type_of[def_id] <- self.tcx.type_of(def_id)); } if should_encode_constness(def_kind) { let constness = self.tcx.constness(def_id); - self.tables.constness.set(def_id.index, constness); + record_value!(self.tables.constness[def_id] <- constness) } if let DefKind::Fn | DefKind::AssocFn = def_kind { let asyncness = tcx.asyncness(def_id); - self.tables.asyncness.set(def_id.index, asyncness); + record_value!(self.tables.asyncness[def_id] <- asyncness); record_array!(self.tables.fn_arg_idents[def_id] <- tcx.fn_arg_idents(def_id)); } if let Some(name) = tcx.intrinsic(def_id) { - record!(self.tables.intrinsic[def_id] <- name); + record_some_lazy!(self.tables.intrinsic[def_id] <- name); } if let DefKind::TyParam | DefKind::Trait = def_kind { let default = self.tcx.object_lifetime_default(def_id); - record!(self.tables.object_lifetime_default[def_id] <- default); + record_some_lazy!(self.tables.object_lifetime_default[def_id] <- default); } if let DefKind::Trait = def_kind { - record!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id)); + record_some_lazy!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id)); record_defaulted_array!(self.tables.explicit_super_clauses_of[def_id] <- self.tcx.explicit_super_clauses_of(def_id).skip_binder()); record_defaulted_array!(self.tables.explicit_implied_clauses_of[def_id] <- @@ -1535,7 +1582,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } } if let DefKind::TraitAlias = def_kind { - record!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id)); + record_some_lazy!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id)); record_defaulted_array!(self.tables.explicit_super_clauses_of[def_id] <- self.tcx.explicit_super_clauses_of(def_id).skip_binder()); record_defaulted_array!(self.tables.explicit_implied_clauses_of[def_id] <- @@ -1556,28 +1603,28 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if let DefKind::Closure | DefKind::SyntheticCoroutineBody = def_kind && let Some(coroutine_kind) = self.tcx.coroutine_kind(def_id) { - self.tables.coroutine_kind.set(def_id.index, Some(coroutine_kind)) + record_value!(self.tables.coroutine_kind[def_id] <- Some(coroutine_kind)) } if def_kind == DefKind::Closure && tcx.type_of(def_id).skip_binder().is_coroutine_closure() { let coroutine_for_closure = self.tcx.coroutine_for_closure(def_id); - self.tables - .coroutine_for_closure - .set_some(def_id.index, coroutine_for_closure.into()); + record_some!(self.tables.coroutine_for_closure[def_id] <- coroutine_for_closure.into()); // If this async closure has a by-move body, record it too. if tcx.needs_coroutine_by_move_body_def_id(coroutine_for_closure) { - self.tables.coroutine_by_move_body_def_id.set_some( - coroutine_for_closure.index, - self.tcx.coroutine_by_move_body_def_id(coroutine_for_closure).into(), - ); + let id = self.tcx.coroutine_by_move_body_def_id(coroutine_for_closure); + record_some!( + self.tables.coroutine_by_move_body_def_id[coroutine_for_closure] <- id.into() + ) } } if let DefKind::Static { .. } = def_kind { if !self.tcx.is_foreign_item(def_id) { match self.tcx.eval_static_initializer(def_id) { - Ok(data) => record!(self.tables.eval_static_initializer[def_id] <- data), + Ok(data) => { + record_some_lazy!(self.tables.eval_static_initializer[def_id] <- data) + } Err(err) => match err { interpret::ErrorHandled::Reported(_, _) => { self.tcx.dcx().delayed_bug(format!( @@ -1601,50 +1648,49 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.encode_info_for_macro(local_id); } if let DefKind::TyAlias = def_kind { - self.tables - .type_alias_is_checked - .set(def_id.index, self.tcx.type_alias_is_checked(def_id)); + record_value!(self.tables.type_alias_is_checked[def_id] <- self.tcx.type_alias_is_checked(def_id)); + if self.tcx.type_alias_is_checked(def_id) { - record!(self.tables.args_known_to_outlive_alias_params[def_id] <- tcx.args_known_to_outlive_alias_params(def_id)); + record_some_lazy!(self.tables.args_known_to_outlive_alias_params[def_id] <- tcx.args_known_to_outlive_alias_params(def_id)); } } if let DefKind::OpaqueTy = def_kind { self.encode_explicit_item_bounds(def_id); self.encode_explicit_item_self_bounds(def_id); - record!(self.tables.opaque_ty_origin[def_id] <- self.tcx.opaque_ty_origin(def_id)); + record_some_lazy!(self.tables.opaque_ty_origin[def_id] <- self.tcx.opaque_ty_origin(def_id)); self.encode_precise_capturing_args(def_id); if tcx.is_conditionally_const(def_id) { record_defaulted_array!(self.tables.explicit_implied_const_bounds[def_id] <- tcx.explicit_implied_const_bounds(def_id).skip_binder()); } - record!(self.tables.args_known_to_outlive_alias_params[def_id] <- tcx.args_known_to_outlive_alias_params(def_id)); + record_some_lazy!(self.tables.args_known_to_outlive_alias_params[def_id] <- tcx.args_known_to_outlive_alias_params(def_id)); } if let DefKind::AssocTy = def_kind { let assoc_item = tcx.associated_item(def_id); match assoc_item.container { ty::AssocContainer::Trait => { - record!(self.tables.args_known_to_outlive_alias_params[def_id] <- tcx.args_known_to_outlive_alias_params(def_id)); + record_some_lazy!(self.tables.args_known_to_outlive_alias_params[def_id] <- tcx.args_known_to_outlive_alias_params(def_id)); } ty::AssocContainer::InherentImpl => { - record!(self.tables.args_known_to_outlive_alias_params[def_id] <- tcx.args_known_to_outlive_alias_params(def_id)); + record_some_lazy!(self.tables.args_known_to_outlive_alias_params[def_id] <- tcx.args_known_to_outlive_alias_params(def_id)); } ty::AssocContainer::TraitImpl(_) => {} } } if let DefKind::AnonConst = def_kind { - record!(self.tables.anon_const_kind[def_id] <- self.tcx.anon_const_kind(def_id)); + record_some_lazy!(self.tables.anon_const_kind[def_id] <- self.tcx.anon_const_kind(def_id)); } if let DefKind::Const | DefKind::AssocConst = def_kind { - record!(self.tables.const_of_item[def_id] <- self.tcx.const_of_item(def_id)); + record_some_lazy!(self.tables.const_of_item[def_id] <- self.tcx.const_of_item(def_id)); } if tcx.impl_method_has_trait_impl_trait_tys(def_id) && let Ok(table) = self.tcx.collect_return_position_impl_trait_in_trait_tys(def_id) { - record!(self.tables.collect_return_position_impl_trait_in_trait_tys[def_id] <- table); + record_some_lazy!(self.tables.collect_return_position_impl_trait_in_trait_tys[def_id] <- table); } if let DefKind::Impl { .. } | DefKind::Trait = def_kind { let table = tcx.associated_types_for_impl_traits_in_trait_or_impl(def_id); - record!(self.tables.associated_types_for_impl_traits_in_trait_or_impl[def_id] <- table); + record_some_lazy!(self.tables.associated_types_for_impl_traits_in_trait_or_impl[def_id] <- table); } } @@ -1656,7 +1702,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } for (def_id, res_map) in &tcx.resolutions(()).doc_link_resolutions { - record!(self.tables.doc_link_resolutions[def_id.to_def_id()] <- res_map); + record_some_lazy!(self.tables.doc_link_resolutions[def_id.to_def_id()] <- res_map); } for (def_id, traits) in &tcx.resolutions(()).doc_link_traits_in_scope { @@ -1683,10 +1729,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let def_id = local_def_id.to_def_id(); let tcx = self.tcx; let adt_def = tcx.adt_def(def_id); - record!(self.tables.repr_options[def_id] <- adt_def.repr()); + record_some_lazy!(self.tables.repr_options[def_id] <- adt_def.repr()); let params_in_repr = self.tcx.params_in_repr(def_id); - record!(self.tables.params_in_repr[def_id] <- params_in_repr); + record_some_lazy!(self.tables.params_in_repr[def_id] <- params_in_repr); if adt_def.is_enum() { let module_children = tcx.module_children_local(local_def_id); @@ -1706,7 +1752,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { ctor: variant.ctor.map(|(kind, def_id)| (kind, def_id.index)), is_non_exhaustive: variant.is_field_list_non_exhaustive(), }; - record!(self.tables.variant_data[variant.def_id] <- data); + record_some_lazy!(self.tables.variant_data[variant.def_id] <- data); record_array!(self.tables.associated_item_or_field_def_ids[variant.def_id] <- variant.fields.iter().map(|f| { assert!(f.did.is_local()); @@ -1714,8 +1760,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { })); for field in &variant.fields { - self.tables.safety.set(field.did.index, field.safety); - record!( + record_value!(self.tables.safety[field.did] <- field.safety); + record_some_lazy!( self.tables.mut_restriction[field.did] <- field.mut_restriction ); } @@ -1723,16 +1769,16 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if let Some((CtorKind::Fn, ctor_def_id)) = variant.ctor { let fn_sig = tcx.fn_sig(ctor_def_id); // FIXME only encode signature for ctor_def_id - record!(self.tables.fn_sig[variant.def_id] <- fn_sig); + record_some_lazy!(self.tables.fn_sig[variant.def_id] <- fn_sig); } } if let Some(destructor) = tcx.adt_destructor(local_def_id) { - record!(self.tables.adt_destructor[def_id] <- destructor); + record_some_lazy!(self.tables.adt_destructor[def_id] <- destructor); } if let Some(destructor) = tcx.adt_async_destructor(local_def_id) { - record!(self.tables.adt_async_destructor[def_id] <- destructor); + record_some_lazy!(self.tables.adt_async_destructor[def_id] <- destructor); } } @@ -1748,7 +1794,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // items - we encode information about proc-macros later on. if self.is_proc_macro { // Encode this here because we don't do it in encode_def_ids. - record!(self.tables.expn_that_defined[def_id] <- tcx.expn_that_defined(local_def_id)); + record_some_lazy!(self.tables.expn_that_defined[def_id] <- tcx.expn_that_defined(local_def_id)); } else { let module_children = tcx.module_children_local(local_def_id); @@ -1787,10 +1833,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let item = tcx.associated_item(def_id); if matches!(item.container, AssocContainer::Trait | AssocContainer::TraitImpl(_)) { - self.tables.defaultness.set(def_id.index, item.defaultness(tcx)); + record_value!(self.tables.defaultness[def_id] <- item.defaultness(tcx)); } - record!(self.tables.assoc_container[def_id] <- item.container); + record_some_lazy!(self.tables.assoc_container[def_id] <- item.container); if let AssocContainer::Trait = item.container && item.is_type() @@ -1803,7 +1849,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } } if let ty::AssocKind::Type { data: ty::AssocTypeData::Rpitit(rpitit_info) } = item.kind { - record!(self.tables.opt_rpitit_info[def_id] <- rpitit_info); + record_some_lazy!(self.tables.opt_rpitit_info[def_id] <- rpitit_info); if matches!(rpitit_info, ty::ImplTraitInTraitData::Trait { .. }) { record_array!( self.tables.assumed_wf_types_for_rpitit[def_id] @@ -1839,53 +1885,53 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { debug!("EntryBuilder::encode_mir({:?})", def_id); if encode_opt { - record!(self.tables.optimized_mir[def_id.to_def_id()] <- tcx.optimized_mir(def_id)); - self.tables - .cross_crate_inlinable - .set(def_id.to_def_id().index, self.tcx.cross_crate_inlinable(def_id)); - record!(self.tables.closure_saved_names_of_captured_variables[def_id.to_def_id()] + record_some_lazy!(self.tables.optimized_mir[def_id.to_def_id()] <- tcx.optimized_mir(def_id)); + + record_value!(self.tables.cross_crate_inlinable[def_id.to_def_id()] <- self.tcx.cross_crate_inlinable(def_id)); + + record_some_lazy!(self.tables.closure_saved_names_of_captured_variables[def_id.to_def_id()] <- tcx.closure_saved_names_of_captured_variables(def_id)); if self.tcx.is_coroutine(def_id.to_def_id()) && let Some(witnesses) = tcx.mir_coroutine_witnesses(def_id) { - record!(self.tables.mir_coroutine_witnesses[def_id.to_def_id()] <- witnesses); + record_some_lazy!(self.tables.mir_coroutine_witnesses[def_id.to_def_id()] <- witnesses); } } let mut is_trivial = false; if encode_const { if let Some((val, ty)) = tcx.trivial_const(def_id) { is_trivial = true; - record!(self.tables.trivial_const[def_id.to_def_id()] <- (val, ty)); + record_some_lazy!(self.tables.trivial_const[def_id.to_def_id()] <- (val, ty)); } else { is_trivial = false; - record!(self.tables.mir_for_ctfe[def_id.to_def_id()] <- tcx.mir_for_ctfe(def_id)); + record_some_lazy!(self.tables.mir_for_ctfe[def_id.to_def_id()] <- tcx.mir_for_ctfe(def_id)); } // FIXME(generic_const_exprs): this feels wrong to have in `encode_mir` let abstract_const = tcx.thir_abstract_const(def_id); if let Ok(Some(abstract_const)) = abstract_const { - record!(self.tables.thir_abstract_const[def_id.to_def_id()] <- abstract_const); + record_some_lazy!(self.tables.thir_abstract_const[def_id.to_def_id()] <- abstract_const); } if should_encode_const(tcx.def_kind(def_id)) { let qualifs = tcx.mir_const_qualif(def_id); - record!(self.tables.mir_const_qualif[def_id.to_def_id()] <- qualifs); + record_some_lazy!(self.tables.mir_const_qualif[def_id.to_def_id()] <- qualifs); let body = tcx.hir_maybe_body_owned_by(def_id); if let Some(body) = body { let const_data = rendered_const(self.tcx, &body, def_id); - record!(self.tables.rendered_const[def_id.to_def_id()] <- const_data); + record_some_lazy!(self.tables.rendered_const[def_id.to_def_id()] <- const_data); } } } if !is_trivial { - record!(self.tables.promoted_mir[def_id.to_def_id()] <- tcx.promoted_mir(def_id)); + record_some_lazy!(self.tables.promoted_mir[def_id.to_def_id()] <- tcx.promoted_mir(def_id)); } if self.tcx.is_coroutine(def_id.to_def_id()) && let Some(witnesses) = tcx.mir_coroutine_witnesses(def_id) { - record!(self.tables.mir_coroutine_witnesses[def_id.to_def_id()] <- witnesses); + record_some_lazy!(self.tables.mir_coroutine_witnesses[def_id.to_def_id()] <- witnesses); } } @@ -1911,7 +1957,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // the stability attributes are even enabled before using their queries. if self.feat.staged_api() || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked { if let Some(stab) = self.tcx.lookup_stability(def_id) { - record!(self.tables.lookup_stability[def_id] <- stab) + record_some_lazy!(self.tables.lookup_stability[def_id] <- stab) } } } @@ -1922,7 +1968,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // the stability attributes are even enabled before using their queries. if self.feat.staged_api() || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked { if let Some(stab) = self.tcx.lookup_const_stability(def_id) { - record!(self.tables.lookup_const_stability[def_id] <- stab) + record_some_lazy!(self.tables.lookup_const_stability[def_id] <- stab) } } } @@ -1933,7 +1979,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // the stability attributes are even enabled before using their queries. if self.feat.staged_api() || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked { if let Some(stab) = self.tcx.lookup_default_body_stability(def_id) { - record!(self.tables.lookup_default_body_stability[def_id] <- stab) + record_some_lazy!(self.tables.lookup_default_body_stability[def_id] <- stab) } } } @@ -1941,7 +1987,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { #[instrument(level = "debug", skip(self))] fn encode_deprecation(&mut self, def_id: DefId) { if let Some(depr) = self.tcx.lookup_deprecation(def_id) { - record!(self.tables.lookup_deprecation_entry[def_id] <- depr); + record_some_lazy!(self.tables.lookup_deprecation_entry[def_id] <- depr); } } @@ -1950,8 +1996,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let tcx = self.tcx; let (_, macro_def, _) = tcx.hir_expect_item(def_id).expect_macro(); - self.tables.is_macro_rules.set(def_id.local_def_index, macro_def.macro_rules); - record!(self.tables.macro_definition[def_id.to_def_id()] <- &*macro_def.body); + record_value!(self.tables.is_macro_rules[def_id.to_def_id()] <- macro_def.macro_rules); + record_some_lazy!(self.tables.macro_definition[def_id.to_def_id()] <- &*macro_def.body); } fn encode_native_libraries(&mut self) -> LazyArray { @@ -1967,9 +2013,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } fn encode_hygiene(&mut self) -> (SyntaxContextTable, ExpnDataTable, ExpnHashTable) { - let mut syntax_contexts: TableBuilder<_, _> = Default::default(); - let mut expn_data_table: TableBuilder<_, _> = Default::default(); - let mut expn_hash_table: TableBuilder<_, _> = Default::default(); + let mut syntax_contexts: TableBuilder<_, _, _> = Default::default(); + let mut expn_data_table: TableBuilder<_, _, _> = Default::default(); + let mut expn_hash_table: TableBuilder<_, _, _> = Default::default(); HygieneEncodeContext::encode( &Rc::clone(&self.hygiene_ctxt), @@ -2000,24 +2046,20 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let tcx = self.tcx; let proc_macro_decls_static = tcx.proc_macro_decls_static(()).unwrap().local_def_index; let stability = tcx.lookup_stability(CRATE_DEF_ID); - for (i, span) in self.tcx.sess.proc_macro_quoted_spans() { - let span = self.lazy(span); - self.tables.proc_macro_quoted_spans.set_some(i, span); - } - self.tables.def_kind.set_some(LOCAL_CRATE.as_def_id().index, DefKind::Mod); - record!(self.tables.def_span[LOCAL_CRATE.as_def_id()] <- tcx.def_span(LOCAL_CRATE.as_def_id())); + record_some!(self.tables.def_kind[LOCAL_CRATE.as_def_id()] <- DefKind::Mod); + record_some_lazy!(self.tables.def_span[LOCAL_CRATE.as_def_id()] <- tcx.def_span(LOCAL_CRATE.as_def_id())); self.encode_attrs(LOCAL_CRATE.as_def_id().expect_local()); let vis = tcx .local_visibility(CRATE_DEF_ID) .map_id(|mod_id| mod_id.to_local_def_id().local_def_index); - record!(self.tables.visibility[LOCAL_CRATE.as_def_id()] <- vis); + record_some_lazy!(self.tables.visibility[LOCAL_CRATE.as_def_id()] <- vis); if let Some(stability) = stability { - record!(self.tables.lookup_stability[LOCAL_CRATE.as_def_id()] <- stability); + record_some_lazy!(self.tables.lookup_stability[LOCAL_CRATE.as_def_id()] <- stability); } self.encode_deprecation(LOCAL_CRATE.as_def_id()); if let Some(res_map) = tcx.resolutions(()).doc_link_resolutions.get(&CRATE_MOD_ID) { - record!(self.tables.doc_link_resolutions[LOCAL_CRATE.as_def_id()] <- res_map); + record_some_lazy!(self.tables.doc_link_resolutions[LOCAL_CRATE.as_def_id()] <- res_map); } if let Some(traits) = tcx.resolutions(()).doc_link_traits_in_scope.get(&CRATE_MOD_ID) { record_array!(self.tables.doc_link_traits_in_scope[LOCAL_CRATE.as_def_id()] <- traits); @@ -2064,20 +2106,33 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { def_key.disambiguated_data.data = DefPathData::MacroNs(name); let def_id = id.to_def_id(); - self.tables.def_kind.set_some(def_id.index, DefKind::Macro(macro_kind.into())); + record_some!(self.tables.def_kind[def_id] <- DefKind::Macro(macro_kind.into())); + self.encode_attrs(id); - record!(self.tables.def_keys[def_id] <- def_key); - record!(self.tables.def_ident_span[def_id] <- span); - record!(self.tables.def_span[def_id] <- span); - record!(self.tables.visibility[def_id] <- ty::Visibility::Public); + record_some_lazy!(self.tables.def_keys[def_id] <- def_key); + record_some_lazy!(self.tables.def_ident_span[def_id] <- span); + record_some_lazy!(self.tables.def_span[def_id] <- span); + record_some_lazy!(self.tables.visibility[def_id] <- ty::Visibility::Public); if let Some(stability) = stability { - record!(self.tables.lookup_stability[def_id] <- stability); + record_some_lazy!(self.tables.lookup_stability[def_id] <- stability); } } let macros = self.lazy_array(macros); - Some(ProcMacroData { proc_macro_decls_static, stability, macros }) + let mut proc_macro_quoted_spans = TableBuilder::default(); + for (i, span) in self.tcx.sess.proc_macro_quoted_spans() { + proc_macro_quoted_spans.set_some(i, self.lazy(span)); + } + + let proc_macro_quoted_spans = proc_macro_quoted_spans.encode(&mut self.opaque); + + Some(ProcMacroData { + proc_macro_decls_static, + stability, + macros, + proc_macro_quoted_spans, + }) } else { None } @@ -2110,7 +2165,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { hash: self.tcx.crate_hash(cnum), host_hash: self.tcx.crate_host_hash(cnum), kind: self.tcx.crate_dep_kind(cnum), - extra_filename: self.tcx.extra_filename(cnum).to_owned(), is_private: self.tcx.is_private_dep(cnum), }; (cnum, dep) @@ -2225,15 +2279,13 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if of_trait { let header = tcx.impl_trait_header(def_id); - record!(self.tables.impl_trait_header[def_id] <- header); + record_some_lazy!(self.tables.impl_trait_header[def_id] <- header); let impl_is_fully_generic_for_reflection = tcx.impl_is_fully_generic_for_reflection(def_id); - self.tables - .impl_is_fully_generic_for_reflection - .set(def_id.index, impl_is_fully_generic_for_reflection); - self.tables.defaultness.set(def_id.index, tcx.defaultness(def_id)); + record_value!(self.tables.impl_is_fully_generic_for_reflection[def_id] <- impl_is_fully_generic_for_reflection); + record_value!(self.tables.defaultness[def_id] <- tcx.defaultness(def_id)); let trait_ref = header.trait_ref.instantiate_identity().skip_norm_wip(); let simplified_self_ty = fast_reject::simplify_type( @@ -2250,14 +2302,14 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if let Ok(mut an) = trait_def.ancestors(tcx, def_id) && let Some(specialization_graph::Node::Impl(parent)) = an.nth(1) { - self.tables.impl_parent.set_some(def_id.index, parent.into()); + record_some!(self.tables.impl_parent[def_id] <- parent.into()); } // if this is an impl of `CoerceUnsized`, create its // "unsized info", else just store None if tcx.is_lang_item(trait_ref.def_id, LangItem::CoerceUnsized) { let coerce_unsized_info = tcx.coerce_unsized_info(def_id).unwrap(); - record!(self.tables.coerce_unsized_info[def_id] <- coerce_unsized_info); + record_some_lazy!(self.tables.coerce_unsized_info[def_id] <- coerce_unsized_info); } } } @@ -2480,41 +2532,50 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) { // there's no need to do dep-graph tracking for any of it. tcx.dep_graph.assert_ignored(); - // Generate the metadata stub manually, as that is a small file compared to full metadata. - if let Some(ref_path) = ref_path { - let _prof_timer = tcx.prof.verbose_generic_activity("generate_crate_metadata_stub"); - - with_encode_metadata_header(tcx, ref_path, |ecx| { - let header: LazyValue = ecx.lazy(CrateHeader { - name: tcx.crate_name(LOCAL_CRATE), - triple: tcx.sess.opts.target_triple.clone(), - hash: tcx.crate_hash(LOCAL_CRATE), - is_proc_macro_crate: false, - is_stub: true, - }); - header.position.get() - }) - } - let _prof_timer = tcx.prof.verbose_generic_activity("generate_crate_metadata"); let dep_node = tcx.metadata_dep_node(); // If the metadata dep-node is green, try to reuse the saved work product. if tcx.dep_graph.is_fully_enabled() + && let incr_comp_session = tcx.incr_comp_session.unwrap() + && let Some(old_incr_comp_session_dir) = &incr_comp_session.old_session_directory && let work_product_id = WorkProductId::from_cgu_name(WorkProduct::METADATA_WORKPRODUCT_CGU_NAME) && let Some(work_product) = tcx.dep_graph.previous_work_product(&work_product_id) && tcx.dep_graph.try_mark_green(tcx, &dep_node).is_some() { let saved_path = &work_product.saved_files[OutputType::Metadata.extension()]; - let incr_comp_session_dir = &tcx.incr_comp_session.unwrap().session_directory; - let source_file_in_incr_dir = &incr_comp_session_dir.join(saved_path); + let source_file_in_incr_dir = &old_incr_comp_session_dir.join(saved_path); debug!("copying preexisting metadata from {source_file_in_incr_dir:?} to {path:?}"); match rustc_fs_util::link_or_copy(&source_file_in_incr_dir, path) { Ok(_) => {} Err(err) => tcx.dcx().emit_fatal(FailCreateFileEncoder { err }), }; + + // Read the SVH from the old metadata header. + let file = std::fs::File::open(&source_file_in_incr_dir).unwrap(); + let mmap = unsafe { Mmap::map(file) }.unwrap(); + let owned = slice_owned(mmap, Deref::deref); + let blob = MetadataBlob::new(owned); + let hash = blob.expect("file already created").get_crate_hash(); + tcx.untracked().local_crate_hash.set(hash).expect("local_crate_hash set twice"); + + // Generate the metadata stub manually, as that is a small file compared to full metadata. + if let Some(ref_path) = ref_path { + let _prof_timer = tcx.prof.verbose_generic_activity("generate_crate_metadata_stub"); + + with_encode_metadata_header(tcx, ref_path, |ecx| { + let header: LazyValue = ecx.lazy(CrateHeader { + name: tcx.crate_name(LOCAL_CRATE), + triple: tcx.sess.opts.target_triple.clone(), + is_proc_macro_crate: false, + is_stub: true, + }); + (header.position.get(), 0) + }) + } + return; }; @@ -2540,7 +2601,7 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) { with_encode_metadata_header(tcx, path, |ecx| { // Encode all the entries and extra information in the crate, // culminating in the `CrateRoot` which points to all of it. - let root = ecx.encode_crate_root(); + let (root, unhashed) = ecx.encode_crate_root(); // Flush buffer to ensure backing file has the correct size. ecx.opaque.flush(); @@ -2551,25 +2612,80 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) { ecx.opaque.file().metadata().unwrap().len(), ); - root.position.get() + (root.position.get(), unhashed.position.get()) }) }, None, ); + + // Generate the metadata stub manually, as that is a small file compared to full metadata. + if let Some(ref_path) = ref_path { + let _prof_timer = tcx.prof.verbose_generic_activity("generate_crate_metadata_stub"); + + with_encode_metadata_header(tcx, ref_path, |ecx| { + let header: LazyValue = ecx.lazy(CrateHeader { + name: tcx.crate_name(LOCAL_CRATE), + triple: tcx.sess.opts.target_triple.clone(), + is_proc_macro_crate: false, + is_stub: true, + }); + (header.position.get(), 0) + }) + } } fn with_encode_metadata_header( tcx: TyCtxt<'_>, path: &Path, - f: impl FnOnce(&mut EncodeContext<'_, '_>) -> usize, + f: impl FnOnce(&mut EncodeContext<'_, '_>) -> (usize, usize), ) { - let mut encoder = opaque::FileEncoder::new(path) - .unwrap_or_else(|err| tcx.dcx().emit_fatal(FailCreateFileEncoder { err })); + // By default the crate hash (SVH) is computed from the bytes of the encoded metadata, + // Under `-Z metadata-crate-hash=no` the SVH comes from the legacy `crate_hash` query instead and + // this hasher is never consulted, so we skip seeding it and feeding metadata bytes into it. + let metadata_crate_hash = tcx.sess.opts.unstable_opts.metadata_crate_hash; + // The SVH is an XXH3-128 digest of the encoded metadata bytes. XXH3 is a fast, non-cryptographic + // hash; that is sufficient here because the SVH is only a change detector, is never keyed + // secretly (it must be reproducible), and compiling a crate already runs its build scripts and + // proc-macros, so the crate author is trusted regardless. + let metadata_hasher = Arc::new(Mutex::new(XxHash3_128::new())); + if metadata_crate_hash { + // Fold in inputs that are not part of the encoded metadata bytes, reduced to a single + // fingerprint via the stable hasher and then mixed into the byte digest. + let hir_body_hash = compute_hir_hash(tcx); + let supplement: Fingerprint = tcx.with_stable_hashing_context(|mut hcx| { + let mut hasher = StableHasher::new(); + // Add dep_tracking_hash to ensure the SVH changes when any tracked flag changes. + tcx.sess.opts.dep_tracking_hash(true).stable_hash(&mut hcx, &mut hasher); + // Add HIR hash for untracked elements, e.g. DefKind::GlobalAsm. + hir_body_hash.stable_hash(&mut hcx, &mut hasher); + hasher.finish() + }); + metadata_hasher.lock().unwrap().write(&supplement.to_le_bytes()); + } + + // Feed every flushed byte into `metadata_hasher` so the SVH covers the entire encoded metadata. + let mut flush_strategy = { + let metadata_hasher = Arc::clone(&metadata_hasher); + move |bytes: &[u8]| metadata_hasher.lock().unwrap().write(bytes) + }; + + let mut encoder = if metadata_crate_hash { + FileEncoder::with_flush_strategy(path, &mut flush_strategy) + } else { + FileEncoder::new(path) + } + .unwrap_or_else(|err| tcx.dcx().emit_fatal(FailCreateFileEncoder { err })); encoder.emit_raw_bytes(METADATA_HEADER); // Will be filled with the root position after encoding everything. encoder.emit_raw_bytes(&0u64.to_le_bytes()); + // Same with unhashed_position. + encoder.emit_raw_bytes(&0u64.to_le_bytes()); + + // Same with crate_hash. + encoder.emit_raw_bytes(&Fingerprint::ZERO.to_le_bytes()); + let source_map_files = tcx.sess.source_map().files(); let source_file_cache = (Arc::clone(&source_map_files[0]), 0); let required_source_files = Some(FxIndexSet::default()); @@ -2577,6 +2693,7 @@ fn with_encode_metadata_header( let mut ecx = EncodeContext { opaque: encoder, + metadata_hasher: Arc::clone(&metadata_hasher), tcx, feat: tcx.features(), tables: Default::default(), @@ -2595,7 +2712,7 @@ fn with_encode_metadata_header( // Encode the rustc version string in a predictable location. rustc_version(tcx.sess.cfg_version).encode(&mut ecx); - let root_position = f(&mut ecx); + let (root_position, unhashed_position) = f(&mut ecx); // Make sure we report any errors from writing to the file. // If we forget this, compilation can succeed with an incomplete rmeta file, @@ -2604,23 +2721,48 @@ fn with_encode_metadata_header( tcx.dcx().emit_fatal(FailWriteFile { path: &path, err }); } - let file = ecx.opaque.file(); + let mut file = ecx.opaque.file(); + // We will return to this position after writing the root position and crate hash. + let pos_before_seek = file.stream_position().unwrap(); + if let Err(err) = encode_root_position(file, root_position) { tcx.dcx().emit_fatal(FailWriteFile { path: ecx.opaque.path(), err }); } + + if let Err(err) = encode_unhashed_position(file, unhashed_position) { + tcx.dcx().emit_fatal(FailWriteFile { path: ecx.opaque.path(), err }); + } + + let hash = tcx + .untracked() + .local_crate_hash + .get() + .copied() + .expect("local_crate_hash set during encoding"); + if let Err(err) = encode_crate_hash(file, hash) { + tcx.dcx().emit_fatal(FailWriteFile { path: ecx.opaque.path(), err }); + } + + if let Err(err) = file.seek(SeekFrom::Start(pos_before_seek)) { + tcx.dcx().emit_fatal(FailWriteFile { path: ecx.opaque.path(), err }); + } } fn encode_root_position(mut file: &File, pos: usize) -> Result<(), std::io::Error> { - // We will return to this position after writing the root position. - let pos_before_seek = file.stream_position().unwrap(); + file.seek(SeekFrom::Start(ROOT_POS_OFFSET as u64))?; + file.write_all(&pos.to_le_bytes())?; + Ok(()) +} - // Encode the root position. - let header = METADATA_HEADER.len(); - file.seek(std::io::SeekFrom::Start(header as u64))?; +fn encode_unhashed_position(mut file: &File, pos: usize) -> Result<(), std::io::Error> { + file.seek(SeekFrom::Start(UNHASHED_POS_OFFSET as u64))?; file.write_all(&pos.to_le_bytes())?; + Ok(()) +} - // Return to the position where we are before writing the root position. - file.seek(std::io::SeekFrom::Start(pos_before_seek))?; +fn encode_crate_hash(mut file: &File, hash: Svh) -> Result<(), std::io::Error> { + file.seek(SeekFrom::Start(CRATE_HASH_OFFSET as u64))?; + file.write_all(&hash.as_fingerprint().to_le_bytes())?; Ok(()) } diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index f89be5368da9a..f7a437043dade 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -1,4 +1,5 @@ use std::marker::PhantomData; +use std::mem::size_of; use std::num::NonZero; use decoder::LazyDecoder; @@ -9,16 +10,17 @@ pub use encoder::{EncodedMetadata, encode_metadata, rendered_const}; pub(crate) use parameterized::ParameterizedOverTcx; use rustc_abi::{FieldIdx, ReprOptions, VariantIdx}; use rustc_ast as ast; +use rustc_attr_ir::lang_items::LangItem; +use rustc_attr_ir::{Stability, StrippedCfgItem}; use rustc_crate_store::{CrateDepKind, ForeignModule, LinkagePreference, NativeLib}; +use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::svh::Svh; use rustc_hir as hir; -use rustc_hir::attrs::StrippedCfgItem; -use rustc_hir::attrs::lang_items::LangItem; +use rustc_hir::PreciseCapturingArgKind; use rustc_hir::def::{CtorKind, DefKind, MacroKinds}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIndex, DefPathHash, StableCrateId}; use rustc_hir::definitions::DefKey; -use rustc_hir::{PreciseCapturingArgKind, attrs}; use rustc_index::IndexVec; use rustc_index::bit_set::DenseBitSet; use rustc_macros::{ @@ -46,6 +48,7 @@ use rustc_target::spec::{PanicStrategy, TargetTuple}; use table::TableBuilder; use crate::eii::EiiMapEncodedKeyValue; +use crate::rmeta::table::TableBuilderSingleIdx; mod decoder; mod def_path_hash_map; @@ -60,15 +63,27 @@ pub(crate) fn rustc_version(cfg_version: &'static str) -> String { /// Metadata encoding version. /// N.B., increment this if you change the format of metadata such that /// the rustc version can't be found to compare with `rustc_version()`. -const METADATA_VERSION: u8 = 10; +const METADATA_VERSION: u8 = 11; /// Metadata header which includes `METADATA_VERSION`. /// -/// This header is followed by the length of the compressed data, then -/// the position of the `CrateRoot`, which is encoded as a 64-bit little-endian -/// unsigned integer, and further followed by the rustc version string. +/// This header is followed by the `CrateRoot` and `CrateRootUnhashed` positions +/// which represent the hashed and unhashed metadata contents respectively, the +/// crate hash (SVH), and the rustc version string. See the offset constants +/// below for the exact layout. pub const METADATA_HEADER: &[u8] = &[b'r', b'u', b's', b't', 0, 0, 0, METADATA_VERSION]; +/// Fixed-size fields encoded immediately after `METADATA_HEADER`, in order: +/// `CrateRoot` position (u64), `CrateRootUnhashed` position (u64), crate hash +/// (`Fingerprint`/SVH), then the variable-length rustc version string. +const ROOT_POS_OFFSET: usize = METADATA_HEADER.len(); +const ROOT_POS_LEN: usize = size_of::(); +const UNHASHED_POS_OFFSET: usize = ROOT_POS_OFFSET + ROOT_POS_LEN; +const UNHASHED_POS_LEN: usize = size_of::(); +const CRATE_HASH_OFFSET: usize = UNHASHED_POS_OFFSET + UNHASHED_POS_LEN; +const CRATE_HASH_LEN: usize = size_of::(); +const VERSION_OFFSET: usize = CRATE_HASH_OFFSET + CRATE_HASH_LEN; + /// A value of type T referred to by its absolute position /// in the metadata, and which can be decoded lazily. /// @@ -129,22 +144,27 @@ impl LazyArray { /// Random-access table (i.e. offering constant-time `get`/`set`), similar to /// `LazyArray`, but without requiring encoding or decoding all the values /// eagerly and in-order. -struct LazyTable { +/// +/// `IdxEncode` - is a type of index that is used to write to the table, +/// `IdxDecode` - is a type of index that is used to read from the table. +struct LazyTable { position: NonZero, /// The encoded size of the elements of a table is selected at runtime to drop /// trailing zeroes. This is the number of bytes used for each table element. width: usize, /// How many elements are in the table. len: usize, - _marker: PhantomData T>, + _marker: PhantomData T>, } -impl LazyTable { +type LazyTableSingleIdx = LazyTable; + +impl LazyTable { fn from_position_and_encoded_size( position: NonZero, width: usize, len: usize, - ) -> LazyTable { + ) -> LazyTable { LazyTable { position, width, len, _marker: PhantomData } } } @@ -163,8 +183,8 @@ impl Clone for LazyArray { } } -impl Copy for LazyTable {} -impl Clone for LazyTable { +impl Copy for LazyTable {} +impl Clone for LazyTable { fn clone(&self) -> Self { *self } @@ -185,15 +205,16 @@ enum LazyState { Previous(NonZero), } -type SyntaxContextTable = LazyTable>>; -type ExpnDataTable = LazyTable>>; -type ExpnHashTable = LazyTable>>; +type SyntaxContextTable = LazyTableSingleIdx>>; +type ExpnDataTable = LazyTableSingleIdx>>; +type ExpnHashTable = LazyTableSingleIdx>>; #[derive(MetadataEncodable, LazyDecodable)] pub(crate) struct ProcMacroData { proc_macro_decls_static: DefIndex, - stability: Option, + stability: Option, macros: LazyArray<(DefIndex, LazyValue)>, + proc_macro_quoted_spans: LazyTableSingleIdx>>, } #[derive(MetadataEncodable, LazyDecodable)] @@ -213,7 +234,6 @@ pub enum ProcMacroKind { #[derive(MetadataEncodable, BlobDecodable)] pub(crate) struct CrateHeader { pub(crate) triple: TargetTuple, - pub(crate) hash: Svh, pub(crate) name: Symbol, /// Whether this is the header for a proc-macro crate. /// @@ -250,7 +270,6 @@ pub(crate) struct CrateRoot { /// A header used to detect if this is the right crate to load. header: CrateHeader, - extra_filename: String, stable_crate_id: StableCrateId, required_panic_strategy: Option, panic_in_drop_strategy: PanicStrategy, @@ -293,7 +312,7 @@ pub(crate) struct CrateRoot { def_path_hash_map: LazyValue>, - source_map: LazyTable>>, + source_map: LazyTableSingleIdx>>, target_modifiers: LazyArray, denied_partial_mitigations: LazyArray, @@ -308,6 +327,18 @@ pub(crate) struct CrateRoot { specialization_enabled_in: bool, } +/// A separate struct for extra metadata that must be encoded *after* +/// the main crate hash is finalized. +#[derive(MetadataEncodable, LazyDecodable)] +pub(crate) struct CrateRootUnhashed { + extra_filename: String, + + /// The `-C extra-filename` of each dependency, indexed by the `CrateNum` they had in *this* + /// crate's encoding. The `LOCAL_CRATE` slot is unused filler so that dependency `CrateNum`s + /// can index this directly; this crate's own value is `extra_filename` above. + dep_extra_filenames: IndexVec, +} + /// On-disk representation of `DefId`. /// This creates a type-safe way to enforce that we remap the CrateNum between the on-disk /// representation and the compilation session. @@ -332,13 +363,16 @@ impl RawDefId { } } +/// A dependency record, as stored in the hashed [`CrateRoot`]. +/// +/// Note the absence of the dependency's `-C extra-filename`: it lives in +/// [`CrateRootUnhashed::dep_extra_filenames`] instead, deliberately outside the hash. #[derive(Encodable, BlobDecodable)] pub(crate) struct CrateDep { pub name: Symbol, pub hash: Svh, pub host_hash: Option, pub kind: CrateDepKind, - pub extra_filename: String, pub is_private: bool, } @@ -362,14 +396,14 @@ macro_rules! define_tables { ) => { #[derive(MetadataEncodable, LazyDecodable)] pub(crate) struct LazyTables { - $($name1: LazyTable<$IDX1, $T1>,)+ - $($name2: LazyTable<$IDX2, Option<$T2>>,)+ + $($name1: LazyTableSingleIdx<$IDX1, $T1>,)+ + $($name2: LazyTableSingleIdx<$IDX2, Option<$T2>>,)+ } #[derive(Default)] struct TableBuilders { - $($name1: TableBuilder<$IDX1, $T1>,)+ - $($name2: TableBuilder<$IDX2, Option<$T2>>,)+ + $($name1: TableBuilderSingleIdx<$IDX1, $T1>,)+ + $($name2: TableBuilderSingleIdx<$IDX2, Option<$T2>>,)+ } impl TableBuilders { @@ -417,7 +451,7 @@ define_tables! { impl_is_fully_generic_for_reflection: Table, - optional: - attributes: Table>, + attributes: Table>, // For non-reexported names in a module every name is associated with a separate `DefId`, // so we can take their names, visibilities etc from other encoded tables. module_children_non_reexports: Table>, @@ -426,10 +460,10 @@ define_tables! { visibility: Table>>, def_span: Table>, def_ident_span: Table>, - lookup_stability: Table>, - lookup_const_stability: Table>, - lookup_default_body_stability: Table>, - lookup_deprecation_entry: Table>, + lookup_stability: Table>, + lookup_const_stability: Table>, + lookup_default_body_stability: Table>, + lookup_deprecation_entry: Table>, explicit_clauses_of: Table>>, generics_of: Table>, type_of: Table>>>, @@ -470,7 +504,6 @@ define_tables! { // `DefPathTable` up front, since we may only ever use a few // definitions from any given crate. def_keys: Table>, - proc_macro_quoted_spans: Table>, variant_data: Table>, assoc_container: Table>, macro_definition: Table>, diff --git a/compiler/rustc_metadata/src/rmeta/parameterized.rs b/compiler/rustc_metadata/src/rmeta/parameterized.rs index 4eb922446b71b..670d875f885c9 100644 --- a/compiler/rustc_metadata/src/rmeta/parameterized.rs +++ b/compiler/rustc_metadata/src/rmeta/parameterized.rs @@ -78,6 +78,7 @@ trivially_parameterized_over_tcx! { crate::rmeta::CrateDep, crate::rmeta::CrateHeader, crate::rmeta::CrateRoot, + crate::rmeta::CrateRootUnhashed, crate::rmeta::IncoherentImpls, crate::rmeta::ProcMacroKind, crate::rmeta::RawDefId, @@ -85,24 +86,24 @@ trivially_parameterized_over_tcx! { crate::rmeta::VariantData, rustc_abi::ReprOptions, rustc_ast::DelimArgs, + rustc_attr_ir::Attribute, + rustc_attr_ir::ConstStability, + rustc_attr_ir::DefaultBodyStability, + rustc_attr_ir::Deprecation, + rustc_attr_ir::EiiDecl, + rustc_attr_ir::EiiImpl, + rustc_attr_ir::Stability, + rustc_attr_ir::StrippedCfgItem, + rustc_attr_ir::lang_items::LangItem, rustc_crate_store::ForeignModule, rustc_crate_store::LinkagePreference, rustc_crate_store::NativeLib, - rustc_hir::Attribute, - rustc_hir::ConstStability, rustc_hir::Constness, rustc_hir::CoroutineKind, - rustc_hir::DefaultBodyStability, rustc_hir::Defaultness, rustc_hir::OpaqueTyOrigin, rustc_hir::PreciseCapturingArgKind, rustc_hir::Safety, - rustc_hir::Stability, - rustc_hir::attrs::Deprecation, - rustc_hir::attrs::EiiDecl, - rustc_hir::attrs::EiiImpl, - rustc_hir::attrs::StrippedCfgItem, - rustc_hir::attrs::lang_items::LangItem, rustc_hir::def::DefKind, rustc_hir::def_id::DefId, rustc_hir::def_id::DefIndex, diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index 811d370248dd9..c023b61d73571 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -140,16 +140,8 @@ macro_rules! defaulted_enum { } } -// Workaround; need const traits to construct bitflags in a const -macro_rules! const_macro_kinds { - ($($name:ident),+$(,)?) => (MacroKinds::from_bits_truncate($(MacroKinds::$name.bits())|+)) -} -const MACRO_KINDS_ATTR_BANG: MacroKinds = const_macro_kinds!(ATTR, BANG); -const MACRO_KINDS_DERIVE_BANG: MacroKinds = const_macro_kinds!(DERIVE, BANG); -const MACRO_KINDS_DERIVE_ATTR: MacroKinds = const_macro_kinds!(DERIVE, ATTR); -const MACRO_KINDS_DERIVE_ATTR_BANG: MacroKinds = const_macro_kinds!(DERIVE, ATTR, BANG); // Ensure that we get a compilation error if MacroKinds gets extended without updating metadata. -const _: () = assert!(MACRO_KINDS_DERIVE_ATTR_BANG.is_all()); +const _: () = assert!(MacroKinds::DERIVE_ATTR_BANG.is_all()); fixed_size_enum! { DefKind { @@ -195,10 +187,10 @@ fixed_size_enum! { ( Macro(MacroKinds::BANG) ) ( Macro(MacroKinds::ATTR) ) ( Macro(MacroKinds::DERIVE) ) - ( Macro(MACRO_KINDS_ATTR_BANG) ) - ( Macro(MACRO_KINDS_DERIVE_ATTR) ) - ( Macro(MACRO_KINDS_DERIVE_BANG) ) - ( Macro(MACRO_KINDS_DERIVE_ATTR_BANG) ) + ( Macro(MacroKinds::ATTR_BANG) ) + ( Macro(MacroKinds::DERIVE_ATTR) ) + ( Macro(MacroKinds::DERIVE_BANG) ) + ( Macro(MacroKinds::DERIVE_ATTR_BANG) ) ( SyntheticCoroutineBody ) ( TestBinderConstraints ) } unreachable { @@ -435,34 +427,38 @@ impl FixedSizeEncoding for Option> { } /// Helper for constructing a table's serialization (also see `Table`). -pub(super) struct TableBuilder { +pub(super) struct TableBuilder { width: usize, - blocks: IndexVec, - _marker: PhantomData, + blocks: IndexVec, + _marker: PhantomData<(IdxDecode, T)>, } -impl Default for TableBuilder { +pub(super) type TableBuilderSingleIdx = TableBuilder; + +impl Default for TableBuilder { fn default() -> Self { TableBuilder { width: 0, blocks: Default::default(), _marker: PhantomData } } } -impl TableBuilder> +impl TableBuilder> where Option: FixedSizeEncoding, { - pub(crate) fn set_some(&mut self, i: I, value: T) { + pub(crate) fn set_some(&mut self, i: Ie, value: T) { self.set(i, Some(value)) } } -impl> TableBuilder { +impl> + TableBuilder +{ /// Sets the table value if it is not default. /// ATTENTION: For optimization default values are simply ignored by this function, because /// right now metadata tables never need to reset non-default values to default. If such need /// arises in the future then a new method (e.g. `clear` or `reset`) will need to be introduced /// for doing that explicitly. - pub(crate) fn set(&mut self, i: I, value: T) { + pub(crate) fn set(&mut self, i: Ie, value: T) { #[cfg(debug_assertions)] { debug_assert!( @@ -485,7 +481,7 @@ impl> TableBui } } - pub(crate) fn encode(&self, buf: &mut FileEncoder<'_>) -> LazyTable { + pub(crate) fn encode(&self, buf: &mut FileEncoder<'_>) -> LazyTable { let pos = buf.position(); let width = self.width; @@ -508,13 +504,17 @@ fn trailing_zeros(x: &[u8]) -> usize { x.iter().rev().take_while(|b| **b == 0).count() } -impl + ParameterizedOverTcx> - LazyTable +impl< + Ie: Idx, + Id: Idx, + const N: usize, + T: FixedSizeEncoding + ParameterizedOverTcx, +> LazyTable where for<'tcx> T::Value<'tcx>: FixedSizeEncoding, { /// Given the metadata, extract out the value at a particular index (if any). - pub(super) fn get<'a, 'tcx, M: MetaBlob<'a>>(&self, metadata: M, i: I) -> T::Value<'tcx> { + pub(super) fn get<'a, 'tcx, M: MetaBlob<'a>>(&self, metadata: M, i: Id) -> T::Value<'tcx> { // Access past the end of the table returns a Default if i.index() >= self.len { return Default::default(); diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index ee71d4d46e48f..a423b6bdca5b2 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -107,9 +107,9 @@ rustc_arena::declare_arena! { upvars_mentioned: rustc_data_structures::fx::FxIndexMap, dyn_compatibility_violations: rustc_middle::traits::DynCompatibilityViolation, codegen_unit: rustc_middle::mono::CodegenUnit<'tcx>, - attribute: rustc_hir::Attribute, + attribute: rustc_attr_ir::Attribute, name_set: rustc_data_structures::unord::UnordSet, - autodiff_item: rustc_hir::attrs::AutoDiffItem, + autodiff_item: rustc_attr_ir::AutoDiffItem, ordered_name_set: rustc_data_structures::fx::FxIndexSet, stable_order_of_exportable_impls: rustc_data_structures::fx::FxIndexMap, @@ -131,7 +131,7 @@ rustc_arena::declare_arena! { >, external_constraints: rustc_middle::traits::solve::ExternalConstraintsData>, doc_link_resolutions: rustc_middle::middle::resolve::DocLinkResMap, - stripped_cfg_items: rustc_hir::attrs::StrippedCfgItem, + stripped_cfg_items: rustc_attr_ir::StrippedCfgItem, mod_child: rustc_middle::middle::resolve::ModChild, features: rustc_feature::Features, specialization_graph: rustc_middle::traits::specialization_graph::Graph, @@ -204,9 +204,9 @@ impl_ref_decodable_into_arena! { Spanned>, rustc_ast::InlineAsmTemplatePiece, rustc_ast::tokenstream::TokenStream, + rustc_attr_ir::Attribute, rustc_data_structures::unord::UnordMap>>, rustc_data_structures::unord::UnordSet, - rustc_hir::Attribute, rustc_index::IndexVec>, rustc_middle::middle::deduced_param_attrs::DeducedParamAttrs, rustc_middle::mir::Body<'tcx>, diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs index 1743f0d05dfcd..ea266e0032add 100644 --- a/compiler/rustc_middle/src/hir/map.rs +++ b/compiler/rustc_middle/src/hir/map.rs @@ -4,13 +4,14 @@ use rustc_abi::ExternAbi; use rustc_ast::visit::{VisitorResult, walk_list}; +use rustc_attr_ir::{Attribute, find_attr}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::stable_hash::{StableHash, StableHasher}; use rustc_data_structures::steal::Steal; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::{DynSend, DynSync, par_for_each_in, try_par_for_each_in}; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId, LocalModId}; +use rustc_hir::def_id::{DefId, LocalDefId, LocalModId}; use rustc_hir::definitions::{DefKey, DefPath, DefPathHash}; use rustc_hir::intravisit::Visitor; use rustc_hir::lints::DelayedLints; @@ -18,6 +19,7 @@ use rustc_hir::*; use rustc_span::def_id::{CRATE_MOD_ID, StableCrateId}; use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, bug, kw, span_bug, with_metavar_spans}; +use crate::hir::def_id::LOCAL_CRATE; use crate::hir::{ModuleItems, ProjectedMaybeOwner, nested_filter}; use crate::middle::debugger_visualizer::DebuggerVisualizerFile; use crate::query::{IntoQueryKey, LocalCrate}; @@ -409,11 +411,10 @@ impl<'tcx> TyCtxt<'tcx> { find_attr!(self.hir_krate_attrs(), RustcCoherenceIsCore) } - pub fn hir_get_module(self, module: LocalModId) -> (&'tcx Mod<'tcx>, Span, HirId) { - let hir_id = HirId::make_owner(module.to_local_def_id()); - match self.hir_owner_node(hir_id.owner) { - OwnerNode::Item(&Item { span, kind: ItemKind::Mod(_, m), .. }) => (m, span, hir_id), - OwnerNode::Crate(item) => (item, item.spans.inner_span, hir_id), + pub fn hir_get_module(self, module: LocalModId) -> (&'tcx Mod<'tcx>, Span) { + match self.hir_owner_node(module.into()) { + OwnerNode::Item(&Item { span, kind: ItemKind::Mod(_, m), .. }) => (m, span), + OwnerNode::Crate(item) => (item, item.spans.inner_span), node => panic!("not a module: {node:?}"), } } @@ -423,8 +424,8 @@ impl<'tcx> TyCtxt<'tcx> { where V: Visitor<'tcx>, { - let (top_mod, span, hir_id) = self.hir_get_module(CRATE_MOD_ID); - visitor.visit_mod(top_mod, span, hir_id) + let (top_mod, span) = self.hir_get_module(CRATE_MOD_ID); + visitor.visit_mod(top_mod, span, CRATE_MOD_ID) } /// Walks the attributes in a crate. @@ -793,6 +794,7 @@ impl<'tcx> TyCtxt<'tcx> { } Node::Crate(..) => String::from("(root_crate)"), Node::WherePredicate(_) => node_str("where predicate"), + Node::NestedUseTree(_) => node_str("use"), Node::PreciseCapturingNonLifetimeArg(_param) => node_str("parameter"), Node::TestBinderForall(_) => node_str("forall"), Node::TestBinderExists(_) => node_str("exists"), @@ -1000,10 +1002,10 @@ impl<'tcx> TyCtxt<'tcx> { } // Other cases. Node::Item(item) => match &item.kind { - ItemKind::Use(path, _) => { + ItemKind::Use(use_tree) => { // Ensure that the returned span has the item's SyntaxContext, and not the // SyntaxContext of the path. - path.span.find_ancestor_in_same_ctxt(item.span).unwrap_or(item.span) + use_tree.prefix.span.find_ancestor_in_same_ctxt(item.span).unwrap_or(item.span) } _ => { if let Some(ident) = item.kind.ident() { @@ -1072,6 +1074,7 @@ impl<'tcx> TyCtxt<'tcx> { Node::Crate(item) => item.spans.inner_span, Node::WherePredicate(pred) => pred.span, Node::PreciseCapturingNonLifetimeArg(param) => param.ident.span, + Node::NestedUseTree(tree) => tree.prefix.span, Node::TestBinderForall(forall) => forall.span, Node::TestBinderExists(exists) => exists.span, Node::TestBinderBoundTypeConstraint(bound_type) => bound_type.span, @@ -1161,7 +1164,47 @@ impl<'tcx> intravisit::HirTyCtxt<'tcx> for TyCtxt<'tcx> { } pub(super) fn crate_hash(tcx: TyCtxt<'_>, _: LocalCrate) -> Svh { + // `-Z metadata-crate-hash=no` opts back into computing the SVH from the HIR rather than + // from the encoded crate metadata. This is a safety fallback for the metadata-based hashing. + if !tcx.sess.opts.unstable_opts.metadata_crate_hash { + return legacy_crate_hash(tcx); + } + + // If metadata is being encoded, the crate hash has already been computed as part of the + // metadata encoding. + if tcx.needs_metadata() { + *tcx.untracked() + .local_crate_hash + .get() + .expect("crate_hash(LOCAL_CRATE) called before metadata encoding") + } else { + // When metadata isn't encoded, use an HIR based approximation. Encoding metadata for a + // dylib/binary is expensive and fragile. These fields are enough to identify the session. + let hir_body_hash = compute_hir_hash(tcx); + + let upstream_crates = upstream_crates(tcx); + + let crate_hash: Fingerprint = tcx.with_stable_hashing_context(|mut hcx| { + let mut stable_hasher = StableHasher::new(); + hir_body_hash.stable_hash(&mut hcx, &mut stable_hasher); + upstream_crates.stable_hash(&mut hcx, &mut stable_hasher); + tcx.sess.opts.dep_tracking_hash(true).stable_hash(&mut hcx, &mut stable_hasher); + tcx.stable_crate_id(LOCAL_CRATE).stable_hash(&mut hcx, &mut stable_hasher); + + stable_hasher.finish() + }); + + Svh::new(crate_hash) + } +} + +/// Only reached when `-Z metadata-crate-hash=no` reverts to the pre-metadata-hashing behavior. +/// The HIR-based hashing scheme here matches the `crate_hash` query as it existed before this PR. +/// (The resulting SVH still differs from a pre-PR compiler, because `metadata_crate_hash` is a +/// `[TRACKED]` option and therefore contributes to the dep-tracking hash hashed in below.) +fn legacy_crate_hash(tcx: TyCtxt<'_>) -> Svh { let krate = tcx.hir_crate_items(()); + let upstream_crates = upstream_crates(tcx); let resolutions = tcx.resolutions(()); @@ -1237,6 +1280,19 @@ pub(super) fn crate_hash(tcx: TyCtxt<'_>, _: LocalCrate) -> Svh { Svh::new(crate_hash) } +/// Compute the new, metadata-oriented HIR hash for the full crate. +/// +/// Unlike the legacy hash (see [`legacy_crate_hash`]), this is an order-independent combine of each +/// owner's fingerprint, so it does not depend on the iteration order of the owners. It becomes part +/// of the `crate_hash` which is stored in the crate metadata. +pub fn compute_hir_hash(tcx: TyCtxt<'_>) -> Fingerprint { + tcx.hir_crate_items(()) + .owners() + .filter_map(|owner| Some(tcx.lower_to_hir(owner.def_id).as_owner()?.fingerprint())) + .reduce(Fingerprint::combine_commutative) + .expect("HIR hash requested without any content") +} + fn upstream_crates(tcx: TyCtxt<'_>) -> Vec<(StableCrateId, Svh)> { let mut upstream_crates: Vec<_> = tcx .crates(()) @@ -1254,8 +1310,8 @@ fn upstream_crates(tcx: TyCtxt<'_>) -> Vec<(StableCrateId, Svh)> { pub(super) fn hir_module_items(tcx: TyCtxt<'_>, module_id: LocalModId) -> ModuleItems { let mut collector = ItemCollector::new(tcx, false); - let (hir_mod, span, hir_id) = tcx.hir_get_module(module_id); - collector.visit_mod(hir_mod, span, hir_id); + let (hir_mod, span) = tcx.hir_get_module(module_id); + collector.visit_mod(hir_mod, span, module_id); let ItemCollector { submodules, diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index eae84fde7e305..02865e81c4c69 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -6,12 +6,13 @@ pub mod map; pub mod nested_filter; pub mod place; +use rustc_attr_ir::Attribute; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::stable_hash::{StableHash, StableHasher}; use rustc_data_structures::steal::Steal; use rustc_data_structures::sync::{DynSend, DynSync, try_par_for_each_in}; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdMap, LocalModId}; use rustc_hir::lints::DelayedLints; @@ -343,6 +344,7 @@ impl<'tcx> TyCtxt<'tcx> { | Node::Synthetic | Node::Err(_) | Node::Ctor(_) + | Node::NestedUseTree(_) | Node::Lifetime(_) | Node::GenericParam(_) | Node::Crate(_) diff --git a/compiler/rustc_middle/src/hooks.rs b/compiler/rustc_middle/src/hooks.rs index dafed9037615f..7bccc34dbc720 100644 --- a/compiler/rustc_middle/src/hooks.rs +++ b/compiler/rustc_middle/src/hooks.rs @@ -3,6 +3,8 @@ //! similar to queries, but queries come with a lot of machinery for caching and incremental //! compilation, whereas hooks are just plain function pointers without any of the query magic. +use std::io; + use rustc_hir::def_id::{DefId, DefPathHash}; use rustc_index::bit_set::DenseBitSet; use rustc_session::StableCrateId; @@ -123,6 +125,12 @@ declare_hooks! { instance: ty::Instance<'tcx>, typing_env: ty::TypingEnv<'tcx>, ) -> DenseBitSet; + + /// Configures extra data to be included when pretty printing MIR + hook mir_pretty_extra_data( + body: &mir::Body<'tcx>, + ) -> Option io::Result<()>>>; + } #[cold] diff --git a/compiler/rustc_middle/src/ich.rs b/compiler/rustc_middle/src/ich.rs index 6e0a855707f9d..feb162ad1e162 100644 --- a/compiler/rustc_middle/src/ich.rs +++ b/compiler/rustc_middle/src/ich.rs @@ -5,10 +5,11 @@ use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::stable_hash::{ RawDefId, RawSpan, StableHash, StableHashControls, StableHashCtxt, StableHasher, }; +use rustc_data_structures::sync::AppendOnlyIndexVec; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_session::Session; use rustc_span::source_map::SourceMap; -use rustc_span::{BytePos, CachingSourceMapView, DUMMY_SP, Pos, Span}; +use rustc_span::{BytePos, CachingSourceMapView, DUMMY_SP, Pos, Span, SpanData}; // Very often, we are hashing something that does not need the `CachingSourceMapView`, so we // initialize it lazily. @@ -28,6 +29,7 @@ pub struct StableHashState<'a> { incremental_ignore_spans: bool, caching_source_map: CachingSourceMap<'a>, stable_hash_controls: StableHashControls, + source_span_cache: SourceSpanCache, } impl<'a> StableHashState<'a> { @@ -40,6 +42,7 @@ impl<'a> StableHashState<'a> { incremental_ignore_spans: sess.opts.unstable_opts.incremental_ignore_spans, caching_source_map: CachingSourceMap::Unused(sess.source_map()), stable_hash_controls: StableHashControls { hash_spans: hash_spans_initial }, + source_span_cache: SourceSpanCache::default(), } } @@ -62,11 +65,6 @@ impl<'a> StableHashState<'a> { } } - #[inline] - fn def_span(&self, def_id: LocalDefId) -> Span { - self.untracked.source_span.get(def_id).unwrap_or(DUMMY_SP) - } - #[inline] pub fn stable_hash_controls(&self) -> StableHashControls { self.stable_hash_controls @@ -119,7 +117,9 @@ impl<'a> StableHashCtxt for StableHashState<'a> { return; } - let parent = span.parent.map(|parent| self.def_span(parent).data_untracked()); + let parent = span + .parent + .map(|parent| self.source_span_cache.lookup(parent, &self.untracked.source_span)); if let Some(parent) = parent && parent.contains(span) { @@ -207,3 +207,31 @@ impl<'a> StableHashCtxt for StableHashState<'a> { self.stable_hash_controls } } + +/// A single-entry cache for a `SpanData`, to optimize a particular scenario: when iterating +/// through child spans (e.g. during encoding/decoding/hashing), many of those spans will have the +/// same parent span. In the case where that parent span is interned (e.g. the parent is a const +/// literal item exceeding MAX_LEN bytes of source code), this cache avoids having to fetch the +/// parent's `SpanData` from the interner (which involves TLS, locking, etc.) over and over. A +/// single entry is enough for an extremely high hit rate. +#[derive(Default)] +pub(crate) struct SourceSpanCache(Option<(LocalDefId, SpanData)>); + +impl SourceSpanCache { + #[inline] + pub(crate) fn lookup( + &mut self, + def_id: LocalDefId, + source_span: &AppendOnlyIndexVec, + ) -> SpanData { + if let Some(cache) = self.0 + && cache.0 == def_id + { + cache.1 + } else { + let span_data = source_span.get(def_id).unwrap_or(DUMMY_SP).data_untracked(); + self.0 = Some((def_id, span_data)); + span_data + } + } +} diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 48d90f9c704fc..695eca6225858 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -27,15 +27,14 @@ // tidy-alphabetical-start #![allow(internal_features)] #![allow(rustc::direct_use_of_rustc_type_ir)] -#![cfg_attr(bootstrap, feature(never_type))] +#![cfg_attr(bootstrap, feature(debug_closure_helpers))] #![cfg_attr(doc, feature(intra_doc_pointers))] -#![feature(allocator_api)] +#![feature(allocator_ext)] #![feature(associated_type_defaults)] #![feature(closure_track_caller)] #![feature(const_default)] #![feature(const_trait_impl)] #![feature(core_intrinsics)] -#![feature(debug_closure_helpers)] #![feature(decl_macro)] #![feature(default_field_values)] #![feature(deref_patterns)] diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 49fbbd9164f38..8822ffee65c29 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -2,7 +2,7 @@ use std::cmp::min; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sorted_map::SortedMap; -use rustc_errors::{Diag, DiagLocation, Diagnostic, MultiSpan}; +use rustc_errors::{Diag, Diagnostic, EmissionOverride, MultiSpan}; use rustc_hir::{HirId, ItemLocalId}; use rustc_lint_defs::{ EditionFcw, FutureIncompatibilityReason, Level, Lint, LintExpectationId, LintId, @@ -285,7 +285,7 @@ fn explain_lint_level_source( lint: &'static Lint, level: Level, src: LintLevelSource, - err: &mut Diag<'_, ()>, + err: &mut Diag<'_>, ) { // Find the name of the lint group that contains the given lint. // Assumes the lint only belongs to one group. @@ -382,7 +382,7 @@ fn explain_lint_level_source( /// - [`TyCtxt::emit_node_span_lint`] /// - `LintContext::opt_span_lint` #[track_caller] -pub fn emit_lint_base<'a, D: Diagnostic<'a, ()> + 'a>( +pub fn emit_lint_base<'a, D: Diagnostic<'a> + 'a>( sess: &'a Session, lint: &'static Lint, level_spec: impl Into, @@ -398,7 +398,7 @@ pub fn emit_lint_base<'a, D: Diagnostic<'a, ()> + 'a>( level_spec: LevelSpec, span: Option, decorate: Box< - dyn FnOnce(rustc_errors::DiagCtxtHandle<'a>, rustc_errors::Level) -> Diag<'a, ()> + 'a, + dyn FnOnce(rustc_errors::DiagCtxtHandle<'a>, rustc_errors::Level) -> Diag<'a> + 'a, >, ) { let LevelSpec { level, lint_id, src } = level_spec; @@ -416,7 +416,7 @@ pub fn emit_lint_base<'a, D: Diagnostic<'a, ()> + 'a>( let err_level = match level { Level::Allow => { if has_future_breakage { - rustc_errors::Level::Allow + rustc_errors::Level::Warning(Some(EmissionOverride::Allowed)) } else { return; } @@ -426,13 +426,17 @@ pub fn emit_lint_base<'a, D: Diagnostic<'a, ()> + 'a>( // we can't return early like in the case for `Level::Allow` because we still // need the lint diagnostic to be emitted to `rustc_error::DiagCtxtInner`. // - // We can also not mark the lint expectation as fulfilled here right away, as it - // can still be cancelled in the decorate function. All of this means that we simply - // create a `Diag` and continue as we would for warnings. - rustc_errors::Level::Expect + // We also cannot mark the lint expectation as fulfilled here right away, because + // it can still be cancelled in the decorate function. So we create a `Diag` and + // continue as we would for `Level::Warn`. + rustc_errors::Level::Warning(Some(EmissionOverride::Expected { + lint_id: lint_id.unwrap(), + })) } - Level::ForceWarn => rustc_errors::Level::ForceWarning, - Level::Warn => rustc_errors::Level::Warning, + Level::ForceWarn => { + rustc_errors::Level::Warning(Some(EmissionOverride::Forced { lint_id })) + } + Level::Warn => rustc_errors::Level::Warning(None), Level::Deny | Level::Forbid => rustc_errors::Level::Error, }; @@ -477,25 +481,25 @@ pub fn emit_lint_base<'a, D: Diagnostic<'a, ()> + 'a>( // emitted or we'll get a `must_produce_diag` ICE. // // When is a diagnostic *eventually* emitted? Well, that is determined by 2 factors: - // 1. If the corresponding `rustc_errors::Level` is beyond warning, i.e. `ForceWarning(_)` - // or `Error`, then the diagnostic will be emitted regardless of CLI options. + // 1. If the corresponding `rustc_errors::Level` is beyond warning, i.e. `Error`, then the + // diagnostic will be emitted regardless of CLI options. // 2. If the corresponding `rustc_errors::Level` is warning, then that can be affected by // `-A warnings` or `--cap-lints=xxx` on the command line. In which case, the diagnostic // will be emitted if `can_emit_warnings` is true. - let skip = err_level == rustc_errors::Level::Warning && !sess.dcx().can_emit_warnings(); + let skip = + err_level == rustc_errors::Level::Warning(None) && !sess.dcx().can_emit_warnings(); - let mut err: Diag<'_, ()> = if !skip { + let mut err: Diag<'_> = if !skip { decorate(sess.dcx(), err_level) } else { Diag::new(sess.dcx(), err_level, "") }; - // FIXME: Find a nicer way to expose the `DiagLocation` - err.emitted_at = DiagLocation::caller(); if let Some(span) = span && err.span.primary_span().is_none() { - // We can't use `err.span()` because it overwrites the labels, so we need to do it manually. + // We can't use `err.span()` because it overwrites the labels, so we need to do it + // manually. for primary in span.primary_spans() { err.span.push_primary_span(*primary); } @@ -503,9 +507,6 @@ pub fn emit_lint_base<'a, D: Diagnostic<'a, ()> + 'a>( err.span.push_span_diag(*label_span, label.clone()); } } - if let Some(lint_id) = lint_id { - err.lint_id(lint_id); - } if disable_suggestions { // Any suggestions made here are likely to be incorrect, so anything we diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index f15dc1001fd95..fc5345b0b0b92 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -270,7 +270,9 @@ impl CodegenFnAttrs { /// * `#[export_name(...)]` is present /// * `#[linkage]` is present /// - /// Keep this in sync with the logic for the unused_attributes for `#[inline]` lint. + /// Keep this in sync with [`AttributeKind::is_extern_indicator`]. + /// + /// [`AttributeKind::is_extern_indicator`]: rustc_attr_ir::AttributeKind::is_extern_indicator pub fn contains_extern_indicator(&self) -> bool { if self.flags.contains(CodegenFnAttrFlags::FOREIGN_ITEM) { return false; diff --git a/compiler/rustc_middle/src/middle/debugger_visualizer.rs b/compiler/rustc_middle/src/middle/debugger_visualizer.rs index b6fc2d4f1a8d0..0fded7a77662d 100644 --- a/compiler/rustc_middle/src/middle/debugger_visualizer.rs +++ b/compiler/rustc_middle/src/middle/debugger_visualizer.rs @@ -1,7 +1,7 @@ use std::path::PathBuf; use std::sync::Arc; -use rustc_hir::attrs::DebuggerVisualizerType; +use rustc_attr_ir::DebuggerVisualizerType; use rustc_macros::{Decodable, Encodable, StableHash}; /// A single debugger visualizer file. diff --git a/compiler/rustc_middle/src/middle/lang_items.rs b/compiler/rustc_middle/src/middle/lang_items.rs index 829df12ff543c..9d07c2edeb386 100644 --- a/compiler/rustc_middle/src/middle/lang_items.rs +++ b/compiler/rustc_middle/src/middle/lang_items.rs @@ -7,7 +7,7 @@ //! * Traits that represent operators; e.g., `Add`, `Sub`, `Index`. //! * Functions called by the compiler itself. -use rustc_hir::attrs::lang_items::LangItem; +use rustc_attr_ir::lang_items::LangItem; use rustc_hir::def_id::DefId; use rustc_span::Span; use rustc_target::spec::PanicStrategy; diff --git a/compiler/rustc_middle/src/middle/privacy.rs b/compiler/rustc_middle/src/middle/privacy.rs index a59e690541bc4..3a008514f7d07 100644 --- a/compiler/rustc_middle/src/middle/privacy.rs +++ b/compiler/rustc_middle/src/middle/privacy.rs @@ -5,10 +5,10 @@ use std::cmp::Ordering; use std::hash::Hash; -use rustc_data_structures::fx::{FxIndexMap, IndexEntry}; +use rustc_data_structures::fx::{FxIndexMap, IndexEntry, default}; use rustc_data_structures::stable_hash::{StableHash, StableHashCtxt, StableHasher}; use rustc_hir::def::DefKind; -use rustc_hir::{ItemKind, Node, UseKind}; +use rustc_hir::{ItemKind, Node, UseKind, UseTree}; use rustc_macros::StableHash; use rustc_span::def_id::{CRATE_DEF_ID, LocalDefId}; use rustc_span::span_bug; @@ -189,7 +189,7 @@ impl EffectiveVisibilities { let nominal_vis = tcx.visibility(def_id); if ev.reachable.greater_than(nominal_vis, tcx) { if let Node::Item(item) = tcx.hir_node_by_def_id(def_id) - && let ItemKind::Use(_, UseKind::Glob) = item.kind + && let ItemKind::Use(UseTree { kind: UseKind::Glob, .. }) = item.kind { // Glob import visibilities can be increased by other // more public glob imports in cases of ambiguity. @@ -277,9 +277,9 @@ impl EffectiveVisibilities { } } -impl Default for EffectiveVisibilities { +const impl Default for EffectiveVisibilities { fn default() -> Self { - EffectiveVisibilities { map: Default::default() } + EffectiveVisibilities { map: default::fx_index_map() } } } diff --git a/compiler/rustc_middle/src/middle/resolve.rs b/compiler/rustc_middle/src/middle/resolve.rs index 8267cde89ad27..bafb45469a56e 100644 --- a/compiler/rustc_middle/src/middle/resolve.rs +++ b/compiler/rustc_middle/src/middle/resolve.rs @@ -180,6 +180,9 @@ pub struct ResolverGlobalCtxt { /// A list of proc macro LocalDefIds, written out in the order in which /// they are declared in the static array generated by proc_macro_harness. pub proc_macros: Vec, + /// Path spans where the ident matches an associated type for an enclosing trait. Used to + /// improve certain error messages. + pub paths_matching_assoc_types: UnordSet, /// Mapping from ident span to path span for paths that don't exist as written, but that /// exist under `std`. For example, wrote `str::from_utf8` instead of `std::str::from_utf8`. pub confused_type_with_std_module: FxIndexMap, @@ -207,8 +210,8 @@ pub struct PerOwnerResolverData<'tcx> { pub trait_map: NodeMap<&'tcx [TraitCandidate<'tcx>]> = Default::default(), - /// Resolution for import nodes, which have multiple resolutions in different namespaces. - pub import_res: PerNS>> = Default::default(), + /// Resolutions for import nodes, which have multiple resolutions in different namespaces. + pub import_res: NodeMap>>> = Default::default(), /// Lifetime parameters that lowering will have to introduce. pub extra_lifetime_params_map: NodeMap> = Default::default(), @@ -291,6 +294,7 @@ pub struct DelegationInfo { } #[derive(Debug, StableHash)] +// FIXME(fn_delegation): replace with `Result`. pub enum TypeRelativeDelegationRes { Ok(DefId), Ambig(ErrorGuaranteed), @@ -330,9 +334,6 @@ pub type DocLinkResMap = FxIndexMap<(Symbol, Namespace), Option>>; pub enum AstOwner { /// This definition does not correspond to a HIR owner. NonOwner, - /// This definition corresponds to a nested `use` tree. - /// The `LocalDefId` points to its HIR owner. - NestedUseTree(LocalDefId), Crate(Box), Item(Box), TraitItem(Box), diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 2ad73c8894da5..0235d5bb17224 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -7,7 +7,7 @@ use rustc_ast::NodeId; use rustc_attr_ir::{ ConstStability, DefaultBodyStability, DeprecatedSince, Deprecation, Stability, StabilityLevel, }; -use rustc_errors::{Applicability, Diag, Diagnostic, LintBuffer, msg}; +use rustc_errors::{Diag, Diagnostic, LintBuffer, msg}; use rustc_feature::GateIssue; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{self as hir, HirId}; @@ -62,7 +62,6 @@ pub fn report_unstable( feature: Symbol, reason: Option, issue: Option>, - suggestion: Option<(Span, String, String, Applicability)>, span: Span, kind: UnstableKind, ) { @@ -77,9 +76,6 @@ pub fn report_unstable( }; let mut err = feature_err_issue(sess, feature, span, GateIssue::Library(issue), msg); - if let Some((inner_types, msg, sugg, applicability)) = suggestion { - err.span_suggestion(inner_types, msg, sugg, applicability); - } if let UnstableKind::Const(kw) = kind { err.span_label(kw, "trait is not stable as const yet"); } @@ -114,12 +110,12 @@ pub(crate) struct Deprecated { pub since_kind: DeprecatedSinceKind, } -impl<'a, G> rustc_errors::Diagnostic<'a, G> for Deprecated { +impl<'a> rustc_errors::Diagnostic<'a> for Deprecated { fn into_diag( self, dcx: rustc_errors::DiagCtxtHandle<'a>, level: rustc_errors::Level, - ) -> Diag<'a, G> { + ) -> Diag<'a> { let Self { sub, kind, path, note, since_kind } = self; let mut diag = Diag::new(dcx, level, match &since_kind { DeprecatedSinceKind::InEffect => msg!( @@ -260,42 +256,11 @@ pub enum EvalResult { Allow, /// We cannot use the item because it is unstable and we did not provide the /// corresponding feature gate. - Deny { - feature: Symbol, - reason: Option, - issue: Option>, - suggestion: Option<(Span, String, String, Applicability)>, - }, + Deny { feature: Symbol, reason: Option, issue: Option> }, /// The item does not have the `#[stable]` or `#[unstable]` marker assigned. Unmarked, } -// See issue #83250. -fn suggestion_for_allocator_api( - tcx: TyCtxt<'_>, - def_id: DefId, - span: Span, - feature: Symbol, -) -> Option<(Span, String, String, Applicability)> { - if feature == sym::allocator_api { - if let Some(trait_) = tcx.opt_parent(def_id) { - if tcx.is_diagnostic_item(sym::Vec, trait_) { - let sm = tcx.sess.psess.source_map(); - let inner_types = sm.span_extend_to_prev_char(span, '<', true); - if let Ok(snippet) = sm.span_to_snippet(inner_types) { - return Some(( - inner_types, - "consider wrapping the inner types in tuple".to_string(), - format!("({snippet})"), - Applicability::MaybeIncorrect, - )); - } - } - } - } - None -} - /// An override option for eval_stability. pub enum AllowUnstable { /// Don't emit an unstable error for the item @@ -425,8 +390,7 @@ impl<'tcx> TyCtxt<'tcx> { return EvalResult::Allow; } - let suggestion = suggestion_for_allocator_api(self, def_id, span, feature); - EvalResult::Deny { feature, reason: reason.to_opt_reason(), issue, suggestion } + EvalResult::Deny { feature, reason: reason.to_opt_reason(), issue } } Some(_) => { // Stable APIs are always ok to call and deprecated APIs are @@ -472,12 +436,7 @@ impl<'tcx> TyCtxt<'tcx> { return EvalResult::Allow; } - EvalResult::Deny { - feature, - reason: reason.to_opt_reason(), - issue, - suggestion: None, - } + EvalResult::Deny { feature, reason: reason.to_opt_reason(), issue } } Some(_) => { // Stable APIs are always ok to call @@ -559,15 +518,9 @@ impl<'tcx> TyCtxt<'tcx> { let is_allowed = matches!(eval_result, EvalResult::Allow); match eval_result { EvalResult::Allow => {} - EvalResult::Deny { feature, reason, issue, suggestion } => report_unstable( - self.sess, - feature, - reason, - issue, - suggestion, - span, - UnstableKind::Regular, - ), + EvalResult::Deny { feature, reason, issue } => { + report_unstable(self.sess, feature, reason, issue, span, UnstableKind::Regular) + } EvalResult::Unmarked => unmarked(span, def_id), } @@ -628,7 +581,6 @@ impl<'tcx> TyCtxt<'tcx> { feature, reason.to_opt_reason(), issue, - None, span, UnstableKind::Const(const_kw_span), ); diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 83f62ee65025c..0acfe437493c0 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -10,6 +10,7 @@ use rustc_type_ir::TypeVisitableExt; use super::interpret::ReportedErrorInfo; use crate::mir::interpret::{AllocId, AllocRange, ErrorHandled, GlobalAlloc, Scalar, alloc_range}; use crate::mir::{Promoted, pretty_print_const_value}; +use crate::ty::consts::ConstExt; use crate::ty::print::{pretty_print_const, with_no_trimmed_paths}; use crate::ty::{self, ConstKind, GenericArgsRef, ScalarInt, Ty, TyCtxt}; diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 870819d7a2b93..ae30093b1cda7 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -18,6 +18,7 @@ use crate::mir::interpret::{ use crate::mir::visit::Visitor; use crate::mir::*; use crate::ty::CoroutineArgsExt; +use crate::ty::consts::ConstExt; const INDENT: &str = " "; /// Alignment for lining up comments following MIR statements @@ -25,7 +26,7 @@ pub(crate) const ALIGN: usize = 40; /// An indication of where we are in the control flow graph. Used for printing /// extra information in `dump_mir` -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq, Eq, Hash)] pub enum PassWhere { /// We have not started dumping the control flow graph, but we are about to. BeforeCFG, @@ -331,7 +332,13 @@ pub fn write_mir_pretty<'tcx>(tcx: TyCtxt<'tcx>, w: &mut dyn io::Write) -> io::R } let render_body = |w: &mut dyn io::Write, body| -> io::Result<()> { - writer.write_mir_fn(body, w)?; + if let Some(mut extra_data) = tcx.mir_pretty_extra_data(body) { + let mut writer = MirWriter::new(tcx); + writer.extra_data = &mut *extra_data; + writer.write_mir_fn(body, w)?; + } else { + writer.write_mir_fn(body, w)?; + } for body in tcx.promoted_mir(def_id) { writeln!(w)?; diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index 616b1719359f1..2e84fd80f35e5 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -7,6 +7,7 @@ use rustc_errors::ErrorGuaranteed; use rustc_index::IndexVec; use rustc_index::bit_set::BitMatrix; use rustc_macros::{StableHash, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; +use rustc_span::def_id::DefId; use rustc_span::{Span, Symbol}; use super::{ConstValue, SourceInfo}; @@ -129,7 +130,7 @@ pub enum ConstraintCategory<'tcx> { /// A constraint from a user-written predicate /// with the provided span, written on the item /// with the given `DefId` - Predicate(Span), + Predicate(Span, DefId), /// A "boring" constraint (caused by the given location) is one that /// the user probably doesn't want to see described in diagnostics, diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index 22366f70fbe4e..b6542fb328aac 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -10,6 +10,7 @@ use tracing::instrument; use super::interpret::GlobalAlloc; use super::*; +use crate::ty::consts::ConstExt; use crate::ty::{CoroutineArgsExt, Unnormalized}; /////////////////////////////////////////////////////////////////////////// diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 4c4a16953d5ed..6bb0190cd70b8 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1712,7 +1712,7 @@ pub enum BinOp { /// - `Ordering::Equal` (`0_i8`, as a Scalar) if `A == B` /// - `Ordering::Greater` (`+1_i8`, as a Scalar) if `A > B` /// - /// [`LangItem::OrderingEnum`]: rustc_hir::attrs::lang_items::LangItem + /// [`LangItem::OrderingEnum`]: rustc_attr_ir::lang_items::LangItem Cmp, /// The `ptr.offset` operator Offset, diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index cc75bebbb3f93..540a49aece678 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -3,9 +3,9 @@ use std::slice; use rustc_ast::InlineAsmOptions; +use rustc_attr_ir::AttributeKind; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::packed::Pu128; -use rustc_hir::attrs::AttributeKind; -use rustc_hir::attrs::lang_items::LangItem; use rustc_macros::{StableHash, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_span::bug; use smallvec::{SmallVec, smallvec}; @@ -419,7 +419,7 @@ impl fmt::Display for AssertKind { pub struct Terminator<'tcx> { pub source_info: SourceInfo, pub kind: TerminatorKind<'tcx>, - pub attributes: ThinVec, + pub loop_hint_attrs: ThinVec, } impl<'tcx> Terminator<'tcx> { diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index a6d15ac6458b8..1b45378c86c0a 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -518,7 +518,7 @@ macro_rules! make_mir_visitor { terminator: &$($mutability)? Terminator<'tcx>, location: Location ) { - let Terminator { source_info, kind, attributes: _ } = terminator; + let Terminator { source_info, kind, loop_hint_attrs: _ } = terminator; self.visit_source_info(source_info); match kind { diff --git a/compiler/rustc_middle/src/mono.rs b/compiler/rustc_middle/src/mono.rs index 76828cbee60f1..cf567fa1c8999 100644 --- a/compiler/rustc_middle/src/mono.rs +++ b/compiler/rustc_middle/src/mono.rs @@ -2,6 +2,7 @@ use std::borrow::Cow; use std::fmt; use std::hash::Hash; +use rustc_attr_ir::{InlineAttr, Linkage}; use rustc_data_structures::base_n::{BaseNString, CASE_INSENSITIVE, ToBaseN}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxIndexMap; @@ -11,11 +12,10 @@ use rustc_data_structures::stable_hash::{ use rustc_data_structures::unord::UnordMap; use rustc_hashes::Hash128; use rustc_hir::ItemId; -use rustc_hir::attrs::{InlineAttr, Linkage}; use rustc_hir::def_id::{CrateNum, DefId, DefIdSet, LOCAL_CRATE}; use rustc_macros::{StableHash, TyDecodable, TyEncodable}; use rustc_session::config::OptLevel; -use rustc_span::{Span, Symbol}; +use rustc_span::{OrdSpan, Span, Symbol}; use rustc_target::spec::SymbolVisibility; use tracing::debug; @@ -528,7 +528,7 @@ impl<'tcx> CodegenUnit<'tcx> { // The codegen tests rely on items being process in the same order as // they appear in the file, so for local items, we sort by span first #[derive(PartialEq, Eq, PartialOrd, Ord)] - struct ItemSortKey<'tcx>(Option, SymbolName<'tcx>); + struct ItemSortKey<'tcx>(Option, SymbolName<'tcx>); // We only want to take HirIds of user-defines instances into account. // The others don't matter for the codegen tests and can even make item @@ -560,9 +560,9 @@ impl<'tcx> CodegenUnit<'tcx> { } fn item_sort_key<'tcx>(tcx: TyCtxt<'tcx>, item: MonoItem<'tcx>) -> ItemSortKey<'tcx> { ItemSortKey( - local_item_id(item) - .map(|def_id| tcx.def_span(def_id).find_ancestor_not_from_macro()) - .flatten(), + local_item_id(item).and_then(|def_id| { + tcx.def_span(def_id).find_ancestor_not_from_macro().map(OrdSpan) + }), item.symbol_name(tcx), ) } diff --git a/compiler/rustc_middle/src/ptrauth/discriminator.rs b/compiler/rustc_middle/src/ptrauth/discriminator.rs index 7501f1b072188..d44ecafb775ef 100644 --- a/compiler/rustc_middle/src/ptrauth/discriminator.rs +++ b/compiler/rustc_middle/src/ptrauth/discriminator.rs @@ -282,9 +282,7 @@ fn compute_fn_ptr_type_discriminator<'tcx>( enc.push(b'E'); - let hash = enc.finish(); - - hash.into() + enc.finish() } // Clang disc type. diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 706d89ca52749..d9d618519446d 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -53,6 +53,7 @@ use rustc_arena::TypedArena; use rustc_ast as ast; use rustc_ast::expand::allocator::AllocatorKind; use rustc_ast::tokenstream::TokenStream; +use rustc_attr_ir::diagnostic_items::DiagnosticItems; use rustc_attr_ir::lang_items::{LangItem, LanguageItems}; use rustc_attr_ir::{CanonicalSymbols, EiiDecl, EiiImpl, StrippedCfgItem}; use rustc_crate_store::{ @@ -287,8 +288,8 @@ rustc_queries! { } /// Returns the const of the RHS of a (free or assoc) const item, if it is a `type const`, or if - /// it is a directly represented `const` (i.e. a const with a `direct_const_arg!` RHS, or a - /// const that `feature(macroless_generic_const_args)` has decided is direct). + /// it is a directly represented `const` (i.e. a const with a `gca!` RHS, or a const that + /// `feature(gca_macroless_args)` has decided is direct). /// /// When a const item is used in a type-level expression, like in equality for an assoc const /// projection, this allows us to retrieve the typesystem-appropriate representation of the @@ -1493,19 +1494,19 @@ rustc_queries! { cache_on_disk } - query lookup_stability(def_id: DefId) -> Option { + query lookup_stability(def_id: DefId) -> Option { desc { "looking up stability of `{}`", tcx.def_path_str(def_id) } cache_on_disk separate_provide_extern } - query lookup_const_stability(def_id: DefId) -> Option { + query lookup_const_stability(def_id: DefId) -> Option { desc { "looking up const stability of `{}`", tcx.def_path_str(def_id) } cache_on_disk separate_provide_extern } - query lookup_default_body_stability(def_id: DefId) -> Option { + query lookup_default_body_stability(def_id: DefId) -> Option { desc { "looking up default body stability of `{}`", tcx.def_path_str(def_id) } separate_provide_extern } @@ -2226,8 +2227,8 @@ rustc_queries! { } /// Do not call this query directly: invoke `Ty::is_opsem_inhabited` instead. - query is_opsem_inhabited_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { - desc { "computing whether `{}` is inhabited on the opsem level", env.value } + query is_opsem_inhabited_adt_cached(env: ty::PseudoCanonicalInput<'tcx, (ty::AdtDef<'tcx>, ty::GenericArgsRef<'tcx>)>) -> bool { + desc { "computing whether `{:?}` is inhabited on the opsem level", env.value } } query crate_dep_kind(_: CrateNum) -> CrateDepKind { @@ -2302,7 +2303,7 @@ rustc_queries! { } /// Returns all diagnostic items defined in all crates. - query all_diagnostic_items(_: ()) -> &'tcx rustc_hir::attrs::diagnostic_items::DiagnosticItems { + query all_diagnostic_items(_: ()) -> &'tcx DiagnosticItems { arena_cache eval_always desc { "calculating the diagnostic items map" } @@ -2322,7 +2323,7 @@ rustc_queries! { } /// Returns the diagnostic items defined in a crate. - query diagnostic_items(_: CrateNum) -> &'tcx rustc_hir::attrs::diagnostic_items::DiagnosticItems { + query diagnostic_items(_: CrateNum) -> &'tcx DiagnosticItems { arena_cache desc { "calculating the diagnostic items map in a crate" } separate_provide_extern @@ -2856,17 +2857,49 @@ rustc_queries! { // "Non-queries" are special dep kinds that are not queries. //----------------------------------------------------------------------------- - /// We use this for most things when incr. comp. is turned off. + /// Sentinel for an unused slot in a dense sequence of decoded dep-nodes. + /// + /// Dep-nodes are written to disk in non-sequential order, and the ID range + /// might have gaps due to IDs being allocated in per-thread chunks. Having a + /// sentinel makes it easier for the decoder to allocate a single dense vector + /// of Null nodes, and then decode the actual nodes into that vector. non_query Null - /// We use this to create a forever-red node. + + /// The singleton always-red node, with index `DepNodeIndex::FOREVER_RED_NODE`. + /// + /// A node that depends on the always-red node is never able to skip execution + /// due to having marked all of its dependencies green. + /// + /// Used when query feeding would copy the dependencies of the enclosing query, + /// but the enclosing query has the `eval_always` modifier. + /// + /// (Conceptually, `eval_always` query nodes should also have a `Red` dependency, + /// but instead they are special-cased to avoid having to store one explicitly.) non_query Red - /// We use this to create a side effect node. + + /// A "side-effect" node, e.g. emitting a diagnosting or recording that an + /// unstable feature was used. + /// + /// "Forcing" a side-effect node causes its side-effect to be replayed. non_query SideEffect - /// We use this to create the anon node with zero dependencies. + + // "Anonymous tasks" are similar to queries, but their identity is based on a + // hash of their dep-graph dependencies, rather than a hash of a query key. + + /// The singleton node with index `DepNodeIndex::SINGLETON_ZERO_DEPS_ANON_NODE`, + /// for anonymous tasks that didn't have any dep-graph dependencies. non_query AnonZeroDeps + /// Anonymous task for trait solving. non_query TraitSelect + + // These special tasks are also similar to queries, and have an associated key. + // But they bypass the usual query system machinery for various reasons. + + /// Special task for compiling a CGU. non_query CompileCodegenUnit + /// Special task for compiling a single `MonoItem`. Used by `rustc_codegen_cranelift`. non_query CompileMonoItem + /// Special task for emitting crate metadata. non_query Metadata } diff --git a/compiler/rustc_middle/src/query/arena_cached.rs b/compiler/rustc_middle/src/query/arena_cached.rs index 4ab2fbe914965..8faf6f68f49c9 100644 --- a/compiler/rustc_middle/src/query/arena_cached.rs +++ b/compiler/rustc_middle/src/query/arena_cached.rs @@ -5,13 +5,15 @@ use rustc_span::ErrorGuaranteed; use crate::ty::TyCtxt; -/// Helper trait that allows `arena_cache` queries to return `Option<&T>` -/// instead of `&Option`, and avoid allocating `None` in the arena. +/// Helper trait that allows `arena_cache` queries to return: +/// - `Option<&T>` instead of `&Option`, and avoid allocating `None` in the arena; or +/// - `Result<&T, ErrorGuaranteed>` instead of `&Result`, and avoid allocating +/// `Err(ErrorGuaranteed)` in the arena. /// -/// An arena-cached query must be declared to return a type that implements -/// this trait, i.e. either `&'tcx T` or `Option<&'tcx T>`. This trait then -/// determines the types returned by the provider and stored in the arena, -/// and provides a function to bridge between the three types. +/// An arena-cached query must be declared to return a type that implements this trait, i.e. +/// `&'tcx T`, `Option<&'tcx T>`, or `Result<&'tcx T, ErrorGuaranteed>`. This trait then determines +/// the types returned by the provider and stored in the arena, and provides a function to bridge +/// between the three types. pub trait ArenaCached<'tcx>: Sized { /// Type that is returned by the query provider. type Provided; diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 15a684491dcab..b0281a70eeb73 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -185,11 +185,11 @@ impl_erasable_for_types_with_no_type_params! { Option<(rustc_span::def_id::DefId, rustc_session::config::EntryFnType)>, Option, Option, + Option, + Option, + Option, Option, - Option, Option, - Option, - Option, Option, Option, Option, diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index eae4148a30ed4..2bfe80f444090 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -308,6 +308,12 @@ impl<'tcx, T: QueryKey> QueryKey for ty::PseudoCanonicalInput<'tcx, T> { } } +impl<'tcx> QueryKey for (ty::AdtDef<'tcx>, ty::GenericArgsRef<'tcx>) { + fn default_span(&self, tcx: TyCtxt<'_>) -> Span { + tcx.def_span(self.0.did()) + } +} + impl QueryKey for Symbol { fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { DUMMY_SP diff --git a/compiler/rustc_middle/src/query/modifiers.rs b/compiler/rustc_middle/src/query/modifiers.rs index 4fd91caa94cd7..3e057e96ddb6b 100644 --- a/compiler/rustc_middle/src/query/modifiers.rs +++ b/compiler/rustc_middle/src/query/modifiers.rs @@ -1,4 +1,5 @@ -//! This contains documentation which is linked from query modifiers used in the `rustc_queries!` proc macro. +//! This contains documentation which is linked from query modifiers used in the `rustc_queries!` +//! proc macro. //! //! The dummy items in this module are used to enable hover documentation for //! modifier names in the query list, and to allow find-all-references to list @@ -10,10 +11,12 @@ /// # `arena_cache` query modifier /// /// Query return values must impl `Copy` and be small, but some queries must return values that -/// doesn't meet those criteria. Queries marked with this modifier have their values allocated in -/// an arena and the query returns a reference to the value. There are two cases. +/// don't meet those criteria. Queries marked with this modifier have their values allocated in +/// an arena and the query returns a reference to the value. There are three cases. /// - If the provider function returns `T` then the query will return `&'tcx T`. /// - If the provider function returns `Option` then the query will return `Option<&'tcx T>`. +/// - If the provider function returns `Result` then the query will return +/// `Result<&'tcx T, ErrorGuaranteed>`. /// /// The query plumbing takes care of the arenas and the type manipulations. pub(crate) struct arena_cache; diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index 20c1c5c16c95c..956c59012d025 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -25,6 +25,7 @@ use rustc_span::{ }; use crate::dep_graph::{DepNodeIndex, QuerySideEffect, SerializedDepNodeIndex}; +use crate::ich::SourceSpanCache; use crate::mir::interpret::{AllocDecodingSession, AllocDecodingState}; use crate::mir::{self, interpret}; use crate::mono::MonoItem; @@ -235,6 +236,7 @@ impl OnDiskCache { file_to_file_index, hygiene_context: Default::default(), symbol_index_table: Default::default(), + source_span_cache: Default::default(), query_values_index: Default::default(), side_effects_index: Default::default(), }; @@ -383,6 +385,7 @@ impl OnDiskCache { expn_data: &self.expn_data, foreign_expn_data: &self.foreign_expn_data, hygiene_context: &self.hygiene_context, + source_span_cache: SourceSpanCache::default(), }; f(&mut decoder) } @@ -403,6 +406,7 @@ pub struct CacheDecoder<'a, 'tcx> { expn_data: &'a UnhashMap, foreign_expn_data: &'a UnhashMap, hygiene_context: &'a HygieneDecodeContext, + source_span_cache: SourceSpanCache, } impl<'a, 'tcx> CacheDecoder<'a, 'tcx> { @@ -611,7 +615,9 @@ impl<'a, 'tcx> SpanDecoder for CacheDecoder<'a, 'tcx> { let dlo = u32::decode(self); let dto = u32::decode(self); - let enclosing = self.tcx.source_span_untracked(parent.unwrap()).data_untracked(); + let enclosing = self + .source_span_cache + .lookup(parent.unwrap(), &self.tcx.untracked().source_span); ( BytePos(enclosing.lo.0.wrapping_add(dlo)), BytePos(enclosing.lo.0.wrapping_add(dto)), @@ -744,6 +750,7 @@ pub struct CacheEncoder<'tcx> { hygiene_context: Rc>, // Used for both `Symbol`s and `ByteSymbol`s. symbol_index_table: FxHashMap, + source_span_cache: SourceSpanCache, query_values_index: Vec<(SerializedDepNodeIndex, AbsoluteBytePos)>, side_effects_index: Vec<(SerializedDepNodeIndex, AbsoluteBytePos)>, @@ -845,8 +852,9 @@ impl<'tcx> SpanEncoder for CacheEncoder<'tcx> { return TAG_PARTIAL_SPAN.encode(self); } - let parent = - span_data.parent.map(|parent| self.tcx.source_span_untracked(parent).data_untracked()); + let parent = span_data + .parent + .map(|parent| self.source_span_cache.lookup(parent, &self.tcx.untracked().source_span)); if let Some(parent) = parent && parent.contains(span_data) { @@ -929,10 +937,12 @@ impl<'tcx> TyEncoder<'tcx> for CacheEncoder<'tcx> { macro_rules! encoder_methods { ($($name:ident($ty:ty);)*) => { - #[inline] - $(fn $name(&mut self, value: $ty) { - self.encoder.$name(value) - })* + $( + #[inline] + fn $name(&mut self, value: $ty) { + self.encoder.$name(value) + } + )* } } diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index fe7b1bf493051..a3e5168e4f1e9 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -15,10 +15,10 @@ use std::sync::Arc; use rustc_abi::{FieldIdx, Integer, Size, VariantIdx}; use rustc_ast::{AsmMacro, InlineAsmOptions, InlineAsmTemplatePiece, Mutability}; +use rustc_attr_ir::AttributeKind; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::thin_vec::ThinVec; use rustc_hir as hir; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::DefId; use rustc_hir::{BindingMode, ByRef, HirId, MatchSource, RangeEnd}; use rustc_index::{IndexVec, newtype_index}; @@ -62,7 +62,7 @@ macro_rules! thir_with_elements { #[derive(Debug, StableHash, Clone)] pub struct Thir<'tcx> { pub body_type: BodyTy<'tcx>, - pub attributes: FxIndexMap>, + pub loop_hint_attrs: FxIndexMap>, $( pub $name: IndexVec<$id, $value>, )* @@ -72,7 +72,7 @@ macro_rules! thir_with_elements { pub fn new(body_type: BodyTy<'tcx>) -> Thir<'tcx> { Thir { body_type, - attributes: FxIndexMap::default(), + loop_hint_attrs: FxIndexMap::default(), $( $name: IndexVec::new(), )* @@ -661,7 +661,7 @@ pub struct PatExtra<'tcx> { /// /// This is used by some diagnostics for non-exhaustive matches, to map /// the pattern node back to the `DefId` of its original constant. - pub expanded_const: Option, + pub expanded_const: Option>, /// User-written types that must be preserved into MIR so that they can be /// checked. diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index f88b7ff0f8a85..493f6989f342c 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -21,7 +21,7 @@ use rustc_macros::{ Decodable, Encodable, StableHash, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable, }; use rustc_span::def_id::{CRATE_DEF_ID, LocalDefId}; -use rustc_span::{DUMMY_SP, Span, Symbol}; +use rustc_span::{DUMMY_SP, OrdSpan, Span, Symbol, sym}; use smallvec::{SmallVec, smallvec}; use thin_vec::ThinVec; @@ -137,8 +137,8 @@ impl<'tcx> ObligationCause<'tcx> { pub fn to_constraint_category(&self) -> ConstraintCategory<'tcx> { match self.code() { ObligationCauseCode::MatchImpl(cause, _) => cause.to_constraint_category(), - ObligationCauseCode::AscribeUserTypeProvePredicate(predicate_span) => { - ConstraintCategory::Predicate(*predicate_span) + ObligationCauseCode::AscribeUserTypeProvePredicate(predicate_span, def_id) => { + ConstraintCategory::Predicate(*predicate_span, *def_id) } _ => ConstraintCategory::BoringNoLocation, } @@ -402,7 +402,7 @@ pub enum ObligationCauseCode<'tcx> { output_ty: Option>, }, - AscribeUserTypeProvePredicate(Span), + AscribeUserTypeProvePredicate(Span, DefId), RustCall, @@ -863,15 +863,15 @@ impl DynCompatibilityViolation { | Self::SupertraitConst(_) => DynCompatibilityViolationSolution::None, Self::Method( name, - MethodViolation::StaticMethod(Some((add_self_sugg, make_sized_sugg))), + MethodViolation::StaticMethod(Some(((add_s, add_sp), (make_s, make_sp)))), _, ) => DynCompatibilityViolationSolution::AddSelfOrMakeSized { name: *name, - add_self_sugg: add_self_sugg.clone(), - make_sized_sugg: make_sized_sugg.clone(), + add_self_sugg: (add_s.clone(), OrdSpan(*add_sp)), + make_sized_sugg: (make_s.clone(), OrdSpan(*make_sp)), }, - Self::Method(name, MethodViolation::UndispatchableReceiver(Some(span)), _) => { - DynCompatibilityViolationSolution::ChangeToRefSelf(*name, *span) + Self::Method(name, MethodViolation::UndispatchableReceiver(Some((span, lt))), _) => { + DynCompatibilityViolationSolution::ChangeToRefSelf(*name, OrdSpan(*span), *lt) } Self::Method(name, ..) | Self::AssocConst(name, ..) | Self::GenericAssocTy(name, _) => { DynCompatibilityViolationSolution::MoveToAnotherTrait(*name) @@ -901,20 +901,20 @@ impl DynCompatibilityViolation { } } -#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum DynCompatibilityViolationSolution { None, AddSelfOrMakeSized { name: Symbol, - add_self_sugg: (String, Span), - make_sized_sugg: (String, Span), + add_self_sugg: (String, OrdSpan), + make_sized_sugg: (String, OrdSpan), }, - ChangeToRefSelf(Symbol, Span), + ChangeToRefSelf(Symbol, OrdSpan, Symbol), MoveToAnotherTrait(Symbol), } impl DynCompatibilityViolationSolution { - pub fn add_to(self, err: &mut Diag<'_, G>) { + pub fn add_to(self, err: &mut Diag<'_>) { match self { DynCompatibilityViolationSolution::None => {} DynCompatibilityViolationSolution::AddSelfOrMakeSized { @@ -922,30 +922,30 @@ impl DynCompatibilityViolationSolution { add_self_sugg, make_sized_sugg, } => { - err.span_suggestion( - add_self_sugg.1, + err.span_suggestion_verbose( + add_self_sugg.1.0, format!( - "consider turning `{name}` into a method by giving it a `&self` \ - argument, so that it is accessible through the trait object's vtable" + "consider turning `{name}` into a method by giving it a `&self` argument, \ + so that it is accessible through the trait object's vtable", ), add_self_sugg.0, Applicability::MaybeIncorrect, ); - err.span_suggestion( - make_sized_sugg.1, + err.span_suggestion_verbose( + make_sized_sugg.1.0, format!( - "alternatively, consider constraining `{name}` so it is explicitly \ - marked as not applying to trait objects" + "alternatively, consider constraining `{name}` so it is explicitly marked \ + as not applying to trait objects", ), make_sized_sugg.0, Applicability::MaybeIncorrect, ); } - DynCompatibilityViolationSolution::ChangeToRefSelf(name, span) => { - err.span_suggestion( - span, + DynCompatibilityViolationSolution::ChangeToRefSelf(name, span, lt) => { + err.span_suggestion_verbose( + span.0, format!("consider changing method `{name}`'s `self` parameter to be `&self`"), - "&Self", + format!("&{lt}{}self", if lt != sym::empty { " " } else { "" }), Applicability::MachineApplicable, ); } @@ -983,14 +983,17 @@ pub enum MethodViolation { /// e.g., `fn (mut ap: ...)` CVariadic, - /// the method's receiver (`self` argument) can't be dispatched on - UndispatchableReceiver(Option), + /// The method's receiver (`self` argument) can't be dispatched on + /// + /// The `Span` points at the receiver. The `Symbol` is the lifetime's name `'a` when we have + /// Arbitrary Self Types like `self: &'a ()`. + UndispatchableReceiver(Option<(Span, Symbol)>), } /// Reasons an associated const might not be dyn compatible. #[derive(Clone, Debug, PartialEq, Eq, Hash, StableHash)] pub enum AssocConstViolation { - /// Unstable feature `min_generic_const_args` wasn't enabled. + /// Unstable feature `gca_min_const_items` wasn't enabled. FeatureNotEnabled, /// Not defined as a type-level associated const. diff --git a/compiler/rustc_middle/src/traits/specialization_graph.rs b/compiler/rustc_middle/src/traits/specialization_graph.rs index 19b72d1a7d1c4..b13af2705c736 100644 --- a/compiler/rustc_middle/src/traits/specialization_graph.rs +++ b/compiler/rustc_middle/src/traits/specialization_graph.rs @@ -1,7 +1,7 @@ +use rustc_attr_ir::find_attr; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::{DefId, DefIdMap}; -use rustc_hir::find_attr; use rustc_macros::{StableHash, TyDecodable, TyEncodable}; use crate::diagnostics::StrictCoherenceNeedsNegativeCoherence; diff --git a/compiler/rustc_middle/src/ty/abstract_const.rs b/compiler/rustc_middle/src/ty/abstract_const.rs index 2853c43ae079d..2227841923514 100644 --- a/compiler/rustc_middle/src/ty/abstract_const.rs +++ b/compiler/rustc_middle/src/ty/abstract_const.rs @@ -52,9 +52,14 @@ impl<'tcx> TyCtxt<'tcx> { } fn fold_const(&mut self, c: Const<'tcx>) -> Const<'tcx> { let ct = match c.kind() { - ty::ConstKind::Alias(_, alias_const) - if let Some(def_id) = alias_const.kind.opt_def_id() => - { + ty::ConstKind::Alias(_, alias_const) => { + let def_id = match alias_const.kind { + ty::AliasConstKind::Projection { def_id } + | ty::AliasConstKind::InherentSelf { def_id } + | ty::AliasConstKind::InherentImpl { def_id } + | ty::AliasConstKind::Free { def_id } + | ty::AliasConstKind::Anon { def_id } => def_id, + }; match self.tcx.thir_abstract_const(def_id) { Err(e) => ty::Const::new_error(self.tcx, e), Ok(Some(bac)) => { diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index 00efbe912b883..b3a2925b24bfa 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -1,6 +1,6 @@ use rustc_abi::FieldIdx; +use rustc_attr_ir::lang_items::LangItem; use rustc_hir as hir; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def_id::DefId; use rustc_macros::{StableHash, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_span::Span; diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index d189fbc5d59bd..8ad5de3fa85cc 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -4,6 +4,8 @@ use std::ops::Range; use std::str; use rustc_abi::{FIRST_VARIANT, FieldIdx, ReprOptions, VariantIdx}; +use rustc_attr_ir::find_attr; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::intern::Interned; @@ -11,10 +13,9 @@ use rustc_data_structures::stable_hash::{ StableHash, StableHashControls, StableHashCtxt, StableHasher, }; use rustc_errors::ErrorGuaranteed; -use rustc_hir::attrs::lang_items::LangItem; +use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, find_attr}; use rustc_index::{IndexSlice, IndexVec}; use rustc_macros::{StableHash, TyDecodable, TyEncodable}; use rustc_session::DataTypeKind; diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index a3ccbc7742df4..e52d74f4c23fe 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -143,7 +143,7 @@ impl AssocItem { match self.kind { ty::AssocKind::Type { .. } => true, ty::AssocKind::Const { .. } => { - tcx.features().generic_const_args() || tcx.is_direct_const(self.def_id) + tcx.features().gca_const_items() || tcx.is_direct_const(self.def_id) } ty::AssocKind::Fn { .. } => false, } diff --git a/compiler/rustc_middle/src/ty/cast.rs b/compiler/rustc_middle/src/ty/cast.rs index 25fc0775b1a32..fc8a6de5d232e 100644 --- a/compiler/rustc_middle/src/ty/cast.rs +++ b/compiler/rustc_middle/src/ty/cast.rs @@ -1,35 +1,20 @@ -// Helpers for handling cast expressions, used in both -// typeck and codegen. +// Helpers for handling cast expressions. -use rustc_macros::{StableHash, TyDecodable, TyEncodable}; use rustc_span::bug; use crate::mir; use crate::ty::{self, Ty}; -/// Types that are represented as ints. +/// Valid types for the result of a non-coercion cast #[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum IntTy { - U(ty::UintTy), - I, +pub enum CastTy<'tcx> { + /// `iN`, `uN`, and integer inference variables. + Int, + /// C-like (fieldless) enums. CEnum, Bool, Char, -} - -impl IntTy { - pub fn is_signed(self) -> bool { - matches!(self, Self::I) - } -} - -// Valid types for the result of a non-coercion cast -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum CastTy<'tcx> { - /// Various types that are represented as ints and handled mostly - /// in the same way, merged for easier matching. - Int(IntTy), - /// Floating-point types. + /// `fN` and float inference variables. Float, /// Function pointers. FnPtr, @@ -37,40 +22,28 @@ pub enum CastTy<'tcx> { Ptr(ty::TypeAndMut<'tcx>), } -/// Cast Kind. See [RFC 401](https://rust-lang.github.io/rfcs/0401-coercions.html) -/// (or rustc_hir_analysis/check/cast.rs). -#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, StableHash)] -pub enum CastKind { - PtrPtrCast, - PtrAddrCast, - AddrPtrCast, - NumericCast, - EnumCast, - PrimIntCast, - U8CharCast, - ArrayPtrCast, - FnPtrPtrCast, - FnPtrAddrCast, -} - impl<'tcx> CastTy<'tcx> { /// Returns `Some` for integral/pointer casts. /// Casts like unsizing casts will return `None`. pub fn from_ty(t: Ty<'tcx>) -> Option> { match *t.kind() { - ty::Bool => Some(CastTy::Int(IntTy::Bool)), - ty::Char => Some(CastTy::Int(IntTy::Char)), - ty::Int(_) => Some(CastTy::Int(IntTy::I)), - ty::Infer(ty::InferTy::IntVar(_)) => Some(CastTy::Int(IntTy::I)), - ty::Infer(ty::InferTy::FloatVar(_)) => Some(CastTy::Float), - ty::Uint(u) => Some(CastTy::Int(IntTy::U(u))), - ty::Float(_) => Some(CastTy::Float), - ty::Adt(d, _) if d.is_enum() && d.is_payloadfree() => Some(CastTy::Int(IntTy::CEnum)), + ty::Bool => Some(CastTy::Bool), + ty::Char => Some(CastTy::Char), + ty::Int(_) | ty::Uint(_) | ty::Infer(ty::InferTy::IntVar(_)) => Some(CastTy::Int), + ty::Float(_) | ty::Infer(ty::InferTy::FloatVar(_)) => Some(CastTy::Float), + ty::Adt(d, _) if d.is_enum() && d.is_payloadfree() => Some(CastTy::CEnum), ty::RawPtr(ty, mutbl) => Some(CastTy::Ptr(ty::TypeAndMut { ty, mutbl })), ty::FnPtr(..) => Some(CastTy::FnPtr), _ => None, } } + + pub fn is_int_like(self) -> bool { + match self { + CastTy::Int | CastTy::CEnum | CastTy::Bool | CastTy::Char => true, + CastTy::Float | CastTy::FnPtr | CastTy::Ptr(_) => false, + } + } } /// Returns `mir::CastKind` from the given parameters. @@ -78,15 +51,17 @@ pub fn mir_cast_kind<'tcx>(from_ty: Ty<'tcx>, cast_ty: Ty<'tcx>) -> mir::CastKin let from = CastTy::from_ty(from_ty); let cast = CastTy::from_ty(cast_ty); let cast_kind = match (from, cast) { - (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Int(_))) => { + (Some(from), Some(cast)) if from.is_int_like() && cast.is_int_like() => { + mir::CastKind::IntToInt + } + (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Int)) => { mir::CastKind::PointerExposeProvenance } - (Some(CastTy::Int(_)), Some(CastTy::Ptr(_))) => mir::CastKind::PointerWithExposedProvenance, - (Some(CastTy::Int(_)), Some(CastTy::Int(_))) => mir::CastKind::IntToInt, + (Some(CastTy::Int), Some(CastTy::Ptr(_))) => mir::CastKind::PointerWithExposedProvenance, (Some(CastTy::FnPtr), Some(CastTy::Ptr(_))) => mir::CastKind::FnPtrToPtr, - (Some(CastTy::Float), Some(CastTy::Int(_))) => mir::CastKind::FloatToInt, - (Some(CastTy::Int(_)), Some(CastTy::Float)) => mir::CastKind::IntToFloat, + (Some(CastTy::Float), Some(CastTy::Int)) => mir::CastKind::FloatToInt, + (Some(CastTy::Int), Some(CastTy::Float)) => mir::CastKind::IntToFloat, (Some(CastTy::Float), Some(CastTy::Float)) => mir::CastKind::FloatToFloat, (Some(CastTy::Ptr(_)), Some(CastTy::Ptr(_))) => mir::CastKind::PtrToPtr, diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs index 652c353d418c2..4b410b3259bff 100644 --- a/compiler/rustc_middle/src/ty/closure.rs +++ b/compiler/rustc_middle/src/ty/closure.rs @@ -1,3 +1,4 @@ +use std::cmp; use std::fmt::Write; use rustc_data_structures::fx::FxIndexMap; @@ -46,16 +47,32 @@ impl UpvarId { #[derive(Eq, PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, StableHash, Hash)] #[derive(TypeFoldable, TypeVisitable)] pub enum UpvarCapture { + /// Upvar is captured by reference. + ByRef(BorrowKind), + + /// Upvar is captured by use. This is true when the closure is labeled `use`. + ByUse, + /// Upvar is captured by value. This is always true when the /// closure is labeled `move`, but can also be true in other cases /// depending on inference. ByValue, +} - /// Upvar is captured by use. This is true when the closure is labeled `use`. - ByUse, - - /// Upvar is captured by reference. - ByRef(BorrowKind), +// Used in rustc_hir_typeck::upvar::determine_capture_info +impl PartialOrd for UpvarCapture { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + match (self, other) { + (Self::ByValue, Self::ByValue) | (Self::ByUse, Self::ByUse) => { + Some(cmp::Ordering::Equal) + } + (Self::ByValue | Self::ByUse, Self::ByRef(_)) => Some(cmp::Ordering::Greater), + (Self::ByRef(_), Self::ByValue | Self::ByUse) => Some(cmp::Ordering::Less), + (Self::ByRef(left), Self::ByRef(right)) => Some(left.cmp(&right)), + (Self::ByUse, Self::ByValue) | (Self::ByValue, Self::ByUse) => None, + } + } } /// Given the closure DefId this map provides a map of root variables to minimum @@ -339,8 +356,21 @@ pub fn place_to_string_for_capture<'tcx>(tcx: TyCtxt<'tcx>, place: &HirPlace<'tc curr_string } -#[derive(Eq, Clone, PartialEq, Debug, TyEncodable, TyDecodable, Copy, StableHash, Hash)] -#[derive(TypeFoldable, TypeVisitable)] +#[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, // Order of variants is load-bearing + Ord, + TyEncodable, + TyDecodable, + StableHash, + TypeFoldable, + TypeVisitable +)] pub enum BorrowKind { /// Data must be immutable and is aliasable. Immutable, diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index 9d4899d6fd450..81462e214b20f 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -142,12 +142,6 @@ impl<'tcx, E: TyEncoder<'tcx>> Encodable for ty::Clause<'tcx> { } } -impl<'tcx, E: TyEncoder<'tcx>> Encodable for ty::Const<'tcx> { - fn encode(&self, e: &mut E) { - self.0.0.encode(e); - } -} - impl<'tcx, E: TyEncoder<'tcx>> Encodable for ty::Pattern<'tcx> { fn encode(&self, e: &mut E) { self.0.0.encode(e); @@ -292,13 +286,6 @@ impl<'tcx, D: TyDecoder<'tcx>> Decodable for ty::ParamEnv<'tcx> { } } -impl<'tcx, D: TyDecoder<'tcx>> Decodable for ty::Const<'tcx> { - fn decode(decoder: &mut D) -> Self { - let kind: ty::ConstKind<'tcx> = Decodable::decode(decoder); - decoder.interner().mk_ct_from_kind(kind) - } -} - impl<'tcx, D: TyDecoder<'tcx>> Decodable for ty::Pattern<'tcx> { fn decode(decoder: &mut D) -> Self { decoder.interner().mk_pat(Decodable::decode(decoder)) diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 9e9f8d5df45de..cbe7b6d732c0f 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -1,10 +1,7 @@ -use std::borrow::Cow; - -use rustc_data_structures::intern::Interned; -use rustc_macros::StableHash; -use rustc_span::{Span, bug}; +use rustc_macros::extension; +use rustc_span::bug; use rustc_type_ir::walk::TypeWalker; -use rustc_type_ir::{self as ir, TypeFlags, WithCachedTypeInfo}; +use rustc_type_ir::{self as ir}; use crate::mir::interpret::Scalar; use crate::ty::{self, Ty, TyCtxt}; @@ -17,204 +14,21 @@ mod valtree; pub use int::*; pub use kind::*; pub use lit::*; -use rustc_span::{DUMMY_SP, ErrorGuaranteed}; pub use valtree::*; pub type ConstKind<'tcx> = ir::ConstKind>; pub type AliasConst<'tcx> = ir::AliasConst>; pub type AliasConstKind<'tcx> = ir::AliasConstKind>; +pub type Const<'tcx> = ir::Const>; #[cfg(target_pointer_width = "64")] rustc_data_structures::static_assert_size!(ConstKind<'_>, 32); -#[derive(Copy, Clone, PartialEq, Eq, Hash, StableHash)] -#[rustc_pass_by_value] -pub struct Const<'tcx>(pub(super) Interned<'tcx, WithCachedTypeInfo>>); - -impl<'tcx> rustc_type_ir::inherent::IntoKind for Const<'tcx> { - type Kind = ConstKind<'tcx>; - - fn kind(self) -> ConstKind<'tcx> { - self.kind() - } -} - -impl<'tcx> rustc_type_ir::Flags for Const<'tcx> { - fn flags(&self) -> TypeFlags { - self.0.flags - } - - fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex { - self.0.outer_exclusive_binder - } -} - -impl<'tcx> Const<'tcx> { - #[inline] - pub fn kind(self) -> ConstKind<'tcx> { - let a: &ConstKind<'tcx> = self.0.0; - *a - } - - #[inline] - pub fn new(tcx: TyCtxt<'tcx>, kind: ty::ConstKind<'tcx>) -> Const<'tcx> { - tcx.mk_ct_from_kind(kind) - } - - #[inline] - pub fn new_param(tcx: TyCtxt<'tcx>, param: ty::ParamConst) -> Const<'tcx> { - Const::new(tcx, ty::ConstKind::Param(param)) - } - - #[inline] - pub fn new_var(tcx: TyCtxt<'tcx>, infer: ty::ConstVid) -> Const<'tcx> { - Const::new(tcx, ty::ConstKind::Infer(ty::InferConst::Var(infer))) - } - - #[inline] - pub fn new_fresh(tcx: TyCtxt<'tcx>, fresh: u32) -> Const<'tcx> { - Const::new(tcx, ty::ConstKind::Infer(ty::InferConst::Fresh(fresh))) - } - - #[inline] - pub fn new_infer(tcx: TyCtxt<'tcx>, infer: ty::InferConst) -> Const<'tcx> { - Const::new(tcx, ty::ConstKind::Infer(infer)) - } - - #[inline] - pub fn new_bound( - tcx: TyCtxt<'tcx>, - debruijn: ty::DebruijnIndex, - bound_const: ty::BoundConst<'tcx>, - ) -> Const<'tcx> { - Const::new(tcx, ty::ConstKind::Bound(ty::BoundVarIndexKind::Bound(debruijn), bound_const)) - } - - #[inline] - pub fn new_canonical_bound(tcx: TyCtxt<'tcx>, var: ty::BoundVar) -> Const<'tcx> { - Const::new( - tcx, - ty::ConstKind::Bound(ty::BoundVarIndexKind::Canonical, ty::BoundConst::new(var)), - ) - } - - #[inline] - pub fn new_placeholder( - tcx: TyCtxt<'tcx>, - placeholder: ty::PlaceholderConst<'tcx>, - ) -> Const<'tcx> { - Const::new(tcx, ty::ConstKind::Placeholder(placeholder)) - } - - #[inline] - pub fn new_alias( - tcx: TyCtxt<'tcx>, - is_rigid: ty::IsRigid, - alias_const: ty::AliasConst<'tcx>, - ) -> Const<'tcx> { - Const::new(tcx, ty::ConstKind::Alias(is_rigid, alias_const)) - } - - #[inline] - pub fn new_value(tcx: TyCtxt<'tcx>, valtree: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> { - Const::new(tcx, ty::ConstKind::Value(ty::Value { ty, valtree })) - } - - #[inline] - pub fn new_expr(tcx: TyCtxt<'tcx>, expr: ty::Expr<'tcx>) -> Const<'tcx> { - Const::new(tcx, ty::ConstKind::Expr(expr)) - } - - #[inline] - pub fn new_error(tcx: TyCtxt<'tcx>, e: ty::ErrorGuaranteed) -> Const<'tcx> { - Const::new(tcx, ty::ConstKind::Error(e)) - } - - /// Like [Ty::new_error] but for constants. - #[track_caller] - pub fn new_misc_error(tcx: TyCtxt<'tcx>) -> Const<'tcx> { - Const::new_error_with_message( - tcx, - DUMMY_SP, - "ty::ConstKind::Error constructed but no error reported", - ) - } - - /// Like [Ty::new_error_with_message] but for constants. - #[track_caller] - pub fn new_error_with_message( - tcx: TyCtxt<'tcx>, - span: Span, - msg: impl Into>, - ) -> Const<'tcx> { - let reported = tcx.dcx().span_delayed_bug(span, msg); - Const::new_error(tcx, reported) - } - - pub fn is_trivially_wf(self) -> bool { - match self.kind() { - ty::ConstKind::Param(_) | ty::ConstKind::Placeholder(_) | ty::ConstKind::Bound(..) => { - true - } - ty::ConstKind::Infer(_) - | ty::ConstKind::Alias(..) - | ty::ConstKind::Value(_) - | ty::ConstKind::Error(_) - | ty::ConstKind::Expr(_) => false, - } - } -} - -impl<'tcx> rustc_type_ir::inherent::Const> for Const<'tcx> { - fn new_infer(tcx: TyCtxt<'tcx>, infer: ty::InferConst) -> Self { - Const::new_infer(tcx, infer) - } - - fn new_var(tcx: TyCtxt<'tcx>, vid: ty::ConstVid) -> Self { - Const::new_var(tcx, vid) - } - - fn new_bound( - interner: TyCtxt<'tcx>, - debruijn: ty::DebruijnIndex, - bound_const: ty::BoundConst<'tcx>, - ) -> Self { - Const::new_bound(interner, debruijn, bound_const) - } - - fn new_anon_bound(tcx: TyCtxt<'tcx>, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self { - Const::new_bound(tcx, debruijn, ty::BoundConst::new(var)) - } - - fn new_canonical_bound(tcx: TyCtxt<'tcx>, var: rustc_type_ir::BoundVar) -> Self { - Const::new_canonical_bound(tcx, var) - } - - fn new_placeholder(tcx: TyCtxt<'tcx>, placeholder: ty::PlaceholderConst<'tcx>) -> Self { - Const::new_placeholder(tcx, placeholder) - } - - fn new_alias( - interner: TyCtxt<'tcx>, - is_rigid: ty::IsRigid, - alias_const: ty::AliasConst<'tcx>, - ) -> Self { - Const::new_alias(interner, is_rigid, alias_const) - } - - fn new_expr(interner: TyCtxt<'tcx>, expr: ty::Expr<'tcx>) -> Self { - Const::new_expr(interner, expr) - } - - fn new_error(interner: TyCtxt<'tcx>, guar: ErrorGuaranteed) -> Self { - Const::new_error(interner, guar) - } -} - +#[extension(pub trait ConstExt<'tcx>)] impl<'tcx> Const<'tcx> { /// Creates a constant with the given integer value and interns it. #[inline] - pub fn from_bits( + fn from_bits( tcx: TyCtxt<'tcx>, bits: u128, typing_env: ty::TypingEnv<'tcx>, @@ -224,33 +38,36 @@ impl<'tcx> Const<'tcx> { .layout_of(typing_env.as_query_input(ty)) .unwrap_or_else(|e| panic!("could not compute layout for {ty:?}: {e:?}")) .size; - ty::Const::new_value( - tcx, - ty::ValTree::from_scalar_int(tcx, ScalarInt::try_from_uint(bits, size).unwrap()), - ty, - ) + let valtree = + ty::ValTree::from_scalar_int(tcx, ScalarInt::try_from_uint(bits, size).unwrap()); + ty::Const::new_value(tcx, valtree, ty) } #[inline] /// Creates an interned zst constant. - pub fn zero_sized(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Self { + fn zero_sized(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Self { ty::Const::new_value(tcx, ty::ValTree::zst(tcx), ty) } + #[inline] + fn new_value(tcx: TyCtxt<'tcx>, valtree: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> { + Const::new(tcx, ty::ConstKind::Value(ty::Value { ty, valtree })) + } + #[inline] /// Creates an interned bool constant. - pub fn from_bool(tcx: TyCtxt<'tcx>, v: bool) -> Self { + fn from_bool(tcx: TyCtxt<'tcx>, v: bool) -> Self { Self::from_bits(tcx, v as u128, ty::TypingEnv::fully_monomorphized(), tcx.types.bool) } #[inline] /// Creates an interned usize constant. - pub fn from_target_usize(tcx: TyCtxt<'tcx>, n: u64) -> Self { + fn from_target_usize(tcx: TyCtxt<'tcx>, n: u64) -> Self { Self::from_bits(tcx, n as u128, ty::TypingEnv::fully_monomorphized(), tcx.types.usize) } /// Panics if `self.kind != ty::ConstKind::Value`. - pub fn to_value(self) -> ty::Value<'tcx> { + fn to_value(self) -> ty::Value<'tcx> { match self.kind() { ty::ConstKind::Value(cv) => cv, _ => bug!("expected ConstKind::Value, got {:?}", self.kind()), @@ -260,7 +77,7 @@ impl<'tcx> Const<'tcx> { /// Attempts to convert to a value. /// /// Note that this does not normalize the constant. - pub fn try_to_value(self) -> Option> { + fn try_to_value(self) -> Option> { match self.kind() { ty::ConstKind::Value(cv) => Some(cv), _ => None, @@ -272,7 +89,7 @@ impl<'tcx> Const<'tcx> { /// /// Note that this does not normalize the constant. #[inline] - pub fn to_leaf(self) -> ScalarInt { + fn to_leaf(self) -> ScalarInt { self.to_value().to_leaf() } @@ -281,28 +98,28 @@ impl<'tcx> Const<'tcx> { /// /// Note that this does not normalize the constant. #[inline] - pub fn to_branch(self) -> &'tcx [ty::Const<'tcx>] { + fn to_branch(self) -> &'tcx [ty::Const<'tcx>] { self.to_value().to_branch() } /// Attempts to convert to a `ValTreeKind::Leaf` value. /// /// Note that this does not normalize the constant. - pub fn try_to_leaf(self) -> Option { + fn try_to_leaf(self) -> Option { self.try_to_value()?.try_to_leaf() } /// Attempts to convert to a `ValTreeKind::Leaf` value. /// /// Note that this does not normalize the constant. - pub fn try_to_scalar(self) -> Option { + fn try_to_scalar(self) -> Option { self.try_to_leaf().map(Scalar::Int) } /// Attempts to convert to a `ValTreeKind::Branch` value. /// /// Note that this does not normalize the constant. - pub fn try_to_branch(self) -> Option<&'tcx [ty::Const<'tcx>]> { + fn try_to_branch(self) -> Option<&'tcx [ty::Const<'tcx>]> { self.try_to_value()?.try_to_branch() } @@ -311,21 +128,10 @@ impl<'tcx> Const<'tcx> { /// /// Note that this does not evaluate the constant. #[inline] - pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option { + fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option { self.try_to_value()?.try_to_target_usize(tcx) } - pub fn is_ct_infer(self) -> bool { - matches!(self.kind(), ty::ConstKind::Infer(_)) - } - - pub fn ct_vid(self) -> Option { - match self.kind() { - ConstKind::Infer(ty::InferConst::Var(vid)) => Some(vid), - _ => None, - } - } - /// Iterator that walks `self` and any types reachable from /// `self`, in depth-first order. Note that just walks the types /// that appear in `self`, it does not descend into the fields of @@ -336,7 +142,7 @@ impl<'tcx> Const<'tcx> { /// Foo> => { Foo>, Bar, isize } /// [isize] => { [isize], isize } /// ``` - pub fn walk(self) -> TypeWalker> { + fn walk(self) -> TypeWalker> { TypeWalker::new(self.into()) } } diff --git a/compiler/rustc_middle/src/ty/consts/lit.rs b/compiler/rustc_middle/src/ty/consts/lit.rs index 08dadf6ad9f5f..9dd8c958cd2f0 100644 --- a/compiler/rustc_middle/src/ty/consts/lit.rs +++ b/compiler/rustc_middle/src/ty/consts/lit.rs @@ -1,6 +1,5 @@ use rustc_ast::{LitFloatType, LitIntType, LitKind}; -use rustc_hir; -use rustc_hir::attrs::lang_items::LangItem; +use rustc_attr_ir::lang_items::LangItem; use rustc_macros::StableHash; use crate::ty::{self, Ty, TyCtxt}; diff --git a/compiler/rustc_middle/src/ty/consts/valtree.rs b/compiler/rustc_middle/src/ty/consts/valtree.rs index 9c4700b41d1ff..28e32a4a82d0d 100644 --- a/compiler/rustc_middle/src/ty/consts/valtree.rs +++ b/compiler/rustc_middle/src/ty/consts/valtree.rs @@ -11,6 +11,7 @@ use rustc_span::bug; use super::ScalarInt; use crate::mir::interpret::{ErrorHandled, Scalar}; +use crate::ty::consts::ConstExt; use crate::ty::print::{FmtPrinter, PrettyPrinter}; use crate::ty::{self, Ty, TyCtxt, ValTreeKind}; diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 9d0b334b63fbd..aa7fe0872f20c 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -17,6 +17,8 @@ use std::{debug_assert_matches, fmt, iter, mem}; use rustc_abi::{ExternAbi, FieldIdx, Layout, LayoutData, TargetDataLayout, VariantIdx}; use rustc_ast as ast; +use rustc_attr_ir::find_attr; +use rustc_attr_ir::lang_items::LangItem; use rustc_crate_store::{CrateStoreDyn, Untracked}; use rustc_data_structures::defer; use rustc_data_structures::fx::FxHashMap; @@ -29,16 +31,16 @@ use rustc_data_structures::sync::{ self, DynSend, DynSync, FreezeReadGuard, Lock, RwLock, WorkerLocal, }; use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, MultiSpan}; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def::DefKind; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::definitions::{DefPathData, Definitions, PerParentDisambiguatorState}; use rustc_hir::intravisit::Visitor; -use rustc_hir::{self as hir, CRATE_HIR_ID, HirId, Node, TraitCandidate, find_attr}; +use rustc_hir::{self as hir, CRATE_HIR_ID, HirId, Node, TraitCandidate}; use rustc_index::IndexVec; use rustc_lint_defs::Lint; use rustc_lint_defs::builtin::UNUSED_FEATURES; use rustc_macros::Diagnostic; +use rustc_session::config::NextSolverConfig; use rustc_session::{IncrCompSession, Session}; use rustc_span::def_id::{CRATE_DEF_ID, DefPathHash, StableCrateId}; use rustc_span::{DUMMY_SP, Ident, Span, Symbol, bug, kw, sym}; @@ -69,11 +71,11 @@ use crate::traits::solve::{ }; use crate::ty::predicate::ExistentialPredicateStableCmpExt as _; use crate::ty::{ - self, AdtDef, AdtDefData, AdtKind, Binder, Clause, ClausePolarity, Clauses, Const, FnSigKind, - GenericArg, GenericArgs, GenericArgsRef, GenericParamDefKind, List, ListWithCachedTypeInfo, - ParamConst, Pattern, PatternKind, PolyExistentialPredicate, PolyFnSig, Predicate, - PredicateKind, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVid, ValTree, - ValTreeKind, Visibility, + self, AdtDef, AdtDefData, AdtKind, Binder, Clause, ClausePolarity, Clauses, Const, ConstKind, + FnSigKind, GenericArg, GenericArgs, GenericArgsRef, GenericParamDefKind, List, + ListWithCachedTypeInfo, ParamConst, Pattern, PatternKind, PolyExistentialPredicate, PolyFnSig, + Predicate, PredicateKind, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, + TyVid, ValTree, ValTreeKind, Visibility, }; impl<'tcx> rustc_type_ir::inherent::DefId> for DefId { @@ -109,8 +111,8 @@ impl<'tcx> rustc_type_ir::inherent::Features> for &'tcx rustc_featu self.generic_const_exprs() } - fn generic_const_args(self) -> bool { - self.generic_const_args() + fn gca_const_items(self) -> bool { + self.gca_const_items() } fn coroutine_clone(self) -> bool { @@ -990,7 +992,7 @@ impl<'tcx> TyCtxt<'tcx> { } /// Obtain all lang items of this crate and all dependencies (recursively) - pub fn lang_items(self) -> &'tcx rustc_hir::attrs::lang_items::LanguageItems { + pub fn lang_items(self) -> &'tcx rustc_attr_ir::lang_items::LanguageItems { self.get_lang_items(()) } @@ -1029,7 +1031,7 @@ impl<'tcx> TyCtxt<'tcx> { /// because it has a directly represented RHS, or is a trait definition that is marked as /// requiring its implementation to have a directly represented RHS. /// - /// Note: Be very careful with using this method - under `generic_const_args`, a trait can + /// Note: Be very careful with using this method - under `gca_const_items`, a trait can /// declare a regular const, but an `impl` could implement it with a directly represented const /// (a la refinement). This method would return false in such a case. pub fn is_direct_const(self, def_id: DefId) -> bool { @@ -1134,7 +1136,7 @@ impl<'tcx> TyCtxt<'tcx> { | CrateType::Cdylib | CrateType::Sdylib => false, CrateType::Rlib | CrateType::Dylib | CrateType::ProcMacro => true, - }) + }) && !self.sess.opts.actually_rustdoc } pub fn needs_hir_hash(self) -> bool { @@ -1156,6 +1158,27 @@ impl<'tcx> TyCtxt<'tcx> { || self.sess.opts.unstable_opts.metrics_dir.is_some() } + /// Whether the combined per-owner HIR hash (`OwnerInfo::opt_hash`, which folds `parenting`, + /// `trait_map` and `children` on top of the node/attr hashes) needs to be computed during + /// lowering. + /// + /// This is a strict subset of [`Self::needs_hir_hash`]: notably it drops the plain + /// `needs_metadata` case. With metadata-based crate hashing (the default) the crate hash is + /// built from the encoded metadata plus each owner's cheaper `OwnerInfo::fingerprint` (just the + /// node and attr sub-hashes), so the combined hash is never read and computing it is wasted + /// work. It is still required for: + /// - `-Z metadata-crate-hash=no`, where `crate_hash` falls back to hashing each `OwnerInfo`; + /// - incremental, where the `lower_to_hir` result is fingerprinted for red/green tracking; + /// - debug assertions, where every query result is fingerprinted to catch nondeterminism. + /// + /// The `needs_hir_hash()` conjunct guarantees the node/attr sub-hashes it folds in are present. + pub fn needs_owner_info_hash(self) -> bool { + self.needs_hir_hash() + && (!self.sess.opts.unstable_opts.metadata_crate_hash + || self.sess.opts.incremental.is_some() + || cfg!(debug_assertions)) + } + #[inline] pub fn stable_crate_id(self, crate_num: CrateNum) -> StableCrateId { if crate_num == LOCAL_CRATE { @@ -1377,9 +1400,7 @@ impl<'tcx> TyCtxt<'tcx> { self.untracked.definitions.freeze() } - pub fn def_path_hash_to_def_index_map( - self, - ) -> &'tcx rustc_hir::def_path_hash_map::DefPathHashMap { + pub fn def_path_hash_to_def_index_map(self) -> &'tcx rustc_hir::definitions::DefPathToIndexMap { // Create a dependency to the crate to be sure we re-execute this when the amount of // definitions change. self.ensure_ok().hir_crate_items(()); @@ -1406,13 +1427,6 @@ impl<'tcx> TyCtxt<'tcx> { self.untracked.definitions.read() } - /// Note that this is *untracked* and should only be used within the query - /// system if the result is otherwise tracked through queries - #[inline] - pub fn source_span_untracked(self, def_id: LocalDefId) -> Span { - self.untracked.source_span.get(def_id).unwrap_or(DUMMY_SP) - } - #[inline(always)] pub fn with_stable_hashing_context(self, f: impl FnOnce(StableHashState<'_>) -> R) -> R { f(StableHashState::new(self.sess, &self.untracked)) @@ -1711,7 +1725,6 @@ macro_rules! nop_list_lift { } nop_lift! { type_; Ty<'a> => Ty<'tcx> } -nop_lift! { const_; Const<'a> => Const<'tcx> } nop_lift! { pat; Pattern<'a> => Pattern<'tcx> } nop_lift! { const_allocation; ConstAllocation<'a> => ConstAllocation<'tcx> } nop_lift! { predicate; Predicate<'a> => Predicate<'tcx> } @@ -1731,6 +1744,19 @@ impl<'a, 'tcx> Lift> for Interned<'a, RegionKind<'a>> { } } +// FIXME: unclear why exactly the macro doesn't work. +impl<'a, 'tcx> Lift> for Interned<'a, WithCachedTypeInfo>> { + type Lifted = Interned<'tcx, WithCachedTypeInfo>>; + + #[track_caller] + fn lift_to_interner(self, tcx: TyCtxt<'tcx>) -> Self::Lifted { + assert!(tcx.interners.const_.contains_pointer_to(&InternedInSet(&*self.0))); + // SAFETY: we just checked that `self` is interned in this `TyCtxt`, so + // its pointee is valid for the entire lifetime of the target `TyCtxt`. + unsafe { mem::transmute(self) } + } +} + nop_list_lift! { type_lists; Ty<'a> => Ty<'tcx> } nop_list_lift! { clauses: ListWithCachedTypeInfo; Clause<'a> => Clause<'tcx> } nop_list_lift! { @@ -2609,7 +2635,7 @@ impl<'tcx> TyCtxt<'tcx> { lint: &'static Lint, hir_id: HirId, span: impl Into, - decorator: impl for<'a> Diagnostic<'a, ()>, + decorator: impl for<'a> Diagnostic<'a>, ) { let level_spec = self.lint_level_spec_at_node(lint, hir_id); emit_lint_base(self.sess, lint, level_spec, Some(span.into()), decorator) @@ -2622,9 +2648,9 @@ impl<'tcx> TyCtxt<'tcx> { m.spans.inject_use_span.shrink_to_lo() } - pub fn disabled_nightly_features( + pub fn disabled_nightly_features( self, - diag: &mut Diag<'_, G>, + diag: &mut Diag<'_>, features: impl IntoIterator, ) { if !self.sess.is_nightly_build() { @@ -2652,7 +2678,7 @@ impl<'tcx> TyCtxt<'tcx> { self, lint: &'static Lint, id: HirId, - decorator: impl for<'a> Diagnostic<'a, ()>, + decorator: impl for<'a> Diagnostic<'a>, ) { let level_spec = self.lint_level_spec_at_node(lint, id); emit_lint_base(self.sess, lint, level_spec, None, decorator); @@ -2791,11 +2817,8 @@ impl<'tcx> TyCtxt<'tcx> { } pub fn next_trait_solver_globally(self) -> bool { - self.sess.opts.unstable_opts.next_solver.globally && !self.features().generic_const_exprs() - } - - pub fn next_trait_solver_in_coherence(self) -> bool { - self.sess.opts.unstable_opts.next_solver.coherence + self.sess.opts.unstable_opts.next_solver == NextSolverConfig::Globally + && !self.features().generic_const_exprs() } pub fn disable_trait_solver_fast_paths(self) -> bool { diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index 50e823f88178f..a6688b668aab3 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -2,10 +2,10 @@ use std::{debug_assert_matches, fmt}; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::intern::Interned; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_span::{DUMMY_SP, Span, Symbol, bug}; @@ -13,7 +13,7 @@ use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverProjectionLangItem, Sol use rustc_type_ir::solve::CanonicalInputData; use rustc_type_ir::{ BoundVar, CollectAndApply, DebruijnIndex, Interner, RegionVid, TypeFoldable, Unnormalized, - VisitorResult, search_graph, try_visit, + VisitorResult, WithCachedTypeInfo, search_graph, try_visit, }; use crate::dep_graph::{DepKind, DepNodeIndex}; @@ -109,8 +109,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type Pat = Pattern<'tcx>; type PatList = &'tcx List>; type Safety = hir::Safety; - type Const = ty::Const<'tcx>; - type Consts = &'tcx List; + type Consts = &'tcx List>; type ParamConst = ty::ParamConst; type ValueConst = ty::Value<'tcx>; @@ -118,6 +117,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type ValTree = ty::ValTree<'tcx>; type ScalarInt = ty::ScalarInt; type InternedRegionKind = Interned<'tcx, ty::RegionKind<'tcx>>; + type InternedConstKind = Interned<'tcx, WithCachedTypeInfo>>; type EarlyParamRegion = ty::EarlyParamRegion; type LateParamRegionKind = ty::LateParamRegionKind; @@ -292,6 +292,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.mk_type_list_from_iter(args) } + fn mk_ct_from_kind(self, kind: ty::ConstKind<'tcx>) -> ty::Const<'tcx> { + self.mk_ct_from_kind(kind) + } + fn projection_parent(self, def_id: Self::TraitAssocTermId) -> Self::TraitId { self.parent(def_id) } @@ -411,6 +415,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.impl_super_outlives(impl_def_id) } + fn supertrait_def_ids(self, trait_def_id: DefId) -> impl Iterator { + rustc_type_ir::elaborate::supertrait_def_ids(self, trait_def_id) + } + fn impl_is_const(self, def_id: DefId) -> bool { debug_assert_matches!(self.def_kind(def_id), DefKind::Impl { of_trait: true }); self.is_conditionally_const(def_id) @@ -572,6 +580,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.trait_is_auto(trait_def_id) } + fn trait_is_marker(self, trait_def_id: DefId) -> bool { + self.trait_def(trait_def_id).is_marker + } + fn trait_is_coinductive(self, trait_def_id: DefId) -> bool { self.trait_is_coinductive(trait_def_id) } @@ -729,9 +741,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { } } -impl<'tcx, T: std::fmt::Debug + Clone + Copy> rustc_type_ir::intern::Interned> - for Interned<'tcx, T> -{ +impl<'tcx, T: Clone + Copy> rustc_type_ir::intern::Interned> for Interned<'tcx, T> { type Value = T; fn get(self) -> T { *self.0 diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 1f2a9aca60142..df1daac366c53 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -3,9 +3,9 @@ use std::fmt::Write; use std::ops::ControlFlow; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::{Applicability, Diag, DiagArgValue, IntoDiagArg, listify, pluralize}; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def::{DefKind, Namespace}; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, AmbigArg, PredicateOrigin, WherePredicateKind}; diff --git a/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs b/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs index 6b0234c971830..61e3a40e363d9 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs @@ -4,6 +4,7 @@ use rustc_span::def_id::{LocalModId, ModId}; use smallvec::SmallVec; use tracing::instrument; +use crate::ty::consts::ConstExt; use crate::ty::{self, OpaqueTypeKey, Ty, TyCtxt, TypingEnv, Unnormalized}; /// Represents whether some type is inhabited in a given context. diff --git a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs index 60ea2bf49a046..03c5c21de02c9 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs @@ -5,7 +5,6 @@ //! //! # Example //! ```rust -#![cfg_attr(bootstrap, doc = "#![feature(never_type)]")] //! mod a { //! pub mod b { //! pub struct SecretlyUninhabited { @@ -43,8 +42,6 @@ //! This code should only compile in modules where the uninhabitedness of `Foo` //! is visible. -use std::assert_matches; - use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::DefKind; use rustc_span::bug; @@ -53,6 +50,7 @@ use rustc_type_ir::TyKind::*; use tracing::instrument; use crate::query::Providers; +use crate::ty::consts::ConstExt; use crate::ty::{ self, AdtDef, DefId, Ty, TyCtxt, TypeVisitableExt, TypingEnv, VariantDef, Visibility, }; @@ -65,7 +63,7 @@ pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { inhabited_predicate_for_def, inhabited_predicate_type, - is_opsem_inhabited_raw, + is_opsem_inhabited_adt_cached, ..*providers }; } @@ -174,7 +172,6 @@ impl<'tcx> Ty<'tcx> { /// /// # Example /// ``` - #[cfg_attr(bootstrap, doc = "#![feature(never_type)]")] /// # fn main() {} /// enum Void {} /// mod a { @@ -272,7 +269,7 @@ struct OpsemInhabitedCtx<'tcx> { tcx: TyCtxt<'tcx>, typing_env: TypingEnv<'tcx>, /// IDs of ADTs that have been encountered in the current stack. - /// It's `None` unless we are inside the `is_opsem_inhabited_raw` query, + /// It's `None` unless we are inside the `is_opsem_inhabited_adt_cached` query, /// which is only invoked for more complex types. seen: Option>, /// If an ADT is encountered recursively within itself, then `stop_at_ref` @@ -331,7 +328,7 @@ impl<'tcx> OpsemInhabitedCtx<'tcx> { let base = tcx.instantiate_bound_regions_with_erased((*base).into()); self.is_inhabited_ty(base) } - ty::Adt(..) => self.is_inhabited_adt_ty(ty), + ty::Adt(def, args) => self.is_inhabited_adt_ty(def, args), ty::Error(_error_guaranteed) => { // We have a token proving there was an error, so we can return a dummy value. @@ -349,10 +346,11 @@ impl<'tcx> OpsemInhabitedCtx<'tcx> { } } - fn is_inhabited_adt_ty(&mut self, ty: Ty<'tcx>) -> bool { - let ty::Adt(adt_def, adt_args) = *ty.kind() else { - unreachable! {} - }; + fn is_inhabited_adt_ty( + &mut self, + adt_def: ty::AdtDef<'tcx>, + adt_args: ty::GenericArgsRef<'tcx>, + ) -> bool { let Self { tcx, typing_env, .. } = *self; if adt_def.is_union() { @@ -362,7 +360,8 @@ impl<'tcx> OpsemInhabitedCtx<'tcx> { let Some(seen) = self.seen.as_mut() else { // stop recursing, invoke the query. - return tcx.is_opsem_inhabited_raw(typing_env.as_query_input(ty)); + return tcx + .is_opsem_inhabited_adt_cached(typing_env.as_query_input((adt_def, adt_args))); }; let new_adt = seen.insert(adt_def.did()); @@ -392,17 +391,11 @@ impl<'tcx> OpsemInhabitedCtx<'tcx> { } } -fn is_opsem_inhabited_raw<'tcx>( +fn is_opsem_inhabited_adt_cached<'tcx>( tcx: TyCtxt<'tcx>, - env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>, + env: ty::PseudoCanonicalInput<'tcx, (ty::AdtDef<'tcx>, ty::GenericArgsRef<'tcx>)>, ) -> bool { - let (ty, typing_env) = (env.value, env.typing_env); - assert_matches!( - ty.kind(), - ty::Adt(..), - "the query should only be invoked by `Ty::is_opsem_inhabited`" - ); - + let ((def, args), typing_env) = (env.value, env.typing_env); OpsemInhabitedCtx { tcx, typing_env, seen: Some(FxHashSet::default()), stop_at_ref: false } - .is_inhabited_adt_ty(ty) + .is_inhabited_adt_ty(def, args) } diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 44e5a875a74ae..1a00293f8ffc6 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -1,9 +1,9 @@ use std::{assert_matches, fmt}; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::fx::FxHashMap; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def::{CtorKind, DefKind, Namespace}; use rustc_hir::def_id::{CrateNum, DefId}; use rustc_macros::{Lift, StableHash, TyDecodable, TyEncodable}; @@ -231,7 +231,7 @@ impl<'tcx> Instance<'tcx> { if !tcx.sess.opts.share_generics() // However, if the def_id is marked inline(never), then it's fine to just reuse the // upstream monomorphization. - && tcx.codegen_fn_attrs(self.def_id()).inline != rustc_hir::attrs::InlineAttr::Never + && tcx.codegen_fn_attrs(self.def_id()).inline != rustc_attr_ir::InlineAttr::Never { return None; } diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 22fbb306849da..8806bf992fc0e 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -6,9 +6,9 @@ use rustc_abi::{ PointerKind, Primitive, ReprFlags, ReprOptions, Scalar, Size, TagEncoding, TargetDataLayout, TyAbiInterface, VariantIdx, Variants, }; +use rustc_attr_ir::lang_items::LangItem; use rustc_errors::{Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, IntoDiagArg, Level}; use rustc_hir as hir; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def_id::DefId; use rustc_macros::{StableHash, TyDecodable, TyEncodable, extension}; use rustc_session::config::OptLevel; @@ -21,6 +21,7 @@ use tracing::debug; use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::query::TyCtxtAt; use crate::traits::ObligationCause; +use crate::ty::consts::ConstExt; use crate::ty::normalize_erasing_regions::NormalizationError; use crate::ty::{self, CoroutineArgsExt, Ty, TyCtxt, TypeVisitableExt, Unnormalized}; @@ -137,6 +138,11 @@ impl abi::Float { use abi::Float::*; match *self { F16 => tcx.types.f16, + F16B => Ty::new_adt( + tcx, + tcx.adt_def(tcx.require_lang_item(LangItem::F16B, DUMMY_SP)), + ty::List::empty(), + ), F32 => tcx.types.f32, F64 => tcx.types.f64, F128 => tcx.types.f128, @@ -1055,19 +1061,40 @@ where } ty::Ref(_, ty, mt) if offset.bytes() == 0 => { tcx.layout_of(typing_env.as_query_input(ty)).ok().map(|layout| { - let kind = match mt { + let kind; + let size; + + match mt { hir::Mutability::Not => { let frozen = optimize && ty.is_freeze(tcx, typing_env); - PointerKind::SharedRef { frozen } + kind = PointerKind::SharedRef { frozen }; + + // Set the `size` to zero for non-frozen shared references. + // + // `PointeeInfo::size` is defined to allow adding spurious reads. + // Adding spurious reads is only valid for `&Freeze` and `&mut Unpin` + // references, as otherwise the spurious read can invalidate aliasing + // references. + // + // See discussions in + // - https://github.com/rust-lang/unsafe-code-guidelines/issues/381 + // - https://github.com/llvm/llvm-project/pull/218413 + // - https://discourse.llvm.org/t/interaction-of-noalias-and-dereferenceable/66979 + size = layout.size * frozen as _; } hir::Mutability::Mut => { let unpin = optimize && ty.is_unpin(tcx, typing_env) && ty.is_unsafe_unpin(tcx, typing_env); - PointerKind::MutableRef { unpin } + kind = PointerKind::MutableRef { unpin }; + + // Set the `size` to zero for non-unpin unique references. + // See the above comment for reasons to do this. + size = layout.size * unpin as _; } }; - PointeeInfo { safe: Some(kind), size: layout.size, align: layout.align.abi } + + PointeeInfo { safe: Some(kind), size, align: layout.align.abi } }) } @@ -1075,16 +1102,18 @@ where if offset.bytes() == 0 && let Some(pointee) = this.ty.boxed_ty() => { - tcx.layout_of(typing_env.as_query_input(pointee)).ok().map(|layout| PointeeInfo { - safe: Some(PointerKind::Box { - // Same logic as for mutable references above. - unpin: optimize - && pointee.is_unpin(tcx, typing_env) - && pointee.is_unsafe_unpin(tcx, typing_env), - global: this.ty.is_box_global(tcx), - }), - size: layout.size, - align: layout.align.abi, + tcx.layout_of(typing_env.as_query_input(pointee)).ok().map(|layout| { + // Same logic as for mutable references above. + let unpin = optimize + && pointee.is_unpin(tcx, typing_env) + && pointee.is_unsafe_unpin(tcx, typing_env); + let size = layout.size * unpin as _; + + PointeeInfo { + safe: Some(PointerKind::Box { unpin, global: this.ty.is_box_global(tcx) }), + size, + align: layout.align.abi, + } }) } @@ -1346,8 +1375,8 @@ pub enum FnAbiError<'tcx> { Layout(LayoutError<'tcx>), } -impl<'a, 'b, G> Diagnostic<'a, G> for FnAbiError<'b> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { +impl<'a, 'b> Diagnostic<'a> for FnAbiError<'b> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { match self { Self::Layout(e) => Diag::new(dcx, level, e.to_string()), } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index e23ce6b246f12..1f2ffb17cc2c7 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -124,6 +124,8 @@ pub mod abstract_const; pub mod adjustment; pub mod cast; pub mod codec; +// FIXME(#159654): This should get deleted soon +pub mod consts; pub mod error; pub mod fast_reject; pub mod inhabitedness; @@ -143,7 +145,6 @@ pub mod vtable; mod adt; mod assoc; mod closure; -mod consts; mod context; mod diagnostics; mod elaborate_impl; @@ -428,19 +429,19 @@ impl Visibility { } } -impl> Visibility { - /// Returns `true` if an item with this visibility is accessible from the given module. - pub fn is_accessible_from(self, module: impl Into, tcx: TyCtxt<'_>) -> bool { +impl> Visibility { + /// Returns `true` if an item with this visibility is accessible from the given definition. + pub fn is_accessible_from(self, def_id: impl Into, tcx: TyCtxt<'_>) -> bool { match self { // Public items are visible everywhere. Visibility::Public => true, - Visibility::Restricted(id) => tcx.is_descendant_of(module, id), + Visibility::Restricted(id) => tcx.is_descendant_of(def_id, id.into()), } } pub fn partial_cmp( self, - vis: Visibility>, + vis: Visibility>, tcx: TyCtxt<'_>, ) -> Option { match (self, vis) { @@ -449,18 +450,18 @@ impl> Visibility { (Visibility::Restricted(_), Visibility::Public) => Some(Ordering::Less), (Visibility::Restricted(lhs_id), Visibility::Restricted(rhs_id)) => { let (lhs_id, rhs_id) = (lhs_id.into(), rhs_id.into()); - tcx.def_id_partial_cmp(lhs_id, rhs_id) + tcx.def_id_partial_cmp(lhs_id.to_def_id(), rhs_id.to_def_id()) } } } } -impl + Debug + Copy> Visibility { +impl + Debug + Copy> Visibility { /// Returns `true` if this visibility is strictly larger than the given visibility. #[track_caller] pub fn greater_than( self, - vis: Visibility + Debug + Copy>, + vis: Visibility + Debug + Copy>, tcx: TyCtxt<'_>, ) -> bool { match self.partial_cmp(vis, tcx) { @@ -1869,7 +1870,7 @@ impl<'tcx> TyCtxt<'tcx> { } /// Gets all attributes with the given name. - #[deprecated = "Though there are valid usecases for this method, especially when your attribute is not a parsed attribute, usually you want to call rustc_hir::find_attr! instead."] + #[deprecated = "Though there are valid usecases for this method, especially when your attribute is not a parsed attribute, usually you want to use `rustc_attr_ir::find_attr!` instead."] pub fn get_attrs( self, did: impl Into, @@ -1888,7 +1889,7 @@ impl<'tcx> TyCtxt<'tcx> { /// /// /// - #[deprecated = "Though there are valid usecases for this method, especially when your attribute is not a parsed attribute, usually you want to call rustc_hir::find_attr! instead."] + #[deprecated = "Though there are valid usecases for this method, especially when your attribute is not a parsed attribute, usually you want to use `rustc_attr_ir::find_attr!` instead."] pub fn get_all_attrs(self, did: impl Into) -> &'tcx [rustc_attr_ir::Attribute] { let did: DefId = did.into(); if let Some(did) = did.as_local() { @@ -2183,13 +2184,13 @@ impl<'tcx> TyCtxt<'tcx> { self, mut ident: Ident, scope: DefId, - item_id: LocalDefId, + mod_id: LocalModId, ) -> (Ident, ModId) { let scope = ident .span .normalize_to_macros_2_0_and_adjust(self.expn_that_defined(scope)) .and_then(|actual_expansion| actual_expansion.expn_data().parent_module) - .unwrap_or_else(|| self.parent_module_from_def_id(item_id).to_mod_id()); + .unwrap_or(mod_id.to_mod_id()); (ident, scope) } diff --git a/compiler/rustc_middle/src/ty/opaque_types.rs b/compiler/rustc_middle/src/ty/opaque_types.rs index f4a3c708e0e68..7333d17e259e0 100644 --- a/compiler/rustc_middle/src/ty/opaque_types.rs +++ b/compiler/rustc_middle/src/ty/opaque_types.rs @@ -125,7 +125,7 @@ impl<'tcx> TypeFolder> for ReverseMapper<'tcx> { Some(u) => panic!("region mapped to unexpected kind: {u:?}"), None if self.do_not_error => self.tcx.lifetimes.re_static, None => { - let e = self + let guar = self .tcx .dcx() .struct_span_err(self.span, "non-defining opaque type use in defining scope") @@ -136,9 +136,9 @@ impl<'tcx> TypeFolder> for ReverseMapper<'tcx> { parameter list of the `impl Trait` type alias", ), ) - .emit(); + .emit_err(); - ty::Region::new_error(self.cx(), e) + ty::Region::new_error(self.cx(), guar) } } } @@ -179,7 +179,7 @@ impl<'tcx> TypeFolder> for ReverseMapper<'tcx> { used in parameter list for the `impl Trait` type alias" ), ) - .emit(); + .emit_err(); Ty::new_error(self.tcx, guar) } } @@ -201,14 +201,10 @@ impl<'tcx> TypeFolder> for ReverseMapper<'tcx> { Some(GenericArgKind::Const(c1)) => c1, Some(u) => panic!("const mapped to unexpected kind: {u:?}"), None => { - let guar = self - .tcx - .dcx() - .create_err(ConstNotUsedTraitAlias { - ct: ct.to_string(), - span: self.span, - }) - .emit(); + let guar = self.tcx.dcx().emit_err(ConstNotUsedTraitAlias { + ct: ct.to_string(), + span: self.span, + }); ty::Const::new_error(self.tcx, guar) } } diff --git a/compiler/rustc_middle/src/ty/pattern.rs b/compiler/rustc_middle/src/ty/pattern.rs index 2d73124265fc2..71d3bf0853f04 100644 --- a/compiler/rustc_middle/src/ty/pattern.rs +++ b/compiler/rustc_middle/src/ty/pattern.rs @@ -7,6 +7,7 @@ use rustc_type_ir::{self as ir, FlagComputation, Flags}; use super::TyCtxt; use crate::ty; +use crate::ty::consts::ConstExt; pub type PatternKind<'tcx> = ir::PatternKind>; diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs index 983293e81e5f9..7870b20c7d304 100644 --- a/compiler/rustc_middle/src/ty/predicate.rs +++ b/compiler/rustc_middle/src/ty/predicate.rs @@ -171,9 +171,17 @@ impl<'tcx> Clause<'tcx> { } pub fn kind(self) -> ty::Binder<'tcx, ClauseKind<'tcx>> { - self.0.internee.map_bound(|kind| match kind { + // Outline the unreachable! call to make the happy path faster. + // see: https://github.com/rust-lang/rust/pull/163657#issuecomment-5956974964 + #[cold] + #[inline(never)] + fn unreachable_inner() -> ! { + unreachable!() + } + + self.0.internee.map_bound_no_validate_bound_vars(|kind| match kind { PredicateKind::Clause(clause) => clause, - _ => unreachable!(), + _ => unreachable_inner(), }) } @@ -520,7 +528,8 @@ impl<'tcx> UpcastFrom, TraitClause<'tcx>> for Predicate<'tcx> { impl<'tcx> UpcastFrom, PolyTraitClause<'tcx>> for Predicate<'tcx> { fn upcast_from(from: PolyTraitClause<'tcx>, tcx: TyCtxt<'tcx>) -> Self { - from.map_bound(|p| PredicateKind::Clause(ClauseKind::Trait(p))).upcast(tcx) + from.map_bound_no_validate_bound_vars(|p| PredicateKind::Clause(ClauseKind::Trait(p))) + .upcast(tcx) } } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 1da8071ce69ce..cafe93c591090 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -6,11 +6,11 @@ use std::ops::{Deref, DerefMut}; use rustc_abi::{ExternAbi, Size}; use rustc_apfloat::Float; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; +use rustc_attr_ir::lang_items::LangItem; use rustc_crate_store::{ExternCrate, ExternCrateSource}; use rustc_data_structures::fx::{FxIndexMap, IndexEntry}; use rustc_data_structures::unord::UnordMap; use rustc_hir as hir; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def::{self, CtorKind, DefKind, Namespace}; use rustc_hir::def_id::{DefIdMap, DefIdSet, LOCAL_CRATE, ModId}; use rustc_hir::definitions::{DefKey, DefPathDataName}; @@ -24,6 +24,7 @@ use smallvec::SmallVec; use super::*; use crate::mir::interpret::{AllocRange, GlobalAlloc, Pointer, Provenance, Scalar}; use crate::query::{IntoQueryKey, Providers}; +use crate::ty::consts::ConstExt; use crate::ty::{ ConstInt, Expr, GenericArgKind, ParamConst, ScalarInt, Term, TermKind, TraitClause, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, @@ -2395,12 +2396,9 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError> { self.empty_path = true; if cnum == LOCAL_CRATE && !with_resolve_crate_name() { - if self.tcx.sess.at_least_rust_2018() { - // We add the `crate::` keyword on Rust 2018, only when desired. - if with_crate_prefix() { - write!(self, "{}", kw::Crate)?; - self.empty_path = false; - } + if with_crate_prefix() { + write!(self, "{}", kw::Crate)?; + self.empty_path = false; } } else { write!(self, "{}", self.tcx.crate_name(cnum))?; @@ -3143,8 +3141,7 @@ macro_rules! define_print_and_forward_display { forward_display_to_print! { Ty<'tcx>, - &'tcx ty::List>, - ty::Const<'tcx> + &'tcx ty::List> } define_print! { diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index abbfd0f279a93..477e5133cc358 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -98,16 +98,6 @@ impl<'tcx> Relate> for ty::GenericArgsRef<'tcx> { } } -impl<'tcx> Relate> for ty::Const<'tcx> { - fn relate>>( - relation: &mut R, - a: ty::Const<'tcx>, - b: ty::Const<'tcx>, - ) -> RelateResult<'tcx, ty::Const<'tcx>> { - relation.consts(a, b) - } -} - impl<'tcx> Relate> for ty::Expr<'tcx> { fn relate>>( relation: &mut R, diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 0ea7e403ee111..3710a159b3fdd 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -9,7 +9,7 @@ use rustc_abi::TyAndLayout; use rustc_hir::def::Namespace; use rustc_hir::def_id::LocalDefId; use rustc_span::Spanned; -use rustc_type_ir::{ConstKind, PredicateProxy, TypeFolder, Upcast, VisitorResult, try_visit}; +use rustc_type_ir::{PredicateProxy, TypeFolder, Upcast, VisitorResult, try_visit}; use super::{GenericArg, GenericArgKind, Pattern}; use crate::mir::PlaceElem; @@ -141,18 +141,6 @@ impl<'tcx> fmt::Debug for ty::consts::Expr<'tcx> { } } -impl<'tcx> fmt::Debug for ty::Const<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // If this is a value, we spend some effort to make it look nice. - if let ConstKind::Value(cv) = self.kind() { - write!(f, "{}", cv) - } else { - // Fall back to something verbose. - write!(f, "{:?}", self.kind()) - } - } -} - impl<'tcx> fmt::Debug for GenericArg<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.kind() { @@ -229,11 +217,11 @@ TrivialTypeTraversalImpls! { rustc_abi::VariantIdx, rustc_ast::InlineAsmOptions, rustc_ast::InlineAsmTemplatePiece, + rustc_attr_ir::AttributeKind, rustc_hir::CoroutineKind, rustc_hir::HirId, rustc_hir::MatchSource, rustc_hir::RangeEnd, - rustc_hir::attrs::AttributeKind, rustc_hir::def_id::LocalDefId, rustc_span::Ident, rustc_span::Span, @@ -651,80 +639,6 @@ impl<'tcx> TypeSuperFoldable> for ty::Clauses<'tcx> { } } -impl<'tcx> TypeFoldable> for ty::Const<'tcx> { - fn try_fold_with>>( - self, - folder: &mut F, - ) -> Result { - folder.try_fold_const(self) - } - - fn fold_with>>(self, folder: &mut F) -> Self { - folder.fold_const(self) - } -} - -impl<'tcx> TypeVisitable> for ty::Const<'tcx> { - fn visit_with>>(&self, visitor: &mut V) -> V::Result { - visitor.visit_const(*self) - } -} - -impl<'tcx> TypeSuperFoldable> for ty::Const<'tcx> { - fn try_super_fold_with>>( - self, - folder: &mut F, - ) -> Result { - let kind = match self.kind() { - ConstKind::Alias(is_rigid, alias_const) => { - ConstKind::Alias(is_rigid, alias_const.try_fold_with(folder)?) - } - ConstKind::Value(v) => ConstKind::Value(v.try_fold_with(folder)?), - ConstKind::Expr(e) => ConstKind::Expr(e.try_fold_with(folder)?), - - ConstKind::Param(_) - | ConstKind::Infer(_) - | ConstKind::Bound(..) - | ConstKind::Placeholder(_) - | ConstKind::Error(_) => return Ok(self), - }; - if kind != self.kind() { Ok(folder.cx().mk_ct_from_kind(kind)) } else { Ok(self) } - } - - fn super_fold_with>>(self, folder: &mut F) -> Self { - let kind = match self.kind() { - ConstKind::Alias(is_rigid, alias_const) => { - ConstKind::Alias(is_rigid, alias_const.fold_with(folder)) - } - ConstKind::Value(v) => ConstKind::Value(v.fold_with(folder)), - ConstKind::Expr(e) => ConstKind::Expr(e.fold_with(folder)), - - ConstKind::Param(_) - | ConstKind::Infer(_) - | ConstKind::Bound(..) - | ConstKind::Placeholder(_) - | ConstKind::Error(_) => return self, - }; - if kind != self.kind() { folder.cx().mk_ct_from_kind(kind) } else { self } - } -} - -impl<'tcx> TypeSuperVisitable> for ty::Const<'tcx> { - fn super_visit_with>>(&self, visitor: &mut V) -> V::Result { - match self.kind() { - ConstKind::Alias(_, alias_const) => alias_const.visit_with(visitor), - ConstKind::Value(v) => v.visit_with(visitor), - ConstKind::Expr(e) => e.visit_with(visitor), - ConstKind::Error(e) => e.visit_with(visitor), - - ConstKind::Param(_) - | ConstKind::Infer(_) - | ConstKind::Bound(..) - | ConstKind::Placeholder(_) => V::Result::output(), - } - } -} - impl<'tcx> TypeVisitable> for ty::ValTree<'tcx> { fn visit_with>>(&self, visitor: &mut V) -> V::Result { let inner: &ty::ValTreeKind> = &*self; diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index f4b1f0ce0881d..925915c88cdb4 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -8,9 +8,9 @@ use std::ops::{ControlFlow, Range}; use hir::def::{CtorKind, DefKind}; use rustc_abi::{FIRST_VARIANT, FieldIdx, NumScalableVectors, ScalableElt, VariantIdx}; +use rustc_attr_ir::lang_items::LangItem; use rustc_errors::{ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def_id::DefId; use rustc_macros::{StableHash, TyDecodable, TyEncodable, TypeFoldable, extension}; use rustc_span::{DUMMY_SP, Span, Symbol, bug, kw, sym}; @@ -27,6 +27,7 @@ use super::{AdtFlags, GenericParamDefKind}; use crate::infer::canonical::Canonical; use crate::traits::ObligationCause; use crate::ty::InferTy::*; +use crate::ty::consts::ConstExt; use crate::ty::{ self, AdtDef, Const, Discr, GenericArg, GenericArgs, GenericArgsRef, List, ParamEnv, Region, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, ValTree, @@ -2114,28 +2115,9 @@ impl<'tcx> Ty<'tcx> { match self.kind() { ty::Bool => Some(sym::bool), ty::Char => Some(sym::char), - ty::Float(f) => match f { - ty::FloatTy::F16 => Some(sym::f16), - ty::FloatTy::F32 => Some(sym::f32), - ty::FloatTy::F64 => Some(sym::f64), - ty::FloatTy::F128 => Some(sym::f128), - }, - ty::Int(f) => match f { - ty::IntTy::Isize => Some(sym::isize), - ty::IntTy::I8 => Some(sym::i8), - ty::IntTy::I16 => Some(sym::i16), - ty::IntTy::I32 => Some(sym::i32), - ty::IntTy::I64 => Some(sym::i64), - ty::IntTy::I128 => Some(sym::i128), - }, - ty::Uint(f) => match f { - ty::UintTy::Usize => Some(sym::usize), - ty::UintTy::U8 => Some(sym::u8), - ty::UintTy::U16 => Some(sym::u16), - ty::UintTy::U32 => Some(sym::u32), - ty::UintTy::U64 => Some(sym::u64), - ty::UintTy::U128 => Some(sym::u128), - }, + ty::Int(i) => Some(i.name()), + ty::Uint(u) => Some(u.name()), + ty::Float(f) => Some(f.name()), ty::Str => Some(sym::str), _ => None, } @@ -2189,7 +2171,7 @@ impl<'tcx> Ty<'tcx> { def.flags().contains(AdtFlags::IS_MAYBE_DANGLING) || def.flags().contains(AdtFlags::IS_MANUALLY_DROP) } - ty::Closure(..) | ty::Coroutine(..) | ty::CoroutineClosure(..) => true, + ty::Coroutine(..) => true, _ => false, } } diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index fce9af1c465af..114e5331b8b9b 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -1,10 +1,11 @@ use std::iter; +use rustc_attr_ir::find_attr; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::ErrorGuaranteed; +use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_hir::{self as hir, find_attr}; use rustc_macros::{Decodable, Encodable, StableHash}; use rustc_span::{Span, bug}; use tracing::debug; diff --git a/compiler/rustc_middle/src/ty/typetree.rs b/compiler/rustc_middle/src/ty/typetree.rs index c2479e0c62bca..54cbc591c5fb3 100644 --- a/compiler/rustc_middle/src/ty/typetree.rs +++ b/compiler/rustc_middle/src/ty/typetree.rs @@ -2,6 +2,7 @@ use rustc_ast::expand::typetree::{FncTree, Kind, Type, TypeTree}; use rustc_span::bug; use tracing::trace; +use crate::ty::consts::ConstExt; use crate::ty::context::TyCtxt; use crate::ty::{self, Ty}; diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index db983038e1af9..0974e0a332ecd 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -4,13 +4,14 @@ use std::{fmt, iter}; use rustc_abi::{Float, Integer, IntegerType, Size}; use rustc_apfloat::Float as _; +use rustc_attr_ir::find_attr; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stable_hash::{StableHash, StableHasher}; use rustc_errors::ErrorGuaranteed; use rustc_hashes::Hash128; +use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; -use rustc_hir::{self as hir, find_attr}; use rustc_index::bit_set::GrowableBitSet; use rustc_macros::{StableHash, TyDecodable, TyEncodable, extension}; use rustc_span::{bug, span_bug, sym}; @@ -25,6 +26,7 @@ use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::mir; use crate::query::Providers; use crate::traits::ObligationCause; +use crate::ty::consts::ConstExt; use crate::ty::layout::{FloatExt, IntegerExt}; use crate::ty::{ self, Asyncness, FallibleTypeFolder, GenericArgKind, GenericArgsRef, Ty, TyCtxt, TypeFoldable, @@ -377,7 +379,6 @@ impl<'tcx> TyCtxt<'tcx> { self, adt_did: LocalDefId, validate: impl Fn(Self, LocalDefId) -> Result<(), ErrorGuaranteed>, - impossible_self_ty: impl Fn(Self, LocalDefId) -> bool, ) -> Option { let drop_trait = self.lang_items().drop_trait()?; self.ensure_result().coherent_trait(drop_trait).ok()?; @@ -395,11 +396,6 @@ impl<'tcx> TyCtxt<'tcx> { continue; } - if impossible_self_ty(self, adt_did) { - // The self ty is unnameable, so it can't be constructed in the first place. - continue; - } - let Some(&item_id) = self.associated_item_def_ids(impl_did).first() else { self.dcx() .span_delayed_bug(self.def_span(impl_did), "Drop impl without drop function"); @@ -430,7 +426,6 @@ impl<'tcx> TyCtxt<'tcx> { self, adt_did: LocalDefId, validate: impl Fn(Self, LocalDefId) -> Result<(), ErrorGuaranteed>, - impossible_self_ty: impl Fn(Self, LocalDefId) -> bool, ) -> Option { let async_drop_trait = self.lang_items().async_drop_trait()?; self.ensure_result().coherent_trait(async_drop_trait).ok()?; @@ -448,11 +443,6 @@ impl<'tcx> TyCtxt<'tcx> { continue; } - if impossible_self_ty(self, adt_did) { - // The self ty is unnameable, so it can't be constructed in the first place. - continue; - } - if let Some(old_impl_did) = dtor_candidate { self.dcx() .struct_span_err(self.def_span(impl_did), "multiple async drop impls found") diff --git a/compiler/rustc_mir_build/Cargo.toml b/compiler/rustc_mir_build/Cargo.toml index ee858550c978f..94574384c1a12 100644 --- a/compiler/rustc_mir_build/Cargo.toml +++ b/compiler/rustc_mir_build/Cargo.toml @@ -10,6 +10,7 @@ rustc_abi = { path = "../rustc_abi" } rustc_apfloat = "0.2.0" rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } +rustc_attr_ir = { path = "../rustc_attr_ir" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_hir = { path = "../rustc_hir" } diff --git a/compiler/rustc_mir_build/src/builder/cfg.rs b/compiler/rustc_mir_build/src/builder/cfg.rs index 63ccf323fc204..a8e68963ec724 100644 --- a/compiler/rustc_mir_build/src/builder/cfg.rs +++ b/compiler/rustc_mir_build/src/builder/cfg.rs @@ -121,7 +121,7 @@ impl<'tcx> CFG<'tcx> { self.block_data(block) ); self.block_data_mut(block).terminator = - Some(Terminator { source_info, kind, attributes: ThinVec::new() }); + Some(Terminator { source_info, kind, loop_hint_attrs: ThinVec::new() }); self.block_data_mut(block).terminator.as_mut().unwrap() } diff --git a/compiler/rustc_mir_build/src/builder/custom/mod.rs b/compiler/rustc_mir_build/src/builder/custom/mod.rs index bca258bf2ba61..f6c8658544b43 100644 --- a/compiler/rustc_mir_build/src/builder/custom/mod.rs +++ b/compiler/rustc_mir_build/src/builder/custom/mod.rs @@ -16,10 +16,10 @@ //! "top-level" constructs can be found in the `parse` submodule, while the parsing for statements, //! terminators, and everything below can be found in the `parse::instruction` submodule. //! - +use rustc_attr_ir as attrs; use rustc_data_structures::fx::FxHashMap; +use rustc_hir::HirId; use rustc_hir::def_id::DefId; -use rustc_hir::{HirId, attrs}; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir::*; use rustc_middle::thir::*; diff --git a/compiler/rustc_mir_build/src/builder/custom/parse.rs b/compiler/rustc_mir_build/src/builder/custom/parse.rs index 32284fe5f42bc..73ffbec6685cc 100644 --- a/compiler/rustc_mir_build/src/builder/custom/parse.rs +++ b/compiler/rustc_mir_build/src/builder/custom/parse.rs @@ -319,7 +319,7 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { data.terminator = Some(Terminator { source_info: SourceInfo { span, scope: self.source_scope }, kind: terminator, - attributes: ThinVec::new(), + loop_hint_attrs: ThinVec::new(), }); Ok(data) diff --git a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs index eb9f56d887684..8f2a732acd1c2 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs @@ -2,7 +2,7 @@ use rustc_abi::Size; use rustc_ast as ast; -use rustc_hir::attrs::lang_items::LangItem; +use rustc_attr_ir::lang_items::LangItem; use rustc_hir::def::DefKind; use rustc_middle::mir; use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, Scalar}; @@ -74,28 +74,41 @@ pub(crate) fn as_constant_inner<'tcx>( ExprKind::NamedConst { def_id, args, ref user_ty } => { let user_ty = user_ty.as_ref().and_then(push_cuta); - // Under generic_const_args, `def_id` might be a regular const declared in a trait, but - // is `impl`d as a directly represented const. We do not know whether it is here, so we - // must use type system normalization for all consts under generic_const_args. - // FIXME(generic_const_args): there's a lot to consider here! `Const::Ty` uses valtrees - // and `Const::Unevaluated` does not, we should revisit this before stabilization. - let def_kind = tcx.def_kind(def_id); - if tcx.features().generic_const_args() - || matches!(def_kind, DefKind::Const | DefKind::AssocConst) - && tcx.is_direct_const(def_id) - { - let kind = match def_kind { - DefKind::AssocConst => { - if let DefKind::Impl { of_trait: false } = tcx.def_kind(tcx.parent(def_id)) - { - ty::AliasConstKind::InherentImpl { def_id } - } else { - ty::AliasConstKind::Projection { def_id } - } + let get_kind = |def_id, def_kind| match def_kind { + DefKind::AssocConst => { + if let DefKind::Impl { of_trait: false } = tcx.def_kind(tcx.parent(def_id)) { + ty::AliasConstKind::InherentImpl { def_id } + } else { + ty::AliasConstKind::Projection { def_id } } - DefKind::Const => ty::AliasConstKind::Free { def_id }, - _ => unreachable!(), + } + DefKind::Const => ty::AliasConstKind::Free { def_id }, + kind => bug!("unexpected DefKind in THIR ExprKind::NamedConst: {kind:?}"), + }; + + let could_be_direct_const = |def_id| { + let def_kind = tcx.def_kind(def_id); + let (DefKind::Const | DefKind::AssocConst) = def_kind else { + return None; }; + if tcx.is_direct_const(def_id) { + return Some(get_kind(def_id, def_kind)); + } + // Under gca_const_items, `def_id` might be a regular const declared in a trait, + // but is `impl`d as a directly represented const. We do not know whether it is + // here, so we must use type system normalization for all const projections. + // FIXME(gca_const_items): there's a lot to consider here! `Const::Ty` uses + // valtrees and `Const::Unevaluated` does not, we should revisit this before + // stabilization. + if tcx.features().gca_const_items() + && let kind @ ty::AliasConstKind::Projection { .. } = get_kind(def_id, def_kind) + { + return Some(kind); + } + None + }; + + if let Some(kind) = could_be_direct_const(def_id) { let alias = ty::AliasConst::new(tcx, kind, args); let ct = ty::Const::new_alias(tcx, ty::IsRigid::No, alias); let const_ = Const::Ty(ty, ct); diff --git a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs index 695684452fc56..09c76d6beb4a1 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs @@ -8,6 +8,7 @@ use rustc_middle::mir::*; use rustc_middle::thir::*; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::cast::{CastTy, mir_cast_kind}; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::{self, Ty, UpvarArgs}; use rustc_span::{DUMMY_SP, Span, Spanned, bug}; diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 2fa0027f969c7..6ca12032362be 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -2,9 +2,9 @@ use rustc_abi::FieldIdx; use rustc_ast::{AsmMacro, InlineAsmOptions}; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; -use rustc_hir::attrs::lang_items::LangItem; use rustc_middle::mir::*; use rustc_middle::thir::*; use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty}; @@ -238,8 +238,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let body_block_end = this.expr_into_dest(tmp, body_block, body).into_block(); let goto = this.cfg.goto(body_block_end, source_info, loop_block); - if let Some(attrs) = this.thir.attributes.get(&expr_id) { - goto.attributes = attrs.clone(); + if let Some(attrs) = this.thir.loop_hint_attrs.get(&expr_id) { + goto.loop_hint_attrs = attrs.clone(); } // Loops are only exited by `break` expressions. diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index 392712619a3f2..57ffee758b325 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use rustc_abi::FieldIdx; use rustc_middle::mir::{Pinnedness, Place, PlaceElem, ProjectionElem}; use rustc_middle::thir::{Ascription, DerefPatBorrowMode, FieldPat, Pat, PatKind}; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; use rustc_span::{Span, span_bug}; diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 6034d657a005e..1e65ddf3372a9 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -11,12 +11,13 @@ use std::{debug_assert_matches, mem}; use itertools::Itertools; use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx}; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::fx::FxIndexMap; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::{BindingMode, ByRef, LetStmt, LocalSource, Node}; use rustc_middle::middle::region::{self, TempLifetime}; use rustc_middle::mir::*; use rustc_middle::thir::{self, *}; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, ValTree, ValTreeKind}; use rustc_pattern_analysis::constructor::RangeEnd; use rustc_pattern_analysis::rustc::{DeconstructedPat, RustcPatCtxt}; diff --git a/compiler/rustc_mir_build/src/builder/matches/test.rs b/compiler/rustc_mir_build/src/builder/matches/test.rs index 2e4d80d6d140d..d116806fc0b73 100644 --- a/compiler/rustc_mir_build/src/builder/matches/test.rs +++ b/compiler/rustc_mir_build/src/builder/matches/test.rs @@ -7,9 +7,9 @@ use std::sync::Arc; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::fx::FxIndexMap; use rustc_hir::RangeEnd; -use rustc_hir::attrs::lang_items::LangItem; use rustc_middle::mir::*; use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::{self, GenericArg, Ty, TyCtxt}; diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index ba079eb726c3c..93114b59ed647 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -24,12 +24,13 @@ use itertools::Itertools; use rustc_abi::{ExternAbi, FieldIdx}; use rustc_apfloat::Float; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; +use rustc_attr_ir::find_attr; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sorted_map::SortedIndexMultiMap; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; -use rustc_hir::{self as hir, BindingMode, ByRef, HirId, ItemLocalId, Node, find_attr}; +use rustc_hir::{self as hir, BindingMode, ByRef, HirId, ItemLocalId, Node}; use rustc_index::bit_set::GrowableBitSet; use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index 872ffec45f30a..4b3c224a84357 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -91,6 +91,7 @@ use rustc_lint_defs::Level; use rustc_middle::middle::region; use rustc_middle::mir::{self, *}; use rustc_middle::thir::{AdtExpr, AdtExprBase, ArmId, ExprId, ExprKind}; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, ValTree}; use rustc_pattern_analysis::rustc::RustcPatCtxt; use rustc_span::{DUMMY_SP, Span, Spanned, bug, span_bug}; diff --git a/compiler/rustc_mir_build/src/check_tail_calls.rs b/compiler/rustc_mir_build/src/check_tail_calls.rs index 6c5b4384bd913..5d421f6c3c885 100644 --- a/compiler/rustc_mir_build/src/check_tail_calls.rs +++ b/compiler/rustc_mir_build/src/check_tail_calls.rs @@ -1,6 +1,6 @@ use rustc_abi::ExternAbi; +use rustc_attr_ir::lang_items::LangItem; use rustc_errors::Applicability; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def::DefKind; use rustc_hir::def_id::CRATE_DEF_ID; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; @@ -205,12 +205,12 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> { } fn report_in_closure(&mut self, expr: &Expr<'_>) { - let err = self.tcx.dcx().span_err(expr.span, "`become` is not allowed in closures"); - self.found_errors = Err(err); + let guar = self.tcx.dcx().span_err(expr.span, "`become` is not allowed in closures"); + self.found_errors = Err(guar); } fn report_builtin_op(&mut self, value: &Expr<'_>, expr: &Expr<'_>) { - let err = self + let guar = self .tcx .dcx() .struct_span_err(value.span, "`become` does not support operators") @@ -221,8 +221,8 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> { "return ", Applicability::MachineApplicable, ) - .emit(); - self.found_errors = Err(err); + .emit_err(); + self.found_errors = Err(guar); } fn report_op(&mut self, fun_ty: Ty<'_>, args: &[ExprId], fn_span: Span, expr: &Expr<'_>) { @@ -265,11 +265,11 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> { } } - self.found_errors = Err(err.emit()); + self.found_errors = Err(err.emit_err()); } fn report_non_call(&mut self, value: &Expr<'_>, expr: &Expr<'_>) { - let err = self + let guar = self .tcx .dcx() .struct_span_err(value.span, "`become` requires a function call") @@ -280,8 +280,8 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> { "return ", Applicability::MaybeIncorrect, ) - .emit(); - self.found_errors = Err(err); + .emit_err(); + self.found_errors = Err(guar); } fn report_calling_closure(&mut self, fun: &Expr<'_>, tupled_args: Ty<'_>, expr: &Expr<'_>) { @@ -291,7 +291,7 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> { _ => "_".to_owned(), }; - let err = self + let guar = self .tcx .dcx() .struct_span_err(expr.span, "tail calling closures directly is not allowed") @@ -303,18 +303,18 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> { ], Applicability::MaybeIncorrect, ) - .emit(); - self.found_errors = Err(err); + .emit_err(); + self.found_errors = Err(guar); } fn report_calling_intrinsic(&mut self, expr: &Expr<'_>) { - let err = self + let guar = self .tcx .dcx() .struct_span_err(expr.span, "tail calling intrinsics is not allowed") - .emit(); + .emit_err(); - self.found_errors = Err(err); + self.found_errors = Err(guar); } fn report_nonfn_callee(&mut self, call_sp: Span, fun_sp: Span, ty: Ty<'_>) { @@ -347,29 +347,29 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> { ); } - let err = err.emit(); - self.found_errors = Err(err); + let guar = err.emit_err(); + self.found_errors = Err(guar); } fn report_abi_mismatch(&mut self, sp: Span, caller_abi: ExternAbi, callee_abi: ExternAbi) { - let err = self + let guar = self .tcx .dcx() .struct_span_err(sp, "mismatched function ABIs") .with_note("`become` requires caller and callee to have the same ABI") .with_note(format!("caller ABI is `{caller_abi}`, while callee ABI is `{callee_abi}`")) - .emit(); - self.found_errors = Err(err); + .emit_err(); + self.found_errors = Err(guar); } fn report_unsupported_abi(&mut self, sp: Span, callee_abi: ExternAbi) { - let err = self + let guar = self .tcx .dcx() .struct_span_err(sp, "ABI does not support guaranteed tail calls") .with_note(format!("`become` is not supported for `extern {callee_abi}` functions")) - .emit(); - self.found_errors = Err(err); + .emit_err(); + self.found_errors = Err(guar); } fn report_signature_mismatch( @@ -378,61 +378,61 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> { caller_sig: ty::FnSig<'_>, callee_sig: ty::FnSig<'_>, ) { - let err = self + let guar = self .tcx .dcx() .struct_span_err(sp, "mismatched signatures") .with_note("`become` requires caller and callee to have matching signatures") .with_note(format!("caller signature: `{caller_sig}`")) .with_note(format!("callee signature: `{callee_sig}`")) - .emit(); - self.found_errors = Err(err); + .emit_err(); + self.found_errors = Err(guar); } fn report_track_caller_caller(&mut self, sp: Span) { - let err = self + let guar = self .tcx .dcx() .struct_span_err( sp, "a function marked with `#[track_caller]` cannot perform a tail-call", ) - .emit(); + .emit_err(); - self.found_errors = Err(err); + self.found_errors = Err(guar); } fn report_c_variadic_caller(&mut self, sp: Span) { - let err = self + let guar = self .tcx .dcx() // FIXME(explicit_tail_calls): highlight the `...` .struct_span_err(sp, "tail-calls are not allowed in c-variadic functions") - .emit(); + .emit_err(); - self.found_errors = Err(err); + self.found_errors = Err(guar); } fn report_c_variadic_callee(&mut self, sp: Span) { - let err = self + let guar = self .tcx .dcx() // FIXME(explicit_tail_calls): highlight the function or something... .struct_span_err(sp, "c-variadic functions can't be tail-called") - .emit(); + .emit_err(); - self.found_errors = Err(err); + self.found_errors = Err(guar); } fn report_unsized_argument(&mut self, sp: Span, arg_ty: Ty<'tcx>) { - let err = self + let guar = self .tcx .dcx() .struct_span_err(sp, format!("unsized arguments cannot be used in a tail call")) .with_note(format!("unsized argument of type `{arg_ty}`")) - .emit(); + .emit_err(); - self.found_errors = Err(err); + self.found_errors = Err(guar); } } diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index e8cc5dc0edfd3..e83c3560a1ee9 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -2,10 +2,11 @@ use std::borrow::Cow; use std::mem; use rustc_ast::AsmMacro; +use rustc_attr_ir::find_attr; +use rustc_attr_ir::lang_items::LangItem; use rustc_errors::DiagArgValue; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def::DefKind; -use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability, find_attr}; +use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability}; use rustc_lint_defs::builtin::{DEPRECATED_SAFE_2024, UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE}; use rustc_middle::middle::codegen_fn_attrs::{TargetFeature, TargetFeatureKind}; use rustc_middle::thir::visit::Visitor; @@ -1104,7 +1105,7 @@ pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) { // Visit the body. visitor.visit_expr(&thir[expr]); - warnings.sort_by_key(|w| w.block_span); + warnings.sort_by_key(|w| w.block_span.lo_hi()); for UnusedUnsafeWarning { hir_id, block_span, enclosing_unsafe } in warnings { let block_span = tcx.sess.source_map().guess_head_span(block_span); tcx.emit_node_span_lint( diff --git a/compiler/rustc_mir_build/src/diagnostics.rs b/compiler/rustc_mir_build/src/diagnostics.rs index 83bfd322d6444..40d9b0990b2fc 100644 --- a/compiler/rustc_mir_build/src/diagnostics.rs +++ b/compiler/rustc_mir_build/src/diagnostics.rs @@ -585,7 +585,7 @@ pub(crate) struct UnsafeNotInheritedLintNote { } impl Subdiagnostic for UnsafeNotInheritedLintNote { - fn add_to_diag(self, diag: &mut Diag<'_, G>) { + fn add_to_diag(self, diag: &mut Diag<'_>) { diag.span_note( self.signature_span, msg!("an unsafe function restricts its caller, but its body is safe by default"), @@ -625,8 +625,8 @@ pub(crate) struct NonExhaustivePatternsTypeNotEmpty<'a, 'tcx> { pub(crate) ty: Ty<'tcx>, } -impl<'a, G> Diagnostic<'a, G> for NonExhaustivePatternsTypeNotEmpty<'_, '_> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { +impl<'a> Diagnostic<'a> for NonExhaustivePatternsTypeNotEmpty<'_, '_> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let mut diag = Diag::new(dcx, level, msg!("non-exhaustive patterns: type `{$ty}` is non-empty")); diag.span(self.scrut_span); @@ -730,9 +730,9 @@ pub(crate) struct UnreachablePattern<'tcx> { pub(crate) inner: UnreachablePatternInner<'tcx>, } -impl<'a, 'tcx, G> Diagnostic<'a, G> for UnreachablePattern<'tcx> { +impl<'a, 'tcx> Diagnostic<'a> for UnreachablePattern<'tcx> { #[track_caller] - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let mut diag = self.inner.into_diag(dcx, level); if let Some(covered_by_many_n_more_count) = self.covered_by_many_n_more_count { diag.arg("covered_by_many_n_more_count", covered_by_many_n_more_count); @@ -1260,7 +1260,7 @@ pub(crate) struct Variant { } impl<'tcx> Subdiagnostic for AdtDefinedHere<'tcx> { - fn add_to_diag(self, diag: &mut Diag<'_, G>) { + fn add_to_diag(self, diag: &mut Diag<'_>) { diag.arg("ty", self.ty); let mut spans = MultiSpan::from(self.adt_def_span); diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs index 667eff50a0d32..a669e09475232 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -1,6 +1,7 @@ use rustc_abi::Size; use rustc_ast::{self as ast, UintTy}; -use rustc_hir::attrs::lang_items::LangItem; +use rustc_attr_ir::lang_items::LangItem; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::{self, LitToConstInput, ScalarInt, Ty, TyCtxt, TypeVisitableExt as _}; use rustc_span::bug; use tracing::trace; diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index feef8d24bd9ca..16e92ae2c51e4 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -1,12 +1,12 @@ use itertools::Itertools; use rustc_abi::{FIRST_VARIANT, FieldIdx, Size, VariantIdx}; use rustc_ast::UnsafeBinderCastKind; +use rustc_attr_ir::lang_items::LangItem; +use rustc_attr_ir::{AttributeKind, HasAttrs, find_attr}; use rustc_data_structures::thin_vec::ThinVec; use rustc_hir as hir; -use rustc_hir::attrs::lang_items::LangItem; -use rustc_hir::attrs::{AttributeKind, HasAttrs}; +use rustc_hir::HirId; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; -use rustc_hir::{HirId, find_attr}; use rustc_index::Idx; use rustc_middle::hir::place::{ Place as HirPlace, PlaceBase as HirPlaceBase, ProjectionKind as HirProjectionKind, @@ -57,12 +57,14 @@ impl<'tcx> SplattedFunc<'tcx> { } } -fn parsed_attrs(id: HirId, tcx: TyCtxt<'_>) -> ThinVec { +fn filter_loop_hint_attrs(id: HirId, tcx: TyCtxt<'_>) -> ThinVec { HasAttrs::get_attrs(id, &tcx) .into_iter() .filter_map(|attr| match attr { - hir::Attribute::Parsed(attrkind) => Some(attrkind.clone()), - hir::Attribute::Unparsed(_) => None, + rustc_attr_ir::Attribute::Parsed(attrkind @ AttributeKind::Unroll(_)) => { + Some(attrkind.clone()) + } + _ => None, }) .collect() } @@ -91,7 +93,7 @@ impl<'tcx> ThirBuildCx<'tcx> { trace!(?expr.ty); - let mut attrs = ThinVec::new(); + let mut loop_hint_attrs = ThinVec::new(); if let hir::ExprKind::Loop(_, _, _, span) = hir_expr.kind { match span.desugaring_kind() { @@ -103,12 +105,12 @@ impl<'tcx> ThirBuildCx<'tcx> { // ignore async for loops if let hir::Node::Expr(expr) = self.tcx.parent_hir_node(expr.hir_id) { std::assert_matches!(expr.kind, hir::ExprKind::DropTemps(..)); - attrs = parsed_attrs(expr.hir_id, self.tcx) + loop_hint_attrs = filter_loop_hint_attrs(expr.hir_id, self.tcx) } } // For loops defined with `loop` and `while`, the expr already has the attrs Some(DesugaringKind::WhileLoop) | None => { - attrs = parsed_attrs(hir_expr.hir_id, self.tcx); + loop_hint_attrs = filter_loop_hint_attrs(hir_expr.hir_id, self.tcx); } _ => (), } @@ -128,8 +130,8 @@ impl<'tcx> ThirBuildCx<'tcx> { let ty = expr.ty; let value = self.thir.exprs.push(expr); - if !attrs.is_empty() { - self.thir.attributes.insert(value, attrs); + if !loop_hint_attrs.is_empty() { + self.thir.loop_hint_attrs.insert(value, loop_hint_attrs); } // Finally, wrap this up in the expr's scope. @@ -827,22 +829,27 @@ impl<'tcx> ThirBuildCx<'tcx> { .operands .iter() .map(|(op, _op_sp)| match *op { - hir::InlineAsmOperand::In { reg, expr } => { - InlineAsmOperand::In { reg, expr: self.mirror_expr(expr) } - } + hir::InlineAsmOperand::In { reg, expr } => InlineAsmOperand::In { + reg: reg.as_target(), + expr: self.mirror_expr(expr), + }, hir::InlineAsmOperand::Out { reg, late, ref expr } => { InlineAsmOperand::Out { - reg, + reg: reg.as_target(), late, expr: expr.map(|expr| self.mirror_expr(expr)), } } hir::InlineAsmOperand::InOut { reg, late, expr } => { - InlineAsmOperand::InOut { reg, late, expr: self.mirror_expr(expr) } + InlineAsmOperand::InOut { + reg: reg.as_target(), + late, + expr: self.mirror_expr(expr), + } } hir::InlineAsmOperand::SplitInOut { reg, late, in_expr, ref out_expr } => { InlineAsmOperand::SplitInOut { - reg, + reg: reg.as_target(), late, in_expr: self.mirror_expr(in_expr), out_expr: out_expr.map(|expr| self.mirror_expr(expr)), diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs index 28d10eeca2294..ad1227bb51df0 100644 --- a/compiler/rustc_mir_build/src/thir/cx/mod.rs +++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs @@ -2,12 +2,13 @@ //! structures into the THIR. The `builder` is generally ignorant of the tcx, //! etc., and instead goes through the `Cx` for most of its work. +use rustc_attr_ir::find_attr; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::steal::Steal; use rustc_errors::ErrorGuaranteed; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::{self as hir, HirId, find_attr}; +use rustc_hir::{self as hir, HirId}; use rustc_middle::thir::*; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::bug; diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index befe4d67253a3..b434bd17e2d8e 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -412,7 +412,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { { let mut redundant_subpats = redundant_subpats.clone(); // Emit lints in the order in which they occur in the file. - redundant_subpats.sort_unstable_by_key(|(pat, _)| pat.data().span); + redundant_subpats.sort_unstable_by_key(|(pat, _)| pat.data().span.lo_hi()); for (pat, explanation) in redundant_subpats { report_unreachable_pattern(cx, arm.arm_data, pat, &explanation, None) } @@ -678,10 +678,13 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { if let Some(def_id) = is_const_pat_that_looks_like_binding(self.tcx, pat) { let span = self.tcx.def_span(def_id); - let variable = self.tcx.item_name(def_id).to_string(); + let name = self.tcx.item_name(def_id); // When we encounter a constant as the binding name, point at the `const` definition. - interpreted_as_const = Some(InterpretedAsConst { span, variable: variable.clone() }); - interpreted_as_const_sugg = Some(InterpretedAsConstSugg { span: pat.span, variable }); + interpreted_as_const = + Some(InterpretedAsConst { span, variable: name.to_ident_string() }); + // The suggested name is suffixed, so it is never a keyword and never needs `r#`. + interpreted_as_const_sugg = + Some(InterpretedAsConstSugg { span: pat.span, variable: name.to_string() }); } else if let PatKind::Constant { .. } = pat.kind && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(pat.span) { @@ -1062,7 +1065,7 @@ fn find_fallback_pattern_typo<'tcx>( if let DefKind::Use = cx.tcx.def_kind(item.owner_id) { // Look for consts being re-exported. let item = cx.tcx.hir_expect_item(item.owner_id.def_id); - let hir::ItemKind::Use(path, _) = item.kind else { + let hir::ItemKind::Use(hir::UseTree { prefix: path, .. }) = item.kind else { continue; }; if let Some(value_ns) = path.res.value_ns @@ -1229,10 +1232,13 @@ fn is_const_pat_that_looks_like_binding<'tcx>(tcx: TyCtxt<'tcx>, pat: &Pat<'tcx> // The pattern must be a named constant, and the name that appears in // the pattern's source text must resemble a plain identifier without any // `::` namespace separators or other non-identifier characters. - if let Some(def_id) = try { pat.extra.as_deref()?.expanded_const? } - && tcx.def_kind(def_id) == DefKind::Const + if let ty::AliasConstKind::Free { def_id } = pat.extra.as_deref()?.expanded_const? && let Ok(snippet) = tcx.sess.source_map().span_to_snippet(pat.span) - && snippet.chars().all(|c| c.is_alphanumeric() || c == '_') + && snippet + .strip_prefix("r#") + .unwrap_or(&snippet) + .chars() + .all(|c| c.is_alphanumeric() || c == '_') { Some(def_id) } else { @@ -1470,7 +1476,7 @@ fn report_non_exhaustive_match<'p, 'tcx>( } else { err.help(msg); } - err.emit() + err.emit_err() } fn joined_uncovered_patterns<'p, 'tcx>( diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 55eef6006f278..53c723bb02a0d 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -2,16 +2,17 @@ use core::ops::ControlFlow; use rustc_abi::{FieldIdx, VariantIdx}; use rustc_apfloat::Float; +use rustc_attr_ir::find_attr; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Diag, msg}; use rustc_hir as hir; -use rustc_hir::attrs::lang_items::LangItem; -use rustc_hir::find_attr; use rustc_index::Idx; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::Obligation; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::thir::{FieldPat, Pat, PatKind}; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::{ self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitableExt, TypeVisitor, Unnormalized, }; @@ -94,7 +95,7 @@ impl<'tcx> ConstToPat<'tcx> { err.span_label(self.tcx.def_span(def_id), msg!("constant defined here")); } } - Box::new(Pat { span: self.span, ty, kind: PatKind::Error(err.emit()), extra: None }) + Box::new(Pat { span: self.span, ty, kind: PatKind::Error(err.emit_err()), extra: None }) } fn alias_to_pat(&mut self, alias_const: ty::AliasConst<'tcx>, ty: Ty<'tcx>) -> Box> { @@ -135,16 +136,16 @@ impl<'tcx> ConstToPat<'tcx> { return self.mk_err(err, ty); }; - // Under generic_const_args, `alias_const` might be a regular const declared in a trait, but + // Under gca_const_items, `alias_const` might be a regular const declared in a trait, but // is `impl`d as a directly represented const. We do not know whether it is here, so we must - // use type system normalization for all consts under generic_const_args. + // use type system normalization for all consts under gca_const_items. // // We probably want to always use type system normalization on stable too, but that would be // a breaking change (in addition to needing significant improvements to diagnostics), so - // right now, we limit this to just generic_const_args. + // right now, we limit this to just gca_const_items. // // See: https://github.com/rust-lang/project-const-generics/issues/105 - let const_value = if self.tcx.features().generic_const_args() + let const_value = if self.tcx.features().gca_const_items() || alias_const.kind.is_direct_const(self.tcx) { let Ok(normalize) = self @@ -224,7 +225,7 @@ impl<'tcx> ConstToPat<'tcx> { // Mark the pattern to indicate that it is the result of lowering a named // constant. This is used for diagnostics. - thir_pat.extra.get_or_insert_default().expanded_const = alias_const.kind.opt_def_id(); + thir_pat.extra.get_or_insert_default().expanded_const = Some(alias_const.kind); thir_pat } diff --git a/compiler/rustc_mir_build/src/thir/pattern/migration.rs b/compiler/rustc_mir_build/src/thir/pattern/migration.rs index e6db030557703..7c789a62b04e2 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/migration.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/migration.rs @@ -90,7 +90,7 @@ impl<'a> PatMigration<'a> { format!("cannot {verb1}{or_verb2} within an implicitly-borrowing pattern{in_rust_2024}") } - fn format_subdiagnostics(self, diag: &mut Diag<'_, G>) { + fn format_subdiagnostics(self, diag: &mut Diag<'_>) { // Format and emit explanatory notes about default binding modes. Reversing the spans' order // means if we have nested spans, the innermost ones will be visited first. for (span, def_br_mutbl) in self.default_mode_labels.into_iter().rev() { diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 5b7a4d626aae3..f5e44d768faa1 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -19,6 +19,7 @@ use rustc_middle::thir::{ Ascription, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary, }; use rustc_middle::ty::adjustment::{PatAdjust, PatAdjustment}; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{ self, CanonicalUserTypeAnnotation, LitToConstInput, Ty, TyCtxt, const_lit_matches_ty, diff --git a/compiler/rustc_mir_dataflow/Cargo.toml b/compiler/rustc_mir_dataflow/Cargo.toml index 156caa7030a03..c1ef4d96f385b 100644 --- a/compiler/rustc_mir_dataflow/Cargo.toml +++ b/compiler/rustc_mir_dataflow/Cargo.toml @@ -8,10 +8,10 @@ edition = "2024" polonius-engine = "0.13.0" regex = "1" rustc_abi = { path = "../rustc_abi" } +rustc_attr_ir = { path = "../rustc_attr_ir" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_graphviz = { path = "../rustc_graphviz" } -rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index be572eab929bc..b1e4bbe98b86b 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -3,16 +3,15 @@ use std::borrow::Cow; use std::ffi::OsString; use std::path::PathBuf; -use std::sync::OnceLock; +use std::sync::LazyLock; use std::{io, ops, str}; use regex::Regex; +use rustc_attr_ir::{BorrowckGraphvizFormatKind, RustcMirKind, find_attr}; use rustc_graphviz as dot; -use rustc_hir::attrs::{BorrowckGraphvizFormatKind, RustcMirKind}; -use rustc_hir::find_attr; use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::{ - self, BasicBlock, Body, Location, MirDumper, graphviz_safe_def_name, traversal, + self, BasicBlock, Body, Location, MirDumper, TerminatorEdges, graphviz_safe_def_name, traversal, }; use rustc_middle::ty::TyCtxt; use rustc_middle::ty::print::with_no_trimmed_paths; @@ -20,9 +19,7 @@ use rustc_span::def_id::DefId; use tracing::debug; use super::fmt::{DebugDiffWithAdapter, DebugWithAdapter, DebugWithContext}; -use super::{ - Analysis, CallReturnPlaces, Direction, Results, ResultsCursor, ResultsVisitor, visit_results, -}; +use super::{Analysis, Direction, Results, ResultsCursor, ResultsVisitor, visit_results}; /// Writes a DOT file containing the results of a dataflow analysis if the user requested it via /// `rustc_mir` attributes and `-Z dump-mir-dataflow`. The `Result` in and the `Results` out are @@ -43,29 +40,23 @@ where let def_id = body.source.def_id(); let attrs = RustcMirAttrs::parse(tcx, def_id); - let file = try { - match attrs.output_path(A::NAME) { - Some(path) => { - debug!("printing dataflow results for {:?} to {}", def_id, path.display()); - if let Some(parent) = path.parent() { - fs::create_dir_all(parent)?; - } - fs::File::create_buffered(&path)? + let mut file = match attrs.output_path(A::NAME) { + Some(path) => { + debug!("printing dataflow results for {:?} to {}", def_id, path.display()); + if let Some(parent) = path.parent() { + fs::create_dir_all(parent)?; } + fs::File::create_buffered(&path)? + } - None => { - let Some(dumper) = MirDumper::new(tcx, A::NAME, body) else { - return Ok(()); - }; - let disambiguator = &pass_name.unwrap_or("-----"); - dumper.set_disambiguator(disambiguator).create_dump_file("dot", body)? - } + None => { + let Some(dumper) = MirDumper::new(tcx, A::NAME, body) else { + return Ok(()); + }; + let disambiguator = &pass_name.unwrap_or("-----"); + dumper.set_disambiguator(disambiguator).create_dump_file("dot", body)? } }; - let mut file = match file { - Ok(f) => f, - Err(e) => return Err(e), - }; let style = attrs.formatter.unwrap_or(OutputStyle::AfterOnly); @@ -77,14 +68,8 @@ where if tcx.sess.opts.unstable_opts.graphviz_dark_mode { render_opts.push(dot::RenderOption::DarkTheme); } - let r = with_no_trimmed_paths!(dot::render_opts(&graphviz, &mut buf, &render_opts)); - - let lhs = try { - r?; - file.write_all(&buf)?; - }; - - lhs + with_no_trimmed_paths!(dot::render_opts(&graphviz, &mut buf, &render_opts))?; + file.write_all(&buf) } #[derive(Default)] @@ -297,7 +282,7 @@ where // +-+----------------------------------+------------+ // B | MIR | STATE | // +-+----------------------------------+------------+ - // C | | (on entry) | {_0,_2,_3} | + // C | | (on start) | {_0,_2,_3} | // +-+----------------------------------+------------+ // D |0| StorageLive(_7) | | // +-+----------------------------------+------------+ @@ -367,68 +352,20 @@ where // FIXME: These should really be printed as part of each outgoing edge rather than the node // for the basic block itself. That way, we could display terminator-specific effects for // backward dataflow analyses as well as effects for `SwitchInt` terminators. - match terminator.kind { - mir::TerminatorKind::Call { destination, .. } => { - self.write_row(w, "", "(on successful return)", |this, w, fmt| { - let state_on_unwind = this.cursor.get().clone(); - this.cursor.apply_custom_effect(|analysis, state| { - analysis.apply_call_return_effect( - state, - block, - CallReturnPlaces::Call(destination), - ); - }); - - write!( - w, - r#"{diff}"#, - colspan = this.style.num_state_columns(), - fmt = fmt, - diff = diff_pretty( - this.cursor.get(), - &state_on_unwind, - this.cursor.analysis() - ), - ) - })?; - } - - mir::TerminatorKind::Yield { resume_arg, .. } => { - self.write_row(w, "", "(on yield resume)", |this, w, fmt| { - let state_on_coroutine_drop = this.cursor.get().clone(); - this.cursor.apply_custom_effect(|analysis, state| { - analysis.apply_call_return_effect( - state, - block, - CallReturnPlaces::Yield(resume_arg), - ); - }); - write!( - w, - r#"{diff}"#, - colspan = this.style.num_state_columns(), - fmt = fmt, - diff = diff_pretty( - this.cursor.get(), - &state_on_coroutine_drop, - this.cursor.analysis() - ), - ) - })?; - } + match terminator.edges() { + TerminatorEdges::AssignOnReturn { return_, place, .. } if !return_.is_empty() => { + let label = match place { + mir::CallReturnPlaces::Call(_) | mir::CallReturnPlaces::InlineAsm(_) => { + "(on successful return)" + } + mir::CallReturnPlaces::Yield(_) => "(on yield resume)", + }; - mir::TerminatorKind::InlineAsm { ref targets, ref operands, .. } - if !targets.is_empty() => - { - self.write_row(w, "", "(on successful return)", |this, w, fmt| { - let state_on_unwind = this.cursor.get().clone(); + self.write_row(w, "", label, |this, w, fmt| { + let state_before_effect = this.cursor.get().clone(); this.cursor.apply_custom_effect(|analysis, state| { - analysis.apply_call_return_effect( - state, - block, - CallReturnPlaces::InlineAsm(operands), - ); + analysis.apply_call_return_effect(state, block, place); }); write!( @@ -438,7 +375,7 @@ where fmt = fmt, diff = diff_pretty( this.cursor.get(), - &state_on_unwind, + &state_before_effect, this.cursor.analysis() ), ) @@ -446,7 +383,7 @@ where } _ => {} - }; + } write!(w, "")?; @@ -581,7 +518,7 @@ where f: impl FnOnce(&mut Self, &mut W, &str) -> io::Result<()>, ) -> io::Result<()> { let bg = self.toggle_background(); - let valign = if mir.starts_with("(on ") && mir != "(on entry)" { "bottom" } else { "top" }; + let valign = if mir.starts_with("(on ") && mir != "(on start)" { "bottom" } else { "top" }; let fmt = format!("valign=\"{}\" sides=\"tl\" {}", valign, bg.attr()); @@ -656,7 +593,7 @@ impl<'a, 'tcx, A: Analysis<'tcx>> StateDiffCollector<'a, 'tcx, A> { } } -impl<'a, 'tcx, A: Analysis<'tcx>> ResultsVisitor<'tcx, A> for StateDiffCollector<'a, 'tcx, A> +impl<'a, 'tcx, A> ResultsVisitor<'tcx, A> for StateDiffCollector<'a, 'tcx, A> where A: Analysis<'tcx>, A::Domain: DebugWithContext, @@ -706,13 +643,6 @@ where } } -macro_rules! regex { - ($re:literal $(,)?) => {{ - static RE: OnceLock = OnceLock::new(); - RE.get_or_init(|| Regex::new($re).unwrap()) - }}; -} - fn diff_pretty(new: T, old: T, ctxt: &C) -> String where T: DebugWithContext, @@ -721,7 +651,7 @@ where return String::new(); } - let re = regex!("\t?\u{001f}([+-])"); + static RE: LazyLock = LazyLock::new(|| Regex::new("\t?\u{001f}([+-])").unwrap()); let raw_diff = format!("{:#?}", DebugDiffWithAdapter { new, old, ctxt }); let raw_diff = dot::escape_html(&raw_diff); @@ -730,7 +660,7 @@ where let raw_diff = raw_diff.replace('\n', r#"
    "#); let mut inside_font_tag = false; - let html_diff = re.replace_all(&raw_diff, |captures: ®ex::Captures<'_>| { + let html_diff = RE.replace_all(&raw_diff, |captures: ®ex::Captures<'_>| { let mut ret = String::new(); if inside_font_tag { ret.push_str(r#""#); diff --git a/compiler/rustc_mir_dataflow/src/framework/lattice.rs b/compiler/rustc_mir_dataflow/src/framework/lattice.rs index 919e346bda747..4566d8e9732da 100644 --- a/compiler/rustc_mir_dataflow/src/framework/lattice.rs +++ b/compiler/rustc_mir_dataflow/src/framework/lattice.rs @@ -104,6 +104,17 @@ pub enum FlatSet { Top, } +impl FlatSet { + #[inline] + pub fn and_then(self, f: impl FnOnce(T) -> FlatSet) -> FlatSet { + match self { + FlatSet::Elem(elem) => f(elem), + FlatSet::Bottom => FlatSet::Bottom, + FlatSet::Top => FlatSet::Top, + } + } +} + impl JoinSemiLattice for FlatSet { fn join(&mut self, other: &Self) -> bool { let result = match (&*self, other) { diff --git a/compiler/rustc_mir_dataflow/src/framework/tests.rs b/compiler/rustc_mir_dataflow/src/framework/tests.rs index ee6330bfe1c2c..9646caab241d9 100644 --- a/compiler/rustc_mir_dataflow/src/framework/tests.rs +++ b/compiler/rustc_mir_dataflow/src/framework/tests.rs @@ -22,7 +22,7 @@ fn mock_body<'tcx>() -> mir::Body<'tcx> { blocks.push(mir::BasicBlockData::new_stmts( std::iter::repeat(&nop).cloned().take(n).collect(), - Some(mir::Terminator { source_info, kind, attributes: ThinVec::new() }), + Some(mir::Terminator { source_info, kind, loop_hint_attrs: ThinVec::new() }), false, )) }; diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs index c0929bef38465..cd42f5f23f7a4 100644 --- a/compiler/rustc_mir_dataflow/src/lib.rs +++ b/compiler/rustc_mir_dataflow/src/lib.rs @@ -1,13 +1,12 @@ // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(never_type))] #![feature(associated_type_defaults)] #![feature(deref_patterns)] #![feature(exact_size_is_empty)] #![feature(file_buffered)] -#![feature(try_blocks)] // tidy-alphabetical-end use rustc_middle::ty; +use rustc_middle::util::Providers; // Please change the public `use` directives cautiously, as they might be used by external tools. // See issue #120130. @@ -29,6 +28,7 @@ mod framework; pub mod impls; pub mod move_paths; pub mod points; +mod pretty; pub mod rustc_peek; mod un_derefer; pub mod value_analysis; @@ -37,3 +37,7 @@ pub struct MoveDataTypingEnv<'tcx> { pub move_data: MoveData<'tcx>, pub typing_env: ty::TypingEnv<'tcx>, } + +pub fn provide(providers: &mut Providers) { + providers.hooks.mir_pretty_extra_data = pretty::mir_pretty_extra_data; +} diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index f22ce4079e58c..3c1c631285f96 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -2,6 +2,7 @@ use std::mem; use rustc_index::IndexVec; use rustc_middle::mir::*; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::{bug, span_bug}; use smallvec::{SmallVec, smallvec}; diff --git a/compiler/rustc_mir_dataflow/src/pretty.rs b/compiler/rustc_mir_dataflow/src/pretty.rs new file mode 100644 index 0000000000000..b37ad708e20e0 --- /dev/null +++ b/compiler/rustc_mir_dataflow/src/pretty.rs @@ -0,0 +1,132 @@ +use std::io; + +use rustc_attr_ir::{RustcMirKind, find_attr}; +use rustc_data_structures::fx::FxIndexMap; +use rustc_middle::mir::{self, Body, Local, MirDumper, PassWhere, traversal}; +use rustc_middle::ty::TyCtxt; + +use crate::debuginfo::debuginfo_locals; +use crate::framework::Analysis; +use crate::impls::{MaybeLiveLocals, MaybeTransitiveLiveLocals, borrowed_locals}; +use crate::{ResultsVisitor, visit_results}; + +type ExtraDataFn = dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>; + +pub(crate) fn mir_pretty_extra_data<'tcx>( + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, +) -> Option> { + let def_id = body.source.def_id(); + let Some(kinds) = find_attr!(tcx, def_id, RustcMir(kind) => kind) else { + return None; + }; + let mut annotations = Annotations::default(); + for kind in kinds { + match kind { + RustcMirKind::PrettyLiveLocals => { + let results = MaybeLiveLocals.iterate_to_fixpoint(tcx, body, None); + let blocks = traversal::reachable(body).map(|(bb, _)| bb); + visit_results( + body, + blocks, + &results, + &mut Annotator { annotations: &mut annotations }, + ); + } + RustcMirKind::PrettyTransitiveLiveLocals => { + let borrowed_locals = borrowed_locals(body); + let debuginfo_locals = debuginfo_locals(body); + let results = MaybeTransitiveLiveLocals::new(&borrowed_locals, &debuginfo_locals) + .iterate_to_fixpoint(tcx, body, None); + let blocks = traversal::reachable(body).map(|(bb, _)| bb); + visit_results( + body, + blocks, + &results, + &mut Annotator { annotations: &mut annotations }, + ); + } + _ => {} + } + } + let extra_data = annotations.into_extra_data()?; + if let Some(dumper) = MirDumper::new(tcx, "dataflow", body) { + dumper.set_extra_data(&*extra_data).dump_mir(body); + } + Some(extra_data) +} + +#[derive(Default)] +struct Annotations { + entries: FxIndexMap>, +} + +impl Annotations { + fn add(&mut self, pass_where: PassWhere, s: String) { + self.entries.entry(pass_where).or_default().push(s); + } + + fn into_extra_data(self) -> Option> { + if self.entries.is_empty() { + return None; + } + Some(Box::new(move |pass_where: PassWhere, out: &mut dyn io::Write| { + let Some(notes) = self.entries.get(&pass_where) else { + return Ok(()); + }; + for s in notes { + writeln!(out, " // {s}")?; + } + Ok(()) + })) + } +} + +/// Examines dataflow results and collects extra annotations for inclusion in MIR pretty printing. +struct Annotator<'a> { + annotations: &'a mut Annotations, +} + +impl<'tcx> ResultsVisitor<'tcx, MaybeLiveLocals> for Annotator<'_> { + fn visit_after_primary_statement_effect( + &mut self, + state: &>::Domain, + _statement: &mir::Statement<'tcx>, + location: mir::Location, + ) { + let live: Vec = state.iter().collect(); + self.annotations.add(PassWhere::BeforeLocation(location), format!("live: {live:?}")); + } + + fn visit_after_primary_terminator_effect( + &mut self, + state: &>::Domain, + _terminator: &mir::Terminator<'tcx>, + location: mir::Location, + ) { + let live: Vec = state.iter().collect(); + self.annotations.add(PassWhere::BeforeLocation(location), format!("live: {live:?}")); + } +} + +impl<'tcx> ResultsVisitor<'tcx, MaybeTransitiveLiveLocals<'_>> for Annotator<'_> { + fn visit_after_primary_statement_effect( + &mut self, + state: &>::Domain, + _statement: &mir::Statement<'tcx>, + location: mir::Location, + ) { + let live: Vec = state.iter().collect(); + self.annotations.add(PassWhere::BeforeLocation(location), format!("live: {live:?}")); + } + + fn visit_after_primary_terminator_effect( + &mut self, + state: &>::Domain, + _terminator: &mir::Terminator<'tcx>, + location: mir::Location, + ) { + let live: Vec = state.iter().collect(); + self.annotations.add(PassWhere::BeforeLocation(location), format!("live: {live:?}")); + } +} diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs index 35c601f09acd4..0e14d1f830492 100644 --- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs +++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs @@ -1,5 +1,4 @@ -use rustc_hir::attrs::RustcMirKind; -use rustc_hir::find_attr; +use rustc_attr_ir::{RustcMirKind, find_attr}; use rustc_middle::mir::{self, Body, Local, Location}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::{Span, sym}; diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index e213a295c0bd0..2acbbab220547 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -214,17 +214,6 @@ impl State { } } - /// Helper method for assignments to a discriminant. - pub fn assign_discr(&mut self, target: PlaceRef<'_>, result: ValueOrPlace, map: &Map<'_>) - where - V: HasTop, - { - self.flood_discr(target, map); - if let Some(target) = map.find_discr(target) { - self.insert_idx(target, result, map); - } - } - /// Retrieve the value stored for a place, or `None` if it is not tracked. fn try_get(&self, place: PlaceRef<'_>, map: &Map<'_>) -> Option { let place = map.find(place)?; diff --git a/compiler/rustc_mir_transform/Cargo.toml b/compiler/rustc_mir_transform/Cargo.toml index 4d157391ed4b0..1697992960177 100644 --- a/compiler/rustc_mir_transform/Cargo.toml +++ b/compiler/rustc_mir_transform/Cargo.toml @@ -10,6 +10,7 @@ itertools = "0.15" rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } +rustc_attr_ir = { path = "../rustc_attr_ir" } rustc_const_eval = { path = "../rustc_const_eval" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_mir_transform/src/add_call_guards.rs b/compiler/rustc_mir_transform/src/add_call_guards.rs index b73cb453752e1..01fd042864019 100644 --- a/compiler/rustc_mir_transform/src/add_call_guards.rs +++ b/compiler/rustc_mir_transform/src/add_call_guards.rs @@ -92,7 +92,7 @@ impl<'tcx> crate::MirPass<'tcx> for AddCallGuards { Some(Terminator { source_info, kind: TerminatorKind::Goto { target }, - attributes: ThinVec::new(), + loop_hint_attrs: ThinVec::new(), }), is_cleanup, ); diff --git a/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs b/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs index 6b74d53801594..e0c07619efbc6 100644 --- a/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs +++ b/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs @@ -98,7 +98,7 @@ fn add_move_for_packed_drop<'tcx>( Some(Terminator { source_info, kind: TerminatorKind::Goto { target }, - attributes: ThinVec::new(), + loop_hint_attrs: ThinVec::new(), }), is_cleanup, )); diff --git a/compiler/rustc_mir_transform/src/check_alignment.rs b/compiler/rustc_mir_transform/src/check_alignment.rs index 56fae176360a1..326936acb82cf 100644 --- a/compiler/rustc_mir_transform/src/check_alignment.rs +++ b/compiler/rustc_mir_transform/src/check_alignment.rs @@ -1,5 +1,5 @@ use rustc_abi::Align; -use rustc_hir::attrs::lang_items::LangItem; +use rustc_attr_ir::lang_items::LangItem; use rustc_index::IndexVec; use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::visit::PlaceContext; diff --git a/compiler/rustc_mir_transform/src/check_call_recursion.rs b/compiler/rustc_mir_transform/src/check_call_recursion.rs index 9627f8988f283..aa5649ed9eb2d 100644 --- a/compiler/rustc_mir_transform/src/check_call_recursion.rs +++ b/compiler/rustc_mir_transform/src/check_call_recursion.rs @@ -1,9 +1,9 @@ use std::ops::ControlFlow; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::graph::iterate::{ NodeStatus, TriColorDepthFirstSearch, TriColorVisitor, }; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def::DefKind; use rustc_lint_defs::builtin::UNCONDITIONAL_RECURSION; use rustc_middle::mir::{self, BasicBlock, BasicBlocks, Body, Terminator, TerminatorKind}; @@ -79,7 +79,7 @@ fn check_recursion<'tcx>( return; } - vis.reachable_recursive_calls.sort(); + vis.reachable_recursive_calls.sort_by_key(|span| span.lo_hi()); let sp = tcx.def_span(def_id); let hir_id = tcx.local_def_id_to_hir_id(def_id); diff --git a/compiler/rustc_mir_transform/src/check_const_item_mutation.rs b/compiler/rustc_mir_transform/src/check_const_item_mutation.rs index 5b25bdc01117b..e8ff3c3a08b79 100644 --- a/compiler/rustc_mir_transform/src/check_const_item_mutation.rs +++ b/compiler/rustc_mir_transform/src/check_const_item_mutation.rs @@ -2,7 +2,7 @@ use rustc_hir::HirId; use rustc_lint_defs::builtin::CONST_ITEM_MUTATION; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; use rustc_span::Span; use rustc_span::def_id::DefId; @@ -35,8 +35,9 @@ impl<'tcx> ConstMutationChecker<'_, 'tcx> { fn is_const_item_without_destructor(&self, local: Local) -> Option { let def_id = self.is_const_item(local)?; - // We avoid linting mutation of a const item if the const's type has a - // Drop impl. The Drop logic observes the mutation which was performed. + // We avoid linting mutation of a const item if the const's type needs + // drop. Any drop logic (including that of fields) may observe the + // mutation which was performed. // // pub struct Log { msg: &'static str } // pub const LOG: Log = Log { msg: "" }; @@ -46,21 +47,30 @@ impl<'tcx> ConstMutationChecker<'_, 'tcx> { // // LOG.msg = "wow"; // prints "wow" // + // Likewise, if a field of the const type has its own Drop impl, that + // drop logic may also observe the mutation: + // + // struct Inner { val: u32 } + // impl Drop for Inner { fn drop(&mut self) { println!("{}", self.val); } } + // struct Outer { inner: Inner } + // const O: Outer = Outer { inner: Inner { val: 0 } }; + // + // O.inner.val = 42; // Inner::drop prints "42" + // // FIXME(https://github.com/rust-lang/rust/issues/77425): // Drop this exception once there is a stable attribute to suppress the - // const item mutation lint for a single specific const only. Something - // equivalent to: - // - // #[const_mutation_allowed] - // pub const LOG: Log = Log { msg: "" }; - // FIXME: this should not be checking for `Drop` impls, - // but whether it or any field has a Drop impl (`needs_drop`) - // as fields' Drop impls may make this observable, too. - match self.tcx.type_of(def_id).skip_binder().ty_adt_def().map(|adt| adt.has_dtor(self.tcx)) - { - Some(true) => None, - Some(false) | None => Some(def_id), + // const item mutation lint for a single specific const only. + let ty = self.tcx.type_of(def_id).instantiate_identity().skip_norm_wip(); + // `needs_drop` is overly conservative for types that contain type + // parameters (e.g. `Self` in a trait associated const): it always + // returns `true` because the parameter *might* implement Drop, even + // when the concrete type at the call site does not. In that case we + // cannot suppress the lint, so fall through and warn. + if ty.has_param() { + return Some(def_id); } + let typing_env = ty::TypingEnv::non_body_analysis(self.tcx, def_id); + if ty.needs_drop(self.tcx, typing_env) { None } else { Some(def_id) } } /// If we should lint on this usage, return the [`HirId`], source [`Span`] diff --git a/compiler/rustc_mir_transform/src/check_enums.rs b/compiler/rustc_mir_transform/src/check_enums.rs index ff62572cf2825..8d2178a07c5c6 100644 --- a/compiler/rustc_mir_transform/src/check_enums.rs +++ b/compiler/rustc_mir_transform/src/check_enums.rs @@ -1,6 +1,6 @@ use rustc_abi::{Scalar, Size, TagEncoding, Variants, WrappingRange}; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::thin_vec::ThinVec; -use rustc_hir::attrs::lang_items::LangItem; use rustc_index::IndexVec; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; @@ -64,7 +64,7 @@ impl<'tcx> crate::MirPass<'tcx> for CheckEnums { basic_blocks[block].terminator = Some(Terminator { source_info, kind: TerminatorKind::Goto { target: new_block }, - attributes: ThinVec::new(), + loop_hint_attrs: ThinVec::new(), }); } EnumCheckType::Direct { source_op, discr, op_size, valid_discrs } => { @@ -395,7 +395,7 @@ fn insert_direct_enum_check<'tcx>( invalid_discr_block, ), }, - attributes: ThinVec::new(), + loop_hint_attrs: ThinVec::new(), }); // Abort in case of an invalid enum discriminant. @@ -415,7 +415,7 @@ fn insert_direct_enum_check<'tcx>( // make a failing UB check turn into much worse UB when we start unwinding. unwind: UnwindAction::Unreachable, }, - attributes: ThinVec::new(), + loop_hint_attrs: ThinVec::new(), }); } @@ -461,7 +461,7 @@ fn insert_uninhabited_enum_check<'tcx>( // make a failing UB check turn into much worse UB when we start unwinding. unwind: UnwindAction::Unreachable, }, - attributes: ThinVec::new(), + loop_hint_attrs: ThinVec::new(), }); } @@ -539,6 +539,6 @@ fn insert_niche_check<'tcx>( // make a failing UB check turn into much worse UB when we start unwinding. unwind: UnwindAction::Unreachable, }, - attributes: ThinVec::new(), + loop_hint_attrs: ThinVec::new(), }); } diff --git a/compiler/rustc_mir_transform/src/check_inline.rs b/compiler/rustc_mir_transform/src/check_inline.rs index c267fb171fba5..bea0d5c81eb2f 100644 --- a/compiler/rustc_mir_transform/src/check_inline.rs +++ b/compiler/rustc_mir_transform/src/check_inline.rs @@ -1,9 +1,8 @@ //! Check that a body annotated with `#[rustc_force_inline]` will not fail to inline based on its //! definition alone (irrespective of any specific caller). -use rustc_hir::attrs::InlineAttr; +use rustc_attr_ir::{InlineAttr, find_attr}; use rustc_hir::def_id::DefId; -use rustc_hir::find_attr; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::{Body, TerminatorKind}; use rustc_middle::ty; diff --git a/compiler/rustc_mir_transform/src/check_null.rs b/compiler/rustc_mir_transform/src/check_null.rs index ab2c48f0eb1e8..37443fed005d7 100644 --- a/compiler/rustc_mir_transform/src/check_null.rs +++ b/compiler/rustc_mir_transform/src/check_null.rs @@ -1,4 +1,4 @@ -use rustc_hir::attrs::lang_items::LangItem; +use rustc_attr_ir::lang_items::LangItem; use rustc_index::IndexVec; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext}; use rustc_middle::mir::*; diff --git a/compiler/rustc_mir_transform/src/check_pointers.rs b/compiler/rustc_mir_transform/src/check_pointers.rs index a8c2626757e05..fb6379a25af95 100644 --- a/compiler/rustc_mir_transform/src/check_pointers.rs +++ b/compiler/rustc_mir_transform/src/check_pointers.rs @@ -1,5 +1,5 @@ +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::thin_vec::ThinVec; -use rustc_hir::attrs::lang_items::LangItem; use rustc_index::IndexVec; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; @@ -117,7 +117,7 @@ pub(crate) fn check_pointers<'tcx, F>( // worse UB when we start unwinding. unwind: UnwindAction::Unreachable, }, - attributes: ThinVec::new(), + loop_hint_attrs: ThinVec::new(), }); } } diff --git a/compiler/rustc_mir_transform/src/coroutine/drop.rs b/compiler/rustc_mir_transform/src/coroutine/drop.rs index 6b26a1f242cf0..44ea6aef0e02d 100644 --- a/compiler/rustc_mir_transform/src/coroutine/drop.rs +++ b/compiler/rustc_mir_transform/src/coroutine/drop.rs @@ -376,7 +376,7 @@ pub(super) fn create_coroutine_drop_shim_proxy_async<'tcx>( drop: None, }; body.basic_blocks_mut()[call_bb].terminator = - Some(Terminator { source_info, kind, attributes: ThinVec::new() }); + Some(Terminator { source_info, kind, loop_hint_attrs: ThinVec::new() }); // Run derefer to fix Derefs that are not in the first place deref_finder(tcx, &mut body, false); diff --git a/compiler/rustc_mir_transform/src/coroutine/layout.rs b/compiler/rustc_mir_transform/src/coroutine/layout.rs index a06ae343123d1..ab500d12b8aa3 100644 --- a/compiler/rustc_mir_transform/src/coroutine/layout.rs +++ b/compiler/rustc_mir_transform/src/coroutine/layout.rs @@ -24,15 +24,17 @@ use std::ops; use itertools::izip; use rustc_abi::{FieldIdx, VariantIdx}; +use rustc_attr_ir::find_attr; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::fx::FxHashSet; use rustc_errors::pluralize; -use rustc_hir::attrs::lang_items::LangItem; -use rustc_hir::{self as hir, find_attr}; +use rustc_hir as hir; use rustc_index::bit_set::{BitMatrix, DenseBitSet}; use rustc_index::{Idx, IndexVec}; use rustc_infer::traits::TraitErrors; use rustc_lint_defs::builtin::MUST_NOT_SUSPEND; use rustc_middle::mir::*; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::{self, CoroutineArgs, CoroutineArgsExt, Ty, TyCtxt, TypingMode}; use rustc_mir_dataflow::impls::{ MaybeBorrowedLocals, MaybeLiveLocals, MaybeRequiresStorage, MaybeStorageLive, @@ -252,6 +254,7 @@ fn compute_storage_conflicts<'mir, 'tcx>( saved_locals, local_conflicts: BitMatrix::from_row_n(&ineligible_locals, body.local_decls.len()), eligible_storage_live: DenseBitSet::new_empty(body.local_decls.len()), + last_recorded_storage_live: DenseBitSet::new_empty(body.local_decls.len()), }; // Filter out: @@ -296,6 +299,10 @@ struct StorageConflictVisitor<'a> { local_conflicts: BitMatrix, // We keep this bitset as a buffer to avoid reallocating memory. eligible_storage_live: DenseBitSet, + // The last live set whose conflicts were recorded. This is just a fast path: + // if the current live set is a subset, we can skip updating the conflict matrix + // since its conflicts have already been recorded. + last_recorded_storage_live: DenseBitSet, } impl<'a, 'tcx> ResultsVisitor<'tcx, MaybeRequiresStorage> for StorageConflictVisitor<'a> { @@ -323,9 +330,14 @@ impl StorageConflictVisitor<'_> { self.eligible_storage_live.clone_from(state); self.eligible_storage_live.intersect(&**self.saved_locals); + if self.last_recorded_storage_live.superset(&self.eligible_storage_live) { + return; + } + for local in self.eligible_storage_live.iter() { self.local_conflicts.union_row_with(&self.eligible_storage_live, local); } + std::mem::swap(&mut self.last_recorded_storage_live, &mut self.eligible_storage_live); } } diff --git a/compiler/rustc_mir_transform/src/coroutine/mod.rs b/compiler/rustc_mir_transform/src/coroutine/mod.rs index 0608458d5e80c..1e63dca78adbb 100644 --- a/compiler/rustc_mir_transform/src/coroutine/mod.rs +++ b/compiler/rustc_mir_transform/src/coroutine/mod.rs @@ -63,8 +63,8 @@ use drop::{ pub(super) use layout::mir_coroutine_witnesses; use layout::{CoroutineSavedLocals, compute_layout, locals_live_across_suspend_points}; use rustc_abi::{FieldIdx, VariantIdx}; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::thin_vec::ThinVec; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::{self as hir, CoroutineDesugaring, CoroutineKind}; use rustc_index::bit_set::{BitMatrix, DenseBitSet}; use rustc_index::{Idx, IndexVec, indexvec}; @@ -256,7 +256,7 @@ impl<'tcx> TransformVisitor<'tcx> { Some(Terminator { source_info, kind: TerminatorKind::Return, - attributes: ThinVec::new(), + loop_hint_attrs: ThinVec::new(), }), false, )); @@ -744,14 +744,14 @@ fn insert_switch<'tcx>( body.basic_blocks_mut()[START_BLOCK].terminator = Some(Terminator { source_info: SourceInfo::outermost(body.span), kind: switch, - attributes: ThinVec::new(), + loop_hint_attrs: ThinVec::new(), }); } fn insert_term_block<'tcx>(body: &mut Body<'tcx>, kind: TerminatorKind<'tcx>) -> BasicBlock { let source_info = SourceInfo::outermost(body.span); body.basic_blocks_mut().push(BasicBlockData::new( - Some(Terminator { source_info, kind, attributes: ThinVec::new() }), + Some(Terminator { source_info, kind, loop_hint_attrs: ThinVec::new() }), false, )) } @@ -776,7 +776,11 @@ fn insert_poll_ready_block<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> Ba let source_info = SourceInfo::outermost(body.span); body.basic_blocks_mut().push(BasicBlockData::new_stmts( [return_poll_ready_assign(tcx, source_info)].to_vec(), - Some(Terminator { source_info, kind: TerminatorKind::Return, attributes: ThinVec::new() }), + Some(Terminator { + source_info, + kind: TerminatorKind::Return, + loop_hint_attrs: ThinVec::new(), + }), false, )) } @@ -835,7 +839,7 @@ fn generate_poison_block_and_redirect_unwinds_there<'tcx>( source_info, kind: TerminatorKind::UnwindResume, - attributes: ThinVec::new(), + loop_hint_attrs: ThinVec::new(), }), true, )); @@ -851,7 +855,7 @@ fn generate_poison_block_and_redirect_unwinds_there<'tcx>( source_info, kind: TerminatorKind::Goto { target: poison_block }, - attributes: ThinVec::new(), + loop_hint_attrs: ThinVec::new(), }; } } else if !block.is_cleanup @@ -1019,7 +1023,7 @@ fn create_cases<'tcx>( source_info, kind: TerminatorKind::Goto { target }, - attributes: ThinVec::new(), + loop_hint_attrs: ThinVec::new(), }), false, )); diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index a4d39f09b724d..2784e864e65e4 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -1,6 +1,6 @@ -use rustc_hir::attrs::CoverageAttrKind; +use rustc_attr_ir::{CoverageAttrKind, find_attr}; +use rustc_hir as hir; use rustc_hir::def::DefKind; -use rustc_hir::{self as hir, find_attr}; use rustc_index::bit_set::DenseBitSet; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::coverage::{ diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index ade8bb7865dda..9aa5f3b9f5ab2 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -50,7 +50,7 @@ pub(super) fn extract_refined_covspans<'tcx>( // Discard any spans not contained within the function body span. // Also discard any spans that fill the entire body, because they tend // to represent compiler-inserted code, e.g. implicitly returning `()`. - if !body_span.contains(covspan_span) || body_span.source_equal(covspan_span) { + if !body_span.contains(covspan_span) || body_span.lo_hi() == covspan_span.lo_hi() { return false; } @@ -91,7 +91,7 @@ pub(super) fn extract_refined_covspans<'tcx>( // preferring the one with the most-dominated BCB. // (Ideally we should try to preserve _all_ non-dominating BCBs, but that // requires a lot more complexity in the span refiner, for little benefit.) - covspans.dedup_by(|b, a| a.span.source_equal(b.span)); + covspans.dedup_by_key(|a| a.span.lo_hi()); // Sort the holes, and merge overlapping/adjacent holes. let mut holes = node.hole_spans.iter().copied().map(|span| Hole { span }).collect::>(); diff --git a/compiler/rustc_mir_transform/src/coverage/tests.rs b/compiler/rustc_mir_transform/src/coverage/tests.rs index ca4fa888a2176..e9876310f112f 100644 --- a/compiler/rustc_mir_transform/src/coverage/tests.rs +++ b/compiler/rustc_mir_transform/src/coverage/tests.rs @@ -73,7 +73,7 @@ impl<'tcx> MockBlocks<'tcx> { Some(Terminator { source_info: SourceInfo::outermost(Span::with_root_ctxt(next_lo, next_hi)), kind, - attributes: ThinVec::new(), + loop_hint_attrs: ThinVec::new(), }), false, )) diff --git a/compiler/rustc_mir_transform/src/cross_crate_inline.rs b/compiler/rustc_mir_transform/src/cross_crate_inline.rs index 8a4e7740650fc..53d58439b0434 100644 --- a/compiler/rustc_mir_transform/src/cross_crate_inline.rs +++ b/compiler/rustc_mir_transform/src/cross_crate_inline.rs @@ -1,13 +1,13 @@ -use rustc_hir::attrs::InlineAttr; +use rustc_attr_ir::{InlineAttr, find_attr}; +use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; -use rustc_hir::{self as hir, find_attr}; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::config::{InliningThreshold, OptLevel}; -use rustc_span::bug; +use rustc_span::{bug, sym}; use crate::{inline, pass_manager as pm}; @@ -64,13 +64,19 @@ fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { return true; } - let sig = tcx.fn_sig(def_id).instantiate_identity().skip_norm_wip(); - for ty in sig.inputs().skip_binder().iter().chain(std::iter::once(&sig.output().skip_binder())) - { - // FIXME(f16_f128): in order to avoid crashes building `core`, always inline to skip - // codegen if the function is not used. - if ty == &tcx.types.f16 || ty == &tcx.types.f128 { - return true; + let reliable_f16 = tcx.sess.config.contains(&(sym::target_has_reliable_f16, None)); + let reliable_f128 = tcx.sess.config.contains(&(sym::target_has_reliable_f128, None)); + if !reliable_f16 || !reliable_f128 { + let sig = tcx.fn_sig(def_id).instantiate_identity().skip_norm_wip(); + for ty in + sig.inputs().skip_binder().iter().chain(std::iter::once(&sig.output().skip_binder())) + { + // FIXME(f16,f128): in order to avoid crashes building `core`, inline on targets that + // have issues to skip codegen if the function is not used. + if (!reliable_f16 && ty == &tcx.types.f16) || (!reliable_f128 && ty == &tcx.types.f128) + { + return true; + } } } diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 21ea02c8ff601..e52a9a847b119 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -119,7 +119,51 @@ impl<'tcx> Analysis<'tcx> for ConstAnalysis<'_, 'tcx> { _location: Location, ) { if state.is_reachable() { - self.handle_statement(statement, state); + match &statement.kind { + StatementKind::Assign((place, rvalue)) => { + self.handle_assign(*place, rvalue, state); + } + StatementKind::SetDiscriminant { place, variant_index } => { + let place = (**place).as_ref(); + state.flood_discr(place, &self.map); + if let Some(target) = self.map.find_discr(place) { + let enum_ty = place.ty(self.local_decls, self.tcx).ty; + if let Some(discr) = self.eval_discriminant(enum_ty, *variant_index) { + state.insert_value_idx(target, FlatSet::Elem(discr), &self.map); + } + } + } + StatementKind::Intrinsic(intrinsic) => { + match intrinsic { + NonDivergingIntrinsic::Assume(..) => { + // Could use this, but ignoring it is sound. + } + NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping { + dst: _, + src: _, + count: _, + }) => { + // This statement represents `*dst = *src`, `count` times. + } + } + } + StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => { + // StorageLive leaves the local in an uninitialized state. + // StorageDead makes it UB to access the local afterwards. + state.flood_with( + Place::from(*local).as_ref(), + &self.map, + FlatSet::::BOTTOM, + ); + } + StatementKind::ConstEvalCounter + | StatementKind::Nop + | StatementKind::FakeRead(..) + | StatementKind::PlaceMention(..) + | StatementKind::Coverage(..) + | StatementKind::BackwardIncompatibleDropHint { .. } + | StatementKind::AscribeUserType(..) => {} + } } } @@ -147,7 +191,34 @@ impl<'tcx> Analysis<'tcx> for ConstAnalysis<'_, 'tcx> { _location: Location, ) { if state.is_reachable() { - self.handle_terminator(terminator, state) + match &terminator.kind { + TerminatorKind::Call { .. } | TerminatorKind::InlineAsm { .. } => { + // Effect is applied by `apply_call_return_effect`. + } + TerminatorKind::Drop { place, .. } => { + state.flood_with(place.as_ref(), &self.map, FlatSet::::BOTTOM); + } + TerminatorKind::Yield { .. } => { + // They would have an effect, but are not allowed in this phase. + bug!("encountered disallowed terminator"); + } + TerminatorKind::TailCall { .. } => { + // FIXME(explicit_tail_calls): determine if we need to do something here + // (probably not) + } + TerminatorKind::SwitchInt { .. } + | TerminatorKind::Goto { .. } + | TerminatorKind::UnwindResume + | TerminatorKind::UnwindTerminate(_) + | TerminatorKind::Return + | TerminatorKind::Unreachable + | TerminatorKind::Assert { .. } + | TerminatorKind::CoroutineDrop + | TerminatorKind::FalseEdge { .. } + | TerminatorKind::FalseUnwind { .. } => { + // These terminators have no effect on the analysis. + } + } } } @@ -158,7 +229,9 @@ impl<'tcx> Analysis<'tcx> for ConstAnalysis<'_, 'tcx> { return_places: CallReturnPlaces<'_, 'tcx>, ) { if state.is_reachable() { - self.handle_call_return(return_places, state) + return_places.for_each(|place| { + state.flood(place.as_ref(), &self.map); + }) } } } @@ -175,55 +248,15 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { } } - fn handle_statement(&self, statement: &Statement<'tcx>, state: &mut State>) { - match &statement.kind { - StatementKind::Assign((place, rvalue)) => { - self.handle_assign(*place, rvalue, state); - } - StatementKind::SetDiscriminant { place, variant_index } => { - self.handle_set_discriminant(**place, *variant_index, state); - } - StatementKind::Intrinsic(intrinsic) => { - self.handle_intrinsic(intrinsic); - } - StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => { - // StorageLive leaves the local in an uninitialized state. - // StorageDead makes it UB to access the local afterwards. - state.flood_with( - Place::from(*local).as_ref(), - &self.map, - FlatSet::::BOTTOM, - ); - } - StatementKind::ConstEvalCounter - | StatementKind::Nop - | StatementKind::FakeRead(..) - | StatementKind::PlaceMention(..) - | StatementKind::Coverage(..) - | StatementKind::BackwardIncompatibleDropHint { .. } - | StatementKind::AscribeUserType(..) => {} - } - } - - fn handle_intrinsic(&self, intrinsic: &NonDivergingIntrinsic<'tcx>) { - match intrinsic { - NonDivergingIntrinsic::Assume(..) => { - // Could use this, but ignoring it is sound. - } - NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping { - dst: _, - src: _, - count: _, - }) => { - // This statement represents `*dst = *src`, `count` times. - } - } - } - fn handle_operand(&self, operand: &Operand<'tcx>) -> ValueOrPlace> { match operand { Operand::RuntimeChecks(_) => ValueOrPlace::TOP, - Operand::Constant(constant) => ValueOrPlace::Value(self.handle_constant(constant)), + Operand::Constant(constant) => ValueOrPlace::Value( + constant + .const_ + .try_eval_scalar(self.tcx, self.typing_env) + .map_or(FlatSet::Top, FlatSet::Elem), + ), Operand::Copy(place) | Operand::Move(place) => { // On move, we would ideally flood the place with bottom. But with the current // framework this is not possible (similar to `InterpCx::eval_operand`). @@ -232,84 +265,19 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { } } - /// The effect of a successful function call return should not be - /// applied here, see [`Analysis::apply_primary_terminator_effect`]. - fn handle_terminator<'mir>( - &self, - terminator: &'mir Terminator<'tcx>, - state: &mut State>, - ) { - match &terminator.kind { - TerminatorKind::Call { .. } | TerminatorKind::InlineAsm { .. } => { - // Effect is applied by `handle_call_return`. - } - TerminatorKind::Drop { place, .. } => { - state.flood_with(place.as_ref(), &self.map, FlatSet::::BOTTOM); - } - TerminatorKind::Yield { .. } => { - // They would have an effect, but are not allowed in this phase. - bug!("encountered disallowed terminator"); - } - TerminatorKind::TailCall { .. } => { - // FIXME(explicit_tail_calls): determine if we need to do something here (probably - // not) - } - TerminatorKind::SwitchInt { .. } - | TerminatorKind::Goto { .. } - | TerminatorKind::UnwindResume - | TerminatorKind::UnwindTerminate(_) - | TerminatorKind::Return - | TerminatorKind::Unreachable - | TerminatorKind::Assert { .. } - | TerminatorKind::CoroutineDrop - | TerminatorKind::FalseEdge { .. } - | TerminatorKind::FalseUnwind { .. } => { - // These terminators have no effect on the analysis. - } - } - } - - fn handle_call_return( - &self, - return_places: CallReturnPlaces<'_, 'tcx>, - state: &mut State>, - ) { - return_places.for_each(|place| { - state.flood(place.as_ref(), &self.map); - }) - } - - fn handle_set_discriminant( - &self, - place: Place<'tcx>, - variant_index: VariantIdx, - state: &mut State>, - ) { - state.flood_discr(place.as_ref(), &self.map); - if self.map.find_discr(place.as_ref()).is_some() { - let enum_ty = place.ty(self.local_decls, self.tcx).ty; - if let Some(discr) = self.eval_discriminant(enum_ty, variant_index) { - state.assign_discr( - place.as_ref(), - ValueOrPlace::Value(FlatSet::Elem(discr)), - &self.map, - ); - } - } - } - fn handle_assign( &self, target: Place<'tcx>, rvalue: &Rvalue<'tcx>, state: &mut State>, ) { - match rvalue { + let result = match rvalue { Rvalue::Use(operand, _) => { state.flood(target.as_ref(), &self.map); if let Some(target) = self.map.find(target.as_ref()) { self.assign_operand(state, target, operand); } + return; } Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in runtime MIR"), Rvalue::Aggregate(kind, operands) => { @@ -355,27 +323,35 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { state.insert_value_idx(discr_idx, FlatSet::Elem(discr_val), &self.map); } } + return; } - Rvalue::BinaryOp(op, (left, right)) if op.is_overflowing() => { - // Flood everything now, so we can use `insert_value_idx` directly later. - state.flood(target.as_ref(), &self.map); + Rvalue::BinaryOp(op, (left, right)) => { + if op.is_overflowing() { + // Flood everything now, so we can use `insert_value_idx` directly later. + state.flood(target.as_ref(), &self.map); - let Some(target) = self.map.find(target.as_ref()) else { return }; + let Some(target) = self.map.find(target.as_ref()) else { return }; - let value_target = self.map.apply(target, TrackElem::Field(0_u32.into())); - let overflow_target = self.map.apply(target, TrackElem::Field(1_u32.into())); + let value_target = self.map.apply(target, TrackElem::Field(0_u32.into())); + let overflow_target = self.map.apply(target, TrackElem::Field(1_u32.into())); - if value_target.is_some() || overflow_target.is_some() { - let (val, overflow) = self.binary_op(state, *op, left, right); + if value_target.is_some() || overflow_target.is_some() { + let (val, overflow) = self.binary_op(state, *op, left, right); - if let Some(value_target) = value_target { - // We have flooded `target` earlier. - state.insert_value_idx(value_target, val, &self.map); - } - if let Some(overflow_target) = overflow_target { - // We have flooded `target` earlier. - state.insert_value_idx(overflow_target, overflow, &self.map); + if let Some(value_target) = value_target { + // We have flooded `target` earlier. + state.insert_value_idx(value_target, val, &self.map); + } + if let Some(overflow_target) = overflow_target { + // We have flooded `target` earlier. + state.insert_value_idx(overflow_target, overflow, &self.map); + } } + return; + } else { + // Overflows must be ignored here. + let (val, _overflow) = self.binary_op(state, *op, left, right); + ValueOrPlace::Value(val) } } Rvalue::Cast( @@ -395,103 +371,73 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { { state.insert_value_idx(target_len, FlatSet::Elem(len.into()), &self.map); } + return; } - _ => { - let result = self.handle_rvalue(rvalue, state); - state.assign(target.as_ref(), result, &self.map); - } - } - } - - fn handle_rvalue( - &self, - rvalue: &Rvalue<'tcx>, - state: &mut State>, - ) -> ValueOrPlace> { - let val = match rvalue { Rvalue::Cast(CastKind::IntToInt | CastKind::IntToFloat, operand, ty) => { - let Ok(layout) = self.tcx.layout_of(self.typing_env.as_query_input(*ty)) else { - return ValueOrPlace::Value(FlatSet::Top); - }; - match self.eval_operand(operand, state) { - FlatSet::Elem(op) => self - .ecx - .int_to_int_or_float(&op, layout) - .discard_err() - .map_or(FlatSet::Top, |result| self.wrap_immediate(*result)), - FlatSet::Bottom => FlatSet::Bottom, - FlatSet::Top => FlatSet::Top, - } + ValueOrPlace::Value( + if let Ok(layout) = self.tcx.layout_of(self.typing_env.as_query_input(*ty)) { + self.eval_operand(operand, state).and_then(|op| { + self.ecx + .int_to_int_or_float(&op, layout) + .discard_err() + .map_or(FlatSet::Top, |result| self.wrap_immediate(*result)) + }) + } else { + FlatSet::Top + }, + ) } Rvalue::Cast(CastKind::FloatToInt | CastKind::FloatToFloat, operand, ty) => { - let Ok(layout) = self.tcx.layout_of(self.typing_env.as_query_input(*ty)) else { - return ValueOrPlace::Value(FlatSet::Top); - }; - match self.eval_operand(operand, state) { - FlatSet::Elem(op) => self - .ecx - .float_to_float_or_int(&op, layout) - .discard_err() - .map_or(FlatSet::Top, |result| self.wrap_immediate(*result)), - FlatSet::Bottom => FlatSet::Bottom, - FlatSet::Top => FlatSet::Top, - } + ValueOrPlace::Value( + if let Ok(layout) = self.tcx.layout_of(self.typing_env.as_query_input(*ty)) { + self.eval_operand(operand, state).and_then(|op| { + self.ecx + .float_to_float_or_int(&op, layout) + .discard_err() + .map_or(FlatSet::Top, |result| self.wrap_immediate(*result)) + }) + } else { + FlatSet::Top + }, + ) } Rvalue::Cast(CastKind::Transmute | CastKind::Subtype, operand, _) => { - match self.eval_operand(operand, state) { - FlatSet::Elem(op) => self.wrap_immediate(*op), - FlatSet::Bottom => FlatSet::Bottom, - FlatSet::Top => FlatSet::Top, - } - } - Rvalue::BinaryOp(op, (left, right)) if !op.is_overflowing() => { - // Overflows must be ignored here. - // The overflowing operators are handled in `handle_assign`. - let (val, _overflow) = self.binary_op(state, *op, left, right); - val + ValueOrPlace::Value( + self.eval_operand(operand, state).and_then(|op| self.wrap_immediate(*op)), + ) } Rvalue::UnaryOp(op, operand) => { if let UnOp::PtrMetadata = op && let Some(place) = operand.place() && let Some(len) = self.map.find_len(place.as_ref()) { - return ValueOrPlace::Place(len); - } - match self.eval_operand(operand, state) { - FlatSet::Elem(value) => self - .ecx - .unary_op(*op, &value) - .discard_err() - .map_or(FlatSet::Top, |val| self.wrap_immediate(*val)), - FlatSet::Bottom => FlatSet::Bottom, - FlatSet::Top => FlatSet::Top, + ValueOrPlace::Place(len) + } else { + ValueOrPlace::Value(self.eval_operand(operand, state).and_then(|value| { + self.ecx + .unary_op(*op, &value) + .discard_err() + .map_or(FlatSet::Top, |val| self.wrap_immediate(*val)) + })) } } - Rvalue::Discriminant(place) => state.get_discr(place.as_ref(), &self.map), - Rvalue::Use(operand, _) => return self.handle_operand(operand), - Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in runtime MIR"), + Rvalue::Discriminant(place) => { + ValueOrPlace::Value(state.get_discr(place.as_ref(), &self.map)) + } Rvalue::Ref(..) | Rvalue::Reborrow(..) | Rvalue::RawPtr(..) => { // We don't track such places. - return ValueOrPlace::TOP; + ValueOrPlace::TOP } Rvalue::Repeat(..) | Rvalue::ThreadLocalRef(..) | Rvalue::Cast(..) - | Rvalue::BinaryOp(..) - | Rvalue::Aggregate(..) | Rvalue::WrapUnsafeBinder(..) => { // No modification is possible through these r-values. - return ValueOrPlace::TOP; + ValueOrPlace::TOP } }; - ValueOrPlace::Value(val) - } - - fn handle_constant(&self, constant: &ConstOperand<'tcx>) -> FlatSet { - constant - .const_ - .try_eval_scalar(self.tcx, self.typing_env) - .map_or(FlatSet::Top, FlatSet::Elem) + // For the arms that didn't return early. + state.assign(target.as_ref(), result, &self.map); } fn get_switch_int_edges<'mir>( @@ -617,7 +563,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { fn binary_op( &self, - state: &mut State>, + state: &State>, op: BinOp, left: &Operand<'tcx>, right: &Operand<'tcx>, @@ -647,7 +593,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { // Exactly one side is known, attempt some algebraic simplifications. (FlatSet::Elem(const_arg), _) | (_, FlatSet::Elem(const_arg)) => { let layout = const_arg.layout; - if !matches!(layout.backend_repr, rustc_abi::BackendRepr::Scalar(..)) { + if !layout.backend_repr.is_scalar() { return (FlatSet::Top, FlatSet::Top); } @@ -677,24 +623,18 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { fn eval_operand( &self, op: &Operand<'tcx>, - state: &mut State>, + state: &State>, ) -> FlatSet> { let value = match self.handle_operand(op) { ValueOrPlace::Value(value) => value, ValueOrPlace::Place(place) => state.get_idx(place, &self.map), }; - match value { - FlatSet::Top => FlatSet::Top, - FlatSet::Elem(scalar) => { - let ty = op.ty(self.local_decls, self.tcx); - self.tcx - .layout_of(self.typing_env.as_query_input(ty)) - .map_or(FlatSet::Top, |layout| { - FlatSet::Elem(ImmTy::from_scalar(scalar, layout)) - }) - } - FlatSet::Bottom => FlatSet::Bottom, - } + value.and_then(|scalar| { + let ty = op.ty(self.local_decls, self.tcx); + self.tcx + .layout_of(self.typing_env.as_query_input(ty)) + .map_or(FlatSet::Top, |layout| FlatSet::Elem(ImmTy::from_scalar(scalar, layout))) + }) } fn eval_discriminant(&self, enum_ty: Ty<'tcx>, variant_index: VariantIdx) -> Option { @@ -753,7 +693,7 @@ struct Patch<'tcx> { } impl<'tcx> Patch<'tcx> { - pub(crate) fn new(tcx: TyCtxt<'tcx>) -> Self { + fn new(tcx: TyCtxt<'tcx>) -> Self { Self { tcx, before_effect: FxHashMap::default(), assignments: FxHashMap::default() } } @@ -770,7 +710,7 @@ struct Collector<'a, 'tcx> { } impl<'a, 'tcx> Collector<'a, 'tcx> { - pub(crate) fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, map: &'a Map<'tcx>) -> Self { + fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, map: &'a Map<'tcx>) -> Self { Self { patch: Patch::new(tcx), local_decls: &body.local_decls, diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs index 9b0e2a0663eca..7c41217f402ff 100644 --- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs +++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs @@ -63,6 +63,13 @@ fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool { loc, ); + // The logic in LivenessTransferFunction isn't quite what we need; it ignores call + // destinations that are just locals because they are killed by the call, which makes + // it eligible to be moved-from in the argument list. That's backwards. + if !destination.is_indirect() { + state.insert(destination.local); + } + for (index, arg) in args.iter().map(|a| &a.node).enumerate().rev() { if let Operand::Copy(place) = *arg && !place.is_indirect() diff --git a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs index 5bba125aefc58..8814670ca4300 100644 --- a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs +++ b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs @@ -135,7 +135,7 @@ impl<'tcx> Visitor<'tcx> for DeduceParamAttrs { } // Like a call, but more conservative because the backend may introduce writes to an - // argument if the argument is passed as `PassMode::Indirect { on_stack: false, ... }`. + // argument if the argument is passed as `PassMode::Indirect { mode: IndirectMode::Pointer, ... }`. TerminatorKind::TailCall { .. } => { for usage in self.usage.iter_mut() { *usage |= UsageSummary::MUTATE; diff --git a/compiler/rustc_mir_transform/src/diagnostics.rs b/compiler/rustc_mir_transform/src/diagnostics.rs index c851b55f9623a..6e083fef20928 100644 --- a/compiler/rustc_mir_transform/src/diagnostics.rs +++ b/compiler/rustc_mir_transform/src/diagnostics.rs @@ -97,8 +97,8 @@ pub(crate) enum AssertLintKind { UnconditionalPanic, } -impl<'a, P: std::fmt::Debug> Diagnostic<'a, ()> for AssertLint

    { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { +impl<'a, P: std::fmt::Debug> Diagnostic<'a> for AssertLint

    Allocator for core::pin::Pin

    +where + P: [const] core::ops::Deref + core::pin::PinSafePointer, +{ + #[inline] + fn allocate(&self, layout: Layout) -> Result, AllocError> { + (**self).allocate(layout) + } + + #[inline] + fn allocate_zeroed(&self, layout: Layout) -> Result, AllocError> { + (**self).allocate_zeroed(layout) + } + + #[inline] + unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { + // SAFETY: the safety contract must be upheld by the caller + unsafe { (**self).deallocate(ptr, layout) } + } + + #[inline] + unsafe fn grow( + &self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + ) -> Result, AllocError> { + // SAFETY: the safety contract must be upheld by the caller + unsafe { (**self).grow(ptr, old_layout, new_layout) } + } + + #[inline] + unsafe fn grow_zeroed( + &self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + ) -> Result, AllocError> { + // SAFETY: the safety contract must be upheld by the caller + unsafe { (**self).grow_zeroed(ptr, old_layout, new_layout) } + } + + #[inline] + unsafe fn shrink( + &self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + ) -> Result, AllocError> { + // SAFETY: the safety contract must be upheld by the caller + unsafe { (**self).shrink(ptr, old_layout, new_layout) } + } +} + +#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] +#[unstable_feature_bound(allocator_ext)] +impl

    AllocatorNightly for core::pin::Pin

    where + P: core::ops::Deref + core::pin::PinSafePointer +{ +} + +#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] unsafe impl AllocatorClone for &A {} // If an allocator is `StaticAllocator` all equivalent allocators must also uphold // its semantics, and references are equivalent to the allocator they reference. -#[unstable(feature = "allocator_api", issue = "32838")] +#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] unsafe impl StaticAllocator for &A {} -#[unstable(feature = "allocator_api", issue = "32838")] +#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] unsafe impl StaticAllocator for &mut A {} + +/// Allocator marker trait that is only implemented stably for `Global`. +/// +/// This trait is used as a bound whenever a function constructing +/// a type with an `#[unstable] A: Allocator = Global` parameter +/// may be callable for `A != Global`. +/// +/// This is to prevent stable code from e.g. constructing `Arc` +/// using the `From> for Arc` impl. +/// +/// Note that this trait *will* be removed in the future, but downstream +/// allocators should still feel free to implement it on nightly, to +/// allow use of all containers with unstable allocator support. +#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] +pub trait AllocatorNightly: Allocator {} + +#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] +#[unstable_feature_bound(allocator_ext)] +impl AllocatorNightly for &A {} + +#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] +#[unstable_feature_bound(allocator_ext)] +impl AllocatorNightly for &mut A {} diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 8c049b118d703..c6cb5b0f50c56 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -86,7 +86,8 @@ #![stable(feature = "rust1", since = "1.0.0")] -use crate::intrinsics::{self, type_id, type_id_vtable}; +use crate::intrinsics::reflection::{type_id, type_id_vtable}; +use crate::intrinsics::{self}; use crate::mem::transmute; use crate::mem::type_info::{TraitImpl, TypeKind}; use crate::{fmt, hash, ptr}; @@ -742,7 +743,7 @@ unsafe impl Sync for TypeId {} const impl PartialEq for TypeId { #[inline] fn eq(&self, other: &Self) -> bool { - crate::intrinsics::type_id_eq(*self, *other) + crate::intrinsics::reflection::type_id_eq(*self, *other) } } @@ -765,7 +766,7 @@ impl TypeId { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_type_id", since = "1.91.0")] pub const fn of() -> TypeId { - const { intrinsics::type_id::() } + const { intrinsics::reflection::type_id::() } } /// Checks if the [TypeId] implements the trait. If it does it returns [TraitImpl] which can be used to build a fat pointer. @@ -903,7 +904,7 @@ impl fmt::Debug for TypeId { #[stable(feature = "type_name", since = "1.38.0")] #[rustc_const_unstable(feature = "const_type_name", issue = "63084")] pub const fn type_name() -> &'static str { - const { intrinsics::type_name::() } + const { intrinsics::reflection::type_name::() } } /// Returns the type name of the pointed-to value as a string slice. diff --git a/library/core/src/arch.rs b/library/core/src/arch.rs index 737a643ef8659..04fc16f50085b 100644 --- a/library/core/src/arch.rs +++ b/library/core/src/arch.rs @@ -65,11 +65,15 @@ pub macro global_asm("assembly template", $(operands,)* $(options($(option),*))? /// to a trapping instruction (e.g. an undefined instruction) instead, or to some other form of /// target-specific abort that may or may not support convenient resumption. /// -/// The precise behavior and the precise instruction generated are not guaranteed, except that in -/// normal execution with no debug tooling involved this will not continue executing. +/// The precise behavior is not guaranteed, it depends on the architecture and operating system. +/// Not all architectures guarantee that a breakpoint instruction interrupts execution in the absence +/// of a debugger, and not all operating systems and execution environments guarantee that such an +/// interrupt aborts the current process. /// +/// The precise instruction is guaranteed only on the following targets: /// - On x86 targets, this produces an `int3` instruction. /// - On aarch64 targets, this produces a `brk #0xf000` instruction. +// When adding more items above, also add cases to the test in `tests/assembly-llvm/breakpoint.rs`. // When stabilizing this, update the comment on `core::intrinsics::breakpoint`. #[unstable(feature = "breakpoint", issue = "133724")] #[inline(always)] diff --git a/library/core/src/attribute_docs.rs b/library/core/src/attribute_docs.rs index 584e1583c9bdb..a397cd436b13a 100644 --- a/library/core/src/attribute_docs.rs +++ b/library/core/src/attribute_docs.rs @@ -189,7 +189,7 @@ const _: () = (); /// /// For more information, see the Reference on [the `cfg` attribute]. /// -/// [`cfg_attr`]: ../reference/conditional-compilation.html#the-cfg_attr-attribute +/// [`cfg_attr`]: ./attribute.cfg_attr.html /// [the `cfg` attribute]: ../reference/conditional-compilation.html#the-cfg-attribute /// [`if`]: ./keyword.if.html const _: () = (); @@ -406,6 +406,104 @@ const _: () = (); /// [the `no_std` attribute]: ../reference/names/preludes.html#the-no_std-attribute const _: () = (); +#[doc(attribute = "no_main")] +/// Prevents emitting the `main` entrypoint symbol. +/// +/// Written as an inner attribute at the top of the crate root, the `no_main` attribute prevents +/// the compiler from generating the program's standard Rust entrypoint, leaving that up to you. +/// This is useful when some other object being linked into the program defines the entrypoint, +/// or when you want to define the platform's `main` symbol yourself. +/// +/// If you define an entry point symbol that is suitable for your target, it must also be exported +/// under an unmangled name using the [`no_mangle` attribute]: +/// +/// ```no_run +/// #![no_main] +/// +/// use std::ffi::{c_char, c_int}; +/// +/// #[unsafe(no_mangle)] +/// pub extern "C" fn main(_argc: c_int, _argv: *const *const c_char) -> c_int { +/// println!("Hello world!"); +/// 0 +/// } +/// ``` +/// +/// Unlike `no_std`, the `no_main` attribute does not prevent the compiler from generating +/// the startup routine or linking the standard library provided by the operating system's executable [ABI]. +/// +/// [ABI]: https://en.wikipedia.org/wiki/Application_binary_interface +/// [`no_mangle` attribute]: ../reference/abi.html#the-no_mangle-attribute +const _: () = (); + +#[doc(attribute = "repr")] +/// Specifies the [representation] of a type. +/// +/// Written as an outer attribute on a `struct`, `enum` or `union`, `repr` controls how the +/// type is laid out in memory. This is needed when passing types across an [FFI] boundary +/// or whenever you rely on a specific layout. +/// +/// The available representations are: +/// +/// * [`Rust`] (the default): the layout is unspecified and may change between compilations. +/// * [`C`]: the layout follows the rules of the C language for the target platform. +/// * The [primitive representations] (`u8`, `i32`, `usize`, ...): set the discriminant +/// type of an enum. +/// * [`transparent`]: the type has the same layout and ABI as its only non-zero-sized field. +/// +/// The `align(N)` and `packed(N)` modifiers raise or lower the alignment of a type, respectively. +/// `N` must be a power of two no greater than 229. The two modifiers cannot be +/// applied to the same type, and references to fields of a `packed` type cannot be created, +/// since they may be unaligned. +/// +/// # Examples +/// +/// ``` +/// #[repr(C, align(16))] +/// struct Foo { +/// first: u32, +/// second: u32, +/// third: u32, +/// } +/// +/// assert_eq!(align_of::(), 16); +/// // 12 bytes of fields, rounded up to a multiple of the alignment. +/// assert_eq!(size_of::(), 16); +/// ``` +/// +/// ``` +/// #[repr(C, packed(2))] +/// struct Foo { +/// first: u32, +/// second: u32, +/// third: u32, +/// } +/// +/// assert_eq!(align_of::(), 2); +/// assert_eq!(size_of::(), 12); +/// ``` +/// +/// ``` +/// #[repr(C)] +/// struct Foo { +/// item: T, +/// } +/// ``` +/// +/// `Foo` is laid out according to the C rules and is guaranteed to be 16-byte aligned. +/// +/// The representation is a property of the type definition, so it does +/// parameters: below, `Foo` and `Foo` both use the `C` representation. Their actual +/// layouts still differ, because they depend on `T`. +/// +/// [representation]: ../reference/type-layout.html#representations +/// [`Rust`]: ../reference/type-layout.html#the-rust-representation +/// [`C`]: ../reference/type-layout.html#the-c-representation +/// [primitive representations]: ../reference/type-layout.html#primitive-representations +/// [`transparent`]: ../reference/type-layout.html#the-transparent-representation +/// [FFI]: https://en.wikipedia.org/wiki/Foreign_function_interface +const _: () = (); + #[doc(attribute = "inline")] // /// Suggest that the compiler inline a function at its call sites. @@ -633,3 +731,122 @@ const _: () = (); /// /// [the `non_exhaustive` attribute]: ../reference/attributes/type_system.html#the-non_exhaustive-attribute const _: () = (); + +#[doc(attribute = "automatically_derived")] +// +/// The `automatically_derived` attribute is used to indicate that an `impl` block is automatically +/// generated by a [derive macro]. It has no direct effect, but it may be used by tools and +/// diagnostic lints, such as [Clippy], to detect these automatically generated implementations. +/// +/// Example: +/// +/// ```rust +/// // Given #[derive(Clone)] on struct Example, the derive macro may produce: +/// #[automatically_derived] +/// impl ::core::clone::Clone for Example { +/// fn clone(&self) -> Self { Example } +/// } +/// # struct Example; +/// ``` +/// +/// The attribute may only be applied to an `impl` block. +/// +/// Because code generated by a macro becomes part of the source code representation of the crate +/// that calls the macro, diagnostic lints for the expanded macro will be emitted in the caller's +/// workspace (whereas warnings generated while building dependencies not in the local workspace +/// are suppressed by Cargo). This may be undesirable, so the author of the macro can use the +/// `automatically_derived` attribute to hint to tools that the generated code is not written by the +/// caller (and that tools should not render warnings for it). +/// +/// For example, this `impl` block lacks `#[automatically_derived]`: +/// +/// ```rust +/// // Suppose this impl block is the output of a derive macro +/// impl ::core::cmp::PartialEq for Example { +/// fn eq(&self, other: &Self) -> bool { true } +/// fn ne(&self, other: &Self) -> bool { !(self == other) } +/// } +/// # struct Example; +/// ``` +/// +/// In the example above, Clippy will emit the following warning: +/// +/// ```text +/// warning: re-implementing `PartialEq::ne` is unnecessary +/// --> src/lib.rs:7:9 +/// | +/// 4 | fn ne(&self, other: &Self) -> bool { !(self == other) } +/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +/// | +/// = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#partialeq_ne_impl +/// = note: `#[warn(clippy::partialeq_ne_impl)]` on by default +/// ``` +/// +/// However, if the derive macro were written so that it outputs `#[automatically_derived]` on the +/// `impl` block, as below, then Clippy will not emit the above `partialeq_ne_impl` lint: +/// +/// ```rust +/// // Suppose this impl block is the output of a derive macro +/// #[automatically_derived] +/// impl ::core::cmp::PartialEq for Example { +/// fn eq(&self, other: &Self) -> bool { true } +/// fn ne(&self, other: &Self) -> bool { !(self == other) } +/// } +/// # struct Example; +/// ``` +/// +/// The `automatically_derived` attribute should be preferred over the [`allow`] attribute for +/// suppressing unintended user-facing diagnostics in automatically generated code. +/// +/// For more information, see the Reference on [the `automatically_derived` attribute]. +/// +/// [`allow`]: ./attribute.allow.html +/// [derive macro]: ./attr.derive.html +/// [Clippy]: https://github.com/rust-lang/rust-clippy +/// [the `automatically_derived` attribute]: ../reference/attributes/derive.html#the-automatically_derived-attribute +const _: () = (); + +#[doc(attribute = "cfg_attr")] +// +/// The `cfg_attr` attribute is used to conditionally apply one or more attributes to an item. +/// +/// Example: +/// +/// ```rust +/// // The struct derives `Debug` when the `debug_impls` feature is enabled. +/// #[cfg_attr(feature = "debug_impls", derive(Debug))] +/// struct X; +/// ``` +/// +/// You can apply multiple attributes by separating them with commas: +/// +/// ```rust +/// #[cfg_attr(feature = "nightly", allow(dead_code), deny(unused_variables))] +/// // This function only gets the `allow(dead_code)` and `deny(unused_variables)` attributes when +/// // `feature = "nightly"` is active. +/// fn nightly_only_function() { +/// let x = 42; +/// } +/// ``` +/// +/// For complex conditions, you can combine `all(...)`, `any(...)`, and `not(...)`. +/// +/// * `all`: True if all given predicates are true. +/// * `any`: True if at least one of the given predicates is true. +/// * `not`: True if the predicate is false. +/// +/// ```rust +/// #[cfg_attr( +/// all(feature = "system", feature = "disk"), +/// doc = "These docs only show up if both `system`, and `disk` are enabled.", +/// )] +/// mod my_module { +/// // ... +/// } +/// ``` +/// +/// For more information, see the Reference on [the `cfg_attr` attribute]. +/// +/// [`cfg`]: ./attribute.cfg.html +/// [the `cfg_attr` attribute]: ../reference/conditional-compilation.html#the-cfg_attr-attribute +const _: () = (); diff --git a/library/core/src/bool.rs b/library/core/src/bool.rs index b3d7686b6336e..e4ca53b59323f 100644 --- a/library/core/src/bool.rs +++ b/library/core/src/bool.rs @@ -151,8 +151,8 @@ impl bool { /// /// [`true`]: ../std/keyword.true.html /// [`false`]: ../std/keyword.false.html - #[stable(feature = "bool_toggle", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "bool_toggle", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "bool_toggle", since = "1.100.0")] + #[rustc_const_stable(feature = "bool_toggle", since = "1.100.0")] #[inline] pub const fn toggle(&mut self) { *self = !*self; diff --git a/library/core/src/bstr/traits.rs b/library/core/src/bstr/traits.rs index 1d8d0e29e9a5a..6e9d836f1a1ff 100644 --- a/library/core/src/bstr/traits.rs +++ b/library/core/src/bstr/traits.rs @@ -6,7 +6,8 @@ use crate::slice::SliceIndex; use crate::{hash, ops, range}; #[unstable(feature = "bstr", issue = "134915")] -impl Ord for ByteStr { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +const impl Ord for ByteStr { #[inline] fn cmp(&self, other: &ByteStr) -> Ordering { Ord::cmp(&self.0, &other.0) @@ -14,7 +15,8 @@ impl Ord for ByteStr { } #[unstable(feature = "bstr", issue = "134915")] -impl PartialOrd for ByteStr { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +const impl PartialOrd for ByteStr { #[inline] fn partial_cmp(&self, other: &ByteStr) -> Option { PartialOrd::partial_cmp(&self.0, &other.0) @@ -22,7 +24,8 @@ impl PartialOrd for ByteStr { } #[unstable(feature = "bstr", issue = "134915")] -impl PartialEq for ByteStr { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +const impl PartialEq for ByteStr { #[inline] fn eq(&self, other: &ByteStr) -> bool { self.0 == other.0 @@ -30,7 +33,8 @@ impl PartialEq for ByteStr { } #[unstable(feature = "bstr", issue = "134915")] -impl Eq for ByteStr {} +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +const impl Eq for ByteStr {} #[unstable(feature = "bstr", issue = "134915")] impl hash::Hash for ByteStr { diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 47312b2bd5649..3dcf033bbd757 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -2376,8 +2376,8 @@ impl UnsafeCell { /// assert_eq!(old, 5); /// ``` #[inline] - #[stable(feature = "unsafe_cell_access", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "unsafe_cell_access", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "unsafe_cell_access", since = "1.100.0")] + #[rustc_const_stable(feature = "unsafe_cell_access", since = "1.100.0")] #[rustc_should_not_be_called_on_const_items] pub const unsafe fn replace(&self, value: T) -> T { // SAFETY: pointer comes from `&self` so naturally satisfies invariants. @@ -2518,8 +2518,8 @@ impl UnsafeCell { /// assert_eq!(val, &5); /// ``` #[inline] - #[stable(feature = "unsafe_cell_access", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "unsafe_cell_access", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "unsafe_cell_access", since = "1.100.0")] + #[rustc_const_stable(feature = "unsafe_cell_access", since = "1.100.0")] #[rustc_should_not_be_called_on_const_items] pub const unsafe fn as_ref_unchecked(&self) -> &T { // SAFETY: pointer comes from `&self` so naturally satisfies ptr-to-ref invariants. @@ -2546,8 +2546,8 @@ impl UnsafeCell { /// assert_eq!(uc.into_inner(), 6); /// ``` #[inline] - #[stable(feature = "unsafe_cell_access", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "unsafe_cell_access", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "unsafe_cell_access", since = "1.100.0")] + #[rustc_const_stable(feature = "unsafe_cell_access", since = "1.100.0")] #[allow(clippy::mut_from_ref)] #[rustc_should_not_be_called_on_const_items] pub const unsafe fn as_mut_unchecked(&self) -> &mut T { diff --git a/library/core/src/cell/covariant_unsafe_cell.rs b/library/core/src/cell/covariant_unsafe_cell.rs index 3876cc404cd2e..11ac9b58bbb90 100644 --- a/library/core/src/cell/covariant_unsafe_cell.rs +++ b/library/core/src/cell/covariant_unsafe_cell.rs @@ -170,14 +170,3 @@ impl fmt::Debug for CovariantUnsafeCell { f.debug_struct("CovariantUnsafeCell").finish_non_exhaustive() } } - -#[cfg(test)] -mod tests { - use super::*; - - fn _covarience<'short, 'long: 'short>( - x: CovariantUnsafeCell<&'long ()>, - ) -> CovariantUnsafeCell<&'short ()> { - x - } -} diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index 126ab37bc0b71..bd6d929c6d2b0 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -732,7 +732,7 @@ mod impls { bool char } - #[stable(feature = "never_type", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "never_type", since = "1.100.0")] #[rustc_const_unstable(feature = "const_clone", issue = "142757")] const impl Clone for ! { #[inline] diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index a7c22a195e5d2..1b57bed2738f7 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -1967,24 +1967,19 @@ macro impl_tuples($($mac:ident,)+) { } /// Implementation detail for [`smallest`] and [`largest`]. -/// Marker indicating that `Self` is a tuple where all members are of the same type. +/// Marker indicating that `Self` is a tuple where all members are of the type `T`. +/// Cannot be an associated type as we require the empty tuple to implement this trait +/// for all types `T`. #[diagnostic::on_unimplemented(message = "`{Self}` is not a homogeneous tuple")] #[unstable(feature = "cmp_splat_internals", issue = "160728")] #[rustc_const_unstable(feature = "cmp_splat_internals", issue = "160728")] -const trait HomogeneousTuple: crate::marker::Tuple { - /// The type of each item in this tuple. - type Item; -} +const trait HomogeneousTuple: crate::marker::Tuple {} /// Implements [`HomogeneousTuple`] for a provided tuple. -macro impl_homogeneous_tuple($($($x:ident,)+)?) { - $( - #[unstable(feature = "cmp_splat_internals", issue = "160728")] - #[rustc_const_unstable(feature = "cmp_splat_internals", issue = "160728")] - const impl HomogeneousTuple for ($(${ignore($x)}T,)+) { - type Item = T; - } - )? +macro impl_homogeneous_tuple($($x:ident,)*) { + #[unstable(feature = "cmp_splat_internals", issue = "160728")] + #[rustc_const_unstable(feature = "cmp_splat_internals", issue = "160728")] + const impl HomogeneousTuple for ($(${ignore($x)}T,)*) { } } impl_tuples! { @@ -2040,36 +2035,34 @@ impl_tuples! { #[expect(private_bounds, reason = "`SmallestArgs` is an internal implementation detail")] #[cfg(not(test))] // FIXME: splat interacts poorly with the double linking of `core` in tests pub const fn smallest( - #[rustc_splat] args: impl [const] SmallestArgs, + v1: T, + #[rustc_splat] args: impl [const] SmallestArgs, ) -> T { - SmallestArgs::smallest(args) + SmallestArgs::smallest(v1, args) } /// Implementation detail for [`smallest`]. #[diagnostic::on_unimplemented(message = "`{Self}` is not a valid set of arguments for `smallest`")] #[unstable(feature = "cmp_splat_internals", issue = "160728")] #[rustc_const_unstable(feature = "cmp_splat_internals", issue = "160728")] -const trait SmallestArgs: HomogeneousTuple { +const trait SmallestArgs: HomogeneousTuple { /// Reduces all elements of a homogeneous tuple to its smallest value. - fn smallest(self) -> Self::Item; + fn smallest(v1: T, args: Self) -> T; } /// Implements [`SmallestArgs`] for a provided tuple if applicable. -macro impl_smallest_args($($x:ident, $($($y:ident,)+)?)?) { - $( - #[unstable(feature = "cmp_splat_internals", issue = "160728")] - #[rustc_const_unstable(feature = "cmp_splat_internals", issue = "160728")] - const impl SmallestArgs for (T, $($(${ignore($y)}T,)+)?) - $(where T: [const] Destruct + [const] Ord, $(${ignore($y)})+)? - { - #[inline] - fn smallest(self) -> Self::Item { - let ($x, $($($y,)+)?) = self; - $($(let $x = $x.min($y);)+)? - $x - } +macro impl_smallest_args($($x:ident,)*) { + #[unstable(feature = "cmp_splat_internals", issue = "160728")] + #[rustc_const_unstable(feature = "cmp_splat_internals", issue = "160728")] + const impl SmallestArgs for ($(${ignore($x)}T,)*) + where + T: [const] Destruct + [const] Ord, + { + #[inline(always)] // improves unoptimised codegen + fn smallest(v1: T, ($($x,)*): Self) -> T { + v1$(.min($x))* } - )? + } } impl_tuples! { @@ -2125,36 +2118,34 @@ impl_tuples! { #[expect(private_bounds, reason = "`LargestArgs` is an internal implementation detail")] #[cfg(not(test))] // FIXME: splat interacts poorly with the double linking of `core` in tests pub const fn largest( - #[rustc_splat] args: impl [const] LargestArgs, + v1: T, + #[rustc_splat] args: impl [const] LargestArgs, ) -> T { - LargestArgs::largest(args) + LargestArgs::largest(v1, args) } /// Implementation detail for [`largest`]. #[diagnostic::on_unimplemented(message = "`{Self}` is not a valid set of arguments for `largest`")] #[unstable(feature = "cmp_splat_internals", issue = "160728")] #[rustc_const_unstable(feature = "cmp_splat_internals", issue = "160728")] -const trait LargestArgs: HomogeneousTuple { +const trait LargestArgs: HomogeneousTuple { /// Reduces all elements of a homogeneous tuple to its largest value. - fn largest(self) -> Self::Item; + fn largest(v1: T, args: Self) -> T; } /// Implements [`LargestArgs`] for a provided tuple if applicable. -macro impl_largest_args($($x:ident, $($($y:ident,)+)?)?) { - $( - #[unstable(feature = "cmp_splat_internals", issue = "160728")] - #[rustc_const_unstable(feature = "cmp_splat_internals", issue = "160728")] - const impl LargestArgs for (T, $($(${ignore($y)}T,)+)?) - $(where T: [const] Destruct + [const] Ord, $(${ignore($y)})+)? - { - #[inline] - fn largest(self) -> Self::Item { - let ($x, $($($y,)+)?) = self; - $($(let $x = $x.max($y);)+)? - $x - } +macro impl_largest_args($($x:ident,)*) { + #[unstable(feature = "cmp_splat_internals", issue = "160728")] + #[rustc_const_unstable(feature = "cmp_splat_internals", issue = "160728")] + const impl LargestArgs for ($(${ignore($x)}T,)*) + where + T: [const] Destruct + [const] Ord, + { + #[inline(always)] // improves unoptimised codegen + fn largest(v1: T, ($($x,)*): Self) -> T { + v1$(.max($x))* } - )? + } } impl_tuples! { @@ -2418,7 +2409,7 @@ mod impls { ord_impl! { char usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } - #[stable(feature = "never_type", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "never_type", since = "1.100.0")] #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] const impl PartialEq for ! { #[inline] @@ -2427,11 +2418,11 @@ mod impls { } } - #[stable(feature = "never_type", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "never_type", since = "1.100.0")] #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] const impl Eq for ! {} - #[stable(feature = "never_type", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "never_type", since = "1.100.0")] #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] const impl PartialOrd for ! { #[inline] @@ -2440,7 +2431,7 @@ mod impls { } } - #[stable(feature = "never_type", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "never_type", since = "1.100.0")] #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] const impl Ord for ! { #[inline] diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs index a57f3d58e3998..b0f74a8afb598 100644 --- a/library/core/src/convert/mod.rs +++ b/library/core/src/convert/mod.rs @@ -886,7 +886,8 @@ const impl AsMut for str { /// } /// ``` /// -/// Note: since CURRENT_RUSTC_VERSION this is an alias to `!`. If targeting that or future versions, -/// prefer using the never type directly. +/// Note: Since [`!`] (the "never" type) was stabilized in Rust 1.100.0, it is +/// preferred to use `!` directly unless targeting older Rust versions. `Infallible` may be +/// deprecated in a future Rust version. #[stable(feature = "convert_infallible", since = "1.34.0")] pub type Infallible = !; diff --git a/library/core/src/error.rs b/library/core/src/error.rs index 7386c52849eab..1b640f2d99803 100644 --- a/library/core/src/error.rs +++ b/library/core/src/error.rs @@ -268,7 +268,7 @@ mod private { pub struct Internal; } -#[stable(feature = "never_type", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "never_type", since = "1.100.0")] impl Error for ! {} // Copied from `any.rs`. diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index c851b56c20bc8..daa68e885d893 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -89,7 +89,8 @@ use crate::{fmt, ops, range, slice, str}; /// ``` /// /// [str]: prim@str "str" -#[derive(PartialEq, Eq, Hash)] +#[derive(Hash)] +#[derive_const(PartialEq, Eq)] #[stable(feature = "core_c_str", since = "1.64.0")] #[rustc_diagnostic_item = "cstr_type"] #[rustc_has_incoherent_inherent_impls] @@ -519,7 +520,8 @@ impl CStr { #[stable(feature = "cstr_count_bytes", since = "1.79.0")] #[rustc_const_stable(feature = "const_cstr_from_ptr", since = "1.81.0")] pub const fn count_bytes(&self) -> usize { - self.inner.len() - 1 + // SAFETY: This length includes the nul-terminator, so it's at least one. + unsafe { self.inner.len().unchecked_sub(1) } } /// Returns `true` if `self.to_bytes()` has a length of 0. @@ -648,17 +650,15 @@ impl CStr { /// # Examples /// /// ``` - /// #![feature(cstr_display)] - /// /// let cstr = c"Hello, world!"; /// println!("{}", cstr.display()); /// ``` - #[unstable(feature = "cstr_display", issue = "139984")] + #[stable(feature = "cstr_display", since = "CURRENT_RUSTC_VERSION")] #[must_use = "this does not display the `CStr`; \ it returns an object that can be displayed"] #[inline] - pub fn display(&self) -> impl fmt::Display { - crate::bstr::ByteStr::from_bytes(self.to_bytes()) + pub fn display(&self) -> Display<'_> { + Display { c_str: self } } /// Returns the same string as a string slice `&CStr`. @@ -690,7 +690,8 @@ impl PartialEq<&Self> for CStr { // because `c_char` is `i8` (not `u8`) on some platforms. // That is why this is implemented manually and not derived. #[stable(feature = "rust1", since = "1.0.0")] -impl PartialOrd for CStr { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +const impl PartialOrd for CStr { #[inline] fn partial_cmp(&self, other: &CStr) -> Option { self.to_bytes().partial_cmp(other.to_bytes()) @@ -698,7 +699,8 @@ impl PartialOrd for CStr { } #[stable(feature = "rust1", since = "1.0.0")] -impl Ord for CStr { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +const impl Ord for CStr { #[inline] fn cmp(&self, other: &CStr) -> Ordering { self.to_bytes().cmp(other.to_bytes()) @@ -854,3 +856,39 @@ impl Iterator for Bytes<'_> { #[unstable(feature = "cstr_bytes", issue = "112115")] impl FusedIterator for Bytes<'_> {} + +/// Helper struct for safely printing a [`CStr`] with [`format!`] and `{}`. +/// +/// A [`CStr`] might contain non-Unicode data. This `struct` implements the +/// [`Display`] trait in a way that mitigates that. It is created by the +/// [`display`](CStr::display) method on [`CStr`]. This may perform lossy +/// conversion, depending on the platform. If you would like an implementation +/// which escapes the [`CStr`] please use [`Debug`] instead. +/// +/// # Examples +/// +/// ``` +/// let s = c"Hello, world!"; +/// println!("{}", s.display()); +/// ``` +/// +/// [`Display`]: fmt::Display +/// [`format!`]: ../../../std/macro.format.html +#[stable(feature = "cstr_display", since = "CURRENT_RUSTC_VERSION")] +pub struct Display<'a> { + c_str: &'a CStr, +} + +#[stable(feature = "cstr_display", since = "CURRENT_RUSTC_VERSION")] +impl fmt::Debug for Display<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.c_str, f) + } +} + +#[stable(feature = "cstr_display", since = "CURRENT_RUSTC_VERSION")] +impl fmt::Display for Display<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(crate::bstr::ByteStr::from_bytes(self.c_str.to_bytes()), f) + } +} diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs index 21f8e264db953..b308cb7f85df2 100644 --- a/library/core/src/ffi/va_list.rs +++ b/library/core/src/ffi/va_list.rs @@ -416,6 +416,55 @@ cfg_select! { #[unstable(feature = "c_variadic_va_arg_safe", issue = "162911", implied_by = "c_variadic")] unsafe impl VaArgSafe for f64 {} +// Implement `VaArgSafe` for f128 on targets where either: +// +// - clang provides `__float128` +// - `long double` is IEEE f128 on the platform. +// +// When updating this cfg, also update the tests to match. Currently this condition +// is duplicated in: +// +// - tests/ui/c-variadic/roundtrip.rs +// - tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs +// +// # Known incompatibilities +// +// Testing versus clang exposed bugs in clang. GCC has no known incompatibilities. +// +// - Clang <= 23 on sparc, see https://github.com/llvm/llvm-project/pull/214981. +// - Clang <= 23 on x86, see https://github.com/llvm/llvm-project/issues/217747. +cfg_select! { + any( + all(target_arch = "x86_64", not(target_vendor = "apple"), not(target_env = "msvc")), + all(target_arch = "x86", not(target_vendor = "apple"), not(target_env = "msvc")), + // PowerPC requires VSX (only little endian has it enabled by default). + all(target_arch = "powerpc64", target_feature = "vsx"), + all( + not(windows), + not(target_vendor = "apple"), + any( + target_arch = "aarch64", + target_arch = "loongarch32", + target_arch = "loongarch64", + target_arch = "mips64", + target_arch = "mips64r6", + target_arch = "riscv32", + target_arch = "riscv64", + target_arch = "s390x", + target_arch = "sparc", + target_arch = "sparc64", + target_arch = "wasm32", + target_arch = "wasm64", + ), + ), + ) => { + #[unstable_feature_bound(f128)] + #[unstable(feature = "f128", issue = "116909")] + unsafe impl VaArgSafe for f128 {} + } + _ => { /* unsupported */ } +} + #[unstable(feature = "c_variadic_va_arg_safe", issue = "162911", implied_by = "c_variadic")] unsafe impl VaArgSafe for *mut T {} #[unstable(feature = "c_variadic_va_arg_safe", issue = "162911", implied_by = "c_variadic")] diff --git a/library/core/src/fmt/builders.rs b/library/core/src/fmt/builders.rs index 6a44f9ef5374e..20ce752dd7c41 100644 --- a/library/core/src/fmt/builders.rs +++ b/library/core/src/fmt/builders.rs @@ -187,8 +187,6 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> { /// # Examples /// /// ``` - /// #![feature(debug_closure_helpers)] - /// /// use std::fmt; /// /// struct Bar { @@ -211,11 +209,12 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> { /// r#"Bar { bar: 0x0000000a, another: "Hello World" }"#, /// ); /// ``` - #[unstable(feature = "debug_closure_helpers", issue = "117729")] - pub fn field_with(&mut self, name: &str, value_fmt: F) -> &mut Self - where - F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, - { + #[stable(feature = "debug_closure_helpers", since = "CURRENT_RUSTC_VERSION")] + pub fn field_with( + &mut self, + name: &str, + value_fmt: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, + ) -> &mut Self { self.field(name, &DebugOnce(Cell::new(Some(value_fmt)))) } @@ -408,8 +407,6 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> { /// # Examples /// /// ``` - /// #![feature(debug_closure_helpers)] - /// /// use std::fmt; /// /// struct Foo(i32, String); @@ -429,11 +426,11 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> { /// r#"Foo(0x0000000a, "Hello World")"#, /// ); /// ``` - #[unstable(feature = "debug_closure_helpers", issue = "117729")] - pub fn field_with(&mut self, value_fmt: F) -> &mut Self - where - F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, - { + #[stable(feature = "debug_closure_helpers", since = "CURRENT_RUSTC_VERSION")] + pub fn field_with( + &mut self, + value_fmt: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, + ) -> &mut Self { self.field(&DebugOnce(Cell::new(Some(value_fmt)))) } @@ -552,10 +549,7 @@ impl<'a, 'b: 'a> DebugInner<'a, 'b> { self.has_fields = true; } - fn entry_with(&mut self, entry_fmt: F) - where - F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, - { + fn entry_with(&mut self, entry_fmt: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result) { self.entry(&DebugOnce(Cell::new(Some(entry_fmt)))); } @@ -639,8 +633,6 @@ impl<'a, 'b: 'a> DebugSet<'a, 'b> { /// # Examples /// /// ``` - /// #![feature(debug_closure_helpers)] - /// /// use std::fmt; /// /// struct Foo(Vec, Vec); @@ -660,11 +652,11 @@ impl<'a, 'b: 'a> DebugSet<'a, 'b> { /// "{[10, 11], {12, 13}}", /// ); /// ``` - #[unstable(feature = "debug_closure_helpers", issue = "117729")] - pub fn entry_with(&mut self, entry_fmt: F) -> &mut Self - where - F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, - { + #[stable(feature = "debug_closure_helpers", since = "CURRENT_RUSTC_VERSION")] + pub fn entry_with( + &mut self, + entry_fmt: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, + ) -> &mut Self { self.inner.entry_with(entry_fmt); self } @@ -856,8 +848,6 @@ impl<'a, 'b: 'a> DebugList<'a, 'b> { /// # Examples /// /// ``` - /// #![feature(debug_closure_helpers)] - /// /// use std::fmt; /// /// struct Foo(Vec, Vec); @@ -877,11 +867,11 @@ impl<'a, 'b: 'a> DebugList<'a, 'b> { /// "[[10, 11], {12, 13}]", /// ); /// ``` - #[unstable(feature = "debug_closure_helpers", issue = "117729")] - pub fn entry_with(&mut self, entry_fmt: F) -> &mut Self - where - F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, - { + #[stable(feature = "debug_closure_helpers", since = "CURRENT_RUSTC_VERSION")] + pub fn entry_with( + &mut self, + entry_fmt: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, + ) -> &mut Self { self.inner.entry_with(entry_fmt); self } @@ -1075,8 +1065,9 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// /// # Panics /// - /// `key` must be called before `value` and each call to `key` must be followed - /// by a corresponding call to `value`. Otherwise this method will panic. + /// `key` or `key_with` must be called before `value` or `value_with`, and each + /// `key` call must be followed by a corresponding `value` call. Otherwise this + /// method will panic. /// /// # Examples /// @@ -1136,11 +1127,15 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// This method is equivalent to [`DebugMap::key`], but formats the /// key using a provided closure rather than by calling [`Debug::fmt`]. /// + /// # Panics + /// + /// `key` or `key_with` must be called before `value` or `value_with`, and each + /// `key` call must be followed by a corresponding `value` call. Otherwise this + /// method will panic. + /// /// # Examples /// /// ``` - /// #![feature(debug_closure_helpers)] - /// /// use std::fmt; /// /// struct Foo(Vec<(String, i32)>); @@ -1163,11 +1158,11 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// r#"{entry A: 0x0000000a, entry B: 0x0000000b}"#, /// ); /// ``` - #[unstable(feature = "debug_closure_helpers", issue = "117729")] - pub fn key_with(&mut self, key_fmt: F) -> &mut Self - where - F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, - { + #[stable(feature = "debug_closure_helpers", since = "CURRENT_RUSTC_VERSION")] + pub fn key_with( + &mut self, + key_fmt: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, + ) -> &mut Self { self.key(&DebugOnce(Cell::new(Some(key_fmt)))) } @@ -1179,8 +1174,9 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// /// # Panics /// - /// `key` must be called before `value` and each call to `key` must be followed - /// by a corresponding call to `value`. Otherwise this method will panic. + /// `key` or `key_with` must be called before `value` or `value_with`, and each + /// `key` call must be followed by a corresponding `value` call. Otherwise this + /// method will panic. /// /// # Examples /// @@ -1229,11 +1225,15 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// This method is equivalent to [`DebugMap::value`], but formats the /// value using a provided closure rather than by calling [`Debug::fmt`]. /// + /// # Panics + /// + /// `key` or `key_with` must be called before `value` or `value_with`, and each + /// `key` call must be followed by a corresponding `value` call. Otherwise this + /// method will panic. + /// /// # Examples /// /// ``` - /// #![feature(debug_closure_helpers)] - /// /// use std::fmt; /// /// struct Foo(Vec<(String, i32)>); @@ -1256,11 +1256,11 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// r#"{entry A: 0x0000000a, entry B: 0x0000000b}"#, /// ); /// ``` - #[unstable(feature = "debug_closure_helpers", issue = "117729")] - pub fn value_with(&mut self, value_fmt: F) -> &mut Self - where - F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, - { + #[stable(feature = "debug_closure_helpers", since = "CURRENT_RUSTC_VERSION")] + pub fn value_with( + &mut self, + value_fmt: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, + ) -> &mut Self { self.value(&DebugOnce(Cell::new(Some(value_fmt)))) } @@ -1359,8 +1359,9 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// /// # Panics /// - /// `key` must be called before `value` and each call to `key` must be followed - /// by a corresponding call to `value`. Otherwise this method will panic. + /// `key` or `key_with` must be called before `value` or `value_with`, and each + /// `key` call must be followed by a corresponding `value` call. Otherwise this + /// method will panic. /// /// # Examples /// diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 43dba18375992..1f1b581c5e546 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -1611,7 +1611,7 @@ pub trait UpperExp: PointeeSized { /// /// let mut output = String::new(); /// fmt::write(&mut output, format_args!("Hello {}!", "world")) -/// .expect("Writing to a `String` should not fail"); +/// .expect("writing to a `String` should not fail"); /// assert_eq!(output, "Hello world!"); /// ``` /// @@ -1622,7 +1622,7 @@ pub trait UpperExp: PointeeSized { /// /// let mut output = String::new(); /// write!(&mut output, "Hello {}!", "world") -/// .expect("Writing to a `String` should not fail"); +/// .expect("writing to a `String` should not fail"); /// assert_eq!(output, "Hello world!"); /// ``` /// @@ -2881,7 +2881,7 @@ macro_rules! fmt_refs { fmt_refs! { Debug, Display, Octal, Binary, LowerHex, UpperHex, LowerExp, UpperExp } -#[stable(feature = "never_type", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "never_type", since = "1.100.0")] impl Debug for ! { #[inline] fn fmt(&self, _: &mut Formatter<'_>) -> Result { @@ -2889,7 +2889,7 @@ impl Debug for ! { } } -#[stable(feature = "never_type", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "never_type", since = "1.100.0")] impl Display for ! { #[inline] fn fmt(&self, _: &mut Formatter<'_>) -> Result { diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index 070d92537ebde..f72e07cc91bd4 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -273,8 +273,8 @@ macro_rules! impl_Display { #[doc = concat!("let n1 = 32", stringify!($Signed), ";")] /// assert_eq!(n1.format_into(&mut buf), "32"); /// - #[doc = concat!("let n2 = ", stringify!($Signed::MAX), ";")] - #[doc = concat!("assert_eq!(n2.format_into(&mut buf), ", stringify!($Signed::MAX), ".to_string());")] + #[doc = concat!("let n2 = ", stringify!($Signed), "::MAX;")] + #[doc = concat!("assert_eq!(n2.format_into(&mut buf), ", stringify!($Signed), "::MAX.to_string());")] /// ``` #[stable(feature = "int_format_into", since = "1.98.0")] pub fn format_into(self, buf: &mut NumBuffer) -> &str { diff --git a/library/core/src/fmt/num_buffer.rs b/library/core/src/fmt/num_buffer.rs index 5c29964b1cff8..62089e30a06e2 100644 --- a/library/core/src/fmt/num_buffer.rs +++ b/library/core/src/fmt/num_buffer.rs @@ -84,3 +84,10 @@ impl NumBuffer { NumBuffer { buf: T::DEFAULT, phantom: core::marker::PhantomData } } } + +#[stable(feature = "num_buffer_default", since = "CURRENT_RUSTC_VERSION")] +impl Default for NumBuffer { + fn default() -> Self { + Self::new() + } +} diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 90326e649058b..616bdd9a97b7c 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -300,11 +300,11 @@ pub fn spin_loop() { all(target_feature = "v6", target_feature = "mclass"), ) ) => { - // SAFETY: the `cfg` attr ensures that we only execute this on arm + // The `cfg` attr ensures that we only execute this on arm // targets with support for the this feature. On ARMv6 in Thumb // mode, T2 is required (see Arm DDI0406C Section A8.8.427), // otherwise ARMv6-M or ARMv6K is enough - unsafe { crate::arch::arm::__yield() } + crate::arch::arm::__yield() } target_arch = "loongarch32" => crate::arch::loongarch32::ibar::<0>(), target_arch = "loongarch64" => crate::arch::loongarch64::ibar::<0>(), diff --git a/library/core/src/intrinsics/fallback.rs b/library/core/src/intrinsics/fallback.rs index d2794a5b1202d..3e6829ab16b18 100644 --- a/library/core/src/intrinsics/fallback.rs +++ b/library/core/src/intrinsics/fallback.rs @@ -112,8 +112,13 @@ const impl CarryingMulAdd for i128 { #[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")] pub const trait DisjointBitOr: Copy + 'static { - /// See [`super::disjoint_bitor`]; we just need the trait indirection to handle - /// different types since calling intrinsics with generics doesn't work. + /// We just need the trait indirection to handle different types + /// since calling intrinsics with generics doesn't work. + /// + /// # Safety + /// + /// See [`super::disjoint_bitor`], with `self` corresponding to `a` and + /// `other` corresponding to `b`. unsafe fn disjoint_bitor(self, other: Self) -> Self; } macro_rules! zero { @@ -149,12 +154,22 @@ impl_disjoint_bitor! { #[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")] pub const trait FunnelShift: Copy + 'static { - /// See [`super::unchecked_funnel_shl`]; we just need the trait indirection to handle - /// different types since calling intrinsics with generics doesn't work. + /// We just need the trait indirection to handle different types + /// since calling intrinsics with generics doesn't work. + /// + /// # Safety + /// + /// See [`super::unchecked_funnel_shl`], with `self` corresponding to `a` and + /// `right` corresponding to `b`. unsafe fn unchecked_funnel_shl(self, right: Self, shift: u32) -> Self; - /// See [`super::unchecked_funnel_shr`]; we just need the trait indirection to handle - /// different types since calling intrinsics with generics doesn't work. + /// We just need the trait indirection to handle different types + /// since calling intrinsics with generics doesn't work. + /// + /// # Safety + /// + /// See [`super::unchecked_funnel_shr`], with `self` corresponding to `a` and + /// `right` corresponding to `b`. unsafe fn unchecked_funnel_shr(self, right: Self, shift: u32) -> Self; } diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index ab2bbb8ab4449..3a5ec85f507ec 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -88,6 +88,7 @@ pub mod fallback; pub mod gpu; mod macros; pub mod mir; +pub mod reflection; pub mod simd; use macros::intrinsic_dispatch_on_type; @@ -95,7 +96,9 @@ use macros::intrinsic_dispatch_on_type; // These imports are used for simplifying intra-doc links #[allow(unused_imports)] #[cfg(all(target_has_atomic = "8", target_has_atomic = "32", target_has_atomic = "ptr"))] -use crate::sync::atomic::{self, AtomicBool, AtomicI32, AtomicIsize, AtomicU32, Ordering}; +use crate::sync::atomic::{ + self, AtomicBool, AtomicI32, AtomicIsize, AtomicPtr, AtomicU32, Ordering, +}; /// A type for atomic ordering parameters for intrinsics. This is a separate type from /// `atomic::Ordering` so that we can make it `ConstParamTy` and fix the values used here without a @@ -115,12 +118,28 @@ pub enum AtomicOrdering { // N.B., these intrinsics take raw pointers because they mutate aliased // memory, which is not valid for either `&` or `&mut`. -/// Stores a value if the current value is the same as the `old` value. +/// Stores a value if the current value is the same as the `old` value, +/// with the returned `T` being the previous value and the returned `bool` being +/// whether the exchange was successful. /// `T` must be an integer or pointer type. /// +/// # Safety +/// +/// * If `T` is an integer type, this is equivalent to [`Atomic::from_ptr`] followed by [`Atomic::compare_exchange`]. +/// Refer to the documentation of [`Atomic::from_ptr`] for safety requirements. +/// +/// * If `T` is a pointer type, this is equivalent to [`AtomicPtr

    ::from_ptr`] followed by [`AtomicPtr

    ::compare_exchange`], +/// where `P` is the pointee type of the pointer. +/// Refer to the documentation of [`AtomicPtr

    ::from_ptr`] for safety requirements. +/// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `compare_exchange` method. /// For example, [`AtomicBool::compare_exchange`]. +/// +/// [`Atomic::from_ptr`]: AtomicI32::from_ptr +/// [`Atomic::compare_exchange`]: AtomicI32::compare_exchange +/// [`AtomicPtr

    ::from_ptr`]: AtomicPtr::from_ptr +/// [`AtomicPtr

    ::compare_exchange`]: AtomicPtr::compare_exchange #[rustc_intrinsic] #[rustc_nounwind] pub const unsafe fn atomic_cxchg< @@ -133,12 +152,28 @@ pub const unsafe fn atomic_cxchg< src: T, ) -> (T, bool); -/// Stores a value if the current value is the same as the `old` value. +/// Stores a value if the current value is the same as the `old` value, +/// with the returned `T` being the previous value and the returned `bool` being +/// whether the exchange was successful. /// `T` must be an integer or pointer type. The comparison may spuriously fail. /// +/// # Safety +/// +/// * If `T` is an integer type, this is equivalent to [`Atomic::from_ptr`] followed by [`Atomic::compare_exchange_weak`]. +/// Refer to the documentation of [`Atomic::from_ptr`] for safety requirements. +/// +/// * If `T` is a pointer type, this is equivalent to [`AtomicPtr

    ::from_ptr`] followed by [`AtomicPtr

    ::compare_exchange_weak`], +/// where `P` is the pointee type of the pointer. +/// Refer to the documentation of [`AtomicPtr

    ::from_ptr`] for safety requirements. +/// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `compare_exchange_weak` method. /// For example, [`AtomicBool::compare_exchange_weak`]. +/// +/// [`Atomic::from_ptr`]: AtomicI32::from_ptr +/// [`Atomic::compare_exchange_weak`]: AtomicI32::compare_exchange_weak +/// [`AtomicPtr

    ::from_ptr`]: AtomicPtr::from_ptr +/// [`AtomicPtr

    ::compare_exchange_weak`]: AtomicPtr::compare_exchange_weak #[rustc_intrinsic] #[rustc_nounwind] pub const unsafe fn atomic_cxchgweak< @@ -154,8 +189,20 @@ pub const unsafe fn atomic_cxchgweak< /// Loads the current value of the pointer. /// `T` must be an integer or pointer type. /// +/// # Safety +/// +/// * If `VOLATILE` is `true`, this is equivalent to [`Atomic::load_volatile`]. +/// Refer to the documentation of that method for safety requirements. +/// +/// * If `VOLATILE` is `false`, this is equivalent to [`Atomic::from_ptr`] followed +/// by [`Atomic::load`]. Refer to the documentation of [`Atomic::from_ptr`] for safety requirements. +/// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `load` method. For example, [`AtomicBool::load`]. +/// +/// [`Atomic::load_volatile`]: AtomicI32::load_volatile +/// [`Atomic::from_ptr`]: AtomicI32::from_ptr +/// [`Atomic::load`]: AtomicI32::load #[rustc_intrinsic] #[rustc_nounwind] pub const unsafe fn atomic_load( @@ -165,8 +212,20 @@ pub const unsafe fn atomic_load::store_volatile`]. +/// Refer to the documentation of that method for safety requirements. +/// +/// * If `VOLATILE` is `false`, this is equivalent to [`Atomic::from_ptr`] followed +/// by [`Atomic::store`]. Refer to the documentation of [`Atomic::from_ptr`] for safety requirements. +/// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `store` method. For example, [`AtomicBool::store`]. +/// +/// [`Atomic::store_volatile`]: AtomicI32::store_volatile +/// [`Atomic::from_ptr`]: AtomicI32::from_ptr +/// [`Atomic::store`]: AtomicI32::store #[rustc_intrinsic] #[rustc_nounwind] pub const unsafe fn atomic_store( @@ -177,8 +236,22 @@ pub const unsafe fn atomic_store::from_ptr`] followed by [`Atomic::swap`]. +/// Refer to the documentation of [`Atomic::from_ptr`] for safety requirements. +/// +/// * If `T` is a pointer type, this is equivalent to [`AtomicPtr

    ::from_ptr`] followed by [`AtomicPtr

    ::swap`], +/// where `P` is the pointee type of the pointer. +/// Refer to the documentation of [`AtomicPtr

    ::from_ptr`] for safety requirements. +/// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `swap` method. For example, [`AtomicBool::swap`]. +/// +/// [`Atomic::from_ptr`]: AtomicI32::from_ptr +/// [`Atomic::swap`]: AtomicI32::swap +/// [`AtomicPtr

    ::from_ptr`]: AtomicPtr::from_ptr +/// [`AtomicPtr

    ::swap`]: AtomicPtr::swap #[rustc_intrinsic] #[rustc_nounwind] pub const unsafe fn atomic_xchg(dst: *mut T, src: T) -> T; @@ -187,8 +260,22 @@ pub const unsafe fn atomic_xchg(dst: *mut T, /// `T` must be an integer or pointer type. /// `U` must be the same as `T` if that is an integer type, or `usize` if `T` is a pointer type. /// +/// # Safety +/// +/// * If `T` is an integer type, this is equivalent to [`Atomic::from_ptr`] followed by [`Atomic::fetch_add`]. +/// Refer to the documentation of [`Atomic::from_ptr`] for safety requirements. +/// +/// * If `T` is a pointer type, this is equivalent to [`AtomicPtr

    ::from_ptr`] followed by [`AtomicPtr

    ::fetch_byte_add`], +/// where `P` is the pointee type of the pointer. +/// Refer to the documentation of [`AtomicPtr

    ::from_ptr`] for safety requirements. +/// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `fetch_add` method. For example, [`AtomicIsize::fetch_add`]. +/// +/// [`Atomic::from_ptr`]: AtomicI32::from_ptr +/// [`Atomic::fetch_add`]: AtomicI32::fetch_add +/// [`AtomicPtr

    ::from_ptr`]: AtomicPtr::from_ptr +/// [`AtomicPtr

    ::fetch_byte_add`]: AtomicPtr::fetch_byte_add #[rustc_intrinsic] #[rustc_nounwind] pub const unsafe fn atomic_xadd( @@ -200,8 +287,22 @@ pub const unsafe fn atomic_xadd( /// `T` must be an integer or pointer type. /// `U` must be the same as `T` if that is an integer type, or `usize` if `T` is a pointer type. /// +/// # Safety +/// +/// * If `T` is an integer type, this is equivalent to [`Atomic::from_ptr`] followed by [`Atomic::fetch_sub`]. +/// Refer to the documentation of [`Atomic::from_ptr`] for safety requirements. +/// +/// * If `T` is a pointer type, this is equivalent to [`AtomicPtr

    ::from_ptr`] followed by [`AtomicPtr

    ::fetch_byte_sub`], +/// where `P` is the pointee type of the pointer. +/// Refer to the documentation of [`AtomicPtr

    ::from_ptr`] for safety requirements. +/// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `fetch_sub` method. For example, [`AtomicIsize::fetch_sub`]. +/// +/// [`Atomic::from_ptr`]: AtomicI32::from_ptr +/// [`Atomic::fetch_sub`]: AtomicI32::fetch_sub +/// [`AtomicPtr

    ::from_ptr`]: AtomicPtr::from_ptr +/// [`AtomicPtr

    ::fetch_byte_sub`]: AtomicPtr::fetch_byte_sub #[rustc_intrinsic] #[rustc_nounwind] pub const unsafe fn atomic_xsub( @@ -213,8 +314,22 @@ pub const unsafe fn atomic_xsub( /// `T` must be an integer or pointer type. /// `U` must be the same as `T` if that is an integer type, or `usize` if `T` is a pointer type. /// +/// # Safety +/// +/// * If `T` is an integer type, this is equivalent to [`Atomic::from_ptr`] followed by [`Atomic::fetch_and`]. +/// Refer to the documentation of [`Atomic::from_ptr`] for safety requirements. +/// +/// * If `T` is a pointer type, this is equivalent to [`AtomicPtr

    ::from_ptr`] followed by [`AtomicPtr

    ::fetch_and`], +/// where `P` is the pointee type of the pointer. +/// Refer to the documentation of [`AtomicPtr

    ::from_ptr`] for safety requirements. +/// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `fetch_and` method. For example, [`AtomicBool::fetch_and`]. +/// +/// [`Atomic::from_ptr`]: AtomicI32::from_ptr +/// [`Atomic::fetch_and`]: AtomicI32::fetch_and +/// [`AtomicPtr

    ::from_ptr`]: AtomicPtr::from_ptr +/// [`AtomicPtr

    ::fetch_and`]: AtomicPtr::fetch_and #[rustc_intrinsic] #[rustc_nounwind] pub const unsafe fn atomic_and( @@ -226,8 +341,16 @@ pub const unsafe fn atomic_and( /// `T` must be an integer or pointer type. /// `U` must be the same as `T` if that is an integer type, or `usize` if `T` is a pointer type. /// +/// # Safety +/// +/// This is equivalent to [`Atomic::from_ptr`] followed by [`Atomic::fetch_nand`]. +/// Refer to the documentation of [`Atomic::from_ptr`] for safety requirements. +/// /// The stabilized version of this intrinsic is available on the -/// [`AtomicBool`] type via the `fetch_nand` method. For example, [`AtomicBool::fetch_nand`]. +/// [`atomic`] types via the `fetch_nand` method. For example, [`AtomicBool::fetch_nand`]. +/// +/// [`Atomic::from_ptr`]: AtomicI32::from_ptr +/// [`Atomic::fetch_nand`]: AtomicI32::fetch_nand #[rustc_intrinsic] #[rustc_nounwind] pub const unsafe fn atomic_nand( @@ -239,8 +362,22 @@ pub const unsafe fn atomic_nand( /// `T` must be an integer or pointer type. /// `U` must be the same as `T` if that is an integer type, or `usize` if `T` is a pointer type. /// +/// # Safety +/// +/// * If `T` is an integer type, this is equivalent to [`Atomic::from_ptr`] followed by [`Atomic::fetch_or`]. +/// Refer to the documentation of [`Atomic::from_ptr`] for safety requirements. +/// +/// * If `T` is a pointer type, this is equivalent to [`AtomicPtr

    ::from_ptr`] followed by [`AtomicPtr

    ::fetch_or`], +/// where `P` is the pointee type of the pointer. +/// Refer to the documentation of [`AtomicPtr

    ::from_ptr`] for safety requirements. +/// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `fetch_or` method. For example, [`AtomicBool::fetch_or`]. +/// +/// [`Atomic::from_ptr`]: AtomicI32::from_ptr +/// [`Atomic::fetch_or`]: AtomicI32::fetch_or +/// [`AtomicPtr

    ::from_ptr`]: AtomicPtr::from_ptr +/// [`AtomicPtr

    ::fetch_or`]: AtomicPtr::fetch_or #[rustc_intrinsic] #[rustc_nounwind] pub const unsafe fn atomic_or( @@ -252,8 +389,22 @@ pub const unsafe fn atomic_or( /// `T` must be an integer or pointer type. /// `U` must be the same as `T` if that is an integer type, or `usize` if `T` is a pointer type. /// +/// # Safety +/// +/// * If `T` is an integer type, this is equivalent to [`Atomic::from_ptr`] followed by [`Atomic::fetch_xor`]. +/// Refer to the documentation of [`Atomic::from_ptr`] for safety requirements. +/// +/// * If `T` is a pointer type, this is equivalent to [`AtomicPtr

    ::from_ptr`] followed by [`AtomicPtr

    ::fetch_xor`], +/// where `P` is the pointee type of the pointer. +/// Refer to the documentation of [`AtomicPtr

    ::from_ptr`] for safety requirements. +/// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `fetch_xor` method. For example, [`AtomicBool::fetch_xor`]. +/// +/// [`Atomic::from_ptr`]: AtomicI32::from_ptr +/// [`Atomic::fetch_xor`]: AtomicI32::fetch_xor +/// [`AtomicPtr

    ::from_ptr`]: AtomicPtr::from_ptr +/// [`AtomicPtr

    ::fetch_xor`]: AtomicPtr::fetch_xor #[rustc_intrinsic] #[rustc_nounwind] pub const unsafe fn atomic_xor( @@ -264,8 +415,16 @@ pub const unsafe fn atomic_xor( /// Maximum with the current value using a signed comparison. /// `T` must be a signed integer type. /// +/// # Safety +/// +/// This is equivalent to [`Atomic::from_ptr`] followed by [`Atomic::fetch_max`]. +/// Refer to the documentation of [`Atomic::from_ptr`] for safety requirements. +/// /// The stabilized version of this intrinsic is available on the /// [`atomic`] signed integer types via the `fetch_max` method. For example, [`AtomicI32::fetch_max`]. +/// +/// [`Atomic::from_ptr`]: AtomicI32::from_ptr +/// [`Atomic::fetch_max`]: AtomicI32::fetch_max #[rustc_intrinsic] #[rustc_nounwind] pub const unsafe fn atomic_max(dst: *mut T, src: T) -> T; @@ -273,8 +432,16 @@ pub const unsafe fn atomic_max(dst: *mut T, /// Minimum with the current value using a signed comparison. /// `T` must be a signed integer type. /// +/// # Safety +/// +/// This is equivalent to [`Atomic::from_ptr`] followed by [`Atomic::fetch_min`]. +/// Refer to the documentation of [`Atomic::from_ptr`] for safety requirements. +/// /// The stabilized version of this intrinsic is available on the /// [`atomic`] signed integer types via the `fetch_min` method. For example, [`AtomicI32::fetch_min`]. +/// +/// [`Atomic::from_ptr`]: AtomicI32::from_ptr +/// [`Atomic::fetch_min`]: AtomicI32::fetch_min #[rustc_intrinsic] #[rustc_nounwind] pub const unsafe fn atomic_min(dst: *mut T, src: T) -> T; @@ -282,8 +449,16 @@ pub const unsafe fn atomic_min(dst: *mut T, /// Minimum with the current value using an unsigned comparison. /// `T` must be an unsigned integer type. /// +/// # Safety +/// +/// This is equivalent to [`Atomic::from_ptr`] followed by [`Atomic::fetch_min`]. +/// Refer to the documentation of [`Atomic::from_ptr`] for safety requirements. +/// /// The stabilized version of this intrinsic is available on the /// [`atomic`] unsigned integer types via the `fetch_min` method. For example, [`AtomicU32::fetch_min`]. +/// +/// [`Atomic::from_ptr`]: AtomicU32::from_ptr +/// [`Atomic::fetch_min`]: AtomicU32::fetch_min #[rustc_intrinsic] #[rustc_nounwind] pub const unsafe fn atomic_umin(dst: *mut T, src: T) -> T; @@ -291,8 +466,16 @@ pub const unsafe fn atomic_umin(dst: *mut T, /// Maximum with the current value using an unsigned comparison. /// `T` must be an unsigned integer type. /// +/// # Safety +/// +/// This is equivalent to [`Atomic::from_ptr`] followed by [`Atomic::fetch_max`]. +/// Refer to the documentation of [`Atomic::from_ptr`] for safety requirements. +/// /// The stabilized version of this intrinsic is available on the /// [`atomic`] unsigned integer types via the `fetch_max` method. For example, [`AtomicU32::fetch_max`]. +/// +/// [`Atomic::from_ptr`]: AtomicU32::from_ptr +/// [`Atomic::fetch_max`]: AtomicU32::fetch_max #[rustc_intrinsic] #[rustc_nounwind] pub const unsafe fn atomic_umax(dst: *mut T, src: T) -> T; @@ -401,26 +584,23 @@ pub fn breakpoint(); #[rustc_intrinsic] pub fn rustc_peek(_: T) -> T; -/// Aborts the execution of the process. +/// Ungracefully aborts the execution of the process. /// -/// Note that, unlike most intrinsics, this is safe to call; -/// it does not require an `unsafe` block. -/// Therefore, implementations must not require the user to uphold -/// any safety invariants. +/// This is the intrinsic for directly implementing [`core::process::abort_immediate`] which, on +/// most platforms, will invoke an invalid instruction. On Unix, the process will probably +/// terminate with a signal like `SIGABRT`, `SIGILL`, `SIGTRAP`, `SIGSEGV` or `SIGBUS`. The +/// precise behavior is not guaranteed and not stable. /// /// [`std::process::abort`](../../std/process/fn.abort.html) is to be preferred if possible, /// as its behavior is more user-friendly and more stable. /// -/// The current implementation of `intrinsics::abort` is to invoke an invalid instruction, -/// on most platforms. -/// On Unix, the -/// process will probably terminate with a signal like `SIGABRT`, `SIGILL`, `SIGTRAP`, `SIGSEGV` or -/// `SIGBUS`. The precise behavior is not guaranteed and not stable. +/// Note that, unlike most intrinsics, this is safe to call; it does not require an `unsafe` block. +/// Therefore, implementations must not require the user to uphold any safety invariants. /// -/// The stabilization-track version of this intrinsic is [`core::process::abort_immediate`]. +/// The stabilized version of this intrinsic is [`core::process::abort_immediate`]. #[rustc_nounwind] #[rustc_intrinsic] -pub fn abort() -> !; +pub const fn abort_immediate() -> !; /// Informs the optimizer that this point in the code is not reachable, /// enabling further optimizations. @@ -1380,6 +1560,9 @@ pub const fn fmaf128(a: f128, b: f128, c: f128) -> f128; /// and add instructions. It is unspecified whether or not a fused operation /// is selected, and that may depend on optimization level and context, for /// example. +/// +/// The stabilized version of this intrinsic is +/// [`f16::mul_add_relaxed`](../../std/primitive.f16.html#method.mul_add_relaxed) #[inline] #[rustc_intrinsic] #[rustc_nounwind] @@ -1396,6 +1579,9 @@ pub const fn fmuladdf16(a: f16, b: f16, c: f16) -> f16 { /// and add instructions. It is unspecified whether or not a fused operation /// is selected, and that may depend on optimization level and context, for /// example. +/// +/// The stabilized version of this intrinsic is +/// [`f32::mul_add_relaxed`](../../std/primitive.f32.html#method.mul_add_relaxed) #[inline] #[rustc_intrinsic] #[rustc_nounwind] @@ -1412,6 +1598,9 @@ pub const fn fmuladdf32(a: f32, b: f32, c: f32) -> f32 { /// and add instructions. It is unspecified whether or not a fused operation /// is selected, and that may depend on optimization level and context, for /// example. +/// +/// The stabilized version of this intrinsic is +/// [`f64::mul_add_relaxed`](../../std/primitive.f64.html#method.mul_add_relaxed) #[inline] #[rustc_intrinsic] #[rustc_nounwind] @@ -1428,6 +1617,9 @@ pub const fn fmuladdf64(a: f64, b: f64, c: f64) -> f64 { /// and add instructions. It is unspecified whether or not a fused operation /// is selected, and that may depend on optimization level and context, for /// example. +/// +/// The stabilized version of this intrinsic is +/// [`f128::mul_add_relaxed`](../../std/primitive.f128.html#method.mul_add_relaxed) #[inline] #[rustc_intrinsic] #[rustc_nounwind] @@ -2109,7 +2301,7 @@ pub const unsafe fn unchecked_mul(x: T, y: T) -> T; #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -#[rustc_allow_const_fn_unstable(const_trait_impl, funnel_shifts)] +#[rustc_allow_const_fn_unstable(const_trait_impl)] #[miri::intrinsic_fallback_is_spec] pub const fn rotate_left(x: T, shift: u32) -> T { // Make sure to call the intrinsic for `funnel_shl`, not the fallback impl. @@ -2131,7 +2323,7 @@ pub const fn rotate_left(x: T, shift: u32) -> #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -#[rustc_allow_const_fn_unstable(const_trait_impl, funnel_shifts)] +#[rustc_allow_const_fn_unstable(const_trait_impl)] #[miri::intrinsic_fallback_is_spec] pub const fn rotate_right(x: T, shift: u32) -> T { // Make sure to call the intrinsic for `funnel_shr`, not the fallback impl. @@ -2227,11 +2419,11 @@ pub const fn saturating_sub(a: T, b: T) -> T; /// /// Safe versions of this intrinsic are available on the integer primitives /// via the `funnel_shl` method. For example, [`u32::funnel_shl`]. +#[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_nounwind] -#[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")] -#[unstable(feature = "funnel_shifts", issue = "145686")] #[track_caller] +#[rustc_allow_const_fn_unstable(const_trait_impl, core_intrinsics_fallbacks)] #[miri::intrinsic_fallback_is_spec] pub const unsafe fn unchecked_funnel_shl( a: T, @@ -2255,11 +2447,11 @@ pub const unsafe fn unchecked_funnel_shl( /// /// Safer versions of this intrinsic are available on the integer primitives /// via the `funnel_shr` method. For example, [`u32::funnel_shr`] +#[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_nounwind] -#[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")] -#[unstable(feature = "funnel_shifts", issue = "145686")] #[track_caller] +#[rustc_allow_const_fn_unstable(const_trait_impl, core_intrinsics_fallbacks)] #[miri::intrinsic_fallback_is_spec] pub const unsafe fn unchecked_funnel_shr( a: T, @@ -2962,25 +3154,6 @@ pub const unsafe fn size_of_val(ptr: *const T) -> usize; #[rustc_intrinsic_const_stable_indirect] pub const unsafe fn align_of_val(ptr: *const T) -> usize; -#[rustc_intrinsic] -#[rustc_comptime] -#[unstable(feature = "core_intrinsics", issue = "none")] -/// Check if a type represented by a `TypeId` implements a trait represented by a `TypeId`. -/// It can only be called at compile time, the backends do -/// not implement it. If it implements the trait the dyn metadata gets returned for vtable access. -pub fn type_id_vtable( - _id: crate::any::TypeId, - _trait: crate::any::TypeId, -) -> Option>; - -/// Compute the type information of a concrete type. -/// It can only be called at compile time, the backends do -/// not implement it. -#[rustc_intrinsic] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_comptime] -pub fn type_of(_id: crate::any::TypeId) -> crate::mem::type_info::Type; - /// Gets a static string slice containing the name of a type. /// /// Note that, unlike most intrinsics, this can only be called at compile-time @@ -2995,121 +3168,6 @@ pub fn type_of(_id: crate::any::TypeId) -> crate::mem::type_info::Type; #[rustc_comptime] pub fn type_name() -> &'static str; -/// Gets an identifier which is globally unique to the specified type. This -/// function will return the same value for a type regardless of whichever -/// crate it is invoked in. -/// -/// Note that, unlike most intrinsics, this can only be called at compile-time -/// as backends do not have an implementation for it. The only caller (its -/// stable counterpart) wraps this intrinsic call in a `const` block so that -/// backends only see an evaluated constant. -/// -/// The stabilized version of this intrinsic is [`core::any::TypeId::of`]. -#[rustc_nounwind] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_intrinsic] -#[rustc_comptime] -pub fn type_id() -> crate::any::TypeId; - -/// Tests (at compile-time) if two [`crate::any::TypeId`] instances identify the -/// same type. This is necessary because at const-eval time the actual discriminating -/// data is opaque and cannot be inspected directly. -/// -/// The stabilized version of this intrinsic is the [PartialEq] impl for [`core::any::TypeId`]. -#[rustc_nounwind] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_intrinsic] -#[rustc_do_not_const_check] -pub const fn type_id_eq(a: crate::any::TypeId, b: crate::any::TypeId) -> bool { - // SAFETY: we know `TypeId` is 16 bytes of initialized data. - // This is runtime-only code so we do not have to worry about provenance. - unsafe { crate::mem::transmute::<_, u128>(a) == crate::mem::transmute::<_, u128>(b) } -} - -/// Returns whether the type represented by this `TypeId` is a signed integer. -/// -/// The more user-friendly version of this intrinsic is [`core::any::TypeId::is_signed`]. -#[rustc_intrinsic] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_comptime] -pub fn type_id_is_signed(_id: crate::any::TypeId) -> bool; - -/// Gets the length of the array represented by this `TypeId`. -/// -/// The more user-friendly version of this intrinsic is [`core::any::TypeId::array_len`]. -#[rustc_intrinsic] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_comptime] -pub fn type_id_array_len(_id: crate::any::TypeId) -> usize; - -/// Gets the type of each element of the array or slice represented by this `TypeId`. -/// -/// The more user-friendly version of this intrinsic is [`core::any::TypeId::element_ty`]. -#[rustc_intrinsic] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_comptime] -pub fn type_id_element_ty(_id: crate::any::TypeId) -> Option; - -/// Gets the size of the type represented by this `TypeId`. -/// -/// The more user-friendly version of this intrinsic is [`core::any::TypeId::size`]. -#[rustc_intrinsic] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_comptime] -pub fn size_of_type_id(_id: crate::any::TypeId) -> Option; - -/// Gets the number of variants of the type represented by this `TypeId`. -/// -/// The more user-friendly version of this intrinsic is [`core::any::TypeId::variants`]. -#[rustc_intrinsic] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_comptime] -pub fn type_id_variants(_id: crate::any::TypeId) -> usize; - -/// Gets the name of the variant represented by the base `TypeId` and variant_idx. -/// -/// The more user-friendly version of this intrinsic is [`core::mem::type_info::VariantId::name`]. -/// -/// [`TypeId`]: crate::any::TypeId -#[rustc_intrinsic] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_comptime] -pub fn variant_name(_base: crate::any::TypeId, _variant_index: usize) -> &'static str; - -/// Returns true when the variant represented by the base `TypeId` and variant_idx is non -/// exhaustive. -/// -/// The more user-friendly version of this intrinsic is -/// [`core::mem::type_info::VariantId::non_exhaustive`]. -/// -/// [`TypeId`]: crate::any::TypeId -#[rustc_intrinsic] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_comptime] -pub fn variant_non_exhaustive(base: crate::any::TypeId, variant: usize) -> bool; - -/// Gets the number of fields at the given `variant_index` represented by this `TypeId`. -/// -/// The more user-friendly version of this intrinsic is [`core::any::TypeId::fields`]. -#[rustc_intrinsic] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_comptime] -pub fn type_id_fields(_id: crate::any::TypeId, _variant_index: usize) -> usize; - -/// Gets the [`FieldRepresentingType`]'s `TypeId` at the given index of the type represented by this `TypeId`. -/// -/// The more user-friendly version of this intrinsic is [`core::any::TypeId::field`]. -/// -/// [`FieldRepresentingType`]: crate::field::FieldRepresentingType -#[rustc_intrinsic] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_comptime] -pub fn type_id_field_representing_type( - _id: crate::any::TypeId, - _variant_index: usize, - _field_index: usize, -) -> crate::any::TypeId; - /// Gets the actual field `TypeId` of the [`FieldRepresentingType`]'s `TypeId`. /// /// The more user-friendly version of this intrinsic is [`core::mem::type_info::FieldId::type_id`]. @@ -3142,48 +3200,6 @@ pub fn field_representing_type_name(_frt_type_id: crate::any::TypeId) -> &'stati #[rustc_comptime] pub fn field_representing_type_offset(_frt_type_id: crate::any::TypeId) -> usize; -/// Given a `TypeId` that represents a function pointer returns an [`core::mem::type_info::FnPtr`]. -/// When called on something else this returns `None`. -/// -/// The more user-friendly version of this intrinsic is [`core::any::TypeId::function_ptr`]. -#[rustc_intrinsic] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_comptime] -pub fn type_id_function_ptr(_type_id: crate::any::TypeId) -> Option; - -/// Checks whether this type is non-exhaustive. -#[rustc_intrinsic] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_comptime] -pub fn non_exhaustive(_id: crate::any::TypeId) -> bool; - -/// Returns the list of generic args on this type. -/// Only meaningful for Adts, closures, ... Everything else returns an empty slice. -#[rustc_intrinsic] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_comptime] -pub fn type_id_generics(_id: crate::any::TypeId) -> &'static [crate::mem::type_info::Generic]; - -// FIXME(reflection): Pick a consistent naming scheme for the intrinsics. Right now we got -// type_id_, _type_id and intrinsics not mentioning type_id at all. -/// Given a `TypeId` that represents a pointer this returns the `TypeId` which that pointer -/// points to. When called on anything else this returns None. -/// -/// The more user-friendly version of this intrinsic is [`core::any::TypeId::points_to`]. -#[rustc_intrinsic] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_comptime] -pub fn type_id_points_to(_id: crate::any::TypeId) -> Option; - -/// Given a `TypeId` that represents a pointer returns whether that pointer is mutable. -/// When called on anything else this returns `false`. -/// -/// The more user-friendly version of this intrinsic is [`core::any::TypeId::points_mutably`]. -#[rustc_intrinsic] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_comptime] -pub fn type_id_points_mutably(_id: crate::any::TypeId) -> bool; - /// Lowers in MIR to `Rvalue::Aggregate` with `AggregateKind::RawPtr`. /// /// This is used to implement functions like `slice::from_raw_parts_mut` and diff --git a/library/core/src/intrinsics/reflection.rs b/library/core/src/intrinsics/reflection.rs new file mode 100644 index 0000000000000..f0e0f9d66e033 --- /dev/null +++ b/library/core/src/intrinsics/reflection.rs @@ -0,0 +1,194 @@ +//! Intrinsics for compile time reflection +//! +//! This are all methods operating on [`TypeId`](core::any::TypeId) which is the +//! entry point to all compile time reflection. + +use crate::ptr; + +/// Gets an identifier which is globally unique to the specified type. This +/// function will return the same value for a type regardless of whichever +/// crate it is invoked in. +/// +/// Note that, unlike most intrinsics, this can only be called at compile-time +/// as backends do not have an implementation for it. The only caller (its +/// stable counterpart) wraps this intrinsic call in a `const` block so that +/// backends only see an evaluated constant. +/// +/// The stabilized version of this intrinsic is [`core::any::TypeId::of`]. +#[rustc_nounwind] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_intrinsic] +#[rustc_comptime] +pub fn type_id() -> crate::any::TypeId; + +/// Compute the type information of a concrete type. +/// It can only be called at compile time, the backends do +/// not implement it. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_type_of(_id: crate::any::TypeId) -> crate::mem::type_info::Type; + +/// Tests (at compile-time) if two [`crate::any::TypeId`] instances identify the +/// same type. This is necessary because at const-eval time the actual discriminating +/// data is opaque and cannot be inspected directly. +/// +/// The stabilized version of this intrinsic is the [PartialEq] impl for [`core::any::TypeId`]. +#[rustc_nounwind] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_intrinsic] +#[rustc_do_not_const_check] +pub const fn type_id_eq(a: crate::any::TypeId, b: crate::any::TypeId) -> bool { + // SAFETY: we know `TypeId` is 16 bytes of initialized data. + // This is runtime-only code so we do not have to worry about provenance. + unsafe { crate::mem::transmute::<_, u128>(a) == crate::mem::transmute::<_, u128>(b) } +} + +#[rustc_intrinsic] +#[rustc_comptime] +#[unstable(feature = "core_intrinsics", issue = "none")] +/// Check if a type represented by a `TypeId` implements a trait represented by a `TypeId`. +/// It can only be called at compile time, the backends do +/// not implement it. If it implements the trait the dyn metadata gets returned for vtable access. +pub fn type_id_vtable( + _id: crate::any::TypeId, + _trait: crate::any::TypeId, +) -> Option>; + +/// Gets a static string slice containing the name of a type. +/// +/// Note that, unlike most intrinsics, this can only be called at compile-time +/// as backends do not have an implementation for it. The only caller (its +/// stable counterpart) wraps this intrinsic call in a `const` block so that +/// backends only see an evaluated constant. +/// +/// The stabilized version of this intrinsic is [`core::any::type_name`]. +#[rustc_nounwind] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_intrinsic] +#[rustc_comptime] +pub fn type_name() -> &'static str; + +/// Returns whether the type represented by this `TypeId` is a signed integer. +/// +/// The more user-friendly version of this intrinsic is [`core::any::TypeId::is_signed`]. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_is_signed(_id: crate::any::TypeId) -> bool; + +/// Gets the length of the array represented by this `TypeId`. +/// +/// The more user-friendly version of this intrinsic is [`core::any::TypeId::array_len`]. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_array_len(_id: crate::any::TypeId) -> usize; + +/// Gets the type of each element of the array or slice represented by this `TypeId`. +/// +/// The more user-friendly version of this intrinsic is [`core::any::TypeId::element_ty`]. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_element_ty(_id: crate::any::TypeId) -> Option; + +/// Gets the size of the type represented by this `TypeId`. +/// +/// The more user-friendly version of this intrinsic is [`core::any::TypeId::size`]. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_size_of(_id: crate::any::TypeId) -> Option; + +/// Gets the number of variants of the type represented by this `TypeId`. +/// +/// The more user-friendly version of this intrinsic is [`core::any::TypeId::variants`]. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_variants(_id: crate::any::TypeId) -> usize; + +/// Gets the name of the variant represented by the base `TypeId` and variant_idx. +/// +/// The more user-friendly version of this intrinsic is [`core::mem::type_info::VariantId::name`]. +/// +/// [`TypeId`]: crate::any::TypeId +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_variant_name(_base: crate::any::TypeId, _variant_index: usize) -> &'static str; + +/// Returns true when the variant represented by the base `TypeId` and variant_idx is non +/// exhaustive. +/// +/// The more user-friendly version of this intrinsic is +/// [`core::mem::type_info::VariantId::non_exhaustive`]. +/// +/// [`TypeId`]: crate::any::TypeId +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_variant_non_exhaustive(base: crate::any::TypeId, variant: usize) -> bool; + +/// Gets the number of fields at the given `variant_index` represented by this `TypeId`. +/// +/// The more user-friendly version of this intrinsic is [`core::any::TypeId::fields`]. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_fields(_id: crate::any::TypeId, _variant_index: usize) -> usize; + +/// Checks whether this type is non-exhaustive. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_non_exhaustive(_id: crate::any::TypeId) -> bool; + +/// Returns the list of generic args on this type. +/// Only meaningful for Adts, closures, ... Everything else returns an empty slice. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_generics(_id: crate::any::TypeId) -> &'static [crate::mem::type_info::Generic]; + +/// Gets the [`FieldRepresentingType`]'s `TypeId` at the given index of the type represented by this `TypeId`. +/// +/// The more user-friendly version of this intrinsic is [`core::any::TypeId::field`]. +/// +/// [`FieldRepresentingType`]: crate::field::FieldRepresentingType +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_field_representing_type( + _id: crate::any::TypeId, + _variant_index: usize, + _field_index: usize, +) -> crate::any::TypeId; + +/// Given a `TypeId` that represents a pointer this returns the `TypeId` which that pointer +/// points to. When called on anything else this returns None. +/// +/// The more user-friendly version of this intrinsic is [`core::any::TypeId::points_to`]. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_points_to(_id: crate::any::TypeId) -> Option; + +/// Given a `TypeId` that represents a pointer returns whether that pointer is mutable. +/// When called on anything else this returns `false`. +/// +/// The more user-friendly version of this intrinsic is [`core::any::TypeId::points_mutably`]. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_points_mutably(_id: crate::any::TypeId) -> bool; + +/// Given a `TypeId` that represents a function pointer returns an [`core::mem::type_info::FnPtr`]. +/// When called on something else this returns `None`. +/// +/// The more user-friendly version of this intrinsic is [`core::any::TypeId::function_ptr`]. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_function_ptr(_type_id: crate::any::TypeId) -> Option; diff --git a/library/core/src/io/borrowed_buf.rs b/library/core/src/io/borrowed_buf.rs index 7b03d58a3fd62..2cc1ebba1a85c 100644 --- a/library/core/src/io/borrowed_buf.rs +++ b/library/core/src/io/borrowed_buf.rs @@ -222,6 +222,14 @@ pub struct BorrowedCursor<'a, T> { borrowed_buf: NonNull>, } +// SAFETY: A `BorrowedCursor<'a, T>` is a unique borrow of a `BorrowedBuf<'a, T>`, which is a +// `&'a mut [MaybeUninit]` and two `Copy` fields. The `buf` raw pointer is used like +// `&mut [MaybeUninit]` so `T: Send` -> `Send` and is: `T: Sync` -> `Sync`, and the +// `borrowed_buf` only touches two `Copy` fields, without depending of `T`. +unsafe impl Send for BorrowedCursor<'_, T> {} +// SAFETY: See the `Send` impl above. +unsafe impl Sync for BorrowedCursor<'_, T> {} + impl Debug for BorrowedCursor<'_, T> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let buf = BorrowedBufDebug { diff --git a/library/core/src/io/error/os_functions.rs b/library/core/src/io/error/os_functions.rs index b34b7c7dc7926..fcc549f0fb643 100644 --- a/library/core/src/io/error/os_functions.rs +++ b/library/core/src/io/error/os_functions.rs @@ -6,7 +6,7 @@ use crate::fmt; /// The provided reference must point to data that is entirely constant; it must /// not be created during runtime. #[inline] -pub(super) unsafe fn set_functions(f: &'static OsFunctions) { +pub(super) unsafe fn set_functions(_f: &'static OsFunctions) { // FIXME: externally implementable items may allow for weak linkage, allowing // these methods to be overridden even when atomic pointers are not supported. } diff --git a/library/core/src/iter/traits/double_ended.rs b/library/core/src/iter/traits/double_ended.rs index a7c8ec9319a07..3c3db756d91ea 100644 --- a/library/core/src/iter/traits/double_ended.rs +++ b/library/core/src/iter/traits/double_ended.rs @@ -189,13 +189,49 @@ pub const trait DoubleEndedIterator: [const] Iterator { where Self::Item: [const] Destruct, { - for i in 0..n { - if self.next_back().is_none() { - // SAFETY: `i` is always less than `n`. - return Err(unsafe { NonZero::new_unchecked(n - i) }); + /// Helper trait to specialize `advance_back_by` via `try_rfold` for `Sized` iterators. + + #[rustc_const_unstable(feature = "const_iter", issue = "92476")] + const trait SpecAdvanceBackBy { + fn spec_advance_back_by(&mut self, n: usize) -> Result<(), NonZero>; + } + + #[rustc_const_unstable(feature = "const_iter", issue = "92476")] + const impl SpecAdvanceBackBy for I + where + I::Item: [const] Destruct, + { + default fn spec_advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { + for i in 0..n { + if self.next_back().is_none() { + // SAFETY: `i` is always less than `n`. + return Err(unsafe { NonZero::new_unchecked(n - i) }); + } + } + Ok(()) } } - Ok(()) + + #[rustc_const_unstable(feature = "const_iter", issue = "92476")] + const impl SpecAdvanceBackBy for I + where + I::Item: [const] Destruct, + { + fn spec_advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { + let Some(n) = NonZero::new(n) else { + return Ok(()); + }; + + let res = self.try_rfold(n, const |n, _| NonZero::new(n.get() - 1)); + + match res { + None => Ok(()), + Some(n) => Err(n), + } + } + } + + self.spec_advance_back_by(n) } /// Returns the `n`th element from the end of the iterator. @@ -245,9 +281,7 @@ pub const trait DoubleEndedIterator: [const] Iterator { where Self::Item: [const] Destruct, { - if self.advance_back_by(n).is_err() { - return None; - } + self.advance_back_by(n).ok()?; self.next_back() } diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index c215560122022..788140e0fca7c 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -131,7 +131,6 @@ #![feature(final_associated_functions)] #![feature(freeze_impls)] #![feature(fundamental)] -#![feature(funnel_shifts)] #![feature(impl_restriction)] #![feature(intra_doc_pointers)] #![feature(intrinsics)] @@ -367,7 +366,6 @@ pub mod primitive; unused_imports, unsafe_op_in_unsafe_fn, ambiguous_glob_reexports, - deprecated_in_future, unreachable_pub, // FIXME: stdach is a submodule so clippy lints should be fixed (and ideally enforced) there clippy::all, diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 217f29d6a86e1..530af0c3af656 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -480,7 +480,7 @@ marker_impls! { } -#[stable(feature = "never_type", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "never_type", since = "1.100.0")] impl Copy for ! {} /// Shared references can be copied, but mutable references *cannot*! @@ -1074,15 +1074,22 @@ pub const trait Destruct: PointeeSized {} #[rustc_dyn_incompatible_trait] pub trait Tuple {} +// HACK(khyperia): if this is missing, the gca macro's `implied_by = "gca_adts"` emits an error +#[unstable(feature = "gca_adts", issue = "163420")] +#[expect(dead_code)] +struct HackGcaAdtsFeature; + /// Creates a new style directly represented const argument. /// ```ignore (cannot test this from within core yet) -/// const BAR: usize = direct_const_arg!(N); -/// const FOO: usize = direct_const_arg!(BAR::); +/// const BAR: usize = gca!(N); +/// const FOO: usize = gca!(BAR::); /// ``` -#[rustc_builtin_macro(direct_const_arg)] -#[unstable(feature = "min_generic_const_args", issue = "132980")] +#[rustc_builtin_macro(gca)] +// HACK: abuse implied_by, this should be enabled if gca_min_const_items or gca_adts is enabled, +// neither one is more particularly special than the other +#[unstable(feature = "gca_min_const_items", issue = "132980", implied_by = "gca_adts")] #[macro_export] -macro_rules! direct_const_arg { +macro_rules! gca { ($($arg:tt)*) => { /* compiler built-in */ }; diff --git a/library/core/src/mem/drop_guard.rs b/library/core/src/mem/drop_guard.rs index 31f1f93b65378..81104ccce29f1 100644 --- a/library/core/src/mem/drop_guard.rs +++ b/library/core/src/mem/drop_guard.rs @@ -27,7 +27,7 @@ use crate::ops::{Deref, DerefMut}; /// // "Chashu likes tuna!!!" /// } /// ``` -#[stable(feature = "drop_guard", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "drop_guard", since = "1.100.0")] #[doc(alias = "ScopeGuard")] #[doc(alias = "defer")] pub struct DropGuard @@ -54,7 +54,7 @@ where /// let value = String::from("Chashu likes tuna"); /// let guard = DropGuard::new(value, |s| println!("{s}")); /// ``` - #[stable(feature = "drop_guard", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "drop_guard", since = "1.100.0")] #[rustc_const_unstable(feature = "const_drop_guard", issue = "none")] #[must_use] pub const fn new(inner: T, f: F) -> Self { @@ -79,7 +79,7 @@ where /// let guard = DropGuard::new(value, |s| println!("{s}")); /// assert_eq!(DropGuard::dismiss(guard), "Nori likes chicken"); /// ``` - #[stable(feature = "drop_guard", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "drop_guard", since = "1.100.0")] #[rustc_const_unstable(feature = "const_drop_guard", issue = "none")] #[inline] pub const fn dismiss(guard: Self) -> T @@ -105,7 +105,7 @@ where } } -#[stable(feature = "drop_guard", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "drop_guard", since = "1.100.0")] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] const impl Deref for DropGuard where @@ -118,7 +118,7 @@ where } } -#[stable(feature = "drop_guard", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "drop_guard", since = "1.100.0")] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] const impl DerefMut for DropGuard where @@ -129,7 +129,7 @@ where } } -#[stable(feature = "drop_guard", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "drop_guard", since = "1.100.0")] #[rustc_const_unstable(feature = "const_drop_guard", issue = "none")] const impl Drop for DropGuard where @@ -146,7 +146,7 @@ where } } -#[stable(feature = "drop_guard", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "drop_guard", since = "1.100.0")] impl Debug for DropGuard where T: Debug, diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 64a48d7266746..a22e52f3a3bc7 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -59,7 +59,7 @@ mod transmutability; pub use transmutability::{Assume, TransmuteFrom}; mod drop_guard; -#[stable(feature = "drop_guard", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "drop_guard", since = "1.100.0")] pub use drop_guard::DropGuard; // This one has to be a re-export (rather than wrapping the underlying intrinsic) so that we can do @@ -712,16 +712,18 @@ pub const fn needs_drop() -> bool { /// This means that, for example, the padding byte in `(u8, u16)` is not /// necessarily zeroed. /// -/// There is no guarantee that an all-zero byte-pattern represents a valid value -/// of some type `T`. For example, the all-zero byte-pattern is not a valid value -/// for reference types (`&T`, `&mut T`) and function pointers. Using `zeroed` -/// on such types causes immediate [undefined behavior][ub] because [the Rust -/// compiler assumes][inv] that there always is a valid value in a variable it -/// considers initialized. -/// /// This has the same effect as [`MaybeUninit::zeroed().assume_init()`][zeroed]. /// It is useful for FFI sometimes, but should generally be avoided. /// +/// +/// # Safety +/// +/// The all-zero byte-pattern must represent a valid value of type `T`. +/// For example, it is not valid for reference types (`&T`, `&mut T`) or function +/// pointers. Using `zeroed` on such types causes immediate [undefined behavior][ub] +/// because [the Rust compiler assumes][inv] that there always is a valid value in a +/// variable it considers initialized. +/// /// [zeroed]: MaybeUninit::zeroed /// [ub]: ../../reference/behavior-considered-undefined.html /// [inv]: MaybeUninit#initialization-invariant @@ -1243,6 +1245,13 @@ pub const unsafe fn transmute_prefix(src: Src) -> Dst { /// /// It will not be stabilized under this name. /// +/// # Safety +/// +/// Refer to [`transmute`] for safety requirements. +/// This function is semantically identical to `transmute`. +/// +/// [`transmute`]: crate::mem::transmute +/// /// # Examples /// /// ``` @@ -1692,7 +1701,6 @@ pub macro offset_of($Container:ty, $($fields:expr)+ $(,)?) { /// # Examples /// /// ``` -/// #![feature(mem_conjure_zst)] /// use std::mem::conjure_zst; /// /// assert_eq!(unsafe { conjure_zst::<()>() }, ()); @@ -1700,8 +1708,9 @@ pub macro offset_of($Container:ty, $($fields:expr)+ $(,)?) { /// ``` /// /// [inhabited]: https://doc.rust-lang.org/reference/glossary.html#inhabited -#[unstable(feature = "mem_conjure_zst", issue = "95383")] -#[rustc_const_unstable(feature = "mem_conjure_zst", issue = "95383")] +#[stable(feature = "mem_conjure_zst", since = "1.100.0")] +#[rustc_const_stable(feature = "mem_conjure_zst", since = "1.100.0")] +#[rustc_allow_const_fn_unstable(const_type_name)] // type_name() called only at run time pub const unsafe fn conjure_zst() -> T { const_assert!( T::IS_ZST, diff --git a/library/core/src/mem/type_info.rs b/library/core/src/mem/type_info.rs index a11d1f1d0dc18..71a9627c24f9c 100644 --- a/library/core/src/mem/type_info.rs +++ b/library/core/src/mem/type_info.rs @@ -2,10 +2,9 @@ //! runtime or const-eval processable way. use crate::any::TypeId; -use crate::fmt; -use crate::intrinsics::{self, type_id, type_of}; use crate::marker::PointeeSized; use crate::ptr::DynMetadata; +use crate::{fmt, intrinsics}; /// Compile-time type information. #[derive(Debug)] @@ -38,7 +37,7 @@ impl TypeId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn info(self) -> Type { - type_of(self) + intrinsics::reflection::type_id_type_of(self) } } @@ -54,7 +53,7 @@ impl Type { #[unstable(feature = "type_info", issue = "146922")] #[rustc_const_unstable(feature = "type_info", issue = "146922")] pub const fn of() -> Self { - const { type_id::().info() } + const { intrinsics::reflection::type_id::().info() } } } @@ -67,7 +66,7 @@ impl Type { #[unstable(feature = "type_info", issue = "146922")] #[rustc_const_unstable(feature = "type_info", issue = "146922")] pub const fn of() -> TypeId { - const { intrinsics::type_id::() } + const { intrinsics::reflection::type_id::() } } /// Compile-time type information. @@ -272,7 +271,7 @@ impl TypeId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn is_signed(self) -> bool { - intrinsics::type_id_is_signed(self) + intrinsics::reflection::type_id_is_signed(self) } /// When called on a `TypeId` representing an array or slice this returns the type of each @@ -291,7 +290,7 @@ impl TypeId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn element_ty(self) -> Option { - intrinsics::type_id_element_ty(self) + intrinsics::reflection::type_id_element_ty(self) } /// When called on a `TypeId` representing an array this returns the length of the array in @@ -310,7 +309,7 @@ impl TypeId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn array_len(self) -> usize { - intrinsics::type_id_array_len(self) + intrinsics::reflection::type_id_array_len(self) } /// Returns the size of the type represented by this `TypeId`. `None` if it is unsized. @@ -328,7 +327,7 @@ impl TypeId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn size(self) -> Option { - intrinsics::size_of_type_id(self) + intrinsics::reflection::type_id_size_of(self) } /// Returns the number of variants of the type represented by this `TypeId`. @@ -354,7 +353,7 @@ impl TypeId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn variants(self) -> usize { - intrinsics::type_id_variants(self) + intrinsics::reflection::type_id_variants(self) } // FIXME(reflection): make the errors nicer. This is a wider problem, @@ -416,7 +415,7 @@ impl TypeId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn variant(self, variant_index: usize) -> VariantId { - intrinsics::type_id_fields(self, variant_index); + intrinsics::reflection::type_id_fields(self, variant_index); VariantId { base: self, variant: variant_index } } @@ -484,7 +483,7 @@ impl TypeId { // type we have) and add methods on that. It's the only way to really sensibly represent // things like `non_exhaustive` which can be applied to variants as well. pub fn fields(self, variant_index: usize) -> usize { - intrinsics::type_id_fields(self, variant_index) + intrinsics::reflection::type_id_fields(self, variant_index) } /// Returns the field representing type at the given index of the type represented by this `TypeId`. @@ -552,7 +551,7 @@ impl TypeId { #[rustc_comptime] pub fn field(self, variant_index: usize, field_index: usize) -> FieldId { FieldId { - frt_type_id: intrinsics::type_id_field_representing_type( + frt_type_id: intrinsics::reflection::type_id_field_representing_type( self, variant_index, field_index, @@ -566,7 +565,7 @@ impl TypeId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn non_exhaustive(self) -> bool { - intrinsics::non_exhaustive(self) + intrinsics::reflection::type_id_non_exhaustive(self) } /// Returns a list of generic parameters of the type. @@ -575,7 +574,7 @@ impl TypeId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn generics(self) -> &'static [Generic] { - intrinsics::type_id_generics(self) + intrinsics::reflection::type_id_generics(self) } /// Given a `TypeId` that represents a pointer this returns the `TypeId` @@ -600,7 +599,7 @@ impl TypeId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn points_to(self) -> Option { - intrinsics::type_id_points_to(self) + intrinsics::reflection::type_id_points_to(self) } /// Given a `TypeId` that represents a pointer returns whether that pointer is mutable. @@ -620,7 +619,7 @@ impl TypeId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn points_mutably(self) -> bool { - intrinsics::type_id_points_mutably(self) + intrinsics::reflection::type_id_points_mutably(self) } /// Given a `TypeId` that represents a function pointer returns an @@ -655,7 +654,7 @@ impl TypeId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn function_ptr(self) -> Option { - intrinsics::type_id_function_ptr(self) + intrinsics::reflection::type_id_function_ptr(self) } } @@ -696,7 +695,7 @@ impl VariantId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn name(self) -> &'static str { - intrinsics::variant_name(self.base, self.variant) + intrinsics::reflection::type_id_variant_name(self.base, self.variant) } /// Returns whether this variant is marked with `#[non_exhaustive]`. @@ -704,7 +703,7 @@ impl VariantId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn non_exhaustive(self) -> bool { - intrinsics::variant_non_exhaustive(self.base, self.variant) + intrinsics::reflection::type_id_variant_non_exhaustive(self.base, self.variant) } } diff --git a/library/core/src/num/bfloat.rs b/library/core/src/num/bfloat.rs new file mode 100644 index 0000000000000..203ededeb2c0a --- /dev/null +++ b/library/core/src/num/bfloat.rs @@ -0,0 +1,184 @@ +//! The 16-bit brain floating-point type. + +#![unstable(feature = "f16b", issue = "160630")] + +use crate::{fmt, mem}; + +/// A 16-bit brain floating-point value. +/// +/// This type stores values using the bfloat16 encoding. It deliberately +/// exposes only raw-bit construction, comparison, formatting, and lossless +/// widening to [`f32`]. +/// +/// The 16-bit brain floating-point intends to preserve the dynamic range of +/// a 32-bit floating-point value while using half the storage. It does +/// this by using 8 bits for the exponent, the same as `f32`, but only +/// using 7 bits for the mantissa. See [Wikipedia on bfloat16][wikipedia] for +/// more information. +/// +/// [wikipedia]: https://en.wikipedia.org/wiki/Bfloat16_floating-point_format +#[lang = "f16b"] +#[doc(alias = "bf16")] // what hardware often names it +#[doc(alias = "bfloat")] // LLVM's name +#[doc(alias = "bfloat16")] // Wikipedia's name +#[doc(alias = "bfloat16_t")] // The C++ `stdfloat` name +#[allow(non_camel_case_types)] +#[repr(transparent)] +#[unstable(feature = "f16b", issue = "160630")] +pub struct f16b(u16); + +#[doc(test(attr( + feature(cfg_target_has_reliable_f16b), + allow(internal_features, unused_features) +)))] +impl f16b { + /// Raw transmutation from `u16`. + /// + /// This is currently identical to `transmute::(v)` on all platforms. + /// It turns out this is incredibly portable, for two reasons: + /// + /// * Floats and Ints have the same endianness on all supported platforms. + /// * IEEE 754 very precisely specifies the bit layout of floats. + /// + /// However there is one caveat: prior to the 2008 version of IEEE 754, how + /// to interpret the NaN signaling bit wasn't actually specified. Most platforms + /// (notably x86 and ARM) picked the interpretation that was ultimately + /// standardized in 2008, but some didn't (notably MIPS). As a result, all + /// signaling NaNs on MIPS are quiet NaNs on x86, and vice-versa. + /// + /// Rather than trying to preserve signaling-ness cross-platform, this + /// implementation favors preserving the exact bits. This means that + /// any payloads encoded in NaNs will be preserved even if the result of + /// this method is sent over the network from an x86 machine to a MIPS one. + /// + /// If the results of this method are only manipulated by the same + /// architecture that produced them, then there is no portability concern. + /// + /// If the input isn't NaN, then there is no portability concern. + /// + /// If you don't care about signalingness (very likely), then there is no + /// portability concern. + /// + /// Note that this function is distinct from `as` casting, which attempts to + /// preserve the *numeric* value, and not the bitwise value. + /// + /// ```no_run + /// #![feature(f16b)] + /// # #[cfg(target_has_reliable_f16b)] { + /// use core::num::f16b; + /// + /// let v = f16b::from_bits(0x4148); + /// assert_eq!(f32::from(v), 12.5); + /// # } + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "f16b", issue = "160630")] + pub const fn from_bits(bits: u16) -> Self { + // SAFETY: `f16b` and `u16` have the same size, and every bit pattern is valid. + unsafe { mem::transmute(bits) } + } + + /// Raw transmutation to `u16`. + /// + /// This is currently identical to `transmute::(self)` on all platforms. + /// + /// See [`from_bits`](#method.from_bits) for some discussion of the + /// portability of this operation (there are almost no issues). + /// + /// Note that this function is distinct from `as` casting, which attempts to + /// preserve the *numeric* value, and not the bitwise value. + /// + /// ```no_run + /// #![feature(f16b)] + /// # #[cfg(target_has_reliable_f16b)] { + /// use core::num::f16b; + /// + /// assert_eq!(f16b::from_bits(0x4148).to_bits(), 0x4148); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f16b", issue = "160630")] + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn to_bits(self) -> u16 { + // SAFETY: `f16b` and `u16` have the same size, and every bit pattern is valid. + unsafe { mem::transmute(self) } + } +} + +// FIXME(f16b) - This should be a `#[rustc_intrinsic]` using LLVM's `fpext` +// with this implementation constituting the fallback. +#[inline] +const fn widen(value: f16b) -> f32 { + f32::from_bits((value.to_bits() as u32) << 16) +} + +#[unstable(feature = "f16b", issue = "160630")] +impl Copy for f16b {} + +#[unstable(feature = "f16b", issue = "160630")] +impl Clone for f16b { + #[inline] + fn clone(&self) -> Self { + *self + } +} + +#[unstable(feature = "f16b", issue = "160630")] +impl Default for f16b { + #[inline] + fn default() -> Self { + Self::from_bits(0) + } +} + +#[unstable(feature = "f16b", issue = "160630")] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +const impl PartialEq for f16b { + #[inline] + fn eq(&self, other: &Self) -> bool { + widen(*self).eq(&widen(*other)) + } +} + +#[unstable(feature = "f16b", issue = "160630")] +impl PartialOrd for f16b { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + widen(*self).partial_cmp(&widen(*other)) + } +} + +#[unstable(feature = "f16b", issue = "160630")] +impl From for f32 { + #[inline] + fn from(value: f16b) -> Self { + widen(value) + } +} + +#[unstable(feature = "f16b", issue = "160630")] +impl fmt::Debug for f16b { + #[inline] + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&widen(*self), formatter) + } +} + +#[cfg(not(no_fp_fmt_parse))] +#[unstable(feature = "f16b", issue = "160630")] +impl fmt::LowerExp for f16b { + #[inline] + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::LowerExp::fmt(&widen(*self), formatter) + } +} + +#[cfg(not(no_fp_fmt_parse))] +#[unstable(feature = "f16b", issue = "160630")] +impl fmt::UpperExp for f16b { + #[inline] + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::UpperExp::fmt(&widen(*self), formatter) + } +} diff --git a/library/core/src/num/complex.rs b/library/core/src/num/complex.rs index 66126c52fadad..c6a56285cdfb1 100644 --- a/library/core/src/num/complex.rs +++ b/library/core/src/num/complex.rs @@ -1,4 +1,5 @@ -use crate::ops::{Add, Neg, Sub}; +use crate::num::imp::libm::complex::*; +use crate::ops::{Add, Div, Mul, Neg, Sub}; /// A complex number. #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -91,3 +92,54 @@ impl> Sub for Complex { Complex::new(self.re - rhs, self.im) } } + +macro_rules! impl_complex_mul_div { + ($ty:ty, $mul:ident, $div:ident) => { + #[unstable(feature = "complex_numbers", issue = "154023")] + impl Mul for Complex<$ty> { + type Output = Self; + + #[inline] + fn mul(self, rhs: Self) -> Self::Output { + let Complex { re: a, im: b } = self; + let Complex { re: c, im: d } = rhs; + + let ac = a * c; + let bd = b * d; + let ad = a * d; + let bc = b * c; + + let z = Complex::new(ac - bd, ad + bc); + + // Only call the libcall when both components are NaN. + // + // The naive algorithm would return NaN + NaNi for an input like + // (1 + 0i) * (inf + infi). The libcall instead returns inf + infi. + // + // We duplicate the fast path here so that it can be inlined. We use a libcall + // for the NaN correction to reduce the size of `core`. + if z.re.is_nan() && z.im.is_nan() { + crate::hint::cold_path(); + $mul(a, b, c, d) + } else { + z + } + } + } + + #[unstable(feature = "complex_numbers", issue = "154023")] + impl Div for Complex<$ty> { + type Output = Self; + + #[inline] + fn div(self, rhs: Self) -> Self::Output { + $div(self.re, self.im, rhs.re, rhs.im) + } + } + }; +} + +impl_complex_mul_div!(f16, __rust_mulhc3, __rust_divhc3); +impl_complex_mul_div!(f32, __mulsc3, __divsc3); +impl_complex_mul_div!(f64, __muldc3, __divdc3); +impl_complex_mul_div!(f128, __rust_multc3, __rust_divtc3); diff --git a/library/core/src/num/error.rs b/library/core/src/num/error.rs index d102c456f3b4a..6a8a2d5b636ed 100644 --- a/library/core/src/num/error.rs +++ b/library/core/src/num/error.rs @@ -34,7 +34,7 @@ impl fmt::Display for TryFromIntError { #[stable(feature = "try_from", since = "1.34.0")] impl Error for TryFromIntError {} -#[stable(feature = "never_type", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "never_type", since = "1.100.0")] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] const impl From for TryFromIntError { #[inline] diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index db05d7fdc5087..2096b2f4d6d15 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -594,7 +594,7 @@ impl f128 { /// conserved over arithmetic operations, the result of `is_sign_positive` on /// a NaN might produce an unexpected or non-portable result. See the [specification /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == 1.0` - /// if you need fully portable behavior (will return NaN for all NaNs). + /// if you need fully portable behavior (evaluates to `false` for all NaNs). /// /// ``` /// #![feature(f128)] @@ -620,7 +620,7 @@ impl f128 { /// conserved over arithmetic operations, the result of `is_sign_negative` on /// a NaN might produce an unexpected or non-portable result. See the [specification /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == -1.0` - /// if you need fully portable behavior (will return NaN for all NaNs). + /// if you need fully portable behavior (evaluates to `false` for all NaNs). /// /// ``` /// #![feature(f128)] @@ -1544,9 +1544,10 @@ impl f128 { /// ``` #[inline] #[unstable(feature = "clamp_magnitude", issue = "148519")] - #[must_use = "this returns the clamped value and does not modify the original"] + #[rustc_const_unstable(feature = "clamp_magnitude", issue = "148519")] + #[must_use = "method returns a new number and does not mutate the original value"] #[expect(clippy::neg_cmp_op_on_partial_ord, reason = "NaN is also invalid")] - pub fn clamp_magnitude(self, limit: f128) -> f128 { + pub const fn clamp_magnitude(self, limit: f128) -> f128 { assert!(limit >= 0.0, "limit must be non-negative and not NaN"); let limit = limit.abs(); // Canonicalises -0.0 to 0.0 self.clamp(-limit, limit) @@ -1997,6 +1998,45 @@ impl f128 { intrinsics::fmaf128(self, a, b) } + /// Computes `(self * a) + b` with nondeterministic rounding. + /// + /// This is similar to [`mul_add`](Self::mul_add), but the intermediate + /// result may be rounded differently depending on the implementation. + /// The operation is either executed as a single fused multiply-add + /// instruction, or as separate multiply and add instructions. + /// + /// The choice of which one is used is unspecified and non-deterministic: + /// it may vary by target, optimization level, and surrounding code, and + /// even two invocations of this operation with the same inputs may + /// produce different results. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// #![feature(float_mul_add_relaxed)] + /// # #[cfg(any(miri, target_has_reliable_f128_math))] { // Miri uses softfloats, always works + /// + /// // When the fused and unfused operations round differently, either + /// // result may be returned: + /// // - 7.824090399073145653039910391751267e-37 is the fused result (one rounding) + /// // - 1.5046327690525280101999827676444745e-36 is the unfused result (two roundings) + /// let r = 0.1_f128.mul_add_relaxed(0.1_f128, -0.01_f128); + /// assert!( + /// r == 7.824090399073145653039910391751267e-37 + /// || r == 1.5046327690525280101999827676444745e-36 + /// ); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[doc(alias = "fmuladd")] + #[unstable(feature = "float_mul_add_relaxed", issue = "151770")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub const fn mul_add_relaxed(self, a: f128, b: f128) -> f128 { + intrinsics::fmuladdf128(self, a, b) + } + /// Calculates Euclidean division, the matching method for `rem_euclid`. /// /// This computes the integer `n` such that diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 273ef3688ca5f..7332c0a586b8f 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -588,7 +588,7 @@ impl f16 { /// conserved over arithmetic operations, the result of `is_sign_positive` on /// a NaN might produce an unexpected or non-portable result. See the [specification /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == 1.0` - /// if you need fully portable behavior (will return NaN for all NaNs). + /// if you need fully portable behavior (evaluates to `false` for all NaNs). /// /// ``` /// #![feature(f16)] @@ -616,7 +616,7 @@ impl f16 { /// conserved over arithmetic operations, the result of `is_sign_negative` on /// a NaN might produce an unexpected or non-portable result. See the [specification /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == -1.0` - /// if you need fully portable behavior (will return NaN for all NaNs). + /// if you need fully portable behavior (evaluates to `false` for all NaNs). /// /// ``` /// #![feature(f16)] @@ -1530,9 +1530,10 @@ impl f16 { /// ``` #[inline] #[unstable(feature = "clamp_magnitude", issue = "148519")] - #[must_use = "this returns the clamped value and does not modify the original"] + #[rustc_const_unstable(feature = "clamp_magnitude", issue = "148519")] + #[must_use = "method returns a new number and does not mutate the original value"] #[expect(clippy::neg_cmp_op_on_partial_ord, reason = "NaN is also invalid")] - pub fn clamp_magnitude(self, limit: f16) -> f16 { + pub const fn clamp_magnitude(self, limit: f16) -> f16 { assert!(limit >= 0.0, "limit must be non-negative and not NaN"); let limit = limit.abs(); // Canonicalises -0.0 to 0.0 self.clamp(-limit, limit) @@ -1983,6 +1984,42 @@ impl f16 { intrinsics::fmaf16(self, a, b) } + /// Computes `(self * a) + b` with nondeterministic rounding. + /// + /// This is similar to [`mul_add`](Self::mul_add), but the intermediate + /// result may be rounded differently depending on the implementation. + /// The operation is either executed as a single fused multiply-add + /// instruction, or as separate multiply and add instructions. + /// + /// The choice of which one is used is unspecified and non-deterministic: + /// it may vary by target, optimization level, and surrounding code, and + /// even two invocations of this operation with the same inputs may + /// produce different results. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// #![feature(float_mul_add_relaxed)] + /// # #[cfg(target_has_reliable_f16)] { + /// + /// // When the fused and unfused operations round differently, either + /// // result may be returned: + /// // - -7.03e-6 is the fused result (one rounding) + /// // - -7.6e-6 is the unfused result (two roundings) + /// let r = 0.1_f16.mul_add_relaxed(0.1_f16, -0.01_f16); + /// assert!(r == -7.03e-6 || r == -7.6e-6); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[doc(alias = "fmuladd")] + #[unstable(feature = "float_mul_add_relaxed", issue = "151770")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub const fn mul_add_relaxed(self, a: f16, b: f16) -> f16 { + intrinsics::fmuladdf16(self, a, b) + } + /// Calculates Euclidean division, the matching method for `rem_euclid`. /// /// This computes the integer `n` such that diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index d3dea38dc2fcf..c98bff20cdbf9 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -811,7 +811,7 @@ impl f32 { /// conserved over arithmetic operations, the result of `is_sign_positive` on /// a NaN might produce an unexpected or non-portable result. See the [specification /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == 1.0` - /// if you need fully portable behavior (will return NaN for all NaNs). + /// if you need fully portable behavior (evaluates to `false` for all NaNs). /// /// ``` /// let f = 7.0_f32; @@ -836,7 +836,7 @@ impl f32 { /// conserved over arithmetic operations, the result of `is_sign_negative` on /// a NaN might produce an unexpected or non-portable result. See the [specification /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == -1.0` - /// if you need fully portable behavior (will return NaN for all NaNs). + /// if you need fully portable behavior (evaluates to `false` for all NaNs). /// /// ``` /// let f = 7.0f32; @@ -1699,11 +1699,11 @@ impl f32 { /// assert_eq!(2.0f32.clamp_magnitude(3.0), 2.0); /// assert_eq!((-2.0f32).clamp_magnitude(3.0), -2.0); /// ``` - #[must_use = "this returns the clamped value and does not modify the original"] - #[unstable(feature = "clamp_magnitude", issue = "148519")] #[inline] + #[unstable(feature = "clamp_magnitude", issue = "148519")] + #[must_use = "method returns a new number and does not mutate the original value"] #[expect(clippy::neg_cmp_op_on_partial_ord, reason = "NaN is also invalid")] - pub fn clamp_magnitude(self, limit: f32) -> f32 { + pub const fn clamp_magnitude(self, limit: f32) -> f32 { assert!(limit >= 0.0, "limit must be non-negative and not NaN"); let limit = limit.abs(); // Canonicalises -0.0 to 0.0 self.clamp(-limit, limit) @@ -1880,6 +1880,47 @@ impl f32 { intrinsics::frem_algebraic(self, rhs) } + /// Computes `(self * a) + b` with nondeterministic rounding. + /// + /// This is similar to [`mul_add`], but the intermediate result may be + /// rounded differently depending on the implementation. The operation is + /// either executed as a single fused multiply-add instruction, or as + /// separate multiply and add instructions. + /// + /// The choice of which one is used is unspecified and non-deterministic: + /// it may vary by target, optimization level, and surrounding code, and + /// even two invocations of this operation with the same inputs may + /// produce different results. + /// + /// # Precision + /// + /// The result of this operation is not guaranteed: it is either the result + /// of [`mul_add`] (one rounding of the infinite-precision result) or of + /// `self * a + b` (two roundings, with an intermediate rounding of the + /// product). + /// + /// # Examples + /// + /// ``` + /// #![feature(float_mul_add_relaxed)] + /// + /// // When the fused and unfused operations round differently, either + /// // result may be returned: + /// // - 5.2154064e-10 is the fused result (one rounding) + /// // - 9.313226e-10 is the unfused result (two roundings) + /// let r = 0.1_f32.mul_add_relaxed(0.1_f32, -0.01_f32); + /// assert!(r == 5.2154064e-10 || r == 9.313226e-10); + /// ``` + /// + /// [`mul_add`]: ../std/primitive.f32.html#method.mul_add + #[must_use = "method returns a new number and does not mutate the original value"] + #[doc(alias = "fmuladd")] + #[unstable(feature = "float_mul_add_relaxed", issue = "151770")] + #[inline] + pub const fn mul_add_relaxed(self, a: f32, b: f32) -> f32 { + intrinsics::fmuladdf32(self, a, b) + } + /// Returns `self` if the value is not NaN, otherwise returns `replacement` /// if `self` is NaN. /// diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 7c5082749cd11..3a51e49f94f1d 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -810,7 +810,7 @@ impl f64 { /// conserved over arithmetic operations, the result of `is_sign_positive` on /// a NaN might produce an unexpected or non-portable result. See the [specification /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == 1.0` - /// if you need fully portable behavior (will return NaN for all NaNs). + /// if you need fully portable behavior (evaluates to `false` for all NaNs). /// /// ``` /// let f = 7.0_f64; @@ -835,7 +835,7 @@ impl f64 { /// conserved over arithmetic operations, the result of `is_sign_negative` on /// a NaN might produce an unexpected or non-portable result. See the [specification /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == -1.0` - /// if you need fully portable behavior (will return NaN for all NaNs). + /// if you need fully portable behavior (evaluates to `false` for all NaNs). /// /// ``` /// let f = 7.0_f64; @@ -1677,11 +1677,11 @@ impl f64 { /// assert_eq!(2.0f64.clamp_magnitude(3.0), 2.0); /// assert_eq!((-2.0f64).clamp_magnitude(3.0), -2.0); /// ``` - #[must_use = "this returns the clamped value and does not modify the original"] - #[unstable(feature = "clamp_magnitude", issue = "148519")] #[inline] + #[unstable(feature = "clamp_magnitude", issue = "148519")] + #[must_use = "method returns a new number and does not mutate the original value"] #[expect(clippy::neg_cmp_op_on_partial_ord, reason = "NaN is also invalid")] - pub fn clamp_magnitude(self, limit: f64) -> f64 { + pub const fn clamp_magnitude(self, limit: f64) -> f64 { assert!(limit >= 0.0, "limit must be non-negative and not NaN"); let limit = limit.abs(); // Canonicalises -0.0 to 0.0 self.clamp(-limit, limit) @@ -1858,6 +1858,50 @@ impl f64 { intrinsics::frem_algebraic(self, rhs) } + /// Computes `(self * a) + b` with nondeterministic rounding. + /// + /// This is similar to [`mul_add`], but the intermediate result may be + /// rounded differently depending on the implementation. The operation is + /// either executed as a single fused multiply-add instruction, or as + /// separate multiply and add instructions. + /// + /// The choice of which one is used is unspecified and non-deterministic: + /// it may vary by target, optimization level, and surrounding code, and + /// even two invocations of this operation with the same inputs may + /// produce different results. + /// + /// # Precision + /// + /// The result of this operation is not guaranteed: it is either the result + /// of [`mul_add`] (one rounding of the infinite-precision result) or of + /// `self * a + b` (two roundings, with an intermediate rounding of the + /// product). + /// + /// # Examples + /// + /// ``` + /// #![feature(float_mul_add_relaxed)] + /// + /// // When the fused and unfused operations round differently, either + /// // result may be returned: + /// // - 9.020562075079397e-19 is the fused result (one rounding) + /// // - 1.734723475976807e-18 is the unfused result (two roundings) + /// # // FIXME(#114479): on `i586`, x87 excess precision gives neither allowed result + /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse2"))))] { + /// let r = 0.1_f64.mul_add_relaxed(0.1_f64, -0.01_f64); + /// assert!(r == 9.020562075079397e-19 || r == 1.734723475976807e-18); + /// # } + /// ``` + /// + /// [`mul_add`]: ../std/primitive.f64.html#method.mul_add + #[must_use = "method returns a new number and does not mutate the original value"] + #[doc(alias = "fmuladd")] + #[unstable(feature = "float_mul_add_relaxed", issue = "151770")] + #[inline] + pub const fn mul_add_relaxed(self, a: f64, b: f64) -> f64 { + intrinsics::fmuladdf64(self, a, b) + } + /// Returns `self` if the value is not NaN, otherwise returns `replacement` /// if `self` is NaN. /// diff --git a/library/core/src/num/imp/bignum.rs b/library/core/src/num/imp/bignum.rs index 95046e96fc8f2..5c2a19f39dfce 100644 --- a/library/core/src/num/imp/bignum.rs +++ b/library/core/src/num/imp/bignum.rs @@ -386,7 +386,7 @@ macro_rules! define_bignum { /// the digit type for `Big32x40` pub type Digit32 = u32; -#[cfg(target_pointer_width = "32")] +#[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))] define_bignum!(Big32x40: type=Digit32, n=40); /// The digit type for `Big64x20`. @@ -395,12 +395,12 @@ pub type Digit64 = u64; #[cfg(target_pointer_width = "64")] define_bignum!(Big64x20: type=Digit64, n=20); -#[cfg(target_pointer_width = "32")] +#[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))] pub type Big = Big32x40; #[cfg(target_pointer_width = "64")] pub type Big = Big64x20; -#[cfg(target_pointer_width = "32")] +#[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))] pub type Digit = Digit32; #[cfg(target_pointer_width = "64")] pub type Digit = Digit64; diff --git a/library/core/src/num/imp/flt2dec/strategy/dragon.rs b/library/core/src/num/imp/flt2dec/strategy/dragon.rs index 4ae0d4b4ab0ba..b63b93034d57f 100644 --- a/library/core/src/num/imp/flt2dec/strategy/dragon.rs +++ b/library/core/src/num/imp/flt2dec/strategy/dragon.rs @@ -16,22 +16,22 @@ static POW10: [Digit; 10] = [1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000]; // precalculated arrays of `Digit`s for 5^(2^n). // FIXME(#162879): these tables have u64 and u32 versions, future versions may be generated by macro. -#[cfg(target_pointer_width = "32")] +#[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))] static POW5TO16: [Digit; 2] = [0x86f26fc1, 0x23]; #[cfg(target_pointer_width = "64")] static POW5TO16: [Digit; 1] = [0x2386f26fc1]; -#[cfg(target_pointer_width = "32")] +#[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))] static POW5TO32: [Digit; 3] = [0x85acef81, 0x2d6d415b, 0x4ee]; #[cfg(target_pointer_width = "64")] static POW5TO32: [Digit; 2] = [0x2d6d415b85acef81, 0x4ee]; -#[cfg(target_pointer_width = "32")] +#[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))] static POW5TO64: [Digit; 5] = [0xbf6a1f01, 0x6e38ed64, 0xdaa797ed, 0xe93ff9f4, 0x184f03]; #[cfg(target_pointer_width = "64")] static POW5TO64: [Digit; 3] = [0x6e38ed64bf6a1f01, 0xe93ff9f4daa797ed, 0x184f03]; -#[cfg(target_pointer_width = "32")] +#[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))] static POW5TO128: [Digit; 10] = [ 0x2e953e01, 0x3df9909, 0xf1538fd, 0x2374e42f, 0xd3cff5ec, 0xc404dc08, 0xbccdb0da, 0xa6337f19, 0xe91f2603, 0x24e, @@ -40,7 +40,7 @@ static POW5TO128: [Digit; 10] = [ static POW5TO128: [Digit; 5] = [0x3df99092e953e01, 0x2374e42f0f1538fd, 0xc404dc08d3cff5ec, 0xa6337f19bccdb0da, 0x24ee91f2603]; -#[cfg(target_pointer_width = "32")] +#[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))] static POW5TO256: [Digit; 19] = [ 0x982e7c01, 0xbed3875b, 0xd8d99f72, 0x12152f87, 0x6bde50c6, 0xcf4a6e70, 0xd595d80f, 0x26b2716e, 0xadc666b0, 0x1d153624, 0x3c42d35a, 0x63ff540e, 0xcc5573c0, 0x65f9ef17, 0x55bc28f2, 0x80dcc7f7, diff --git a/library/core/src/num/imp/libm.rs b/library/core/src/num/imp/libm.rs index a8d6bdc0b5d7c..6ce02ad60a5df 100644 --- a/library/core/src/num/imp/libm.rs +++ b/library/core/src/num/imp/libm.rs @@ -76,6 +76,27 @@ unsafe extern "C" { pub(crate) safe fn truncf16(x: f16) -> f16; } +/// These symbols are always provided by compiler-builtins. +pub(crate) mod complex { + use crate::num::Complex; + + unsafe extern "C" { + pub(crate) safe fn __mulsc3(a: f32, b: f32, c: f32, d: f32) -> Complex; + pub(crate) safe fn __muldc3(a: f64, b: f64, c: f64, d: f64) -> Complex; + + pub(crate) safe fn __divsc3(a: f32, b: f32, c: f32, d: f32) -> Complex; + pub(crate) safe fn __divdc3(a: f64, b: f64, c: f64, d: f64) -> Complex; + } + + unsafe extern "Rust" { + pub(crate) safe fn __rust_mulhc3(a: f16, b: f16, c: f16, d: f16) -> Complex; + pub(crate) safe fn __rust_multc3(a: f128, b: f128, c: f128, d: f128) -> Complex; + + pub(crate) safe fn __rust_divhc3(a: f16, b: f16, c: f16, d: f16) -> Complex; + pub(crate) safe fn __rust_divtc3(a: f128, b: f128, c: f128, d: f128) -> Complex; + } +} + /// These symbols will be available when `std` is available, and on many no-std platforms. However, /// since this isn't a guarantee, we cannot rely on them for stable implementations. pub(crate) mod likely_available { diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index bb37a734824fa..058c0229aa530 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -2485,7 +2485,6 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[allow(unused_attributes)] #[inline] pub const fn wrapping_abs(self) -> Self { if self.is_negative() { @@ -2978,7 +2977,6 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[allow(unused_attributes)] pub const fn overflowing_neg(self) -> (Self, bool) { if intrinsics::unlikely(self == Self::MIN) { (Self::MIN, true) @@ -3630,7 +3628,6 @@ macro_rules! int_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[allow(unused_attributes)] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -3964,14 +3961,15 @@ macro_rules! int_impl { /// ``` /// #![feature(clamp_magnitude)] #[doc = concat!("assert_eq!(120", stringify!($SelfT), ".clamp_magnitude(100), 100);")] - #[doc = concat!("assert_eq!(-120", stringify!($SelfT), ".clamp_magnitude(100), -100);")] + #[doc = concat!("assert_eq!((-120", stringify!($SelfT), ").clamp_magnitude(100), -100);")] #[doc = concat!("assert_eq!(80", stringify!($SelfT), ".clamp_magnitude(100), 80);")] - #[doc = concat!("assert_eq!(-80", stringify!($SelfT), ".clamp_magnitude(100), -80);")] + #[doc = concat!("assert_eq!((-80", stringify!($SelfT), ").clamp_magnitude(100), -80);")] /// ``` - #[must_use = "this returns the clamped value and does not modify the original"] - #[unstable(feature = "clamp_magnitude", issue = "148519")] #[inline] - pub fn clamp_magnitude(self, limit: $UnsignedT) -> Self { + #[must_use = "method returns a new number and does not mutate the original value"] + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + #[unstable(feature = "clamp_magnitude", issue = "148519")] + pub const fn clamp_magnitude(self, limit: $UnsignedT) -> Self { if let Ok(limit) = core::convert::TryInto::<$SelfT>::try_into(limit) { self.clamp(-limit, limit) } else { diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index db41d23770477..25326f4f066c6 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -43,6 +43,7 @@ mod int_macros; // import int_impl! #[macro_use] mod uint_macros; // import uint_impl! +mod bfloat; mod complex; mod error; #[cfg(not(no_fp_fmt_parse))] @@ -56,6 +57,8 @@ mod wrapping; #[doc(hidden)] pub mod niche_types; +#[unstable(feature = "f16b", issue = "160630")] +pub use bfloat::f16b; #[unstable(feature = "complex_numbers", issue = "154023")] pub use complex::Complex; #[stable(feature = "int_error_matching", since = "1.55.0")] @@ -1573,6 +1576,16 @@ pub enum FpCategory { #[inline(always)] #[unstable(issue = "none", feature = "std_internals")] pub const fn can_not_overflow(radix: u32, is_signed_ty: bool, digits: &[u8]) -> bool { + // Assume that `digits` represents a whole number N in base `radix`. + // Then in infinite precision arithmetic (on whole numbers), we have: + // + // |N| <= pow(radix, digits.len()) - 1 + // <= pow(16, 2 * size_of::() - is_signed) - 1 + // == pow(2, 8 * size_of::() - 4 * is_signed) - 1 + // <= pow(2, 8 * size_of::() - is_signed) - 1 + // == T::MAX + // + // Therefore this condition is sufficient for having no overflow. radix <= 16 && digits.len() <= size_of::() * 2 - is_signed_ty as usize } @@ -1816,20 +1829,23 @@ macro_rules! from_str_int_impl { // Consider radix 16 as it has the highest information density per digit and will thus overflow the earliest: // `u8::MAX` is `ff` - any str of len 2 is guaranteed to not overflow. // `i8::MAX` is `7f` - only a str of len 1 is guaranteed to not overflow. - macro_rules! run_unchecked_loop { - ($unchecked_additive_op:tt) => {{ + // + // NOTE: We could use unchecked arithmetic here, but we don't, based on the observation + // that it produces the same assembly as wrapping ones. See #163099. + macro_rules! run_no_check_loop { + ($additive_op:ident) => {{ while let [c, rest @ ..] = digits { - result = result * (radix as $int_ty); + result = <$int_ty>::wrapping_mul(result, radix as _); let x = unwrap_or_PIE!((*c as char).to_digit(radix), InvalidDigit); - result = result $unchecked_additive_op (x as $int_ty); + result = result.$additive_op(x as $int_ty); digits = rest; } }}; } if is_positive { - run_unchecked_loop!(+) + run_no_check_loop!(wrapping_add) } else { - run_unchecked_loop!(-) + run_no_check_loop!(wrapping_sub) }; } else { macro_rules! run_checked_loop { diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 0563c225f7e0b..c97e6f4c45395 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -1,6 +1,6 @@ //! Definitions of integer that is known not to equal zero. -use super::{IntErrorKind, ParseIntError}; +use super::{IntErrorKind, ParseIntError, TryFromIntError}; use crate::clone::{TrivialClone, UseCloned}; use crate::cmp::Ordering; use crate::hash::{Hash, Hasher}; @@ -9,7 +9,7 @@ use crate::num::imp; use crate::ops::{BitOr, BitOrAssign, Div, DivAssign, Neg, Rem, RemAssign}; use crate::panic::{RefUnwindSafe, UnwindSafe}; use crate::str::FromStr; -use crate::{fmt, intrinsics, ptr, ub_checks}; +use crate::{fmt, intrinsics, ptr, slice, ub_checks}; /// A marker trait for primitive types which can be zero. /// @@ -305,6 +305,114 @@ where } } +#[stable(feature = "more_from_nonzero", since = "CURRENT_RUSTC_VERSION")] +impl<'a, T> From<&'a NonZero> for &'a T +where + T: ZeroablePrimitive, +{ + #[inline] + fn from(nonzero: &'a NonZero) -> &'a T { + nonzero.as_ref() + } +} + +// FIXME: see library/std/tests/slice-from-array-issue-113238.rs +/* +#[stable(feature = "more_from_nonzero", since = "CURRENT_RUSTC_VERSION")] +impl<'a, T> From<&'a [NonZero]> for &'a [T] +where + T: ZeroablePrimitive, +{ + #[inline] + fn from(nonzero: &'a [NonZero]) -> &'a [T] { + nonzero.as_zeroable() + } +} +impl [NonZero] +where + T: ZeroablePrimitive, +{ + /// Implementation of `From<&[NonZero]> for &[T]`. + #[must_use] + #[inline] + fn as_zeroable(&self) -> &[T] { + // SAFETY: `repr(transparent)` ensures that `NonZero` has same layout as `T`, and thus + // `[NonZero]` has same layout as `[T]` + unsafe { &*(slice::from_raw_parts(self.as_ptr().cast::(), self.len())) } + } +} +*/ + +#[stable(feature = "more_from_nonzero", since = "CURRENT_RUSTC_VERSION")] +impl From<[NonZero; N]> for [T; N] +where + T: ZeroablePrimitive, +{ + #[inline] + fn from(nonzero: [NonZero; N]) -> [T; N] { + nonzero.into_zeroable() + } +} + +// FIXME: can't detect that ZeroablePrimitive is a sealed trait +macro_rules! impl_ref_try_from { + ($($t:ty),* $(,)?) => { + $( + #[stable(feature = "more_from_nonzero", since = "CURRENT_RUSTC_VERSION")] + impl<'a> TryFrom<&'a $t> for &'a NonZero<$t> { + type Error = TryFromIntError; + + #[inline] + fn try_from(zeroable: &'a $t) -> Result<&'a NonZero<$t>, TryFromIntError> { + NonZero::from_ref(zeroable).ok_or(TryFromIntError(IntErrorKind::Zero)) + } + } + )* + } +} + +impl_ref_try_from! { + u8, + u16, + u32, + u64, + u128, + usize, + i8, + i16, + i32, + i64, + i128, + isize, + char, +} + +#[stable(feature = "more_from_nonzero", since = "CURRENT_RUSTC_VERSION")] +impl<'a, T> TryFrom<&'a [T]> for &'a [NonZero] +where + T: ZeroablePrimitive, +{ + type Error = TryFromIntError; + + #[inline] + fn try_from(zeroable: &'a [T]) -> Result<&'a [NonZero], TryFromIntError> { + NonZero::from_slice(zeroable).ok_or(TryFromIntError(IntErrorKind::Zero)) + } +} + +#[stable(feature = "more_from_nonzero", since = "CURRENT_RUSTC_VERSION")] +impl TryFrom<[T; N]> for [NonZero; N] +where + T: ZeroablePrimitive, +{ + type Error = TryFromIntError; + + #[inline] + fn try_from(zeroable: [T; N]) -> Result<[NonZero; N], TryFromIntError> { + NonZero::from_array(zeroable).ok_or(TryFromIntError(IntErrorKind::Zero)) + } +} + #[stable(feature = "nonzero_bitor", since = "1.45.0")] #[rustc_const_unstable(feature = "const_ops", issue = "143802")] const impl BitOr for NonZero @@ -460,6 +568,45 @@ where } } + /// Implementation of `From<&NonZero> for &T`. + #[must_use] + #[inline] + fn as_ref(&self) -> &T { + // SAFETY: `repr(transparent)` ensures that `NonZero` has same layout as `T` + unsafe { &*(ptr::from_ref(self).cast::()) } + } + + /// Implementation of `TryFrom<&T> for &NonZero`. + #[must_use] + #[inline] + fn from_ref(n: &T) -> Option<&Self> { + // SAFETY: Memory layout optimization guarantees that `Option>` has + // the same layout and size as `T`, with `0` representing `None`. + let opt_n = unsafe { &*(ptr::from_ref(n).cast::>()) }; + + opt_n.as_ref() + } + + /// Implementation of `TryFrom<&[T]> for &[NonZero]`. + #[must_use] + #[inline] + fn from_slice(n: &[T]) -> Option<&[Self]> { + if n.iter().all(|x| NonZero::new(*x).is_some()) { + // SAFETY: We explicitly checked that all elements are nonzero, and because of `repr(transparent)` + // the layout remains unchanged + Some(unsafe { slice::from_raw_parts(n.as_ptr().cast::>(), n.len()) }) + } else { + None + } + } + + /// Implementation of `TryFrom<[T; N]> for [NonZero; N]`. + #[must_use] + #[inline] + fn from_array(n: [T; N]) -> Option<[Self; N]> { + n.try_map(NonZero::new) + } + /// Returns the contained value as a primitive type. #[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "const_nonzero_get", since = "1.34.0")] @@ -487,6 +634,18 @@ where } } +impl [NonZero; N] +where + T: ZeroablePrimitive, +{ + /// Implementation of `From<[NonZero; N]> for [T; N]`. + #[must_use] + #[inline] + fn into_zeroable(self) -> [T; N] { + self.map(NonZero::get) + } +} + macro_rules! nonzero_integer { ( #[$stability:meta] @@ -1412,7 +1571,8 @@ macro_rules! nonzero_integer { } #[stable(feature = "nonzero_parse", since = "1.35.0")] - impl FromStr for NonZero<$Int> { + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] + const impl FromStr for NonZero<$Int> { type Err = ParseIntError; /// Parses a non-zero integer from a string slice with decimal digits. @@ -1448,6 +1608,7 @@ macro_rules! nonzero_integer { /// #[doc = concat!("assert!(NonZero::<", stringify!($Int), ">::from_str(\"1 \").is_err());")] /// ``` + #[inline] fn from_str(src: &str) -> Result { Self::from_str_radix(src, 10) } @@ -1571,38 +1732,6 @@ macro_rules! nonzero_integer_signedness_dependent_impls { *self = *self % other; } } - - impl NonZero<$Int> { - /// Calculates the quotient of `self` and `rhs`, rounding the result towards positive infinity. - /// - /// The result is guaranteed to be non-zero. - /// - /// # Examples - /// - /// ``` - /// # use std::num::NonZero; - #[doc = concat!("let one = NonZero::new(1", stringify!($Int), ").unwrap();")] - #[doc = concat!("let max = NonZero::new(", stringify!($Int), "::MAX).unwrap();")] - /// assert_eq!(one.div_ceil(max), one); - /// - #[doc = concat!("let two = NonZero::new(2", stringify!($Int), ").unwrap();")] - #[doc = concat!("let three = NonZero::new(3", stringify!($Int), ").unwrap();")] - /// assert_eq!(three.div_ceil(two), two); - /// ``` - #[stable(feature = "unsigned_nonzero_div_ceil", since = "1.92.0")] - #[rustc_const_stable(feature = "unsigned_nonzero_div_ceil", since = "1.92.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn div_ceil(self, rhs: Self) -> Self { - // An implementation of the function without calculating the remainder. - // It is better than the implementation for normal integers, but it can only - // be used here because of the possibility to subtract by one without overflow. - let v = (self.get() - 1) / rhs.get() + 1; - // SAFETY: ceiled division of two positive integers can never be zero. - unsafe { Self::new_unchecked(v) } - } - } }; // Impls for signed nonzero types only. (signed $Int:ty) => { @@ -1770,6 +1899,36 @@ macro_rules! nonzero_integer_signedness_dependent_methods { unsafe { Self::new_unchecked(self.get().unchecked_add(other)) } } + /// Calculates the quotient of `self` and `rhs`, rounding the result towards positive infinity. + /// + /// The result is guaranteed to be non-zero. + /// + /// # Examples + /// + /// ``` + /// # use std::num::NonZero; + #[doc = concat!("let one = NonZero::new(1", stringify!($Int), ").unwrap();")] + #[doc = concat!("let max = NonZero::new(", stringify!($Int), "::MAX).unwrap();")] + /// assert_eq!(one.div_ceil(max), one); + /// + #[doc = concat!("let two = NonZero::new(2", stringify!($Int), ").unwrap();")] + #[doc = concat!("let three = NonZero::new(3", stringify!($Int), ").unwrap();")] + /// assert_eq!(three.div_ceil(two), two); + /// ``` + #[stable(feature = "unsigned_nonzero_div_ceil", since = "1.92.0")] + #[rustc_const_stable(feature = "unsigned_nonzero_div_ceil", since = "1.92.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn div_ceil(self, rhs: Self) -> Self { + // An implementation of the function without calculating the remainder. + // It is better than the implementation for normal integers, but it can only + // be used here because of the possibility to subtract by one without overflow. + let v = (self.get() - 1) / rhs.get() + 1; + // SAFETY: ceiled division of two positive integers can never be zero. + unsafe { Self::new_unchecked(v) } + } + /// Returns the smallest power of two greater than or equal to `self`. /// Checks for overflow and returns [`None`] /// if the next power of two is greater than the type’s maximum value. @@ -2453,6 +2612,50 @@ macro_rules! nonzero_integer_signedness_dependent_methods { unsafe { NonZero::new_unchecked(self.get().cast_unsigned()) } } + /// Clamps this number to a symmetric range centred around zero. + /// + /// The method clamps the number's magnitude (absolute value) to be at most `limit`. + /// + /// This is functionally equivalent to `self.clamp(-limit, limit)`, but is more + /// explicit about the intent. + /// + /// # Examples + /// + /// ``` + /// #![feature(clamp_magnitude)] + /// # use std::num::NonZero; + /// # + #[doc = concat!("let limit = NonZero::<", stringify!($Uint), ">::new(100).unwrap();")] + /// + /// assert_eq!( + #[doc = concat!(" NonZero::<", stringify!($Int), ">::new(120).unwrap().clamp_magnitude(limit),")] + #[doc = concat!(" NonZero::<", stringify!($Int), ">::new(100).unwrap(),")] + /// ); + /// + /// assert_eq!( + #[doc = concat!(" NonZero::<", stringify!($Int), ">::new(-120).unwrap().clamp_magnitude(limit),")] + #[doc = concat!(" NonZero::<", stringify!($Int), ">::new(-100).unwrap(),")] + /// ); + /// + /// assert_eq!( + #[doc = concat!(" NonZero::<", stringify!($Int), ">::new(80).unwrap().clamp_magnitude(limit),")] + #[doc = concat!(" NonZero::<", stringify!($Int), ">::new(80).unwrap(),")] + /// ); + /// + /// assert_eq!( + #[doc = concat!(" NonZero::<", stringify!($Int), ">::new(-80).unwrap().clamp_magnitude(limit),")] + #[doc = concat!(" NonZero::<", stringify!($Int), ">::new(-80).unwrap(),")] + /// ); + /// ``` + #[inline] + #[must_use = "method returns a new number and does not mutate the original value"] + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + #[unstable(feature = "clamp_magnitude", issue = "148519")] + pub const fn clamp_magnitude(self, limit: NonZero<$Uint>) -> Self { + // SAFETY: a non-zero value clamped to the magnitude of a non-zero value is still non-zero. + unsafe { Self::new_unchecked(self.get().clamp_magnitude(limit.get())) } + } + }; } diff --git a/library/core/src/num/saturating.rs b/library/core/src/num/saturating.rs index efe96a9ab7494..88961a1a28de8 100644 --- a/library/core/src/num/saturating.rs +++ b/library/core/src/num/saturating.rs @@ -31,6 +31,10 @@ use crate::ops::{ /// /// assert_eq!(u32::MAX, (max + one).0); /// ``` +/// +/// # Layout +/// +/// `Saturating` is guaranteed to have the same layout and ABI as `T`. #[stable(feature = "saturating_int_impl", since = "1.74.0")] #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default, Hash)] #[repr(transparent)] diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index a53c11e2d876d..fcda1edcf4cb2 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -526,8 +526,6 @@ macro_rules! uint_impl { /// # Examples /// /// ``` - /// #![feature(funnel_shifts)] - /// #[doc = concat!("let a = ", $rot_op, "_", stringify!($SelfT), ";")] #[doc = concat!("let b = ", $fsh_op, "_", stringify!($SelfT), ";")] /// @@ -547,10 +545,8 @@ macro_rules! uint_impl { /// rotating by an unbounded amount like [`rotate_left`](Self::rotate_left) does: /// /// ```should_panic - /// #![feature(funnel_shifts)] /// # #![feature(cfg_overflow_checks)] /// # #[cfg(overflow_checks)] { - /// #[doc = concat!("let a = ", stringify!($SelfT), "::MAX;")] /// // Okay #[doc = concat!("let _ = a.rotate_left(", stringify!($SelfT), "::BITS);")] @@ -559,8 +555,8 @@ macro_rules! uint_impl { /// # } /// # #[cfg(not(overflow_checks))] panic!("fulfill should_panic"); /// ``` - #[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")] - #[unstable(feature = "funnel_shifts", issue = "145686")] + #[rustc_const_stable(feature = "funnel_shifts", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "funnel_shifts", since = "CURRENT_RUSTC_VERSION")] #[must_use = "this returns the result of the operation, without modifying the original"] #[inline(always)] #[rustc_inherit_overflow_checks] @@ -597,8 +593,6 @@ macro_rules! uint_impl { /// # Examples /// /// ``` - /// #![feature(funnel_shifts)] - /// #[doc = concat!("let a = ", $rot_op, "_", stringify!($SelfT), ";")] #[doc = concat!("let b = ", $fsh_op, "_", stringify!($SelfT), ";")] /// @@ -618,10 +612,8 @@ macro_rules! uint_impl { /// rotating by an unbounded amount like [`rotate_right`](Self::rotate_right) does: /// /// ```should_panic - /// #![feature(funnel_shifts)] /// # #![feature(cfg_overflow_checks)] /// # #[cfg(overflow_checks)] { - /// #[doc = concat!("let a = ", stringify!($SelfT), "::MAX;")] /// // Okay #[doc = concat!("let _ = a.rotate_right(", stringify!($SelfT), "::BITS);")] @@ -630,8 +622,8 @@ macro_rules! uint_impl { /// # } /// # #[cfg(not(overflow_checks))] panic!("fulfill should_panic"); /// ``` - #[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")] - #[unstable(feature = "funnel_shifts", issue = "145686")] + #[rustc_const_stable(feature = "funnel_shifts", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "funnel_shifts", since = "CURRENT_RUSTC_VERSION")] #[must_use = "this returns the result of the operation, without modifying the original"] #[inline(always)] #[rustc_inherit_overflow_checks] @@ -654,8 +646,9 @@ macro_rules! uint_impl { #[doc = concat!("`", stringify!($SelfT) , "::BITS`,")] /// i.e. when [`funnel_shl`](Self::funnel_shl) would panic. /// - #[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")] - #[unstable(feature = "funnel_shifts", issue = "145686")] + #[rustc_const_stable(feature = "funnel_shifts", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "funnel_shifts", since = "CURRENT_RUSTC_VERSION")] + #[rustc_allow_const_fn_unstable(const_trait_impl)] #[must_use = "this returns the result of the operation, without modifying the original"] #[inline(always)] #[track_caller] @@ -680,8 +673,9 @@ macro_rules! uint_impl { #[doc = concat!("`", stringify!($SelfT) , "::BITS`,")] /// i.e. when [`funnel_shr`](Self::funnel_shr) would panic. /// - #[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")] - #[unstable(feature = "funnel_shifts", issue = "145686")] + #[rustc_const_stable(feature = "funnel_shifts", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "funnel_shifts", since = "CURRENT_RUSTC_VERSION")] + #[rustc_allow_const_fn_unstable(const_trait_impl)] #[must_use = "this returns the result of the operation, without modifying the original"] #[inline(always)] #[track_caller] diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 2fe7c8c305afe..f53eb4a294194 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -1202,9 +1202,9 @@ impl Option { /// Returns the provided default result (if none), /// or applies a function to the contained value (if any). /// - /// Arguments passed to `map_or` are eagerly evaluated; if you are passing + /// The `default` argument passed to `map_or` is eagerly evaluated; if you are passing /// the result of a function call, it is recommended to use [`map_or_else`], - /// which is lazily evaluated. + /// which lazily evaluates `default`. /// /// [`map_or_else`]: Option::map_or_else /// diff --git a/library/core/src/panic/location.rs b/library/core/src/panic/location.rs index f37f5370997e1..43fbda2adf476 100644 --- a/library/core/src/panic/location.rs +++ b/library/core/src/panic/location.rs @@ -21,7 +21,7 @@ use crate::ptr::NonNull; /// if let Some(location) = panic_info.location() { /// println!("panic occurred in file '{}' at line {}", location.file(), location.line()); /// } else { -/// println!("panic occurred but can't get location information..."); +/// println!("panicked at unknown location"); /// } /// })); /// @@ -47,7 +47,8 @@ pub struct Location<'a> { } #[stable(feature = "panic_hooks", since = "1.10.0")] -impl PartialEq for Location<'_> { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +const impl PartialEq for Location<'_> { fn eq(&self, other: &Self) -> bool { // Compare col / line first as they're cheaper to compare and more likely to differ, // while not impacting the result. @@ -56,20 +57,23 @@ impl PartialEq for Location<'_> { } #[stable(feature = "panic_hooks", since = "1.10.0")] -impl Eq for Location<'_> {} +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +const impl Eq for Location<'_> {} #[stable(feature = "panic_hooks", since = "1.10.0")] -impl Ord for Location<'_> { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +const impl Ord for Location<'_> { fn cmp(&self, other: &Self) -> Ordering { self.file() .cmp(other.file()) - .then_with(|| self.line.cmp(&other.line)) - .then_with(|| self.col.cmp(&other.col)) + .then_with(const || self.line.cmp(&other.line)) + .then_with(const || self.col.cmp(&other.col)) } } #[stable(feature = "panic_hooks", since = "1.10.0")] -impl PartialOrd for Location<'_> { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +const impl PartialOrd for Location<'_> { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } @@ -208,7 +212,7 @@ impl<'a> Location<'a> { /// if let Some(location) = panic_info.location() { /// println!("panic occurred in file '{}'", location.file()); /// } else { - /// println!("panic occurred but can't get location information..."); + /// println!("panicked at unknown location"); /// } /// })); /// @@ -255,7 +259,7 @@ impl<'a> Location<'a> { /// if let Some(location) = panic_info.location() { /// println!("panic occurred at line {}", location.line()); /// } else { - /// println!("panic occurred but can't get location information..."); + /// println!("panicked at unknown location"); /// } /// })); /// @@ -280,7 +284,7 @@ impl<'a> Location<'a> { /// if let Some(location) = panic_info.location() { /// println!("panic occurred at column {}", location.column()); /// } else { - /// println!("panic occurred but can't get location information..."); + /// println!("panicked at unknown location"); /// } /// })); /// diff --git a/library/core/src/panic/panic_info.rs b/library/core/src/panic/panic_info.rs index ee22521cb014c..d11f902b13227 100644 --- a/library/core/src/panic/panic_info.rs +++ b/library/core/src/panic/panic_info.rs @@ -68,29 +68,24 @@ impl<'a> PanicInfo<'a> { /// This method will currently always return [`Some`], but this may change /// in future versions. /// - /// # Examples - /// - /// ```should_panic - /// use std::panic; + /// # Example /// - /// panic::set_hook(Box::new(|panic_info| { + /// ```ignore (no_std) + /// #[panic_handler] + /// fn panic_handler(panic_info: &PanicInfo<'_>) -> ! { /// if let Some(location) = panic_info.location() { - /// println!("panic occurred in file '{}' at line {}", - /// location.file(), - /// location.line(), - /// ); + /// write!(DEBUG_OUTPUT, "panicked at {}", location); /// } else { - /// println!("panic occurred but can't get location information..."); + /// write!(DEBUG_OUTPUT, "panicked at unknown location"); /// } - /// })); - /// - /// panic!("Normal panic"); + /// loop {} + /// } /// ``` #[must_use] #[stable(feature = "panic_hooks", since = "1.10.0")] pub fn location(&self) -> Option<&'static Location<'static>> { // NOTE: If this is changed to sometimes return None, - // deal with that case in std::panicking::default_hook and core::panicking::panic_fmt. + // deal with that case in std::panicking::panic_handler and core::panicking::panic_fmt. Some(self.location) } @@ -106,7 +101,7 @@ impl<'a> PanicInfo<'a> { /// [`std::panic::PanicHookInfo::payload`]: ../../std/panic/struct.PanicHookInfo.html#method.payload #[deprecated(since = "1.81.0", note = "this never returns anything useful")] #[stable(feature = "panic_hooks", since = "1.10.0")] - #[allow(deprecated, deprecated_in_future)] + #[allow(deprecated)] pub fn payload(&self) -> &(dyn crate::any::Any + Send) { struct NoPayload; &NoPayload diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 04722e4e2fc10..bd4fbcf68b256 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -59,7 +59,7 @@ compile_error!( #[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! { if cfg!(panic = "immediate-abort") { - super::intrinsics::abort() + super::intrinsics::abort_immediate() } // NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call @@ -100,7 +100,7 @@ pub const fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: boo panic_fmt(fmt) } else #[track_caller] { if cfg!(panic = "immediate-abort") { - super::intrinsics::abort() + super::intrinsics::abort_immediate() } // NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call @@ -265,7 +265,7 @@ pub const fn panic_display(x: &T) -> ! { #[lang = "panic_bounds_check"] // needed by codegen for panic on OOB array/slice access fn panic_bounds_check(index: usize, len: usize) -> ! { if cfg!(panic = "immediate-abort") { - super::intrinsics::abort() + super::intrinsics::abort_immediate() } panic!("index out of bounds: the len is {len} but the index is {index}") @@ -278,7 +278,7 @@ fn panic_bounds_check(index: usize, len: usize) -> ! { #[rustc_nounwind] // `CheckAlignment` MIR pass requires this function to never unwind fn panic_misaligned_pointer_dereference(required: usize, found: usize) -> ! { if cfg!(panic = "immediate-abort") { - super::intrinsics::abort() + super::intrinsics::abort_immediate() } panic_nounwind_fmt( @@ -296,7 +296,7 @@ fn panic_misaligned_pointer_dereference(required: usize, found: usize) -> ! { #[rustc_nounwind] // `CheckNull` MIR pass requires this function to never unwind fn panic_null_pointer_dereference() -> ! { if cfg!(panic = "immediate-abort") { - super::intrinsics::abort() + super::intrinsics::abort_immediate() } panic_nounwind_fmt( @@ -312,7 +312,7 @@ fn panic_null_pointer_dereference() -> ! { #[rustc_nounwind] // `CheckNull` MIR pass requires this function to never unwind fn panic_null_reference_constructed() -> ! { if cfg!(panic = "immediate-abort") { - super::intrinsics::abort() + super::intrinsics::abort_immediate() } panic_nounwind_fmt(format_args!("null reference produced"), /* force_no_backtrace */ false) @@ -325,7 +325,7 @@ fn panic_null_reference_constructed() -> ! { #[rustc_nounwind] // `CheckEnums` MIR pass requires this function to never unwind fn panic_invalid_enum_construction(source: u128) -> ! { if cfg!(panic = "immediate-abort") { - super::intrinsics::abort() + super::intrinsics::abort_immediate() } panic_nounwind_fmt( diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 4b84a9de4ce72..1c7bf87c54bbc 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -282,7 +282,7 @@ const _: () = (); /// todo!() as std::iter::Empty<_> /// } /// ``` -#[stable(feature = "never_type", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "never_type", since = "1.100.0")] const _: () = (); // Required to make auto trait impls render. @@ -1810,9 +1810,14 @@ const _: () = (); /// `fn name(...) -> ...` implicitly uses the `"Rust"` ABI string and `extern fn name(...) -> ...` /// implicitly uses the `"C"` ABI string. /// -/// The ABI strings are guaranteed to be compatible if they are the same, or if the caller ABI -/// string is `$X-unwind` and the callee ABI string is `$X`, where `$X` is one of the following: -/// "C", "aapcs", "fastcall", "stdcall", "system", "sysv64", "thiscall", "vectorcall", "win64". +/// The ABI strings are guaranteed to be compatible if they are the same, or if one of them is +/// `$X-unwind` and the other one is `$X`, where `$X` is one of the following: "C", "aapcs", +/// "fastcall", "stdcall", "system", "sysv64", "thiscall", "vectorcall", "win64". (Note that [it is +/// undefined behavior][unwind-ub] for a function to unwind unless *both* caller and callee use a +/// signature that permits unwinding, such as "C-unwind". Rust ensures that a function defined with +/// a non-unwinding ABI such as "C" never unwinds due to a panic, so undefined behavior can only +/// arise if the callee ABI is `$X-unwind` and the caller ABI is `$X`, or if a non-Rust unwind +/// occurs.) /// /// The following types are guaranteed to be ABI-compatible: /// @@ -1829,9 +1834,23 @@ const _: () = (); /// call will be valid ABI-wise. The callee receives the result of transmuting the function pointer /// from `fn()` to `fn(i32)`; that transmutation is itself a well-defined operation, it's just /// almost certainly UB to later call that function pointer.) -/// - Any two types with size 0 and alignment 1 are ABI-compatible. -/// - A `repr(transparent)` type `T` is ABI-compatible with its unique non-trivial field, i.e., the -/// unique field that doesn't have size 0 and alignment 1 (if there is such a field). +/// - Any two types with "trivial ABI" are ABI-compatible. +/// A type has trivial ABI if is satisfies all of the following: +/// - It has size 0. +/// - It has alignment 1. +/// - One of the following apply: +/// - It is a `repr(Rust)` `struct`, `enum`, or `union` (regardless of its fields, +/// regardless of whether `repr(Rust)` is specified explicitly or used via the implicit default, +/// and possibly with additional `repr` modifiers such as `packed`). +/// - It is a [prim@tuple] (regardless of its fields, and including [`()`][prim@unit]). +/// - It is a `repr(transparent)` `struct`, `enum`, or `union`, and all fields have trivial ABI. +/// - It is an array, and its element type has trivial ABI. (This requirement applies even to arrays of length 0.) +/// - It is [the never type `!`][never]. +// - It is a pattern type or an unsafe binder type, and the inner type has trivial ABI. +// (These are still unstable so intentionally not included in the user-visible doc comment, as their rules may change.) +/// - It is a function item type or closure type. +/// - A `repr(transparent)` type is ABI-compatible with its unique field that does not have trivial ABI +/// (as defined above), if such a field exists. (Note that if no such field exists, then the `repr(transparent)` type itself has trivial ABI, so the case above applies.) /// - `i32` is ABI-compatible with `NonZero`, and similar for all other integer types. /// - If `T` is guaranteed to be subject to the [null pointer /// optimization](option/index.html#representation), and `E` is an enum satisfying the following @@ -1878,7 +1897,8 @@ const _: () = (); /// Behavior since transmuting `None::>` to `NonZero` violates the non-zero /// requirement. /// -/// [cfi-docs]: https://doc.rust-lang.org/beta/unstable-book/compiler-flags/sanitizer.html#controlflowintegrity +/// [unwind-ub]: ../reference/behavior-considered-undefined.html#r-undefined.call +/// [cfi-docs]: ../unstable-book/compiler-flags/sanitizer.html#controlflowintegrity /// /// ### Trait implementations /// diff --git a/library/core/src/process.rs b/library/core/src/process.rs index 81481b2cba26e..1c2769f07895b 100644 --- a/library/core/src/process.rs +++ b/library/core/src/process.rs @@ -35,9 +35,10 @@ /// Other signals such as `SIGABRT`, `SIGTRAP`, `SIGSEGV`, and `SIGBUS` may be /// produced instead, depending on specifics. This is not an exhaustive list. #[unstable(feature = "abort_immediate", issue = "154601")] +#[rustc_const_unstable(feature = "abort_immediate", issue = "154601")] #[cold] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[doc(alias = "halt")] -pub fn abort_immediate() -> ! { - crate::intrinsics::abort() +pub const fn abort_immediate() -> ! { + crate::intrinsics::abort_immediate() } diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 5ec875cf47868..a279c373dea4a 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -1559,7 +1559,7 @@ impl NonNull<[T]> { /// # Examples /// /// ```rust - /// #![feature(allocator_api, ptr_as_uninit)] + /// #![feature(ptr_as_uninit)] /// /// use std::alloc::{Allocator, Layout, Global}; /// use std::mem::MaybeUninit; diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 472d568c5cf66..9d898df3c6d3c 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -842,9 +842,9 @@ impl Result { /// Returns the provided default (if [`Err`]), or /// applies a function to the contained value (if [`Ok`]). /// - /// Arguments passed to `map_or` are eagerly evaluated; if you are passing + /// The `default` argument passed to `map_or` is eagerly evaluated; if you are passing /// the result of a function call, it is recommended to use [`map_or_else`], - /// which is lazily evaluated. + /// which lazily evaluates `default`. /// /// [`map_or_else`]: Result::map_or_else /// @@ -1343,8 +1343,6 @@ impl Result { /// # Examples /// /// ``` - /// # #![feature(unwrap_infallible)] - /// /// fn only_good_news() -> Result { /// Ok("this is fine".into()) /// } @@ -1352,7 +1350,7 @@ impl Result { /// let s: String = only_good_news().into_ok(); /// println!("{s}"); /// ``` - #[unstable(feature = "unwrap_infallible", issue = "61695")] + #[stable(feature = "unwrap_infallible", since = "CURRENT_RUSTC_VERSION")] #[inline] #[rustc_allow_const_fn_unstable(const_precise_live_drops)] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] @@ -1379,8 +1377,6 @@ impl Result { /// # Examples /// /// ``` - /// # #![feature(unwrap_infallible)] - /// /// fn only_bad_news() -> Result { /// Err("Oops, it failed".into()) /// } @@ -1388,7 +1384,7 @@ impl Result { /// let error: String = only_bad_news().into_err(); /// println!("{error}"); /// ``` - #[unstable(feature = "unwrap_infallible", issue = "61695")] + #[stable(feature = "unwrap_infallible", since = "CURRENT_RUSTC_VERSION")] #[inline] #[rustc_allow_const_fn_unstable(const_precise_live_drops)] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index fa4d6d9f90946..6deba9ce9752e 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -885,7 +885,7 @@ impl [T] { /// Swaps two elements in the slice. /// - /// If `a` equals to `b`, it's guaranteed that elements won't change value. + /// If `a` equals `b`, it's guaranteed that elements won't change value. /// /// # Arguments /// @@ -2125,7 +2125,7 @@ impl [T] { /// Divides one slice into two at an index, returning `None` if the slice is /// too short. /// - /// If `mid ≤ len` returns a pair of slices where the first will contain all + /// If `mid ≤ len`, returns a pair of slices where the first will contain all /// indices from `[0, mid)` (excluding the index `mid` itself) and the /// second will contain all indices from `[mid, len)` (excluding the index /// `len` itself). @@ -2174,7 +2174,7 @@ impl [T] { /// Divides one mutable slice into two at an index, returning `None` if the /// slice is too short. /// - /// If `mid ≤ len` returns a pair of slices where the first will contain all + /// If `mid ≤ len`, returns a pair of slices where the first will contain all /// indices from `[0, mid)` (excluding the index `mid` itself) and the /// second will contain all indices from `[mid, len)` (excluding the index /// `len` itself). @@ -2801,7 +2801,7 @@ impl [T] { /// assert_eq!(b"hello".trim_prefix(prefix.as_bytes()), b"llo".as_ref()); /// ``` #[must_use = "returns the subslice without modifying the original"] - #[stable(feature = "trim_prefix_suffix", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "trim_prefix_suffix", since = "1.100.0")] pub fn trim_prefix + ?Sized>(&self, prefix: &P) -> &[T] where T: PartialEq, @@ -2839,7 +2839,7 @@ impl [T] { /// assert_eq!(v.trim_suffix(&[50, 30]), &[10, 40, 30][..]); /// ``` #[must_use = "returns the subslice without modifying the original"] - #[stable(feature = "trim_prefix_suffix", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "trim_prefix_suffix", since = "1.100.0")] pub fn trim_suffix + ?Sized>(&self, suffix: &P) -> &[T] where T: PartialEq, diff --git a/library/core/src/slice/sort/shared/pivot.rs b/library/core/src/slice/sort/shared/pivot.rs index 9eb60f854ce21..878ec27419fc2 100644 --- a/library/core/src/slice/sort/shared/pivot.rs +++ b/library/core/src/slice/sort/shared/pivot.rs @@ -17,7 +17,7 @@ pub fn choose_pivot bool>(v: &[T], is_less: &mut F) -> us let len = v.len(); if len < 8 { - intrinsics::abort(); + intrinsics::abort_immediate(); } // SAFETY: a, b, c point to initialized regions of len_div_8 elements, diff --git a/library/core/src/slice/sort/shared/smallsort.rs b/library/core/src/slice/sort/shared/smallsort.rs index 40939f922bcb6..1510c3d6d8d6c 100644 --- a/library/core/src/slice/sort/shared/smallsort.rs +++ b/library/core/src/slice/sort/shared/smallsort.rs @@ -228,7 +228,7 @@ fn small_sort_general_with_scratch bool>( } if scratch.len() < len + 16 { - intrinsics::abort(); + intrinsics::abort_immediate(); } let v_base = v.as_mut_ptr(); @@ -321,7 +321,7 @@ where } if len > SMALL_SORT_NETWORK_SCRATCH_LEN { - intrinsics::abort(); + intrinsics::abort_immediate(); } let mut stack_array = MaybeUninit::<[T; SMALL_SORT_NETWORK_SCRATCH_LEN]>::uninit(); @@ -422,7 +422,7 @@ where F: FnMut(&T, &T) -> bool, { if v.len() < 9 { - intrinsics::abort(); + intrinsics::abort_immediate(); } let v_base = v.as_mut_ptr(); @@ -471,7 +471,7 @@ where F: FnMut(&T, &T) -> bool, { if v.len() < 13 { - intrinsics::abort(); + intrinsics::abort_immediate(); } let v_base = v.as_mut_ptr(); @@ -578,7 +578,7 @@ pub fn insertion_sort_shift_left bool>( ) { let len = v.len(); if offset == 0 || offset > len { - intrinsics::abort(); + intrinsics::abort_immediate(); } // SAFETY: see individual comments. diff --git a/library/core/src/slice/sort/stable/quicksort.rs b/library/core/src/slice/sort/stable/quicksort.rs index acc8a5e838e12..1696a5e3c0277 100644 --- a/library/core/src/slice/sort/stable/quicksort.rs +++ b/library/core/src/slice/sort/stable/quicksort.rs @@ -96,7 +96,7 @@ fn stable_partition bool>( let len = v.len(); if intrinsics::unlikely(scratch.len() < len || pivot_pos >= len) { - core::intrinsics::abort() + core::intrinsics::abort_immediate() } let v_base = v.as_ptr(); diff --git a/library/core/src/slice/sort/unstable/quicksort.rs b/library/core/src/slice/sort/unstable/quicksort.rs index 5f2991ee722d2..58a9aae7c7afa 100644 --- a/library/core/src/slice/sort/unstable/quicksort.rs +++ b/library/core/src/slice/sort/unstable/quicksort.rs @@ -102,7 +102,7 @@ where } if pivot >= len { - intrinsics::abort(); + intrinsics::abort_immediate(); } // SAFETY: We checked that `pivot` is in-bounds. @@ -124,7 +124,7 @@ where let num_lt = (const { inst_partition::() })(v_without_pivot, pivot, is_less); if num_lt >= len { - intrinsics::abort(); + intrinsics::abort_immediate(); } // SAFETY: We checked that `num_lt` is in-bounds. diff --git a/library/core/src/str/iter.rs b/library/core/src/str/iter.rs index b221b98a7b791..60b9e81aaeff9 100644 --- a/library/core/src/str/iter.rs +++ b/library/core/src/str/iter.rs @@ -1284,7 +1284,7 @@ pub struct SplitAsciiWhitespace<'a> { } /// An iterator over the substrings of a string, -/// terminated by a substring matching to a predicate function +/// terminated by a substring matching to a predicate function. /// Unlike `Split`, it contains the matched part as a terminator /// of the subslice. /// diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index cc96e51d83393..017d3f50eba32 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -510,7 +510,6 @@ impl str { #[rustc_const_stable(feature = "str_as_bytes", since = "1.39.0")] #[must_use] #[inline(always)] - #[allow(unused_attributes)] pub const fn as_bytes(&self) -> &[u8] { // SAFETY: const sound because we transmute two types with the same layout unsafe { mem::transmute(self) } @@ -2557,7 +2556,7 @@ impl str { /// ``` #[must_use = "this returns the remaining substring as a new slice, \ without modifying the original"] - #[stable(feature = "trim_prefix_suffix", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "trim_prefix_suffix", since = "1.100.0")] pub fn trim_prefix(&self, prefix: P) -> &str { prefix.strip_prefix_of(self).unwrap_or(self) } @@ -2592,7 +2591,7 @@ impl str { /// ``` #[must_use = "this returns the remaining substring as a new slice, \ without modifying the original"] - #[stable(feature = "trim_prefix_suffix", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "trim_prefix_suffix", since = "1.100.0")] pub fn trim_suffix(&self, suffix: P) -> &str where for<'a> P::Searcher<'a>: ReverseSearcher<'a>, diff --git a/library/core/src/str/traits.rs b/library/core/src/str/traits.rs index 77fa6fcfbbb3c..480e4a1b0128a 100644 --- a/library/core/src/str/traits.rs +++ b/library/core/src/str/traits.rs @@ -15,7 +15,8 @@ use crate::{ops, range}; /// culturally-accepted standards requires locale-specific data that is outside the scope of /// the `str` type. #[stable(feature = "rust1", since = "1.0.0")] -impl Ord for str { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +const impl Ord for str { #[inline] fn cmp(&self, other: &str) -> Ordering { self.as_bytes().cmp(other.as_bytes()) @@ -43,7 +44,8 @@ const impl Eq for str {} /// culturally-accepted standards requires locale-specific data that is outside the scope of /// the `str` type. #[stable(feature = "rust1", since = "1.0.0")] -impl PartialOrd for str { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +const impl PartialOrd for str { #[inline] fn partial_cmp(&self, other: &str) -> Option { Some(self.cmp(other)) diff --git a/library/core/src/sync/mod.rs b/library/core/src/sync/mod.rs index 6c2660263a705..bd85bffcf192a 100644 --- a/library/core/src/sync/mod.rs +++ b/library/core/src/sync/mod.rs @@ -4,5 +4,5 @@ pub mod atomic; mod sync_view; -#[unstable(feature = "exclusive_wrapper", issue = "98407")] +#[stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] pub use sync_view::SyncView; diff --git a/library/core/src/sync/sync_view.rs b/library/core/src/sync/sync_view.rs index f4822048d2153..5c1e24125cf74 100644 --- a/library/core/src/sync/sync_view.rs +++ b/library/core/src/sync/sync_view.rs @@ -47,8 +47,6 @@ use core::task::{Context, Poll}; /// functionality: /// /// ```ignore-wasm -/// #![feature(exclusive_wrapper)] -/// /// use std::sync::SyncView; /// use std::sync::mpsc::{self, Receiver}; /// use std::thread; @@ -91,7 +89,7 @@ use core::task::{Context, Poll}; /// for any value. This is a parallel with the fact that /// `&` and `&mut` references together can be thought of as a _compile-time_ /// version of a read-write lock. -#[unstable(feature = "exclusive_wrapper", issue = "98407")] +#[stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] #[doc(alias = "SyncWrapper")] #[doc(alias = "SyncCell")] #[doc(alias = "Unique")] @@ -105,10 +103,10 @@ pub struct SyncView { } // See `SyncView`'s docs for justification. -#[unstable(feature = "exclusive_wrapper", issue = "98407")] +#[stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] unsafe impl Sync for SyncView {} -#[unstable(feature = "exclusive_wrapper", issue = "98407")] +#[stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_default", issue = "143894")] const impl Default for SyncView where @@ -120,7 +118,7 @@ where } } -#[unstable(feature = "exclusive_wrapper", issue = "98407")] +#[stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] impl fmt::Debug for SyncView { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { f.debug_struct("SyncView").finish_non_exhaustive() @@ -129,8 +127,8 @@ impl fmt::Debug for SyncView { impl SyncView { /// Wrap a value in an `SyncView` - #[unstable(feature = "exclusive_wrapper", issue = "98407")] - #[rustc_const_unstable(feature = "exclusive_wrapper", issue = "98407")] + #[stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] #[must_use] #[inline] pub const fn new(t: T) -> Self { @@ -138,8 +136,9 @@ impl SyncView { } /// Unwrap the value contained in the `SyncView` - #[unstable(feature = "exclusive_wrapper", issue = "98407")] - #[rustc_const_unstable(feature = "exclusive_wrapper", issue = "98407")] + #[stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] + #[rustc_allow_const_fn_unstable(const_precise_live_drops)] #[must_use] #[inline] pub const fn into_inner(self) -> T { @@ -154,8 +153,8 @@ impl SyncView { /// value, which means _unpinned_ `SyncView`s can produce _unpinned_ /// access to the underlying value, but _pinned_ `SyncView`s only /// produce _pinned_ access to the underlying value. - #[unstable(feature = "exclusive_wrapper", issue = "98407")] - #[rustc_const_unstable(feature = "exclusive_wrapper", issue = "98407")] + #[stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] #[must_use] #[inline] pub const fn as_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> { @@ -167,8 +166,8 @@ impl SyncView { /// Build a _mutable_ reference to an `SyncView` from /// a _mutable_ reference to a `T`. This allows you to skip /// building an `SyncView` with [`SyncView::new`]. - #[unstable(feature = "exclusive_wrapper", issue = "98407")] - #[rustc_const_unstable(feature = "exclusive_wrapper", issue = "98407")] + #[stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] #[must_use] #[inline] pub const fn from_mut(r: &'_ mut T) -> &'_ mut SyncView { @@ -179,8 +178,8 @@ impl SyncView { /// Build a _pinned mutable_ reference to an `SyncView` from /// a _pinned mutable_ reference to a `T`. This allows you to skip /// building an `SyncView` with [`SyncView::new`]. - #[unstable(feature = "exclusive_wrapper", issue = "98407")] - #[rustc_const_unstable(feature = "exclusive_wrapper", issue = "98407")] + #[stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] #[must_use] #[inline] pub const fn from_pin_mut(r: Pin<&'_ mut T>) -> Pin<&'_ mut SyncView> { @@ -197,8 +196,8 @@ impl SyncView { /// value, which means _unpinned_ `SyncView`s can produce _unpinned_ /// access to the underlying value, but _pinned_ `SyncView`s only /// produce _pinned_ access to the underlying value. - #[unstable(feature = "exclusive_wrapper", issue = "98407")] - #[rustc_const_unstable(feature = "exclusive_wrapper", issue = "98407")] + #[stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] #[must_use] #[inline] pub const fn as_pin_ref(self: Pin<&Self>) -> Pin<&T> { @@ -208,7 +207,7 @@ impl SyncView { } } -#[unstable(feature = "exclusive_wrapper", issue = "98407")] +#[stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] const impl From for SyncView { #[inline] @@ -217,7 +216,7 @@ const impl From for SyncView { } } -#[unstable(feature = "exclusive_wrapper", issue = "98407")] +#[stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_trait_impl", issue = "143874")] const impl FnOnce for SyncView where @@ -231,7 +230,7 @@ where } } -#[unstable(feature = "exclusive_wrapper", issue = "98407")] +#[stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_trait_impl", issue = "143874")] const impl FnMut for SyncView where @@ -243,7 +242,7 @@ where } } -#[unstable(feature = "exclusive_wrapper", issue = "98407")] +#[stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_trait_impl", issue = "143874")] const impl Fn for SyncView where @@ -255,7 +254,7 @@ where } } -#[unstable(feature = "exclusive_wrapper", issue = "98407")] +#[stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] impl AsyncFnOnce for SyncView where F: AsyncFnOnce, @@ -270,7 +269,7 @@ where } } -#[unstable(feature = "exclusive_wrapper", issue = "98407")] +#[stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] impl AsyncFnMut for SyncView where F: AsyncFnMut, @@ -286,7 +285,7 @@ where } } -#[unstable(feature = "exclusive_wrapper", issue = "98407")] +#[stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] impl AsyncFn for SyncView where F: Sync + AsyncFn, @@ -297,7 +296,7 @@ where } } -#[unstable(feature = "exclusive_wrapper", issue = "98407")] +#[stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] impl Future for SyncView where T: Future + ?Sized, @@ -324,7 +323,7 @@ where } } -#[unstable(feature = "exclusive_wrapper", issue = "98407")] +#[stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] const impl AsRef for SyncView where @@ -337,7 +336,7 @@ where } } -#[unstable(feature = "exclusive_wrapper", issue = "98407")] +#[stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] const impl AsMut for SyncView where @@ -350,7 +349,7 @@ where } } -#[unstable(feature = "exclusive_wrapper", issue = "98407")] +#[stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_clone", issue = "142757")] const impl Clone for SyncView where @@ -367,10 +366,10 @@ where #[rustc_const_unstable(feature = "const_clone", issue = "142757")] const unsafe impl TrivialClone for SyncView where T: Sync + [const] TrivialClone {} -#[unstable(feature = "exclusive_wrapper", issue = "98407")] +#[stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] impl Copy for SyncView where T: Sync + Copy {} -#[unstable(feature = "exclusive_wrapper", issue = "98407")] +#[stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] const impl PartialEq> for SyncView where @@ -383,14 +382,14 @@ where } } -#[unstable(feature = "exclusive_wrapper", issue = "98407")] +#[stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] impl StructuralPartialEq for SyncView where T: Sync + StructuralPartialEq + ?Sized {} -#[unstable(feature = "exclusive_wrapper", issue = "98407")] +#[stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] const impl Eq for SyncView where T: Sync + [const] Eq + ?Sized {} -#[unstable(feature = "exclusive_wrapper", issue = "98407")] +#[stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] impl Hash for SyncView where T: Sync + Hash + ?Sized, @@ -401,7 +400,7 @@ where } } -#[unstable(feature = "exclusive_wrapper", issue = "98407")] +#[stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] const impl PartialOrd> for SyncView where @@ -414,7 +413,7 @@ where } } -#[unstable(feature = "exclusive_wrapper", issue = "98407")] +#[stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] const impl Ord for SyncView where diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 473b185d24652..1dbd11426e57e 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -90,6 +90,8 @@ impl RawWaker { /// pointers to *different* functions can compare equal (since identical functions can be /// deduplicated within a codegen unit). /// +/// This struct is guaranteed to be aligned to at least 8 bytes. +/// /// # Thread safety /// If the [`RawWaker`] will be used to construct a [`Waker`] then /// these functions must all be thread-safe (even though [`RawWaker`] is @@ -106,6 +108,8 @@ impl RawWaker { #[stable(feature = "futures_api", since = "1.36.0")] #[allow(unpredictable_function_pointer_comparisons)] #[derive(PartialEq, Copy, Clone, Debug)] +// For bit-stuffing pointers we guarantee align >= 8. +#[repr(align(8))] pub struct RawWakerVTable { /// This function will be called when the [`RawWaker`] gets cloned, e.g. when /// the [`Waker`] in which the [`RawWaker`] is stored gets cloned. diff --git a/library/core/src/time.rs b/library/core/src/time.rs index c123c66ee3bc0..6f33fdd36e53b 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -136,6 +136,86 @@ impl Duration { #[unstable(feature = "duration_constants", issue = "57391")] pub const NANOSECOND: Duration = Duration::from_nanos(1); + /// The duration of one week. + /// + /// For this constant, one week is defined as 7 days, or 604,800 seconds. + /// + /// # Examples + /// + /// ``` + /// #![feature(duration_constants)] + /// #![feature(duration_constructors)] + /// use std::time::Duration; + /// + /// let week = Duration::WEEK; + /// + /// assert_eq!(week, Duration::from_weeks(1)); + /// assert_eq!(week.as_secs(), 604_800); + /// assert_eq!(week.subsec_nanos(), 0); + /// ``` + #[unstable(feature = "duration_constants", issue = "57391")] + // Also, #[unstable(feature = "duration_constructors", issue = "120301")] + pub const WEEK: Duration = Duration::from_weeks(1); + + /// The duration of one day. + /// + /// For this constant, one day is defined as 24 hours, or 86,400 seconds. + /// + /// # Examples + /// + /// ``` + /// #![feature(duration_constants)] + /// #![feature(duration_constructors)] + /// use std::time::Duration; + /// + /// let day = Duration::DAY; + /// + /// assert_eq!(day, Duration::from_days(1)); + /// assert_eq!(day.as_secs(), 86400); + /// assert_eq!(day.subsec_nanos(), 0); + /// ``` + #[unstable(feature = "duration_constants", issue = "57391")] + // Also, #[unstable(feature = "duration_constructors", issue = "120301")] + pub const DAY: Duration = Duration::from_days(1); + + /// The duration of one hour. + /// + /// For this constant, one hour is defined as 60 minutes, or 3,600 seconds. + /// + /// # Examples + /// + /// ``` + /// #![feature(duration_constants)] + /// use std::time::Duration; + /// + /// let hour = Duration::HOUR; + /// + /// assert_eq!(hour, Duration::from_hours(1)); + /// assert_eq!(hour.as_secs(), 3600); + /// assert_eq!(hour.subsec_nanos(), 0); + /// ``` + #[unstable(feature = "duration_constants", issue = "57391")] + pub const HOUR: Duration = Duration::from_hours(1); + + /// The duration of one minute. + /// + /// For this constant, one minute is defined as 60 seconds. + /// + /// # Examples + /// + /// ``` + /// #![feature(duration_constants)] + /// use std::time::Duration; + /// + /// let minute = Duration::MINUTE; + /// + /// assert_eq!(minute, Duration::from_mins(1)); + /// assert_eq!(minute.as_secs(), 60); + /// assert_eq!(minute.subsec_nanos(), 0); + /// ``` + #[unstable(feature = "duration_constants", issue = "57391")] + pub const MINUTE: Duration = Duration::from_mins(1); + /// A duration of zero time. /// /// # Examples diff --git a/library/core/src/unicode/unicode_data.rs b/library/core/src/unicode/unicode_data.rs index b6848706aa902..7eba224224d1d 100644 --- a/library/core/src/unicode/unicode_data.rs +++ b/library/core/src/unicode/unicode_data.rs @@ -1064,6 +1064,17 @@ pub mod conversions { return [c.to_ascii_lowercase(), '\0', '\0']; } + // The rest of Latin-1: À..=Þ (except ×) add 0x20, everything else maps to itself. + if c <= '\u{FF}' { + return match c { + '\u{C0}'..='\u{DE}' if c != '\u{D7}' => { + // SAFETY: U+00C0..=U+00DE plus 0x20 lands in U+00E0..=U+00FE. + [unsafe { char::from_u32_unchecked(c as u32 + 0x20) }, '\0', '\0'] + } + _ => [c, '\0', '\0'], + }; + } + lookup(c, &LOWERCASE_LUT).unwrap_or([c, '\0', '\0']) } @@ -1072,6 +1083,20 @@ pub mod conversions { if c < '\u{B5}' { return [c.to_ascii_uppercase(), '\0', '\0']; } + // The rest of Latin-1: µ maps to U+039C, ß expands to SS, + // à..=þ (except ÷) subtract 0x20, ÿ maps to U+0178. + if c <= '\u{FF}' { + return match c { + '\u{B5}' => ['\u{39C}', '\0', '\0'], + '\u{DF}' => ['S', 'S', '\0'], + '\u{E0}'..='\u{FE}' if c != '\u{F7}' => { + // SAFETY: U+00E0..=U+00FE minus 0x20 lands in U+00C0..=U+00DE. + [unsafe { char::from_u32_unchecked(c as u32 - 0x20) }, '\0', '\0'] + } + '\u{FF}' => ['\u{178}', '\0', '\0'], + _ => [c, '\0', '\0'], + }; + } lookup(c, &UPPERCASE_LUT).unwrap_or([c, '\0', '\0']) } diff --git a/library/core/src/unit.rs b/library/core/src/unit.rs index 5d9c79538e371..6c746c939b84a 100644 --- a/library/core/src/unit.rs +++ b/library/core/src/unit.rs @@ -1,4 +1,4 @@ -use crate::intrinsics::type_id; +use crate::intrinsics::reflection::type_id; /// Collapses all unit items from an iterator into one. /// diff --git a/library/coretests/benches/char/methods.rs b/library/coretests/benches/char/methods.rs index ed71920a4fc2a..5363205b01943 100644 --- a/library/coretests/benches/char/methods.rs +++ b/library/coretests/benches/char/methods.rs @@ -49,41 +49,87 @@ fn bench_to_ascii_lowercase(b: &mut Bencher) { #[bench] fn bench_ascii_mix_to_uppercase(b: &mut Bencher) { b.iter(|| { - (0..=255).cycle().take(10_000).map(|b| black_box(char::from(b)).to_uppercase()).count() + (0..=255) + .cycle() + .take(10_000) + .map(|b| black_box(black_box(char::from(b)).to_uppercase())) + .count() }) } #[bench] fn bench_ascii_mix_to_lowercase(b: &mut Bencher) { b.iter(|| { - (0..=255).cycle().take(10_000).map(|b| black_box(char::from(b)).to_lowercase()).count() + (0..=255) + .cycle() + .take(10_000) + .map(|b| black_box(black_box(char::from(b)).to_lowercase())) + .count() }) } #[bench] fn bench_ascii_char_to_uppercase(b: &mut Bencher) { b.iter(|| { - (0..=127).cycle().take(10_000).map(|b| black_box(char::from(b)).to_uppercase()).count() + (0..=127) + .cycle() + .take(10_000) + .map(|b| black_box(black_box(char::from(b)).to_uppercase())) + .count() }) } #[bench] fn bench_ascii_char_to_lowercase(b: &mut Bencher) { b.iter(|| { - (0..=127).cycle().take(10_000).map(|b| black_box(char::from(b)).to_lowercase()).count() + (0..=127) + .cycle() + .take(10_000) + .map(|b| black_box(black_box(char::from(b)).to_lowercase())) + .count() }) } #[bench] fn bench_non_ascii_char_to_uppercase(b: &mut Bencher) { b.iter(|| { - (128..=255).cycle().take(10_000).map(|b| black_box(char::from(b)).to_uppercase()).count() + (128..=255) + .cycle() + .take(10_000) + .map(|b| black_box(black_box(char::from(b)).to_uppercase())) + .count() }) } #[bench] fn bench_non_ascii_char_to_lowercase(b: &mut Bencher) { b.iter(|| { - (128..=255).cycle().take(10_000).map(|b| black_box(char::from(b)).to_lowercase()).count() + (128..=255) + .cycle() + .take(10_000) + .map(|b| black_box(black_box(char::from(b)).to_lowercase())) + .count() + }) +} + +#[bench] +fn bench_non_latin1_char_to_uppercase(b: &mut Bencher) { + b.iter(|| { + ('\u{100}'..='\u{4ff}') + .cycle() + .take(10_000) + .map(|c| black_box(black_box(c).to_uppercase())) + .count() + }) +} + +#[bench] +fn bench_non_latin1_char_to_lowercase(b: &mut Bencher) { + b.iter(|| { + ('\u{100}'..='\u{4ff}') + .cycle() + .take(10_000) + .map(|c| black_box(black_box(c).to_lowercase())) + .count() }) } diff --git a/library/coretests/tests/intrinsics.rs b/library/coretests/tests/intrinsics.rs index e562f49b0a2fc..0fec76d3bd65a 100644 --- a/library/coretests/tests/intrinsics.rs +++ b/library/coretests/tests/intrinsics.rs @@ -1,7 +1,7 @@ use core::any::TypeId; use core::intrinsics::assume; use std::fmt::Debug; -use std::intrinsics::type_id_vtable; +use std::intrinsics::reflection::type_id_vtable; use std::option::Option; use std::ptr::DynMetadata; diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 69bc6edd1b833..1fdb0c820d662 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -15,6 +15,7 @@ #![feature(cfg_overflow_checks)] #![feature(cfg_target_has_reliable_f16_f128)] #![feature(char_internals)] +#![feature(clamp_magnitude)] #![feature(clamp_to)] #![feature(clone_to_uninit)] #![feature(cmp_minmax)] @@ -46,8 +47,6 @@ #![feature(core_io_borrowed_buf)] #![feature(core_private_bignum)] #![feature(core_private_diy_float)] -#![feature(cstr_display)] -#![feature(debug_closure_helpers)] #![feature(dec2flt)] #![feature(duration_constants)] #![feature(duration_constructors)] @@ -65,7 +64,6 @@ #![feature(fmt_internals)] #![feature(formatting_options)] #![feature(freeze)] -#![feature(funnel_shifts)] #![feature(future_join)] #![feature(generic_assert_internals)] #![feature(hasher_prefixfree_extras)] @@ -111,6 +109,7 @@ #![feature(slice_shift)] #![feature(slice_split_once)] #![feature(sliceindex_wrappers)] +#![feature(splat)] #![feature(split_array)] #![feature(split_as_slice)] #![feature(std_internals)] @@ -130,8 +129,8 @@ #![feature(uint_gather_scatter_bits)] #![feature(unicode_internals)] #![feature(unsize)] -#![feature(unwrap_infallible)] // tidy-alphabetical-end +#![allow(incomplete_features)] #![allow(internal_features)] #![deny(implicit_provenance_casts)] #![deny(unsafe_op_in_unsafe_fn)] diff --git a/library/coretests/tests/mem.rs b/library/coretests/tests/mem.rs index 793c9f489e47f..845929ac54caf 100644 --- a/library/coretests/tests/mem.rs +++ b/library/coretests/tests/mem.rs @@ -10,6 +10,9 @@ use std::rc::Rc; #[test] fn size_of_basic() { + assert_eq!(size_of::<()>(), 0); + assert_eq!(size_of::(), 0); + assert_eq!(size_of::(), 1); assert_eq!(size_of::(), 1); assert_eq!(size_of::(), 2); assert_eq!(size_of::(), 4); @@ -47,6 +50,9 @@ fn size_of_val_basic() { #[test] fn align_of_basic() { + assert_eq!(align_of::<()>(), 1); + assert_eq!(align_of::(), 1); + assert_eq!(align_of::(), 1); assert_eq!(align_of::(), 1); assert_eq!(align_of::(), 2); assert_eq!(align_of::(), 4); diff --git a/library/coretests/tests/num/clamp_magnitude.rs b/library/coretests/tests/num/clamp_magnitude.rs index 0f96e55f6914e..fdb605780a2c6 100644 --- a/library/coretests/tests/num/clamp_magnitude.rs +++ b/library/coretests/tests/num/clamp_magnitude.rs @@ -1,3 +1,7 @@ +use core::num::*; + +use crate::num::assert_biteq; + macro_rules! check_int_clamp { ($t:ty, $ut:ty) => { let min = <$t>::MIN; @@ -32,6 +36,16 @@ macro_rules! check_int_clamp { // Limit larger than type max (uN > iN::MAX) assert_eq!(max.clamp_magnitude(max_u), max); assert_eq!(min.clamp_magnitude(max_u), min); + + // Const clamping + const C1: $t = (100 as $t).clamp_magnitude(50); + assert_eq!(C1, 50); + const C2: $t = (-100 as $t).clamp_magnitude(50); + assert_eq!(C2, -50); + const C3: $t = (30 as $t).clamp_magnitude(50); + assert_eq!(C3, 30); + const C4: $t = (-30 as $t).clamp_magnitude(50); + assert_eq!(C4, -30); }; } @@ -65,8 +79,81 @@ fn test_clamp_magnitude_isize() { check_int_clamp!(isize, usize); } +macro_rules! check_nonzero_clamp { + ($t:ty, $ut:ty) => { + let min = <$t>::MIN; + let max = <$t>::MAX; + let max_u = <$ut>::MAX; + + // Basic clamping + let lim = <$ut>::new(50).unwrap(); + assert_eq!(<$t>::new(100).unwrap().clamp_magnitude(lim), <$t>::new(50).unwrap()); + assert_eq!(<$t>::new(-100).unwrap().clamp_magnitude(lim), <$t>::new(-50).unwrap()); + assert_eq!(<$t>::new(30).unwrap().clamp_magnitude(lim), <$t>::new(30).unwrap()); + assert_eq!(<$t>::new(-30).unwrap().clamp_magnitude(lim), <$t>::new(-30).unwrap()); + + // Exact boundary + assert_eq!(<$t>::new(50).unwrap().clamp_magnitude(lim), <$t>::new(50).unwrap()); + assert_eq!(<$t>::new(-50).unwrap().clamp_magnitude(lim), <$t>::new(-50).unwrap()); + + // MIN/MAX values + // Symmetric range [-MAX, MAX] + assert_eq!(max.clamp_magnitude(max.try_into().unwrap()), max); + assert_eq!(min.clamp_magnitude(max.try_into().unwrap()), -max); + + // Full range (limit covers MIN) + let min_abs = min.unsigned_abs(); + assert_eq!(min.clamp_magnitude(min_abs), min); + + // Limit larger than type max (uN > iN::MAX) + assert_eq!(max.clamp_magnitude(max_u), max); + assert_eq!(min.clamp_magnitude(max_u), min); + + // Const clamping + const LIM: $ut = <$ut>::new(50).unwrap(); + const C1: $t = <$t>::new(100).unwrap().clamp_magnitude(LIM); + assert_eq!(C1, <$t>::new(50).unwrap()); + const C2: $t = <$t>::new(-100).unwrap().clamp_magnitude(LIM); + assert_eq!(C2, <$t>::new(-50).unwrap()); + const C3: $t = <$t>::new(30).unwrap().clamp_magnitude(LIM); + assert_eq!(C3, <$t>::new(30).unwrap()); + const C4: $t = <$t>::new(-30).unwrap().clamp_magnitude(LIM); + assert_eq!(C4, <$t>::new(-30).unwrap()); + }; +} + +#[test] +fn test_clamp_magnitude_nonzero_i8() { + check_nonzero_clamp!(NonZeroI8, NonZeroU8); +} + +#[test] +fn test_clamp_magnitude_nonzero_i16() { + check_nonzero_clamp!(NonZeroI16, NonZeroU16); +} + +#[test] +fn test_clamp_magnitude_nonzero_i32() { + check_nonzero_clamp!(NonZeroI32, NonZeroU32); +} + +#[test] +fn test_clamp_magnitude_nonzero_i64() { + check_nonzero_clamp!(NonZeroI64, NonZeroU64); +} + +#[test] +fn test_clamp_magnitude_nonzero_i128() { + check_nonzero_clamp!(NonZeroI128, NonZeroU128); +} + +#[test] +fn test_clamp_magnitude_nonzero_isize() { + check_nonzero_clamp!(NonZeroIsize, NonZeroUsize); +} + macro_rules! check_float_clamp { - ($t:ty) => { + ($t:ty, $huge: expr) => { // Basic clamping assert_eq!((5.0 as $t).clamp_magnitude(3.0), 3.0); assert_eq!((-5.0 as $t).clamp_magnitude(3.0), -3.0); @@ -78,10 +165,13 @@ macro_rules! check_float_clamp { assert_eq!((-3.0 as $t).clamp_magnitude(3.0), -3.0); // Zero cases - assert_eq!((0.0 as $t).clamp_magnitude(1.0), 0.0); - assert_eq!((-0.0 as $t).clamp_magnitude(1.0), 0.0); - assert_eq!((5.0 as $t).clamp_magnitude(0.0), 0.0); - assert_eq!((-5.0 as $t).clamp_magnitude(0.0), 0.0); + type Float = $t; + assert_biteq!((0.0 as $t).clamp_magnitude(1.0), 0.0); + assert_biteq!((-0.0 as $t).clamp_magnitude(1.0), -0.0); + assert_biteq!((5.0 as $t).clamp_magnitude(0.0), 0.0); + assert_biteq!((-5.0 as $t).clamp_magnitude(0.0), -0.0); + assert_biteq!((5.0 as $t).clamp_magnitude(-0.0), 0.0); + assert_biteq!((-5.0 as $t).clamp_magnitude(-0.0), -0.0); // Special values - Infinity let inf = <$t>::INFINITY; @@ -98,42 +188,95 @@ macro_rules! check_float_clamp { let max = <$t>::MAX; let min = <$t>::MIN; // Large limit - let huge = 1e30; - assert_eq!(max.clamp_magnitude(huge), huge); - assert_eq!(min.clamp_magnitude(huge), -huge); + assert_eq!(max.clamp_magnitude($huge), $huge); + assert_eq!(min.clamp_magnitude($huge), -$huge); + + // NaN + let nan = <$t>::NAN; + assert!(nan.clamp_magnitude(1.0).is_nan()); + + // Const clamping + const C1: $t = (5.0 as $t).clamp_magnitude(3.0); + assert_eq!(C1, 3.0); + const C2: $t = (-5.0 as $t).clamp_magnitude(3.0); + assert_eq!(C2, -3.0); + const C3: $t = (2.0 as $t).clamp_magnitude(3.0); + assert_eq!(C3, 2.0); + const C4: $t = (-2.0 as $t).clamp_magnitude(3.0); + assert_eq!(C4, -2.0); }; } +#[test] +#[cfg(target_has_reliable_f16)] +fn test_clamp_magnitude_f16() { + check_float_clamp!(f16, 1e3); +} + #[test] fn test_clamp_magnitude_f32() { - check_float_clamp!(f32); + check_float_clamp!(f32, 1e30); } #[test] fn test_clamp_magnitude_f64() { - check_float_clamp!(f64); + check_float_clamp!(f64, 1e300); +} + +#[test] +#[cfg(target_has_reliable_f128)] +fn test_clamp_magnitude_f128() { + check_float_clamp!(f128, 1e3000); } #[test] -#[should_panic(expected = "limit must be non-negative")] +#[cfg(target_has_reliable_f16)] +#[should_panic(expected = "limit must be non-negative and not NaN")] +fn test_clamp_magnitude_f16_panic_negative_limit() { + let _ = 1.0f16.clamp_magnitude(-1.0); +} + +#[test] +#[should_panic(expected = "limit must be non-negative and not NaN")] fn test_clamp_magnitude_f32_panic_negative_limit() { let _ = 1.0f32.clamp_magnitude(-1.0); } #[test] -#[should_panic(expected = "limit must be non-negative")] +#[should_panic(expected = "limit must be non-negative and not NaN")] fn test_clamp_magnitude_f64_panic_negative_limit() { let _ = 1.0f64.clamp_magnitude(-1.0); } #[test] -#[should_panic] +#[cfg(target_has_reliable_f128)] +#[should_panic(expected = "limit must be non-negative and not NaN")] +fn test_clamp_magnitude_f128_panic_negative_limit() { + let _ = 1.0f128.clamp_magnitude(-1.0); +} + +#[test] +#[cfg(target_has_reliable_f16)] +#[should_panic(expected = "limit must be non-negative and not NaN")] +fn test_clamp_magnitude_f16_panic_nan_limit() { + let _ = 1.0f16.clamp_magnitude(f16::NAN); +} + +#[test] +#[should_panic(expected = "limit must be non-negative and not NaN")] fn test_clamp_magnitude_f32_panic_nan_limit() { let _ = 1.0f32.clamp_magnitude(f32::NAN); } #[test] -#[should_panic] +#[should_panic(expected = "limit must be non-negative and not NaN")] fn test_clamp_magnitude_f64_panic_nan_limit() { let _ = 1.0f64.clamp_magnitude(f64::NAN); } + +#[test] +#[cfg(target_has_reliable_f128)] +#[should_panic(expected = "limit must be non-negative and not NaN")] +fn test_clamp_magnitude_f128_panic_nan_limit() { + let _ = 1.0f128.clamp_magnitude(f128::NAN); +} diff --git a/library/coretests/tests/num/complex.rs b/library/coretests/tests/num/complex.rs index c22c5b9575b3d..d73d04b1afc98 100644 --- a/library/coretests/tests/num/complex.rs +++ b/library/coretests/tests/num/complex.rs @@ -73,3 +73,63 @@ fn complex_negation() { assert_eq!(-Complex::new(1.0, -2.0), Complex::new(-1.0, 2.0)); assert_eq!(-Complex::new(1.0, f32::INFINITY), Complex::new(-1.0, f32::NEG_INFINITY),); } + +#[test] +fn complex_multiplication() { + #[cfg(target_has_reliable_f16)] + assert_eq!(Complex::new(1.0f16, 2.0) * Complex::new(3.0, 4.0), Complex::new(-5.0, 10.0)); + assert_eq!(Complex::new(1.0f32, 2.0) * Complex::new(3.0, 4.0), Complex::new(-5.0, 10.0)); + assert_eq!(Complex::new(1.0f64, 2.0) * Complex::new(3.0, 4.0), Complex::new(-5.0, 10.0)); + #[cfg(target_has_reliable_f128)] + assert_eq!(Complex::new(1.0f128, 2.0) * Complex::new(3.0, 4.0), Complex::new(-5.0, 10.0)); + + // The naive algorithm would return NaN + NaNi for these inputs, but the libcall handles it. + #[cfg(target_has_reliable_f16)] + assert_eq!( + Complex::new(1.0, 0.0) * Complex::new(f16::INFINITY, f16::INFINITY), + Complex::new(f16::INFINITY, f16::INFINITY) + ); + assert_eq!( + Complex::new(1.0, 0.0) * Complex::new(f32::INFINITY, f32::INFINITY), + Complex::new(f32::INFINITY, f32::INFINITY) + ); + assert_eq!( + Complex::new(1.0, 0.0) * Complex::new(f64::INFINITY, f64::INFINITY), + Complex::new(f64::INFINITY, f64::INFINITY) + ); + #[cfg(target_has_reliable_f128)] + assert_eq!( + Complex::new(1.0, 0.0) * Complex::new(f128::INFINITY, f128::INFINITY), + Complex::new(f128::INFINITY, f128::INFINITY) + ); +} + +#[test] +fn complex_div() { + #[cfg(target_has_reliable_f16)] + assert_eq!(Complex::new(2.0f16, 11.0) / Complex::new(2.0, 1.0), Complex::new(3.0, 4.0)); + assert_eq!(Complex::new(2.0f32, 11.0) / Complex::new(2.0, 1.0), Complex::new(3.0, 4.0)); + assert_eq!(Complex::new(2.0f64, 11.0) / Complex::new(2.0, 1.0), Complex::new(3.0, 4.0)); + #[cfg(target_has_reliable_f128)] + assert_eq!(Complex::new(2.0f128, 11.0) / Complex::new(2.0, 1.0), Complex::new(3.0, 4.0)); + + // The naive algorithm would return NaN + NaNi for these inputs, but the libcall handles it. + #[cfg(target_has_reliable_f16)] + assert_eq!( + Complex::new(f16::INFINITY, 0.0) / Complex::new(1.0, 1.0), + Complex::new(f16::INFINITY, f16::NEG_INFINITY) + ); + assert_eq!( + Complex::new(f32::INFINITY, 0.0) / Complex::new(1.0, 1.0), + Complex::new(f32::INFINITY, f32::NEG_INFINITY) + ); + assert_eq!( + Complex::new(f64::INFINITY, 0.0) / Complex::new(1.0, 1.0), + Complex::new(f64::INFINITY, f64::NEG_INFINITY) + ); + #[cfg(target_has_reliable_f128)] + assert_eq!( + Complex::new(f128::INFINITY, 0.0) / Complex::new(1.0, 1.0), + Complex::new(f128::INFINITY, f128::NEG_INFINITY) + ); +} diff --git a/library/coretests/tests/num/floats.rs b/library/coretests/tests/num/floats.rs index b8f75489bb212..84d645ce31f3c 100644 --- a/library/coretests/tests/num/floats.rs +++ b/library/coretests/tests/num/floats.rs @@ -727,15 +727,10 @@ float_test! { assert_biteq!(flt(9.0).min(black_box(Float::NAN)), 9.0); assert_biteq!(black_box(flt(-9.0)).min(Float::NAN), -9.0); assert!(Float::NAN.min(Float::NAN).is_nan()); - // FIXME(llvm21): LLVM miscompiles the fallback impl on aarch64 and likely other targets - // (https://github.com/llvm/llvm-project/issues/176624). When we require LLVM 22, - // remove the ui test `tests/ui/float/minmax.rs` and unconditionally enable the test here. - if cfg!(miri) { - assert_biteq!(Float::SNAN.min(black_box(9.0)), 9.0); - assert_biteq!(black_box(Float::SNAN).min(-9.0), -9.0); - assert_biteq!(flt(9.0).min(black_box(Float::SNAN)), 9.0); - assert_biteq!(black_box(flt(-9.0)).min(Float::SNAN), -9.0); - } + assert_biteq!(Float::SNAN.min(black_box(9.0)), 9.0); + assert_biteq!(black_box(Float::SNAN).min(-9.0), -9.0); + assert_biteq!(flt(9.0).min(black_box(Float::SNAN)), 9.0); + assert_biteq!(black_box(flt(-9.0)).min(Float::SNAN), -9.0); assert!(Float::SNAN.min(Float::SNAN).is_nan()); } } @@ -770,15 +765,10 @@ float_test! { assert_biteq!(flt(9.0).max(black_box(Float::NAN)), 9.0); assert_biteq!(black_box(flt(-9.0)).max(Float::NAN), -9.0); assert!(Float::NAN.max(Float::NAN).is_nan()); - // FIXME(llvm21): LLVM miscompiles the fallback impl on aarch64 and likely other targets - // (https://github.com/llvm/llvm-project/issues/176624). When we require LLVM 22, - // remove the ui test `tests/ui/float/minmax.rs` and unconditionally enable the test here. - if cfg!(miri) { - assert_biteq!(Float::SNAN.max(black_box(9.0)), 9.0); - assert_biteq!(black_box(Float::SNAN).max(-9.0), -9.0); - assert_biteq!(flt(9.0).max(black_box(Float::SNAN)), 9.0); - assert_biteq!(black_box(flt(-9.0)).max(Float::SNAN), -9.0); - } + assert_biteq!(Float::SNAN.max(black_box(9.0)), 9.0); + assert_biteq!(black_box(Float::SNAN).max(-9.0), -9.0); + assert_biteq!(flt(9.0).max(black_box(Float::SNAN)), 9.0); + assert_biteq!(black_box(flt(-9.0)).max(Float::SNAN), -9.0); assert!(Float::SNAN.max(Float::SNAN).is_nan()); } } @@ -2182,24 +2172,26 @@ float_test! { } } -// FIXME(f128): Uncomment and adapt these tests once the From<{u64,i64}> impls are added. -// float_test! { -// name: from_u64_i64, -// attrs: { -// f16: #[cfg(false)], -// f32: #[cfg(false)], -// f64: #[cfg(false)], -// f128: #[cfg(target_has_reliable_f128)], -// }, -// test { -// assert_biteq!(Float::from(u64::MIN), Float::ZERO); -// assert_biteq!(Float::from(42_u64), 42.0); -// assert_biteq!(Float::from(u64::MAX), 18446744073709551615.0); -// assert_biteq!(Float::from(i64::MIN), -9223372036854775808.0); -// assert_biteq!(Float::from(42_i64), 42.0); -// assert_biteq!(Float::from(i64::MAX), 9223372036854775807.0); -// } -// } +float_test! { + name: from_u64_i64, + attrs: { + f16: #[cfg(false)], + const f16: #[cfg(false)], + f32: #[cfg(false)], + const f32: #[cfg(false)], + f64: #[cfg(false)], + const f64: #[cfg(false)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { + assert_biteq!(Float::from(u64::MIN), Float::ZERO); + assert_biteq!(Float::from(42_u64), 42.0f128); + assert_biteq!(Float::from(u64::MAX), 18446744073709551615.0); + assert_biteq!(Float::from(i64::MIN), -9223372036854775808.0); + assert_biteq!(Float::from(42_i64), 42.0); + assert_biteq!(Float::from(i64::MAX), 9223372036854775807.0); + } +} float_test! { name: real_consts, diff --git a/library/coretests/tests/num/mod.rs b/library/coretests/tests/num/mod.rs index b1c3001790f07..f892734d8b95d 100644 --- a/library/coretests/tests/num/mod.rs +++ b/library/coretests/tests/num/mod.rs @@ -24,6 +24,7 @@ mod u8; mod bignum; mod carryless_mul; mod cast; +mod clamp_magnitude; mod complex; mod const_from; mod dec2flt; diff --git a/library/panic_unwind/Cargo.toml b/library/panic_unwind/Cargo.toml index 72c1041b46795..3b7805a205492 100644 --- a/library/panic_unwind/Cargo.toml +++ b/library/panic_unwind/Cargo.toml @@ -18,3 +18,6 @@ unwind = { path = "../unwind" } [target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] libc = { version = "0.2", default-features = false } + +[lints.cargo] +unused_dependencies = "allow" diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs index 0761549b2e30d..4b12ed0058847 100644 --- a/library/proc_macro/src/bridge/mod.rs +++ b/library/proc_macro/src/bridge/mod.rs @@ -222,6 +222,7 @@ mark_noop! { usize, Delimiter, LitKind, + IdentKind, Level, Bound, Range, @@ -385,11 +386,36 @@ compound_traits!(struct Punct { ch, joint, span }); #[derive(Copy, Clone, Eq, PartialEq)] pub struct Ident { pub sym: Symbol, - pub is_raw: bool, + pub kind: IdentKind, pub span: Span, } -compound_traits!(struct Ident { sym, is_raw, span }); +compound_traits!(struct Ident { sym, kind, span }); + +#[derive(Copy, Clone, Eq, PartialEq)] +pub enum IdentKind { + Normal, + Raw, + ForcedKeyword, +} + +impl IdentKind { + pub fn prefix(self) -> Option<&'static str> { + match self { + Self::Normal => None, + Self::Raw => Some("r#"), + Self::ForcedKeyword => Some("k#"), + } + } +} + +rpc_encode_decode!( + enum IdentKind { + Normal, + Raw, + ForcedKeyword, + } +); #[derive(Clone, Eq, PartialEq)] pub struct Literal { diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index b8ffcd53b25d8..886f258f8fe98 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -27,7 +27,6 @@ #![feature(restricted_std)] #![feature(rustc_attrs)] #![feature(extend_one)] -#![feature(mem_conjure_zst)] #![feature(f16)] #![recursion_limit = "256"] #![allow(internal_features)] @@ -1197,7 +1196,7 @@ impl Ident { pub fn new(string: &str, span: Span) -> Ident { Ident(bridge::Ident { sym: bridge::client::Symbol::new_ident(string, false), - is_raw: false, + kind: bridge::IdentKind::Normal, span: span.0, }) } @@ -1210,7 +1209,7 @@ impl Ident { pub fn new_raw(string: &str, span: Span) -> Ident { Ident(bridge::Ident { sym: bridge::client::Symbol::new_ident(string, true), - is_raw: true, + kind: bridge::IdentKind::Raw, span: span.0, }) } @@ -1234,8 +1233,8 @@ impl Ident { #[stable(feature = "proc_macro_lib2", since = "1.29.0")] impl fmt::Display for Ident { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.0.is_raw { - f.write_str("r#")?; + if let Some(prefix) = self.0.kind.prefix() { + f.write_str(prefix)?; } fmt::Display::fmt(&self.0.sym, f) } diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index de5c1feadb298..67eebd820ca59 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -84,12 +84,12 @@ wasip1 = { version = "1.0.0", features = [ ], default-features = false } [target.'cfg(all(target_os = "wasi", target_env = "p2"))'.dependencies] -wasip2 = { version = '2.0.0', features = [ +wasip2 = { version = '2.0.1', features = [ 'rustc-dep-of-std', ], default-features = false } [target.'cfg(all(target_os = "wasi", target_env = "p3"))'.dependencies] -wasip3 = { version = '0.8.0', features = [ +wasip3 = { version = '0.9.0', features = [ 'rustc-dep-of-std', ], default-features = false } @@ -167,3 +167,7 @@ check-cfg = [ 'cfg(target_has_reliable_f128)', 'cfg(target_has_reliable_f128_math)', ] + +[lints.cargo] +# panic_abort is not directly used but must be linked in +unused_dependencies = "allow" diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index 558ab0f2fc66d..1878ea3e32044 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -102,8 +102,14 @@ use crate::{hint, mem, ptr}; /// } /// ``` /// +/// Note that this does *not* mean you can deallocate allocations from [`Global`] +/// with [`System`] (or vice-versa), as the [`Global`] allocator is special in +/// the compiler. For more details, see the relevant docs about +/// [`#[global_allocator]`][global_allocator]. +/// /// You can also define your own wrapper around `System` if you'd like, such as -/// keeping track of the number of all bytes allocated: +/// keeping track of the number of all bytes allocated (note that due to +/// optimizations, this may not be accurate): /// /// ```rust /// use std::alloc::{System, GlobalAlloc, Layout}; @@ -140,15 +146,17 @@ use crate::{hint, mem, ptr}; /// global allocator has been selected for a Rust program. For example if a Rust /// program opts in to using jemalloc as the global allocator, `System` will /// still allocate memory using `malloc` and `HeapAlloc`. +/// +/// [global_allocator]: GlobalAlloc#the-global_allocator-attribute #[stable(feature = "alloc_system_type", since = "1.28.0")] #[derive(Copy, Debug)] #[derive_const(Clone, Default)] pub struct System; -#[unstable(feature = "allocator_api", issue = "32838")] +#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] unsafe impl core::alloc::AllocatorClone for System {} -#[unstable(feature = "allocator_api", issue = "32838")] +#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] unsafe impl core::alloc::StaticAllocator for System {} impl System { @@ -216,7 +224,7 @@ impl System { // The Allocator impl checks the layout size to be non-zero and forwards to the // platform functions in `std::sys::*::alloc`. -#[unstable(feature = "allocator_api", issue = "32838")] +#[stable(feature = "allocator_api", since = "1.100.0")] unsafe impl Allocator for System { #[inline] fn allocate(&self, layout: Layout) -> Result, AllocError> { @@ -303,7 +311,7 @@ unsafe impl Allocator for System { } } -#[unstable(feature = "allocator_api", issue = "32838")] +#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] unsafe impl GlobalAllocator for System {} static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut()); @@ -444,7 +452,6 @@ pub fn rust_oom(layout: Layout) -> ! { #[cfg(not(test))] #[doc(hidden)] -#[allow(unused_attributes)] #[unstable(feature = "alloc_internals", issue = "none")] pub mod __default_lib_allocator { use super::Layout; @@ -505,3 +512,7 @@ pub mod __default_lib_allocator { } } } + +#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] +#[unstable_feature_bound(allocator_ext)] +impl AllocatorNightly for System {} diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 121a525bd0c4b..d95fcf38a3b49 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -246,7 +246,7 @@ pub struct HashMap< K, V, S = RandomState, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] A: Allocator = Global, > { base: base::HashMap, } @@ -299,7 +299,7 @@ impl HashMap { /// # Examples /// /// ``` - /// # #![feature(allocator_api)] + /// # #![feature(allocator_ext)] /// use std::collections::HashMap; /// use std::alloc::Global; /// @@ -307,7 +307,7 @@ impl HashMap { /// ``` #[inline] #[must_use] - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] pub fn new_in(alloc: A) -> Self { HashMap::with_hasher_in(Default::default(), alloc) } @@ -322,7 +322,7 @@ impl HashMap { /// # Examples /// /// ``` - /// # #![feature(allocator_api)] + /// # #![feature(allocator_ext)] /// use std::collections::HashMap; /// use std::alloc::Global; /// @@ -330,7 +330,7 @@ impl HashMap { /// ``` #[inline] #[must_use] - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] pub fn with_capacity_in(capacity: usize, alloc: A) -> Self { HashMap::with_capacity_and_hasher_in(capacity, Default::default(), alloc) } @@ -418,7 +418,7 @@ impl HashMap { /// # Examples /// /// ``` - /// #![feature(allocator_api)] + /// #![feature(allocator_ext)] /// use std::alloc::Global; /// use std::collections::HashMap; /// use std::hash::RandomState; @@ -428,7 +428,7 @@ impl HashMap { /// ``` #[inline] #[must_use] - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] pub fn with_hasher_in(hash_builder: S, alloc: A) -> Self { HashMap { base: base::HashMap::with_hasher_in(hash_builder, alloc) } } @@ -451,7 +451,7 @@ impl HashMap { /// # Examples /// /// ``` - /// #![feature(allocator_api)] + /// #![feature(allocator_ext)] /// use std::alloc::Global; /// use std::collections::HashMap; /// use std::hash::RandomState; @@ -461,7 +461,7 @@ impl HashMap { /// ``` #[inline] #[must_use] - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] pub fn with_capacity_and_hasher_in(capacity: usize, hash_builder: S, alloc: A) -> Self { HashMap { base: base::HashMap::with_capacity_and_hasher_in(capacity, hash_builder, alloc) } } @@ -1660,7 +1660,7 @@ impl Default for IterMut<'_, K, V> { pub struct IntoIter< K, V, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] A: Allocator = Global, > { base: base::IntoIter, } @@ -1798,7 +1798,7 @@ pub struct Drain< 'a, K: 'a, V: 'a, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] A: Allocator = Global, > { base: base::Drain<'a, K, V, A>, } @@ -1835,7 +1835,7 @@ pub struct ExtractIf< K, V, F, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] A: Allocator = Global, > { base: base::ExtractIf<'a, K, V, F, A>, } @@ -1892,7 +1892,7 @@ impl Default for ValuesMut<'_, K, V> { pub struct IntoKeys< K, V, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] A: Allocator = Global, > { inner: IntoIter, } @@ -1926,7 +1926,7 @@ impl Default for IntoKeys { pub struct IntoValues< K, V, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] A: Allocator = Global, > { inner: IntoIter, } @@ -1950,7 +1950,7 @@ pub enum Entry< 'a, K: 'a, V: 'a, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] A: Allocator = Global, > { /// An occupied entry. #[stable(feature = "rust1", since = "1.0.0")] @@ -1978,7 +1978,7 @@ pub struct OccupiedEntry< 'a, K: 'a, V: 'a, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] A: Allocator = Global, > { base: base::RustcOccupiedEntry<'a, K, V, A>, } @@ -2000,7 +2000,7 @@ pub struct VacantEntry< 'a, K: 'a, V: 'a, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] A: Allocator = Global, > { base: base::RustcVacantEntry<'a, K, V, A>, } @@ -2021,7 +2021,7 @@ pub struct OccupiedError< 'a, K: 'a, V: 'a, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] A: Allocator = Global, > { /// The entry in the map that was already occupied. pub entry: OccupiedEntry<'a, K, V, A>, diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index b0a8e8ff42149..ea1307895b7f8 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -126,7 +126,7 @@ use crate::ops::{BitAnd, BitOr, BitXor, Sub}; pub struct HashSet< T, S = RandomState, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] A: Allocator = Global, > { base: base::HashSet, } @@ -179,7 +179,7 @@ impl HashSet { /// # Examples /// /// ``` - /// # #![feature(allocator_api)] + /// # #![feature(allocator_ext)] /// use std::alloc::Global; /// use std::collections::HashSet; /// @@ -187,7 +187,7 @@ impl HashSet { /// ``` #[inline] #[must_use] - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] pub fn new_in(alloc: A) -> HashSet { HashSet::with_hasher_in(Default::default(), alloc) } @@ -201,7 +201,7 @@ impl HashSet { /// # Examples /// /// ``` - /// # #![feature(allocator_api)] + /// # #![feature(allocator_ext)] /// use std::collections::HashSet; /// use std::alloc::Global; /// @@ -209,7 +209,7 @@ impl HashSet { /// ``` #[inline] #[must_use] - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] pub fn with_capacity_in(capacity: usize, alloc: A) -> HashSet { HashSet::with_capacity_and_hasher_in(capacity, Default::default(), alloc) } @@ -297,7 +297,7 @@ impl HashSet { /// # Examples /// /// ``` - /// # #![feature(allocator_api)] + /// # #![feature(allocator_ext)] /// use std::alloc::Global; /// use std::collections::HashSet; /// use std::hash::RandomState; @@ -307,7 +307,7 @@ impl HashSet { /// ``` #[inline] #[must_use] - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] pub fn with_hasher_in(hasher: S, alloc: A) -> HashSet { HashSet { base: base::HashSet::with_hasher_in(hasher, alloc) } } @@ -330,7 +330,7 @@ impl HashSet { /// # Examples /// /// ``` - /// # #![feature(allocator_api)] + /// # #![feature(allocator_ext)] /// use std::alloc::Global; /// use std::collections::HashSet; /// use std::hash::RandomState; @@ -340,7 +340,7 @@ impl HashSet { /// ``` #[inline] #[must_use] - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] pub fn with_capacity_and_hasher_in(capacity: usize, hasher: S, alloc: A) -> HashSet { HashSet { base: base::HashSet::with_capacity_and_hasher_in(capacity, hasher, alloc) } } @@ -1468,7 +1468,7 @@ impl Default for Iter<'_, K> { #[stable(feature = "rust1", since = "1.0.0")] pub struct IntoIter< K, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] A: Allocator = Global, > { base: base::IntoIter, } @@ -1502,7 +1502,7 @@ impl Default for IntoIter { pub struct Drain< 'a, K: 'a, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] A: Allocator = Global, > { base: base::Drain<'a, K, A>, } @@ -1529,7 +1529,7 @@ pub struct ExtractIf< 'a, K, F, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] A: Allocator = Global, > { base: base::ExtractIf<'a, K, F, A>, } @@ -1558,7 +1558,7 @@ pub struct Intersection< 'a, T: 'a, S: 'a, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] A: Allocator = Global, > { // iterator of the first set iter: Iter<'a, T>, @@ -1590,7 +1590,7 @@ pub struct Difference< 'a, T: 'a, S: 'a, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] A: Allocator = Global, > { // iterator of the first set iter: Iter<'a, T>, @@ -1622,7 +1622,7 @@ pub struct SymmetricDifference< 'a, T: 'a, S: 'a, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] A: Allocator = Global, > { iter: Chain, Difference<'a, T, S, A>>, } @@ -1651,7 +1651,7 @@ pub struct Union< 'a, T: 'a, S: 'a, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] A: Allocator = Global, > { iter: Chain, Difference<'a, T, S, A>>, } @@ -2145,7 +2145,7 @@ pub enum Entry< 'a, T, S, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] A: Allocator = Global, > { /// An occupied entry. /// @@ -2237,7 +2237,7 @@ pub struct OccupiedEntry< 'a, T, S, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] A: Allocator = Global, > { base: base::OccupiedEntry<'a, T, S, A>, } @@ -2282,7 +2282,7 @@ pub struct VacantEntry< 'a, T, S, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] A: Allocator = Global, > { base: base::VacantEntry<'a, T, S, A>, } diff --git a/library/std/src/env.rs b/library/std/src/env.rs index 762ad071e967c..1a6f2a477c7ea 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -502,6 +502,57 @@ impl fmt::Debug for SplitPaths<'_> { } } +/// An iterator that splits an environment variable into paths according to +/// platform-specific conventions. +/// +/// The iterator element type is &[Path]. +/// +/// This structure is created by [`env::split_paths_ref()`]. See its +/// documentation for more. +/// +/// [`env::split_paths_ref()`]: split_paths_ref +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[unstable(feature = "env_split_paths_ref", issue = "none")] +pub struct SplitPathsRef<'a> { + inner: paths_imp::SplitPathsRef<'a>, +} + +/// Parses input according to platform conventions for the `PATH` +/// environment variable. +/// +/// Unlike [`split_paths`], this function does not allocate and instead yields +/// [`Path`]s borrowed from `unparsed`. Returns `None` on platforms that may +/// require allocations to handle `PATH` splitting conventions. +/// +/// # Platform-specific behavior +/// +/// Returns `Some` on Unix platforms and `None` on all other platforms. +/// Note that this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +#[unstable(feature = "env_split_paths_ref", issue = "none")] +pub fn split_paths_ref + ?Sized>(unparsed: &T) -> Option> { + Some(SplitPathsRef { inner: paths_imp::split_paths_ref(unparsed.as_ref())? }) +} + +#[unstable(feature = "env_split_paths_ref", issue = "none")] +impl<'a> Iterator for SplitPathsRef<'a> { + type Item = &'a Path; + fn next(&mut self) -> Option<&'a Path> { + self.inner.next() + } + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +#[unstable(feature = "env_split_paths_ref", issue = "none")] +impl fmt::Debug for SplitPathsRef<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SplitPathsRef").finish_non_exhaustive() + } +} + /// The error type for operations on the `PATH` variable. Possibly returned from /// [`env::join_paths()`]. /// @@ -592,7 +643,7 @@ impl fmt::Display for JoinPathsError { #[stable(feature = "env", since = "1.0.0")] impl Error for JoinPathsError { - #[allow(deprecated, deprecated_in_future)] + #[allow(deprecated)] fn description(&self) -> &str { self.inner.description() } @@ -717,6 +768,11 @@ pub fn temp_dir() -> PathBuf { /// If the executable is renamed while it is running, platforms may return the /// path at the time it was loaded instead of the new path. /// +/// On Linux, if the executable is deleted while it is running, this function +/// may return the path with the string `" (deleted)"` appended. +/// +/// Note that the platform-specific behavior [may change in the future][changes]. +/// /// # Errors /// /// Acquiring the path of the current executable is a platform-specific operation @@ -752,6 +808,8 @@ pub fn temp_dir() -> PathBuf { /// Err(e) => println!("failed to get current exe path: {e}"), /// }; /// ``` +/// +/// [changes]: crate::io#platform-specific-behavior #[stable(feature = "env", since = "1.0.0")] pub fn current_exe() -> io::Result { paths_imp::current_exe() diff --git a/library/std/src/ffi/c_str.rs b/library/std/src/ffi/c_str.rs index cb0ca5d1376ea..e35acce2fef81 100644 --- a/library/std/src/ffi/c_str.rs +++ b/library/std/src/ffi/c_str.rs @@ -8,6 +8,8 @@ pub use alloc::ffi::c_str::IntoStringError; pub use alloc::ffi::c_str::{CString, NulError}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::ffi::c_str::CStr; +#[stable(feature = "cstr_display", since = "CURRENT_RUSTC_VERSION")] +pub use core::ffi::c_str::Display; #[stable(feature = "cstr_from_bytes_until_nul", since = "1.69.0")] pub use core::ffi::c_str::FromBytesUntilNulError; #[stable(feature = "cstr_from_bytes", since = "1.10.0")] diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 6be8f775b441d..b9d344284734b 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -5,6 +5,7 @@ mod tests; use core::clone::CloneToUninit; +use crate::alloc::Allocator; use crate::borrow::{Borrow, Cow}; use crate::collections::TryReserveError; use crate::hash::{Hash, Hasher}; @@ -1395,10 +1396,10 @@ impl From for Box { } #[stable(feature = "more_box_slice_clone", since = "1.29.0")] -impl Clone for Box { +impl Clone for Box { #[inline] fn clone(&self) -> Self { - self.to_os_string().into_boxed_os_str() + Box::clone_from_ref_in(&**self, Self::allocator(self).clone()) } } diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 8df809264a6dd..139275ed2e1c2 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -49,6 +49,13 @@ use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner, fs as fs_imp}; use crate::time::SystemTime; use crate::{error, fmt}; +pub(crate) mod dirs; + +#[unstable(feature = "fs_home_dirs", issue = "162082")] +pub use self::dirs::HomeDirs; +#[unstable(feature = "fs_media_dirs", issue = "162083")] +pub use self::dirs::MediaDirs; + /// An object providing access to an open file on the filesystem. /// /// An instance of a `File` can be read and/or written depending on what options @@ -1637,6 +1644,8 @@ impl Dir { /// Queries metadata about the underlying directory. /// + /// This is equivalent to `dir.metadata(".")` but can be more efficient. + /// /// # Examples /// /// ```no_run @@ -1645,13 +1654,13 @@ impl Dir { /// /// fn main() -> std::io::Result<()> { /// let dir = Dir::open("foo")?; - /// let metadata = dir.metadata()?; + /// let metadata = dir.self_metadata()?; /// Ok(()) /// } /// ``` #[unstable(feature = "dirfd", issue = "120426")] - pub fn metadata(&self) -> io::Result { - self.inner.metadata().map(Metadata) + pub fn self_metadata(&self) -> io::Result { + self.inner.self_metadata().map(Metadata) } /// Attempts to open a file in read-only mode relative to this directory. @@ -1869,6 +1878,76 @@ impl Dir { pub fn remove_dir>(&self, path: P) -> io::Result<()> { self.inner.remove_dir(path.as_ref()) } + + /// Creates a new `Dir` instance that shares the same underlying directory handle + /// as the existing `Dir` instance. + /// + /// # Examples + /// + /// Creates two handles for a directory named `foo`: + /// + /// ```no_run + /// #![feature(dirfd)] + /// use std::fs::Dir; + /// + /// fn main() -> std::io::Result<()> { + /// let dir = Dir::open("foo")?; + /// let dir_copy = dir.try_clone()?; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "dirfd", issue = "120426")] + pub fn try_clone(&self) -> io::Result { + Ok(Dir { inner: self.inner.duplicate()? }) + } + + /// Queries the file system to get information about a file, directory, etc. relative to this + /// directory. + /// + /// This function will traverse symbolic links to query information about the destination file. + /// To query metadata about the path itself without following symbolic links, use + /// [`symlink_metadata`][Self::symlink_metadata]. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(dirfd)] + /// use std::fs::Dir; + /// + /// fn main() -> std::io::Result<()> { + /// let dir = Dir::open("foo")?; + /// let metadata = dir.metadata("subdir/file.txt")?; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "dirfd", issue = "120426")] + pub fn metadata>(&self, path: P) -> io::Result { + self.inner.metadata(path.as_ref()).map(Metadata) + } + + /// Queries the file system to get information about a file, directory, etc. relative to this + /// directory. + /// + /// This function will return the [`Metadata`] of the exact path without traversing symbolic + /// links to a resolved destination file. Using this function on a path that is a file or + /// directory (not a symbolic link) will behave the same as [`metadata`][Self::metadata]. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(dirfd)] + /// use std::fs::Dir; + /// + /// fn main() -> std::io::Result<()> { + /// let dir = Dir::open("foo")?; + /// let metadata = dir.symlink_metadata("subdir/file.txt")?; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "dirfd", issue = "120426")] + pub fn symlink_metadata>(&self, path: P) -> io::Result { + self.inner.symlink_metadata(path.as_ref()).map(Metadata) + } } impl AsInner for Dir { @@ -3623,7 +3702,7 @@ pub fn set_permissions>(path: P, perm: Permissions) -> io::Result /// This function currently corresponds to the following underlying operations: /// * Android: returns [`Unsupported`] on all files. /// * Linux, BSD-based platforms, QNX, NTO: `fchmodat` with `AT_SYMLINK_NOFOLLOW`. -/// If that is not supported, we fall back to: +/// If that is not supported, we fall back to: /// * Unix-based platforms with symlinks: `open` with `O_NOFOLLOW` followed by /// [`fs::set_permissions`]. /// * Unix-based platforms without symlinks: `open` followed by [`fs::set_permissions`]. diff --git a/library/std/src/fs/dirs.rs b/library/std/src/fs/dirs.rs new file mode 100644 index 0000000000000..a9e11e37b2639 --- /dev/null +++ b/library/std/src/fs/dirs.rs @@ -0,0 +1,521 @@ +use crate::path::{Path, PathBuf}; +use crate::sys::fs::{ExtraHomeDirs, ExtraMediaDirs}; + +/// Common user directory paths used for user-specific application files. +/// +/// It is not required that the user directories are accessible by the current +/// user, nor that there is a directory at that path. A robust application +/// should handle the case where user directories are incorrectly configured. +/// +/// Even when configured correctly, multiple paths may point to the same location. +/// You should not assume that a file written relative to one directory will not +/// conflict with the same relative path in a different home directory. +/// +/// # Platform-specific behavior +/// +/// As the filesystem conventions for discovering directories varies between +/// operating systems, constructors for `HomeDirs` that use the host platform's +/// conventions are provided as extension traits under the `std::os` module. +#[unstable(feature = "fs_home_dirs", issue = "162082")] +#[derive(Debug, Clone)] +pub struct HomeDirs { + pub(crate) cache: Option, + pub(crate) config: Option, + pub(crate) data: Option, + pub(crate) state: Option, + #[cfg_attr(not(unix), expect(dead_code, reason = "no extra home dirs"))] + pub(crate) extra: ExtraHomeDirs, +} + +/// Common user directory paths used for user-specific media files. +/// +/// It is not required that the media directories are accessible by the current +/// user, nor that there is a directory at that path. A robust application +/// should handle the case where media directories are incorrectly configured. +/// +/// Even when configured correctly, multiple paths may point to the same location. +/// You should not assume that a file written relative to one directory will not +/// conflict with the same relative path in a different media directory. +/// +/// # Platform-specific behavior +/// +/// As the filesystem conventions for discovering directories varies between +/// operating systems, constructors for `MediaDirs` that use the host platform's +/// conventions are provided as extension traits under the `std::os` module. +#[unstable(feature = "fs_media_dirs", issue = "162083")] +#[derive(Debug, Clone)] +pub struct MediaDirs { + pub(crate) desktop: Option, + pub(crate) documents: Option, + pub(crate) downloads: Option, + pub(crate) music: Option, + pub(crate) pictures: Option, + pub(crate) videos: Option, + #[cfg_attr(not(unix), expect(dead_code, reason = "no extra media dirs"))] + pub(crate) extra: ExtraMediaDirs, +} + +// NB: HomeDirs and MediaDirs intentionally do not implement Default. Self::empty() +// is a logical default, but users may also intuit the default to use default +// platform conventions. Omitting Default pushes users to explicitly choose. + +impl HomeDirs { + /// Create a known user directory set with no known directories. + /// + /// This is useful with the builder `set_*` methods to create a `HomeDirs` + /// with exactly the directories you want, without any other defaults. + #[unstable(feature = "fs_home_dirs", issue = "162082")] + pub fn empty() -> Self { + Self { cache: None, config: None, data: None, state: None, extra: Default::default() } + } + + /// A base directory relative to which user-specific non-essential cache + /// data files should be stored. + /// + /// "Cache" files are temporary data that can be used to cache redundant + /// work of an application, but which can be discarded arbitrarily and + /// recreated as necessary. Files in this directory may potentially be + /// automatically purged any time they are not currently open, or they + /// may not, depending on system configuration. A robust application + /// should ensure that its caches do not grow without a reasonable bound. + /// + /// This is the same directory for all applications. Applications should + /// use a subdirectory for application-specific cache files. + /// + /// # Platform-specific behavior + /// + /// When constructed using platform-specific conventions, the value is: + /// + /// | OS | Path | + /// | -- | ---- | + /// | [XDG] (Linux) | `${XDG_CACHE_HOME:-$HOME/.cache}` | + /// | [Darwin] (macOS) | [`NSCachesDirectory`] (`$HOME/Library/Caches`) | + /// | [Windows] | [`{FOLDERID_LocalAppData}`] (`%LOCALAPPDATA%`) | + /// + /// Other paths can be configured via [`set_cache_home`](Self::set_cache_home). + /// + /// [XDG]: crate::os::unix::fs::HomeDirsExt + /// [Darwin]: crate::os::darwin::fs::HomeDirsExt + /// [Windows]: crate::os::windows::fs::HomeDirsExt + /// [`NSCachesDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/cachesdirectory?language=objc + /// [`{FOLDERID_LocalAppData}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_localappdata + #[unstable(feature = "fs_home_dirs", issue = "162082")] + pub fn cache_home(&self) -> Option<&Path> { + self.cache.as_deref() + } + + /// A base directory relative to which user-specific configuration files + /// should be stored. + /// + /// "Config" files are configuration managed by the user, either by editing + /// the files directly or through a managing application. Configuration is + /// generally expected to be meaningful to the user and portable enough to + /// back up and synchronize across the same user's account on multiple + /// systems. + /// + /// This is the same directory for all applications. Applications should + /// use a subdirectory for application-specific configuration files. + /// + /// # Platform-specific behavior + /// + /// When constructed using platform-specific conventions, the value is: + /// + /// | OS | Path | + /// | -- | ---- | + /// | [XDG] (Linux) | `${XDG_CONFIG_HOME:-$HOME/.config}` | + /// | [Darwin] (macOS) | [`NSApplicationSupportDirectory`] (`$HOME/Library/Application Support`) | + /// | [Windows] | [`{FOLDERID_RoamingAppData}`] (`%APPDATA%`) | + /// + /// Other paths can be configured via [`set_config_home`](Self::set_config_home). + /// + /// [XDG]: crate::os::unix::fs::HomeDirsExt + /// [Darwin]: crate::os::darwin::fs::HomeDirsExt + /// [Windows]: crate::os::windows::fs::HomeDirsExt + /// [`NSApplicationSupportDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/applicationsupportdirectory?language=objc + /// [`{FOLDERID_RoamingAppData}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_roamingappdata + #[unstable(feature = "fs_home_dirs", issue = "162082")] + pub fn config_home(&self) -> Option<&Path> { + self.config.as_deref() + } + + /// A base directory relative to which user-specific data files should be + /// stored. + /// + /// "Data" files are application-specific data that is meaningful to the + /// user in some way and does not implicitly rely on system configuration + /// or details of how the application is installed otherwise irrelevant to + /// the user. As such, data makes sense to back up and synchronize between + /// the same user's account on multiple systems. If a file is specific to + /// a single machine, it's probably [state](Self::state_home). + /// + /// This is the same directory for all applications. Applications should + /// use a subdirectory for application-specific data files. + /// + /// # Platform-specific behavior + /// + /// When constructed using platform-specific conventions, the value is: + /// + /// | OS | Path | + /// | -- | ---- | + /// | [XDG] (Linux) | `${XDG_DATA_HOME:-$HOME/.local/share}` | + /// | [Darwin] (macOS) | [`NSApplicationSupportDirectory`] (`$HOME/Library/Application Support`) | + /// | [Windows] | [`{FOLDERID_RoamingAppData}`] (`%APPDATA%`) | + /// + /// Other paths can be configured via [`set_data_home`](Self::set_data_home). + /// + /// [XDG]: crate::os::unix::fs::HomeDirsExt + /// [Darwin]: crate::os::darwin::fs::HomeDirsExt + /// [Windows]: crate::os::windows::fs::HomeDirsExt + /// [`NSApplicationSupportDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/applicationsupportdirectory?language=objc + /// [`{FOLDERID_RoamingAppData}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_roamingappdata + #[unstable(feature = "fs_home_dirs", issue = "162082")] + pub fn data_home(&self) -> Option<&Path> { + self.data.as_deref() + } + + /// A base directory relative to which user-specific state files should be + /// stored. + /// + /// "State" files are data that should persist between application restarts, + /// but which is not important nor portable enough to the user to synchronize + /// between multiple systems like [data](Self::data_home) are. Common examples + /// include history (such as logs, recently used files, etc) and any current + /// state of the application that should be reused (such as view, layout, open + /// files, undo history, etc). + /// + /// This is the same directory for all applications. Applications should + /// use a subdirectory for application-specific state files. + /// + /// # Platform-specific behavior + /// + /// When constructed using platform-specific conventions, the value is: + /// + /// | OS | Path | + /// | -- | ---- | + /// | [XDG] (Linux) | `${XDG_STATE_HOME:-$HOME/.local/state}` | + /// | [Darwin] (macOS) | [`NSApplicationSupportDirectory`] (`$HOME/Library/Application Support`) | + /// | [Windows] | [`{FOLDERID_LocalAppData}`] (`%LOCALAPPDATA%`) | + /// + /// Other paths can be configured via [`set_state_home`](Self::set_state_home). + /// + /// [XDG]: crate::os::unix::fs::HomeDirsExt + /// [Darwin]: crate::os::darwin::fs::HomeDirsExt + /// [Windows]: crate::os::windows::fs::HomeDirsExt + /// [`NSApplicationSupportDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/applicationsupportdirectory?language=objc + /// [`{FOLDERID_LocalAppData}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_localappdata + #[unstable(feature = "fs_home_dirs", issue = "162082")] + pub fn state_home(&self) -> Option<&Path> { + self.state.as_deref() + } +} + +impl MediaDirs { + /// Create a known user directory set with no known directories. + /// + /// This is useful with the builder `set_*` methods to create a `MediaDirs` + /// with exactly the directories you want, without any other defaults. + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn empty() -> Self { + Self { + desktop: None, + documents: None, + downloads: None, + music: None, + pictures: None, + videos: None, + extra: Default::default(), + } + } + + /// The OS-recognized user "Desktop" directory, often the `Desktop` + /// folder in the user's home directory. + /// + /// As a media directory, this should typically be used as a default path + /// for file selection dialogs, not for automatically accessed file paths. + /// + /// # Platform-specific behavior + /// + /// When constructed using platform-specific conventions, the value is: + /// + /// | OS | Path | + /// | -- | ---- | + /// | [XDG] (Linux) | `xdg-user-dir DESKTOP` (`$HOME/Desktop`) | + /// | [Darwin] (macOS) | [`NSDesktopDirectory`] (`$HOME/Desktop`) | + /// | [Windows] | [`{FOLDERID_Desktop}`] (`%USERPROFILE%\Desktop`) | + /// + /// Other paths can be configured via [`set_desktop`](Self::set_desktop). + /// + /// [XDG]: crate::os::unix::fs::MediaDirsExt + /// [Darwin]: crate::os::darwin::fs::MediaDirsExt + /// [Windows]: crate::os::windows::fs::MediaDirsExt + /// [`NSDesktopDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/desktopdirectory?language=objc + /// [`{FOLDERID_Desktop}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_desktop + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn desktop(&self) -> Option<&Path> { + self.desktop.as_deref() + } + + /// The OS-recognized user "Documents" directory, often the `Documents` + /// folder in the user's home directory. + /// + /// As a media directory, this should typically be used as a default path + /// for file selection dialogs, not for automatically accessed file paths. + /// + /// # Platform-specific behavior + /// + /// When constructed using platform-specific conventions, the value is: + /// + /// | OS | Path | + /// | -- | ---- | + /// | [XDG] (Linux) | `xdg-user-dir DOCUMENTS` (`$HOME/Documents`) | + /// | [Darwin] (macOS) | [`NSDocumentDirectory`] (`$HOME/Documents`) | + /// | [Windows] | [`{FOLDERID_Documents}`] (`%USERPROFILE%\Documents`) | + /// + /// Other paths can be configured via [`set_documents`](Self::set_documents). + /// + /// [XDG]: crate::os::unix::fs::MediaDirsExt + /// [Darwin]: crate::os::darwin::fs::MediaDirsExt + /// [Windows]: crate::os::windows::fs::MediaDirsExt + /// [`NSDocumentDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/documentdirectory?language=objc + /// [`{FOLDERID_Documents}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_documents + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn documents(&self) -> Option<&Path> { + self.documents.as_deref() + } + + /// The OS-recognized user "Downloads" directory, often the `Downloads` + /// folder in the user's home directory. + /// + /// As a media directory, this should typically be used as a default path + /// for file selection dialogs, not for automatically accessed file paths. + /// + /// # Platform-specific behavior + /// + /// When constructed using platform-specific conventions, the value is: + /// + /// | OS | Path | + /// | -- | ---- | + /// | [XDG] (Linux) | `xdg-user-dir DOWNLOAD` (`$HOME/Downloads`) | + /// | [Darwin] (macOS) | [`NSDownloadsDirectory`] (`$HOME/Downloads`) | + /// | [Windows] | [`{FOLDERID_Downloads}`] (`%USERPROFILE%\Downloads`) | + /// + /// Other paths can be configured via [`set_downloads`](Self::set_downloads). + /// + /// [XDG]: crate::os::unix::fs::MediaDirsExt + /// [Darwin]: crate::os::darwin::fs::MediaDirsExt + /// [Windows]: crate::os::windows::fs::MediaDirsExt + /// [`NSDownloadsDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/downloadsdirectory?language=objc + /// [`{FOLDERID_Downloads}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_downloads + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn downloads(&self) -> Option<&Path> { + self.downloads.as_deref() + } + + /// The OS-recognized user "Music" directory, often the `Music` + /// folder in the user's home directory. + /// + /// As a media directory, this should typically be used as a default path + /// for file selection dialogs, not for automatically accessed file paths. + /// + /// # Platform-specific behavior + /// + /// When constructed using platform-specific conventions, the value is: + /// + /// | OS | Path | + /// | -- | ---- | + /// | [XDG] (Linux) | `xdg-user-dir MUSIC` (`$HOME/Music`) | + /// | [Darwin] (macOS) | [`NSMusicDirectory`] (`$HOME/Music`) | + /// | [Windows] | [`{FOLDERID_Music}`] (`%USERPROFILE%\Music`) | + /// + /// Other paths can be configured via [`set_music`](Self::set_music). + /// + /// [XDG]: crate::os::unix::fs::MediaDirsExt + /// [Darwin]: crate::os::darwin::fs::MediaDirsExt + /// [Windows]: crate::os::windows::fs::MediaDirsExt + /// [`NSMusicDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/musicdirectory?language=objc + /// [`{FOLDERID_Music}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_music + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn music(&self) -> Option<&Path> { + self.music.as_deref() + } + + /// The OS-recognized user "Pictures" directory, often the `Pictures` + /// folder in the user's home directory. + /// + /// As a media directory, this should typically be used as a default path + /// for file selection dialogs, not for automatically accessed file paths. + /// + /// # Platform-specific behavior + /// + /// When constructed using platform-specific conventions, the value is: + /// + /// | OS | Path | + /// | -- | ---- | + /// | [XDG] (Linux) | `xdg-user-dir PICTURES` (`$HOME/Pictures`) | + /// | [Darwin] (macOS) | [`NSPicturesDirectory`] (`$HOME/Pictures`) | + /// | [Windows] | [`{FOLDERID_Pictures}`] (`%USERPROFILE%\Pictures`) | + /// + /// Other paths can be configured via [`set_pictures`](Self::set_pictures). + /// + /// [XDG]: crate::os::unix::fs::MediaDirsExt + /// [Darwin]: crate::os::darwin::fs::MediaDirsExt + /// [Windows]: crate::os::windows::fs::MediaDirsExt + /// [`NSPicturesDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/picturesdirectory?language=objc + /// [`{FOLDERID_Pictures}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_pictures + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn pictures(&self) -> Option<&Path> { + self.pictures.as_deref() + } + + /// The OS-recognized user "Videos" directory, often the `Videos` + /// folder in the user's home directory. + /// + /// As a media directory, this should typically be used as a default path + /// for file selection dialogs, not for automatically accessed file paths. + /// + /// # Platform-specific behavior + /// + /// When constructed using platform-specific conventions, the value is: + /// + /// | OS | Path | + /// | -- | ---- | + /// | [XDG] (Linux) | `xdg-user-dir VIDEOS` (`$HOME/Videos`) | + /// | [Darwin] (macOS) | [`NSMoviesDirectory`] (`$HOME/Movies`) | + /// | [Windows] | [`{FOLDERID_Videos}`] (`%USERPROFILE%\Videos`) | + /// + /// Other paths can be configured via [`set_videos`](Self::set_videos). + /// + /// [XDG]: crate::os::unix::fs::MediaDirsExt + /// [Darwin]: crate::os::darwin::fs::MediaDirsExt + /// [Windows]: crate::os::windows::fs::MediaDirsExt + /// [`NSMoviesDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/moviesdirectory?language=objc + /// [`{FOLDERID_Videos}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_videos + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn videos(&self) -> Option<&Path> { + self.videos.as_deref() + } +} + +impl HomeDirs { + /// Set the path for [Self::cache_home]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_home_dirs", issue = "162082")] + pub fn set_cache_home(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "cache home directory path must be absolute"); + self.cache = Some(path); + self + } + + /// Set the path for [Self::config_home]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_home_dirs", issue = "162082")] + pub fn set_config_home(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "config home directory path must be absolute"); + self.config = Some(path); + self + } + + /// Set the path for [Self::data_home]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_home_dirs", issue = "162082")] + pub fn set_data_home(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "data home directory path must be absolute"); + self.data = Some(path); + self + } + + /// Set the path for [Self::state_home]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_home_dirs", issue = "162082")] + pub fn set_state_home(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "state home directory path must be absolute"); + self.state = Some(path); + self + } +} + +impl MediaDirs { + /// Set the path for [Self::desktop]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn set_desktop(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "desktop directory path must be absolute"); + self.desktop = Some(path); + self + } + + /// Set the path for [Self::documents]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn set_documents(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "documents directory path must be absolute"); + self.documents = Some(path); + self + } + + /// Set the path for [Self::downloads]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn set_downloads(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "downloads directory path must be absolute"); + self.downloads = Some(path); + self + } + + /// Set the path for [Self::music]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn set_music(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "music directory path must be absolute"); + self.music = Some(path); + self + } + + /// Set the path for [Self::pictures]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn set_pictures(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "pictures directory path must be absolute"); + self.pictures = Some(path); + self + } + + /// Set the path for [Self::videos]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn set_videos(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "videos directory path must be absolute"); + self.videos = Some(path); + self + } +} diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 148f1c32b08b9..54a6cf3904202 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -1,6 +1,7 @@ use rand::RngCore; use super::Dir; +use super::dirs::{HomeDirs, MediaDirs}; use crate::fs::{self, File, FileTimes, OpenOptions, TryLockError, exists}; use crate::io::prelude::*; use crate::io::{BorrowedBuf, ErrorKind, SeekFrom}; @@ -13,7 +14,7 @@ use crate::os::unix::fs::symlink as symlink_file; use crate::os::unix::fs::symlink as junction_point; #[cfg(windows)] use crate::os::windows::fs::{OpenOptionsExt, junction_point, symlink_dir, symlink_file}; -use crate::path::Path; +use crate::path::{Path, PathBuf}; use crate::sync::Arc; use crate::test_helpers::{TempDir, tmpdir}; use crate::time::{Duration, Instant, SystemTime}; @@ -613,12 +614,13 @@ fn set_get_unix_permissions() { assert_eq!(mask & metadata1.permissions().mode(), 0o0777); } -#[cfg(not(target_os = "android"))] +/// Test set_permissions_nofollow on a regular file. #[test] fn set_get_permissions_nofollows() { let tmpdir = tmpdir(); let filename = tmpdir.join("set_get_unix_permissions_file"); check!(File::create(&filename)); + let file_metadata = check!(fs::metadata(&filename)); assert!(!file_metadata.permissions().readonly()); let mut permission_bits = file_metadata.permissions(); @@ -648,18 +650,9 @@ fn set_get_permissions_nofollows() { } } -// Only Windows and Unix support `fs::set_permissions_nofollow` +/// Test set_permissions_nofollow on a symlink. #[test] -#[cfg(all( - any(windows, unix), - not(any(target_os = "espidf", target_os = "horizon", target_os = "wasi")) -))] fn set_get_permissions_nofollows_symlink() { - #[cfg(not(windows))] - use crate::os::unix::fs::symlink as symlink_file; - #[cfg(windows)] - use crate::os::windows::fs::symlink_file; - let tmpdir = tmpdir(); let filename = tmpdir.join("set_get_unix_permissions_file"); let symlink_name = tmpdir.join("set_get_unix_permissions"); @@ -667,14 +660,20 @@ fn set_get_permissions_nofollows_symlink() { check!(symlink_file(&filename, &symlink_name)); let init_symlink_metadata = check!(fs::symlink_metadata(&symlink_name)); - let mut init_symlink_permissions = init_symlink_metadata.permissions(); - + assert!(!init_symlink_metadata.permissions().readonly()); let init_target_metadata = check!(fs::metadata(&symlink_name)); - let init_target_permissions = init_target_metadata.permissions(); + assert!(!init_target_metadata.permissions().readonly()); // Set symlink permissions to readonly - init_symlink_permissions.set_readonly(true); - let result = fs::set_permissions_nofollow(&symlink_name, init_symlink_permissions); + let result = fs::set_permissions_nofollow(&symlink_name, { + let mut permissions = init_symlink_metadata.permissions(); + permissions.set_readonly(true); + permissions + }); + + // This should not change the permissions of the target! + let after_target_metadata = check!(fs::metadata(&symlink_name)); + assert_eq!(after_target_metadata.permissions(), init_target_metadata.permissions()); cfg_select! { any( @@ -687,16 +686,11 @@ fn set_get_permissions_nofollows_symlink() { target_os = "nto", target_os = "qnx" ) => { + // On these systems, it's confirmed the symlink itself is marked readonly + // https://superuser.com/questions/1099634/change-permissions-symbolic-link-mac-os assert_eq!(result.unwrap(), ()); - let after_target_metadata = check!(fs::metadata(&symlink_name)); - // We should expect the target file to not have its permission bits - // changed - assert_eq!(after_target_metadata.permissions(), init_target_permissions); - let after_symlink_metadata = check!(fs::symlink_metadata(&symlink_name)); - // On these systems, it's confirmed the symlink itself is marked readonly - // https://superuser.com/questions/1099634/change-permissions-symbolic-link-mac-os assert!(after_symlink_metadata.permissions().readonly()); // Reset the read-only bit under Windows 7: avoids the @@ -710,11 +704,7 @@ fn set_get_permissions_nofollows_symlink() { } } _ => { - let after_target_metadata = check!(fs::metadata(&symlink_name)); - // We should expect the target file to not have its permission bits - // changed - assert_eq!(after_target_metadata.permissions(), init_target_permissions); - + // Everywhere else, this just fails. let error_kind = result.unwrap_err().kind(); assert_eq!(error_kind, crate::io::ErrorKind::Unsupported); } @@ -775,6 +765,273 @@ fn file_test_io_seek_read_write() { check!(fs::remove_file(&filename)); } +#[test] +#[cfg(windows)] +fn file_test_io_seek_read_exact_write_all() { + use crate::os::windows::fs::FileExt; + + let tmpdir = tmpdir(); + let filename = tmpdir.join("file_rt_io_file_test_seek_read_exact_write_all.txt"); + let mut buf = [0; 256]; + let write1 = "asdf"; + let write2 = "qwer-"; + let write3 = "-zxcv"; + let content = "qwer-asdf-zxcv"; + { + let oo = OpenOptions::new().create_new(true).write(true).read(true).clone(); + let mut rw = check!(oo.open(&filename)); + check!(rw.seek_write_all(write1.as_bytes(), 5)); + assert_eq!(check!(rw.stream_position()), 9); + check!(rw.seek_read_exact(&mut buf[..write1.len()], 5)); + assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); + assert_eq!(check!(rw.stream_position()), 9); + assert_eq!(check!(rw.seek(SeekFrom::Start(0))), 0); + assert_eq!(check!(rw.write(write2.as_bytes())), write2.len()); + assert_eq!(check!(rw.stream_position()), 5); + assert_eq!(check!(rw.read(&mut buf)), write1.len()); + assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); + assert_eq!(check!(rw.stream_position()), 9); + check!(rw.seek_read_exact(&mut buf[..write2.len()], 0)); + assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok(write2)); + assert_eq!(check!(rw.stream_position()), 5); + check!(rw.seek_write_all(write3.as_bytes(), 9)); + assert_eq!(check!(rw.stream_position()), 14); + } + { + let mut read = check!(File::open(&filename)); + check!(read.seek_read_exact(&mut buf[..content.len()], 0)); + assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); + assert_eq!(check!(read.stream_position()), 14); + assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9); + check!(read.seek_read_exact(&mut buf[..content.len()], 0)); + assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); + assert_eq!(check!(read.stream_position()), 14); + assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9); + assert_eq!(check!(read.read(&mut buf)), write3.len()); + assert_eq!(str::from_utf8(&buf[..write3.len()]), Ok(write3)); + assert_eq!(check!(read.stream_position()), 14); + check!(read.seek_read_exact(&mut buf[..content.len()], 0)); + assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); + assert_eq!(check!(read.stream_position()), 14); + assert!(read.seek_read_exact(&mut buf, 14).is_err()); + assert!(read.seek_read_exact(&mut buf, 15).is_err()); + } + check!(fs::remove_file(&filename)); +} + +#[test] +#[cfg(windows)] +fn file_test_windows_fileext_trait_case_1() { + use crate::os::windows::fs::FileExt; + + // Test when seek_read_exact(), seek_write_all() are called with empty buffers. + struct MockFile {} + + impl FileExt for MockFile { + fn seek_read(&self, _buf: &mut [u8], _offset: u64) -> io::Result { + panic!("should not be called"); + } + + fn seek_write(&self, _buf: &[u8], _offset: u64) -> io::Result { + panic!("should not be called"); + } + } + + let mock_file = MockFile {}; + check!(mock_file.seek_read_exact(&mut [], 0)); + check!(mock_file.seek_write_all(&[], 0)); + check!(mock_file.seek_read_exact(&mut [], 420)); + check!(mock_file.seek_write_all(&[], 420)); +} + +#[test] +#[cfg(windows)] +fn file_test_windows_fileext_trait_case_2() { + use crate::os::windows::fs::FileExt; + + // Test when seek_read(), seek_write() return Ok(0) + struct MockFile { + expected_offset: u64, + } + + impl FileExt for MockFile { + fn seek_read(&self, _buf: &mut [u8], offset: u64) -> io::Result { + assert_eq!(offset, self.expected_offset); + Ok(0) + } + + fn seek_write(&self, _buf: &[u8], offset: u64) -> io::Result { + assert_eq!(offset, self.expected_offset); + Ok(0) + } + } + + { + let mock_file = MockFile { expected_offset: 0 }; + let mut buf = [0; 256]; + assert_eq!( + mock_file.seek_read_exact(&mut buf, 0).unwrap_err().kind(), + io::ErrorKind::UnexpectedEof + ); + assert_eq!(mock_file.seek_write_all(&buf, 0).unwrap_err().kind(), io::ErrorKind::WriteZero); + } + + { + let mock_file = MockFile { expected_offset: 420 }; + let mut buf = [0; 256]; + assert_eq!( + mock_file.seek_read_exact(&mut buf, 420).unwrap_err().kind(), + io::ErrorKind::UnexpectedEof + ); + assert_eq!( + mock_file.seek_write_all(&buf, 420).unwrap_err().kind(), + io::ErrorKind::WriteZero + ); + } +} + +#[test] +#[cfg(windows)] +fn file_test_windows_fileext_trait_case_3() { + use crate::os::windows::fs::FileExt; + + // Test that Err other than io::ErrorKind::Interrupted are propagated up. + struct MockFile { + expected_offset: u64, + } + + impl FileExt for MockFile { + fn seek_read(&self, _buf: &mut [u8], offset: u64) -> io::Result { + assert_eq!(offset, self.expected_offset); + Err(io::Error::new(io::ErrorKind::PermissionDenied, "seek_read")) + } + + fn seek_write(&self, _buf: &[u8], offset: u64) -> io::Result { + assert_eq!(offset, self.expected_offset); + Err(io::Error::new(io::ErrorKind::ConnectionRefused, "seek_write")) + } + } + + { + let mock_file = MockFile { expected_offset: 0 }; + let mut buf = [0; 256]; + assert_eq!( + mock_file.seek_read_exact(&mut buf, 0).unwrap_err().kind(), + io::ErrorKind::PermissionDenied + ); + assert_eq!( + mock_file.seek_write_all(&buf, 0).unwrap_err().kind(), + io::ErrorKind::ConnectionRefused + ); + } + + { + let mock_file = MockFile { expected_offset: 420 }; + let mut buf = [0; 256]; + assert_eq!( + mock_file.seek_read_exact(&mut buf, 420).unwrap_err().kind(), + io::ErrorKind::PermissionDenied + ); + assert_eq!( + mock_file.seek_write_all(&buf, 420).unwrap_err().kind(), + io::ErrorKind::ConnectionRefused + ); + } + // FIXME: Cover io::ErrorKind::Interrupted, but don't infinite loop ;) +} + +#[test] +#[cfg(windows)] +fn file_test_windows_fileext_trait_case_4() { + use crate::os::windows::fs::FileExt; + + const MSG: &[u8] = + b"The Rust programming language helps you write faster, more reliable software."; + + // Test when the entire read or write is satisfied by only one call to seek_read() or + // seek_write(), respectively. + struct MockFile { + expected_offset: u64, + } + + impl FileExt for MockFile { + fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result { + assert_eq!(offset, self.expected_offset); + assert_eq!(buf.len(), MSG.len()); + assert_eq!(buf, &[0; MSG.len()]); + buf.copy_from_slice(MSG); + Ok(MSG.len()) + } + + fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result { + assert_eq!(offset, self.expected_offset); + assert_eq!(buf.len(), MSG.len()); + assert_eq!(buf, MSG); + Ok(MSG.len()) + } + } + + { + let mock_file = MockFile { expected_offset: 0 }; + let mut buf = [0; MSG.len()]; + check!(mock_file.seek_read_exact(&mut buf, 0)); + assert_eq!(&buf, MSG); + check!(mock_file.seek_write_all(&buf, 0)); + } + + { + let mock_file = MockFile { expected_offset: 420 }; + let mut buf = [0; MSG.len()]; + check!(mock_file.seek_read_exact(&mut buf, 420)); + assert_eq!(&buf, MSG); + check!(mock_file.seek_write_all(&buf, 420)); + } +} + +#[test] +#[cfg(windows)] +fn file_test_windows_fileext_trait_case_5() { + use crate::os::windows::fs::FileExt; + + const MSG: &[u8] = + b"Rust is for students and those who are interested in learning about systems concepts."; + + // Test pathological case where seek_read(), seek_write() only do 1 byte per call, return Ok(1) + struct MockFile { + base_offset: u64, + } + + impl FileExt for MockFile { + fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result { + let offset = (offset - self.base_offset) as usize; + buf[0..1].copy_from_slice(&MSG[offset..offset + 1]); + Ok(1) + } + + fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result { + let offset = (offset - self.base_offset) as usize; + assert_eq!(buf[0..1], MSG[offset..offset + 1]); + Ok(1) + } + } + + { + let mock_file = MockFile { base_offset: 0 }; + let mut buf = [0; MSG.len()]; + check!(mock_file.seek_read_exact(&mut buf, 0)); + assert_eq!(&buf, MSG); + check!(mock_file.seek_write_all(&buf, 0)); + } + + { + let mock_file = MockFile { base_offset: 420 }; + let mut buf = [0; MSG.len()]; + check!(mock_file.seek_read_exact(&mut buf, 420)); + assert_eq!(&buf, MSG); + check!(mock_file.seek_write_all(&buf, 420)); + } +} + #[test] #[cfg(windows)] fn test_seek_read_buf() { @@ -808,6 +1065,43 @@ fn test_seek_read_buf() { check!(fs::remove_file(&filename)); } +#[test] +#[cfg(windows)] +fn test_seek_read_buf_exact() { + use crate::os::windows::fs::FileExt; + + let tmpdir = tmpdir(); + let filename = tmpdir.join("file_rt_io_file_test_seek_read_buf_exact.txt"); + { + let oo = OpenOptions::new().create_new(true).write(true).read(true).clone(); + let mut file = check!(oo.open(&filename)); + check!(file.write_all(b"0123456789")); + } + { + let mut file = check!(File::open(&filename)); + let mut buf: [MaybeUninit; 5] = [MaybeUninit::uninit(); 5]; + let mut buf = BorrowedBuf::from(buf.as_mut_slice()); + + // Exact read + check!(file.seek_read_buf_exact(buf.unfilled(), 2)); + assert_eq!(buf.filled(), b"23456"); + assert_eq!(check!(file.stream_position()), 7); + + // Already full + check!(file.seek_read_buf_exact(buf.unfilled(), 3)); + assert_eq!(check!(file.stream_position()), 7); + check!(file.seek_read_buf_exact(buf.unfilled(), 10)); // No call to seek_read() + assert_eq!(buf.filled(), b"23456"); + assert_eq!(check!(file.stream_position()), 7); + + // Non-empty exact read past eof fails + let err = file.seek_read_buf_exact(buf.clear().unfilled(), 6).unwrap_err(); + assert_eq!(err.kind(), ErrorKind::UnexpectedEof); + assert_eq!(check!(file.stream_position()), 10); + } + check!(fs::remove_file(&filename)); +} + #[test] fn file_test_read_buf() { let tmpdir = tmpdir(); @@ -1000,10 +1294,6 @@ fn recursive_mkdir_empty() { } #[test] -#[cfg_attr( - all(windows, target_arch = "aarch64"), - ignore = "SymLinks not enabled on Arm64 Windows runners https://github.com/actions/partner-runner-images/issues/94" -)] fn recursive_rmdir() { let tmpdir = tmpdir(); let d1 = tmpdir.join("d1"); @@ -1015,7 +1305,9 @@ fn recursive_rmdir() { check!(fs::create_dir_all(&d2)); check!(check!(File::create(&canary)).write(b"foo")); check!(junction_point(&d2, &dt.join("d2"))); - let _ = symlink_file(&canary, &d1.join("canary")); + if got_symlink_permission(&tmpdir) { + let _ = symlink_file(&canary, &d1.join("canary")); + } check!(fs::remove_dir_all(&d1)); assert!(!d1.is_dir()); @@ -1023,10 +1315,6 @@ fn recursive_rmdir() { } #[test] -#[cfg_attr( - all(windows, target_arch = "aarch64"), - ignore = "SymLinks not enabled on Arm64 Windows runners https://github.com/actions/partner-runner-images/issues/94" -)] fn recursive_rmdir_of_symlink() { // test we do not recursively delete a symlink but only dirs. let tmpdir = tmpdir(); @@ -1640,6 +1928,14 @@ fn open_flavors() { assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 9); } +#[test] +#[cfg(windows)] +fn windows_access_mode_override() { + // ensure that using access_mode negates the need for using write or append + use crate::os::windows::fs::OpenOptionsExt; + File::options().create(true).access_mode(0).open(tmpdir().join("foo.txt")).unwrap(); +} + #[test] fn _assert_send_sync() { fn _assert_send_sync() {} @@ -1834,10 +2130,6 @@ fn file_open_not_found() { } #[test] -#[cfg_attr( - all(windows, target_arch = "aarch64"), - ignore = "SymLinks not enabled on Arm64 Windows runners https://github.com/actions/partner-runner-images/issues/94" -)] fn create_dir_all_with_junctions() { let tmpdir = tmpdir(); let target = tmpdir.join("target"); @@ -2374,10 +2666,6 @@ fn test_rename_symlink() { #[test] #[cfg(windows)] -#[cfg_attr( - all(windows, target_arch = "aarch64"), - ignore = "SymLinks not enabled on Arm64 Windows runners https://github.com/actions/partner-runner-images/issues/94" -)] fn test_rename_junction() { let tmpdir = tmpdir(); let original = tmpdir.join("original"); @@ -2727,10 +3015,23 @@ fn test_dir_read_file() { } #[test] -fn test_dir_metadata() { +fn test_dir_clone() { let tmpdir = tmpdir(); + let mut f = check!(File::create(tmpdir.join("foo.txt"))); + check!(f.write_all(b"bar")); + drop(f); + let dir = check!(Dir::open(tmpdir.path())); - let metadata = check!(dir.metadata()); + let dir2 = check!(dir.try_clone()); + let f = check!(dir2.open_file("foo.txt")); + drop(f); +} + +#[test] +fn test_dir_self_metadata() { + let tmpdir = tmpdir(); + let dir = check!(Dir::open(tmpdir.path())); + let metadata = check!(dir.self_metadata()); assert!(metadata.is_dir()); } @@ -2774,6 +3075,10 @@ fn test_dir_rename_file() { assert_eq!(b"bar", &buf); } +// FIXME: re-enable once QNX fixes TOCTOU bug for fs::remove_dir +// Note that it may get fixed in QNX 8 in a future libc release +// ... https://github.com/rust-lang/rust/issues/153781 +#[cfg_attr(any(target_os = "nto", target_os = "qnx"), ignore)] #[test] fn test_dir_remove_dir() { let tmpdir = tmpdir(); @@ -2783,6 +3088,16 @@ fn test_dir_remove_dir() { assert!(!matches!(exists(tmpdir.join("foo")), Ok(true))); } +#[test] +fn test_dir_rename_dir() { + let tmpdir = tmpdir(); + check!(fs::create_dir(tmpdir.join("foo"))); + let dir = check!(Dir::open(tmpdir.path())); + check!(dir.rename("foo", &dir, "baz")); + let m = check!(tmpdir.join("baz").metadata()); + assert!(m.is_dir()); +} + #[test] fn test_dir_create_dir() { let tmpdir = tmpdir(); @@ -2808,3 +3123,89 @@ fn test_dir_open_dir() { check!(f.read_exact(&mut buf)); assert_eq!(b"baz", &buf); } + +#[test] +fn test_dir_metadata() { + let tmpdir = tmpdir(); + let dir = check!(Dir::open(tmpdir.path())); + check!(dir.create_dir("subdir")); + // FIXME: `/` does not work as path separator on Windows. + let barpath = PathBuf::from("subdir").join("bar.txt"); + drop(check!(dir.open_file_with(&barpath, &OpenOptions::new().create(true).write(true)))); + check!(symlink_file(&tmpdir.join("subdir/bar.txt"), &tmpdir.join("link"))); + + let metadata = check!(dir.metadata(&barpath)); + assert!(metadata.is_file()); + let metadata = check!(dir.metadata("subdir")); + assert!(metadata.is_dir()); + dir.metadata("does-not-exist").unwrap_err(); + + let metadata = check!(dir.metadata("link")); + assert!(metadata.is_file()); + assert!(!metadata.is_symlink()); + let metadata = check!(dir.symlink_metadata("link")); + assert!(!metadata.is_file()); + assert!(metadata.is_symlink()); +} + +fn root_test_dir(what: &str) -> PathBuf { + crate::env::current_dir().unwrap().ancestors().last().unwrap().join(what) +} + +#[test] +fn test_home_dirs_field_hookup_matches() { + let mut dirs = HomeDirs::empty(); + + assert_eq!(dirs.config_home(), None); + assert_eq!(dirs.data_home(), None); + assert_eq!(dirs.state_home(), None); + assert_eq!(dirs.cache_home(), None); + + let config = root_test_dir("config"); + let data = root_test_dir("data"); + let state = root_test_dir("state"); + let cache = root_test_dir("cache"); + + dirs.set_config_home(config.clone()); + dirs.set_data_home(data.clone()); + dirs.set_state_home(state.clone()); + dirs.set_cache_home(cache.clone()); + + assert_eq!(dirs.config_home(), Some(config.as_ref())); + assert_eq!(dirs.data_home(), Some(data.as_ref())); + assert_eq!(dirs.state_home(), Some(state.as_ref())); + assert_eq!(dirs.cache_home(), Some(cache.as_ref())); +} + +#[test] +fn test_media_dirs_field_hookup_matches() { + let mut dirs = MediaDirs::empty(); + + assert_eq!(dirs.desktop(), None); + assert_eq!(dirs.documents(), None); + assert_eq!(dirs.downloads(), None); + assert_eq!(dirs.music(), None); + assert_eq!(dirs.pictures(), None); + assert_eq!(dirs.videos(), None); + + let desktop = root_test_dir("desktop"); + let documents = root_test_dir("documents"); + let downloads = root_test_dir("downloads"); + let music = root_test_dir("music"); + let pictures = root_test_dir("pictures"); + let videos = root_test_dir("videos"); + + dirs.set_desktop(desktop.clone()); + dirs.set_documents(documents.clone()); + dirs.set_downloads(downloads.clone()); + dirs.set_music(music.clone()); + dirs.set_pictures(pictures.clone()); + dirs.set_videos(videos.clone()); + + assert_eq!(dirs.desktop(), Some(desktop.as_ref())); + assert_eq!(dirs.documents(), Some(documents.as_ref())); + assert_eq!(dirs.downloads(), Some(downloads.as_ref())); + assert_eq!(dirs.music(), Some(music.as_ref())); + assert_eq!(dirs.pictures(), Some(pictures.as_ref())); + assert_eq!(dirs.videos(), Some(videos.as_ref())); +} diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 3f853df52bc53..52a87540ac0f0 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -244,7 +244,6 @@ #![warn(unreachable_pub)] #![warn(missing_debug_implementations)] #![allow(explicit_outlives_requirements)] -#![allow(unused_lifetimes)] #![allow(internal_features)] #![deny(implicit_provenance_casts)] #![deny(unsafe_op_in_unsafe_fn)] @@ -314,7 +313,6 @@ #![feature(try_blocks)] #![feature(try_trait_v2)] #![feature(type_alias_impl_trait)] -#![feature(unwrap_infallible)] // tidy-alphabetical-end // // Library features (core): @@ -335,21 +333,18 @@ #![feature(core_io)] #![feature(core_io_borrowed_buf)] #![feature(core_io_internals)] -#![feature(cstr_display)] #![feature(cursor_split)] #![feature(derive_const)] #![feature(duration_constants)] #![feature(error_generic_member_access)] #![feature(error_iter)] #![feature(exact_size_is_empty)] -#![feature(exclusive_wrapper)] #![feature(extend_one)] #![feature(float_gamma)] #![feature(float_minimum_maximum)] #![feature(fmt_internals)] #![feature(fn_static)] #![feature(formatting_options)] -#![feature(funnel_shifts)] #![feature(generic_atomic)] #![feature(hash_map_internals)] #![feature(hash_map_macro)] @@ -369,6 +364,7 @@ #![feature(maybe_dangling)] #![feature(maybe_uninit_array_assume_init)] #![feature(maybe_uninit_fill)] +#![feature(once_lazy_lock_get_unchecked)] #![feature(panic_can_unwind)] #![feature(panic_internals)] #![feature(pin_coerce_unsized_trait)] @@ -400,7 +396,7 @@ // Library features (alloc): // tidy-alphabetical-start #![feature(alloc_io)] -#![feature(allocator_api)] +#![feature(allocator_ext)] #![feature(buf_read_has_data_left)] #![feature(clone_from_ref)] #![feature(get_mut_unchecked)] @@ -539,24 +535,24 @@ pub use core::future; #[stable(feature = "core_hint", since = "1.27.0")] pub use core::hint; #[stable(feature = "rust1", since = "1.0.0")] -#[allow(deprecated, deprecated_in_future, clippy::legacy_numeric_constants)] +#[allow(deprecated, clippy::legacy_numeric_constants)] pub use core::i8; #[stable(feature = "rust1", since = "1.0.0")] -#[allow(deprecated, deprecated_in_future, clippy::legacy_numeric_constants)] +#[allow(deprecated, clippy::legacy_numeric_constants)] pub use core::i16; #[stable(feature = "rust1", since = "1.0.0")] -#[allow(deprecated, deprecated_in_future, clippy::legacy_numeric_constants)] +#[allow(deprecated, clippy::legacy_numeric_constants)] pub use core::i32; #[stable(feature = "rust1", since = "1.0.0")] -#[allow(deprecated, deprecated_in_future, clippy::legacy_numeric_constants)] +#[allow(deprecated, clippy::legacy_numeric_constants)] pub use core::i64; #[stable(feature = "i128", since = "1.26.0")] -#[allow(deprecated, deprecated_in_future, clippy::legacy_numeric_constants)] +#[allow(deprecated, clippy::legacy_numeric_constants)] pub use core::i128; #[stable(feature = "rust1", since = "1.0.0")] pub use core::intrinsics; #[stable(feature = "rust1", since = "1.0.0")] -#[allow(deprecated, deprecated_in_future, clippy::legacy_numeric_constants)] +#[allow(deprecated, clippy::legacy_numeric_constants)] pub use core::isize; #[stable(feature = "rust1", since = "1.0.0")] pub use core::iter; @@ -577,24 +573,24 @@ pub use core::range; #[stable(feature = "rust1", since = "1.0.0")] pub use core::result; #[stable(feature = "rust1", since = "1.0.0")] -#[allow(deprecated, deprecated_in_future, clippy::legacy_numeric_constants)] +#[allow(deprecated, clippy::legacy_numeric_constants)] pub use core::u8; #[stable(feature = "rust1", since = "1.0.0")] -#[allow(deprecated, deprecated_in_future, clippy::legacy_numeric_constants)] +#[allow(deprecated, clippy::legacy_numeric_constants)] pub use core::u16; #[stable(feature = "rust1", since = "1.0.0")] -#[allow(deprecated, deprecated_in_future, clippy::legacy_numeric_constants)] +#[allow(deprecated, clippy::legacy_numeric_constants)] pub use core::u32; #[stable(feature = "rust1", since = "1.0.0")] -#[allow(deprecated, deprecated_in_future, clippy::legacy_numeric_constants)] +#[allow(deprecated, clippy::legacy_numeric_constants)] pub use core::u64; #[stable(feature = "i128", since = "1.26.0")] -#[allow(deprecated, deprecated_in_future, clippy::legacy_numeric_constants)] +#[allow(deprecated, clippy::legacy_numeric_constants)] pub use core::u128; #[unstable(feature = "unsafe_binders", issue = "130516")] pub use core::unsafe_binder; #[stable(feature = "rust1", since = "1.0.0")] -#[allow(deprecated, deprecated_in_future, clippy::legacy_numeric_constants)] +#[allow(deprecated, clippy::legacy_numeric_constants)] pub use core::usize; #[stable(feature = "rust1", since = "1.0.0")] @@ -740,7 +736,7 @@ pub mod alloc; mod panicking; #[path = "../../backtrace/src/lib.rs"] -#[allow(dead_code, unused_attributes, implicit_provenance_casts, unsafe_op_in_unsafe_fn)] +#[allow(implicit_provenance_casts, unsafe_op_in_unsafe_fn)] #[allow(clippy::len_zero, clippy::needless_borrow, clippy::filter_map_next)] // FIXME mod backtrace_rs; @@ -759,13 +755,13 @@ pub use core::concat_bytes; )] #[unstable(feature = "derive_macro_global_path", issue = "154645")] pub use core::derive; +#[unstable(feature = "gca_min_const_items", issue = "132980", implied_by = "gca_adts")] +pub use core::gca; #[stable(feature = "matches_macro", since = "1.42.0")] -#[allow(deprecated, deprecated_in_future)] pub use core::matches; #[stable(feature = "core_primitive", since = "1.43.0")] pub use core::primitive; #[stable(feature = "todo_macro", since = "1.40.0")] -#[allow(deprecated, deprecated_in_future)] pub use core::todo; // Re-export built-in macros defined through core. #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] @@ -776,7 +772,7 @@ pub use core::{ }; // Re-export macros defined in core. #[stable(feature = "rust1", since = "1.0.0")] -#[allow(deprecated, deprecated_in_future)] +#[expect(deprecated, reason = "`try! macro")] pub use core::{ assert_eq, assert_ne, debug_assert, debug_assert_eq, debug_assert_ne, r#try, unimplemented, unreachable, write, writeln, diff --git a/library/std/src/num/f32.rs b/library/std/src/num/f32.rs index ac3f2e263195d..5792d236b4e89 100644 --- a/library/std/src/num/f32.rs +++ b/library/std/src/num/f32.rs @@ -13,7 +13,7 @@ #![allow(missing_docs)] #[stable(feature = "rust1", since = "1.0.0")] -#[allow(deprecated, deprecated_in_future, clippy::legacy_numeric_constants)] +#[allow(deprecated, clippy::legacy_numeric_constants)] pub use core::f32::{ DIGITS, EPSILON, INFINITY, MANTISSA_DIGITS, MAX, MAX_10_EXP, MAX_EXP, MIN, MIN_10_EXP, MIN_EXP, MIN_POSITIVE, NAN, NEG_INFINITY, RADIX, consts, @@ -80,6 +80,11 @@ impl f32 { /// /// This function always returns the precise result. /// + /// On most hardware platforms, [`round_ties_even`](Self::round_ties_even) may execute faster + /// than `round`. If both rounding methods fit the use case, consider using `round_ties_even`. + /// Note that the two methods apply different rounding rules to values exactly halfway between + /// two integers. + /// /// # Examples /// /// ``` diff --git a/library/std/src/num/f64.rs b/library/std/src/num/f64.rs index 9b3086b1ce12e..8ca38cc0be69e 100644 --- a/library/std/src/num/f64.rs +++ b/library/std/src/num/f64.rs @@ -13,7 +13,7 @@ #![allow(missing_docs)] #[stable(feature = "rust1", since = "1.0.0")] -#[allow(deprecated, deprecated_in_future, clippy::legacy_numeric_constants)] +#[allow(deprecated, clippy::legacy_numeric_constants)] pub use core::f64::{ DIGITS, EPSILON, INFINITY, MANTISSA_DIGITS, MAX, MAX_10_EXP, MAX_EXP, MIN, MIN_10_EXP, MIN_EXP, MIN_POSITIVE, NAN, NEG_INFINITY, RADIX, consts, @@ -80,6 +80,11 @@ impl f64 { /// /// This function always returns the precise result. /// + /// On most hardware platforms, [`round_ties_even`](Self::round_ties_even) may execute faster + /// than `round`. If both rounding methods fit the use case, consider using `round_ties_even`. + /// Note that the two methods apply different rounding rules to values exactly halfway between + /// two integers. + /// /// # Examples /// /// ``` diff --git a/library/std/src/os/darwin/fs.rs b/library/std/src/os/darwin/fs.rs index fc2869d6b13f6..b41391f19dccf 100644 --- a/library/std/src/os/darwin/fs.rs +++ b/library/std/src/os/darwin/fs.rs @@ -7,6 +7,13 @@ use crate::fs::{self, Metadata}; use crate::sys::{AsInner, AsInnerMut, IntoInner}; use crate::time::SystemTime; +mod dirs; + +#[unstable(feature = "fs_home_dirs", issue = "162082")] +pub use dirs::HomeDirsExt; +#[unstable(feature = "fs_media_dirs", issue = "162083")] +pub use dirs::MediaDirsExt; + /// OS-specific extensions to [`fs::Metadata`]. /// /// [`fs::Metadata`]: crate::fs::Metadata diff --git a/library/std/src/os/darwin/fs/dirs.rs b/library/std/src/os/darwin/fs/dirs.rs new file mode 100644 index 0000000000000..6aaa910981b0b --- /dev/null +++ b/library/std/src/os/darwin/fs/dirs.rs @@ -0,0 +1,313 @@ +use crate::env; +use crate::fs::{HomeDirs, MediaDirs}; +use crate::io::{self, ErrorKind, const_error}; +use crate::path::PathBuf; + +/// Darwin-specific extensions to [`fs::HomeDirs`](HomeDirs). +#[unstable(feature = "fs_home_dirs", issue = "162082")] +pub impl(self) trait HomeDirsExt: Sized { + /// Load the standard user directory paths for the current user. + /// + /// On iOS, tvOS, watchOS, visionOS, and sandboxed macOS applications, + /// these directories are within the application's container. Outside + /// the sandbox, these are subdirectories of the `~/Library` directory on + /// macOS. + /// + /// The produced directory paths are not guaranteed to be the canonical + /// paths to the directories; they are allowed to be sandbox-redirected + /// paths as long as the directory is accessible there. + /// + /// The loaded common directories are: + /// + /// | `HomeDirs` | [`NSSearchPathDirectory`] | + /// | ---------- | ----------------------- | + /// | [`cache_home`] | [`NSCachesDirectory`] (`~/Library/Caches`) | + /// | [`config_home`] | [`NSApplicationSupportDirectory`] (`~/Library/Application Support`) | + /// | [`data_home`] | [`NSApplicationSupportDirectory`] (`~/Library/Application Support`) | + /// | [`state_home`] | [`NSApplicationSupportDirectory`] (`~/Library/Application Support`) | + /// + /// Note that the Application Support directory is used for the config, + /// data, and state directories. It is always possible for multiple user + /// directories to be configured to the same path, but this is the common + /// configuration on Apple platforms, making it even more important to not + /// assume files in different user directories cannot alias each other. + /// + /// # Errors + /// + /// Errors if the [user home](env::home_dir) cannot be determined. + // Errors due to the underlying sysdir(3) API should never occur, as + // - the user domain only has one directory for each search path; + // - the user domain always returns subdirectory paths of `~`; and + // - the username and OS defined path segments are always valid UTF-8. + /// + /// # Implementation-specific behavior + /// + /// Uses the `sysdir(3)` API from `libSystem` to discover the standard + /// user directories. + /// + /// This behavior may change in the future. One example change that we + /// explicitly reserve the right to make is to load additional common + /// directories not currently in this list. + /// + /// [`cache_home`]: HomeDirs::cache_home + /// [`config_home`]: HomeDirs::config_home + /// [`data_home`]: HomeDirs::data_home + /// [`state_home`]: HomeDirs::state_home + /// + /// [`NSSearchPathDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory?language=objc + /// [`NSCachesDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/cachesdirectory?language=objc + /// [`NSApplicationSupportDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/applicationsupportdirectory?language=objc + #[unstable(feature = "fs_home_dirs", issue = "162082")] + fn sysdir() -> io::Result; +} + +/// Darwin-specific extensions to [`fs::MediaDirs`](MediaDirs). +#[unstable(feature = "fs_media_dirs", issue = "162083")] +pub impl(self) trait MediaDirsExt: Sized { + /// Load the standard user directory paths for the current user. + /// + /// The produced directory paths are not guaranteed to be the canonical + /// paths to the directories; they are allowed to be sandbox-redirected + /// paths as long as the directory is accessible there. + /// + /// The loaded common directories are: + /// + /// | `MediaDirs` | [`NSSearchPathDirectory`] | + /// | ---------- | ----------------------- | + /// | [`desktop`] | [`NSDesktopDirectory`] (`~/Desktop`) | + /// | [`documents`] | [`NSDocumentDirectory`] (`~/Documents`) | + /// | [`downloads`] | [`NSDownloadsDirectory`] (`~/Downloads`) | + /// | [`music`] | [`NSMusicDirectory`] (`~/Music`) | + /// | [`pictures`] | [`NSPicturesDirectory`] (`~/Pictures`) | + /// | [`videos`] | [`NSMoviesDirectory`] (`~/Movies`) | + /// + /// # Errors + /// + /// Errors if the the [user home](env::home_dir) cannot be determined. + // Errors due to the underlying sysdir(3) API should never occur, as + // - the user domain only has one directory for each search path; + // - the user domain always returns subdirectory paths of `~`; + // - the username plus OS defined path segments cannot exceed PATH_MAX; and + // - the username and OS defined path segments are always valid UTF-8. + /// + /// # Implementation-specific behavior + /// + /// Uses the `sysdir(3)` API from `libSystem` to discover the standard + /// user directories. + /// + /// This behavior may change in the future. One example change that we + /// explicitly reserve the right to make is to load additional common + /// directories not currently in this list. + /// + /// [`desktop`]: MediaDirs::desktop + /// [`documents`]: MediaDirs::documents + /// [`downloads`]: MediaDirs::downloads + /// [`music`]: MediaDirs::music + /// [`pictures`]: MediaDirs::pictures + /// [`videos`]: MediaDirs::videos + /// + /// [`NSSearchPathDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory?language=objc + /// [`NSDesktopDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/desktopdirectory?language=objc + /// [`NSDocumentDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/documentdirectory?language=objc + /// [`NSDownloadsDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/downloadsdirectory?language=objc + /// [`NSMusicDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/musicdirectory?language=objc + /// [`NSPicturesDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/picturesdirectory?language=objc + /// [`NSMoviesDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/moviesdirectory?language=objc + #[unstable(feature = "fs_media_dirs", issue = "162083")] + fn sysdir() -> io::Result; +} + +fn user_home() -> io::Result { + env::home_dir() + .filter(|p| p.is_absolute()) + .ok_or(const_error!(ErrorKind::InvalidData, "home path not absolute")) +} + +#[unstable(feature = "fs_home_dirs", issue = "162082")] +#[cfg(target_vendor = "apple")] +impl HomeDirsExt for HomeDirs { + fn sysdir() -> io::Result { + use libc::sysdir_search_path_directory_t::*; + + let mut dirs = HomeDirs::empty(); + let home = user_home()?; + + let caches = sys::get_user_dir(&home, SYSDIR_DIRECTORY_CACHES)?; + let application_support = sys::get_user_dir(&home, SYSDIR_DIRECTORY_APPLICATION_SUPPORT)?; + + dirs.cache = caches; + // Apple puts config/data/state all in Application Support + dirs.config = application_support.clone(); + dirs.data = application_support.clone(); + dirs.state = application_support; + + Ok(dirs) + } +} + +#[unstable(feature = "fs_media_dirs", issue = "162083")] +#[cfg(target_vendor = "apple")] +impl MediaDirsExt for MediaDirs { + fn sysdir() -> io::Result { + use libc::sysdir_search_path_directory_t::*; + + let mut dirs = MediaDirs::empty(); + let home = user_home()?; + + let desktop = sys::get_user_dir(&home, SYSDIR_DIRECTORY_DESKTOP)?; + let documents = sys::get_user_dir(&home, SYSDIR_DIRECTORY_DOCUMENT)?; + let downloads = sys::get_user_dir(&home, SYSDIR_DIRECTORY_DOWNLOADS)?; + let movies = sys::get_user_dir(&home, SYSDIR_DIRECTORY_MOVIES)?; + let music = sys::get_user_dir(&home, SYSDIR_DIRECTORY_MUSIC)?; + let pictures = sys::get_user_dir(&home, SYSDIR_DIRECTORY_PICTURES)?; + + dirs.desktop = desktop; + dirs.documents = documents; + dirs.downloads = downloads; + dirs.music = music; + dirs.pictures = pictures; + dirs.videos = movies; + + Ok(dirs) + } +} + +/// Safer wrapper around the sysdir(3) API +#[cfg(target_vendor = "apple")] +mod sys { + use crate::ffi::{CStr, c_char}; + use crate::io::{self, ErrorKind, const_error}; + use crate::path::{Path, PathBuf}; + + /// Get the path for a system directory using `sysdir(3)`. + pub fn get_user_dir( + home: &Path, + kind: libc::sysdir_search_path_directory_t, + ) -> io::Result> { + use libc::sysdir_search_path_domain_mask_t::SYSDIR_DOMAIN_MASK_USER; + + // SAFETY: SYSDIR_DOMAIN_MASK_USER < SYSDIR_DOMAIN_MASK_ALL + let mut iter = unsafe { Iter::new(home, kind, SYSDIR_DOMAIN_MASK_USER) }; + let Some(path) = iter.next() else { + return Ok(None); + }; + let path = path?; + + if iter.next().is_some() { + // more than one path returned (shouldn't happen for SYSDIR_DOMAIN_MASK_USER) + return Err(const_error!( + ErrorKind::InvalidData, + "multiple paths returned for standard user directory", + )); + } + + Ok(Some(path)) + } + + struct Iter<'a> { + home: &'a Path, + state: libc::sysdir_search_path_enumeration_state, + } + + impl Drop for Iter<'_> { + fn drop(&mut self) { + for _ in self {} + } + } + + impl<'a> Iter<'a> { + // SAFETY: `mask` must be <= `SYSDIR_DOMAIN_MASK_ALL` + pub unsafe fn new( + home: &'a Path, + kind: libc::sysdir_search_path_directory_t, + mask: libc::sysdir_search_path_domain_mask_t, + ) -> Self { + // SAFETY: forwarded to the caller + let state = unsafe { libc::sysdir_start_search_path_enumeration(kind, mask) }; + Self { home, state } + } + } + + impl Iterator for Iter<'_> { + type Item = io::Result; + + fn next(&mut self) -> Option> { + let mut buf = [0u8; libc::PATH_MAX as usize]; + if self.state != 0 { + // SAFETY: `self.state` is nonzero and comes from prior sysdir_{start|get_next}_search_path_enumeration call + // SAFETY: sysdir_get_next_search_path_enumeration will write at most `PATH_MAX` bytes to `path` + self.state = unsafe { + libc::sysdir_get_next_search_path_enumeration( + self.state, + buf.as_mut_ptr() as *mut c_char, + ) + }; + } + + if self.state == 0 { + // exhausted + return None; + } + + let Ok(path) = CStr::from_bytes_until_nul(&buf) else { + // should be impossible given home-relative paths, but be defensive + return Some(Err(const_error!( + ErrorKind::InvalidData, + "standard user directory path too long", + ))); + }; + + let Ok(path) = path.to_str() else { + // should be impossible on a working system, but be defensive + return Some(Err(const_error!( + ErrorKind::InvalidData, + "standard user directory path not valid UTF-8", + ))); + }; + + // expand `~` shorthand + Some(match path { + "~" => Ok(self.home.into()), + _ if path.starts_with("~/") => Ok(self.home.join(&path[2..])), + _ if path.starts_with("~") => Err(const_error!( + ErrorKind::InvalidData, + "standard user directory relative to different user", + )), + _ => { + let path = PathBuf::from(path); + if path.is_relative() { + Err(const_error!( + ErrorKind::InvalidData, + "standard user directory path not absolute", + )) + } else { + Ok(path) + } + } + }) + } + } +} + +#[cfg(test)] +#[cfg(target_vendor = "apple")] +mod tests { + use super::*; + + #[test] + fn can_fetch_sysdir_paths() { + let dirs = HomeDirs::sysdir().unwrap(); + assert!(dirs.cache_home().is_some()); + assert!(dirs.config_home().is_some()); + assert!(dirs.data_home().is_some()); + assert!(dirs.state_home().is_some()); + + let dirs = MediaDirs::sysdir().unwrap(); + assert!(dirs.desktop().is_some()); + assert!(dirs.documents().is_some()); + assert!(dirs.downloads().is_some()); + assert!(dirs.music().is_some()); + assert!(dirs.pictures().is_some()); + assert!(dirs.videos().is_some()); + } +} diff --git a/library/std/src/os/unix/fs.rs b/library/std/src/os/unix/fs.rs index b93f94225070a..fcafe1c29797f 100644 --- a/library/std/src/os/unix/fs.rs +++ b/library/std/src/os/unix/fs.rs @@ -22,6 +22,13 @@ use crate::{io, sys}; #[cfg(test)] mod tests; +pub(crate) mod dirs; + +#[unstable(feature = "fs_media_dirs", issue = "162083")] +pub use dirs::MediaDirsExt; +#[unstable(feature = "fs_home_dirs", issue = "162082")] +pub use dirs::{HomeDirsExt, XdgDirs}; + /// Unix-specific extensions to [`fs::File`]. #[stable(feature = "file_offset", since = "1.15.0")] pub trait FileExt { diff --git a/library/std/src/os/unix/fs/dirs.rs b/library/std/src/os/unix/fs/dirs.rs new file mode 100644 index 0000000000000..5d8e13c7a730a --- /dev/null +++ b/library/std/src/os/unix/fs/dirs.rs @@ -0,0 +1,585 @@ +use crate::env::{self, SplitPathsRef}; +use crate::ffi::{OsStr, OsString}; +use crate::fs::{self, HomeDirs, MediaDirs}; +use crate::io::{self, ErrorKind, const_error}; +use crate::path::{Path, PathBuf}; + +#[derive(Debug, Default, Clone)] +#[non_exhaustive] +pub struct ExtraHomeDirs { + runtime: Option, + config_path: Option, + data_path: Option, +} + +#[derive(Debug, Default, Clone)] +#[non_exhaustive] +pub struct ExtraMediaDirs { + templates: Option, +} + +/// XDG-specific extensions to [`fs::HomeDirs`](HomeDirs). +/// +/// The XDG conventions are defined by the Freedesktop.org project in the +/// [XDG Base Directory Specification][xdg-basedir]. These conventions have +/// been largely adopted by Linux distributions. +/// +/// The XDG conventions are written to be usable on any Unix-like filesystem, +/// thus this extension being provided in `os::unix` rather than `os::linux`. +/// However, while some tooling does use XDG conventions on macOS, note that +/// macOS has its own separate conventions for user directories. Consider +/// carefully what conventions your users will expect your application to +/// follow along with any legacy path compatibility you might need to support. +/// +/// [xdg-basedir]: https://specifications.freedesktop.org/basedir/ +#[unstable(feature = "fs_home_dirs", issue = "162082")] +pub impl(self) trait HomeDirsExt: Sized { + /// Load the user directory paths according to the + /// [XDG Base Directory Specification][xdg-basedir]. + /// + /// Each base directory path is set to the value of its corresponding + /// `XDG_*` environment variable (if it is set and non-empty), else to + /// the default value defined by the specification. + /// + /// | Field | Environment Variable | Default Value | + /// | ----- | -------------------- | ------------- | + /// | [`cache_home`] | `XDG_CACHE_HOME` | `$HOME/.cache` | + /// | [`config_home`] | `XDG_CONFIG_HOME` | `$HOME/.config` | + /// | [`data_home`] | `XDG_DATA_HOME` | `$HOME/.local/share` | + /// | [`state_home`] | `XDG_STATE_HOME` | `$HOME/.local/state` | + /// | [`runtime_home`] | `XDG_RUNTIME_DIR` | (see method docs) | + /// | [`config_dirs`] | `XDG_CONFIG_DIRS` | `/etc/xdg` | + /// | [`data_dirs`] | `XDG_DATA_DIRS` | `/usr/local/share/`, `/usr/share/` | + /// + /// Note that `$HOME` here means [`env::home_dir`], which uses + /// `$HOME` if set and non-empty, but falls back to the system password + /// database if it isn't set. + /// + /// All paths are required to be absolute. If a relative path is configured + /// by the environment, it is ignored and the default value is used instead. + /// + /// `config_dirs` and `data_dirs` are a list of delimited paths using the + /// [`env::split_paths`] delimiter. If some but not all paths in the list are + /// relative, those relative paths are ignored and the remaining absolute + /// paths are used. If there are no valid absolute paths, the default value + /// is used instead. + /// + /// # Errors + /// + /// Errors if the user's home directory cannot be determined. + /// + /// [xdg-basedir]: https://specifications.freedesktop.org/basedir/ + /// [`cache_home`]: HomeDirs::cache_home + /// [`config_home`]: HomeDirs::config_home + /// [`data_home`]: HomeDirs::data_home + /// [`state_home`]: HomeDirs::state_home + /// [`runtime_home`]: HomeDirsExt::runtime_home + /// [`config_dirs`]: HomeDirsExt::config_dirs + /// [`data_dirs`]: HomeDirsExt::data_dirs + /// [`env::split_paths`]: crate::env::split_paths + #[unstable(feature = "fs_home_dirs", issue = "162082")] + fn xdg() -> io::Result; + + /// A base directory relative to which user-specific runtime files + /// (such as sockets, named pipes, etc) should be stored. + /// + /// Files in this directory may be subjected to periodic clean-up. + /// Larger files should not be placed here, since it might reside in + /// runtime memory and cannot necessarily be swapped out to disk. + /// + /// This path does not have a default if not set. If it isn't set, + /// applications should fall back to a replacement directory with + /// similar capabilities and print a warning message. + #[unstable(feature = "fs_home_dirs", issue = "162082")] + fn runtime_home(&self) -> Option<&Path>; + + /// A preference-ordered list of base directories to search for config + /// files *in addition to* [`config_home`]. + /// + /// The order of directories denotes their importance; the first directory + /// is the most important. Information defined relative to the more + /// important base directory takes precedent. [`config_home`] is not + /// necessarily present in this list, and is considered more important + /// than any base directory in this list. + /// + /// [`config_home`]: HomeDirs::config_home + #[unstable(feature = "fs_home_dirs", issue = "162082")] + fn config_dirs(&self) -> Option>; + + /// A preference-ordered list of base directories to search for data + /// files *in addition to* [`data_home`]. + /// + /// The order of directories denotes their importance; the first directory + /// is the most important. Information defined relative to the more + /// important base directory takes precedent. [`data_home`] is not + /// necessarily present in this list, and is considered more important + /// than any base directory in this list. + /// + /// [`data_home`]: HomeDirs::data_home + #[unstable(feature = "fs_home_dirs", issue = "162082")] + fn data_dirs(&self) -> Option>; + + /// Set the path for [Self::runtime_home]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_home_dirs", issue = "162082")] + fn set_runtime_home(&mut self, path: PathBuf) -> &mut Self; + + /// Set the paths for [Self::config_dirs]. + /// + /// Takes one or more paths joined appropriately for the `PATH` environment + /// variable, as by [`env::join_paths`]. + /// + /// # Panics + /// + /// Panics if any of the provided paths are not absolute. + #[unstable(feature = "fs_home_dirs", issue = "162082")] + fn set_config_dirs(&mut self, paths: OsString) -> &mut Self; + + /// Set the paths for [Self::data_dirs]. + /// + /// Takes one or more paths joined appropriately for the `PATH` environment + /// variable, as by [`env::join_paths`]. + /// + /// # Panics + /// + /// Panics if any of the provided paths are not absolute. + #[unstable(feature = "fs_home_dirs", issue = "162082")] + fn set_data_dirs(&mut self, paths: OsString) -> &mut Self; +} + +/// XDG-specific extensions to [`fs::MediaDirs`](MediaDirs). +/// +/// The XDG conventions are defined by the Freedesktop.org project through the +/// [xdg-user-dirs] tool. This configuration is generally present on desktop +/// Linux distributions, although adoption is less widespread than the base +/// directory specification. +/// +/// The XDG conventions are written to be usable on any Unix-like filesystem, +/// thus this extension being provided in `os::unix` rather than `os::linux`. +/// However, while some tooling does use XDG conventions on macOS, note that +/// macOS has its own separate conventions for user directories. Consider +/// carefully what conventions your users will expect your application to +/// follow along with any legacy path compatibility you might need to support. +/// +/// [xdg-user-dirs]: https://www.freedesktop.org/wiki/Software/xdg-user-dirs/ +#[unstable(feature = "fs_media_dirs", issue = "162083")] +pub impl(self) trait MediaDirsExt: Sized { + /// Load the user directory paths according to the [xdg-user-dirs] tool. + /// + /// This directly reads and parses the `$XDG_CONFIG_HOME/user-dirs.dirs` + /// file as defined and maintained by the [xdg-user-dirs] tool. + /// + /// # Errors + /// + /// Errors if the user's home directory cannot be determined or if the + /// `$XDG_CONFIG_HOME/user-dirs.dirs` file cannot be read. + /// + /// # Implementation-specific behavior + /// + /// Only the format maintained by xdg-user-dirs-update is supported. Any + /// configuration that does not match the expected format will result in + /// loading an unspecified path or `None` for that directory. To be more + /// specific: + /// + /// - Any line not in the format of `XDG_{NAME}_DIR={path}` where `{NAME}` + /// is one of `DESKTOP`, `DOWNLOAD`, `TEMPLATES`, `PUBLICSHARE`, + /// `DOCUMENTS`, `MUSIC`, `PICTURES`, or `VIDEOS` is ignored. + /// - `{path}` must be a `"`-quoted shell-escaped path. + /// - `{path}` may only start with `/` or `$HOME/`. A home-relative path + /// is returned relative to [`env::home_dir`]; shell expansion is not + /// performed. + /// - A directory set to just `$HOME` marks it as removed, and results in + /// a `None` value for that path. + /// - If shell expansion syntax other than a leading `$HOME` is present, + /// the produced directory path is unspecified. This is invalid config + /// according to the xdg-user-dirs tooling. + /// + /// This behavior may change in the future. One example change that we + /// explicitly reserve the right to make is to load paths that we currently + /// ignore, such as path formats that are not canonically supported by + /// xdg-user-dirs but which may occur in manually-edited `user-dirs.dirs`. + /// + /// [xdg-user-dirs]: https://www.freedesktop.org/wiki/Software/xdg-user-dirs/ + #[unstable(feature = "fs_media_dirs", issue = "162083")] + fn xdg() -> io::Result; + + /// The OS-privileged user "Templates" directory, often the `Templates` + /// folder in the user's home directory. + /// + /// As a media directory, this should typically be used as a default path + /// for file selection dialogs, not for automatically accessed file paths. + #[unstable(feature = "fs_media_dirs", issue = "162083")] + fn templates(&self) -> Option<&Path>; + + /// Set the paths for [Self::templates]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_media_dirs", issue = "162083")] + fn set_templates(&mut self, path: PathBuf) -> &mut Self; +} + +#[unstable(feature = "fs_home_dirs", issue = "162082")] +#[derive(Debug)] +pub struct XdgDirs<'a>(SplitPathsRef<'a>); + +#[unstable(feature = "fs_home_dirs", issue = "162082")] +impl HomeDirsExt for HomeDirs { + fn xdg() -> io::Result { + let mut dirs = HomeDirs::empty(); + let user_home = xdg::user_home()?; + + dirs.set_cache_home(xdg::dir_or_else(|| user_home.join(".cache"), "XDG_CACHE_HOME")); + dirs.set_config_home(xdg::dir_or_else(|| user_home.join(".config"), "XDG_CONFIG_HOME")); + dirs.set_data_home(xdg::dir_or_else(|| user_home.join(".local/share"), "XDG_DATA_HOME")); + dirs.set_state_home(xdg::dir_or_else(|| user_home.join(".local/state"), "XDG_STATE_HOME")); + if let Some(runtime) = xdg::dir("XDG_RUNTIME_DIR") { + dirs.set_runtime_home(runtime); + } + + dirs.set_config_dirs(xdg::dirs_or("/etc/xdg", "XDG_CONFIG_DIRS")); + dirs.set_data_dirs(xdg::dirs_or("/usr/local/share/:/usr/share/", "XDG_DATA_DIRS")); + + Ok(dirs) + } + + fn runtime_home(&self) -> Option<&Path> { + self.extra.runtime.as_deref() + } + + fn config_dirs(&self) -> Option> { + self.extra.config_path.as_deref().map(XdgDirs::new) + } + + fn data_dirs(&self) -> Option> { + self.extra.data_path.as_deref().map(XdgDirs::new) + } + + fn set_runtime_home(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "runtime directory path must be absolute"); + self.extra.runtime = Some(path); + self + } + + fn set_config_dirs(&mut self, paths: OsString) -> &mut Self { + for path in split_paths_ref(&paths) { + assert!(path.is_absolute(), "config directory paths must be absolute"); + } + self.extra.config_path = Some(paths); + self + } + + fn set_data_dirs(&mut self, paths: OsString) -> &mut Self { + for path in split_paths_ref(&paths) { + assert!(path.is_absolute(), "data directory paths must be absolute"); + } + self.extra.data_path = Some(paths); + self + } +} + +#[unstable(feature = "fs_media_dirs", issue = "162083")] +impl MediaDirsExt for MediaDirs { + fn xdg() -> io::Result { + let user_home = xdg::user_home()?; + let config_home = xdg::dir_or_else(|| user_home.join(".config"), "XDG_CONFIG_HOME"); + + let spec = match fs::read(config_home.join("user-dirs.dirs")) { + Ok(spec) => spec, + Err(e) if e.kind() == ErrorKind::NotFound => { + return Err(const_error!( + ErrorKind::NotFound, + "missing `$XDG_CONFIG_HOME/user-dirs.dirs`", + )); + } + Err(e) => return Err(e), + }; + + Ok(xdg::parse_user_dirs(&spec, &user_home)) + } + + fn templates(&self) -> Option<&Path> { + self.extra.templates.as_deref() + } + + fn set_templates(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "templates directory path must be absolute"); + self.extra.templates = Some(path); + self + } +} + +impl<'a> XdgDirs<'a> { + fn new(paths: &'a OsStr) -> Self { + XdgDirs(split_paths_ref(paths)) + } +} + +#[unstable(feature = "fs_home_dirs", issue = "162082")] +impl<'a> Iterator for XdgDirs<'a> { + type Item = &'a Path; + + fn next(&mut self) -> Option { + self.0.next() + } +} + +fn split_paths_ref<'a>(paths: &'a OsStr) -> SplitPathsRef<'a> { + // returns Some on cfg(unix) + env::split_paths_ref(paths).unwrap() +} + +mod xdg { + use super::{MediaDirs, split_paths_ref}; + use crate::env::{self, join_paths, var_os}; + use crate::ffi::OsString; + use crate::io::{self, ErrorKind, const_error}; + use crate::ops::Deref; + use crate::os::unix::ffi::{OsStrExt, OsStringExt}; + use crate::path::{Path, PathBuf}; + + pub fn user_home() -> io::Result { + env::home_dir() + .filter(|p| p.is_absolute()) + .ok_or(const_error!(ErrorKind::InvalidData, "user home directory path not absolute")) + } + + pub fn dir(env: &str) -> Option { + var_os(env).filter(|s| !s.is_empty()).map(PathBuf::from).filter(|path| path.is_absolute()) + } + + pub fn dir_or_else(fallback: impl FnOnce() -> PathBuf, env: &str) -> PathBuf { + dir(env).unwrap_or_else(fallback) + } + + fn dirs(env: &str) -> Option { + let dirs = var_os(env).filter(|s| !s.is_empty())?; + if split_paths_ref(&dirs).all(|p| p.is_absolute()) { + return Some(dirs); + } + + struct DerefAsRef(pub T); + impl>, U: ?Sized> AsRef for DerefAsRef { + fn as_ref(&self) -> &U { + (*self.0).as_ref() + } + } + + let paths = split_paths_ref(&dirs).filter(|p| p.is_absolute()).map(DerefAsRef); + join_paths(paths).ok().filter(|s| !s.is_empty()) + } + + pub fn dirs_or(fallback: impl Into, env: &str) -> OsString { + dirs(env).unwrap_or_else(|| fallback.into()) + } + + pub fn parse_user_dirs<'a>(spec: &'a [u8], user_home: &Path) -> MediaDirs { + let mut dirs = MediaDirs::empty(); + + for (xdg, path) in + spec.split(|&b| b == b'\n').flat_map(|line| parse_user_dirs_line(line, user_home)) + { + // load the known user directories + match xdg { + b"XDG_DESKTOP_DIR" => dirs.desktop = path, + b"XDG_DOCUMENTS_DIR" => dirs.documents = path, + b"XDG_DOWNLOAD_DIR" => dirs.downloads = path, + b"XDG_MUSIC_DIR" => dirs.music = path, + b"XDG_PICTURES_DIR" => dirs.pictures = path, + b"XDG_VIDEOS_DIR" => dirs.videos = path, + b"XDG_TEMPLATES_DIR" => dirs.extra.templates = path, + b"XDG_PUBLICSHARE_DIR" => { + // we don't expose this directory yet as Windows also has a "public" directory + // and it's not yet clear if we want to expose this at the target-agnostic level + } + _ => { + // ignore unknown variable assignment + } + } + } + + dirs + } + + fn parse_user_dirs_line<'a>( + line: &'a [u8], + user_home: &Path, + ) -> Option<(&'a [u8], Option)> { + // trim whitespace + let trimmed = line.trim_ascii(); + // skip empty lines and comments + if trimmed.is_empty() || trimmed.starts_with(&[b'#']) { + return None; + } + + // only variable assignment lines are allowed; split on `=` + let mut split = trimmed.splitn(2, |&b| b == b'='); + // extract assignment parts; ignore lines not in this format + let var = split.next()?; + let val = split.next()?; + debug_assert_eq!(split.next(), None); + + // the path value is quoted; unquote it + let path = unquote(val, user_home)?; + + let path = Some(path) + // ignore non-absolute paths + .filter(|path| path.is_absolute()) + // setting to the home dir disables the directory configuration + .filter(|path| path != user_home); + + Some((var, path)) + } + + fn unquote(bytes: &[u8], user_home: &Path) -> Option { + let [b'"', rest @ .., b'"'] = bytes else { return None }; + + // setting to the home dir disables the directory configuration; + // if done symbolically, return None here and bypass later work + if matches!(rest, b"$HOME" | b"$HOME/") { + return None; + } + + let mut rest = rest; + let mut s = Vec::with_capacity(rest.len()); + + // expand leading $HOME + if rest.starts_with(b"$HOME/") { + s.extend_from_slice(user_home.as_os_str().as_bytes()); + if !user_home.has_trailing_sep() { + s.push(b'/'); + } + rest = &rest[6..]; + } + + loop { + let i = rest + .iter() + .position(|&b| matches!(b, b'"' | b'\\' | b'$' | b'`')) + .unwrap_or(rest.len()); + s.extend_from_slice(&rest[..i]); + match &rest[i..] { + [] => break, + [b'\\', c @ (b'"' | b'\\' | b'$' | b'`'), tail @ ..] => { + // supported escapes + s.push(*c); + rest = tail; + } + [b'"' | b'\\' | b'$' | b'`', ..] => { + // unsupported shell syntax + return None; + } + _ => { + if cfg!(debug_assertions) { + unreachable!() + } else { + return None; + } + } + } + } + + Some(PathBuf::from(OsString::from_vec(s))) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_fetch_xdg_base_dirs() { + let dirs = HomeDirs::xdg().unwrap(); + + assert!(dirs.cache_home().is_some()); + assert!(dirs.config_home().is_some()); + assert!(dirs.data_home().is_some()); + assert!(dirs.state_home().is_some()); + // dirs.runtime() may not exist + assert!(dirs.config_dirs().is_some()); + assert!(dirs.data_dirs().is_some()); + } + + #[test] + fn can_fetch_xdg_media_dirs() { + let dirs = match MediaDirs::xdg() { + Ok(dirs) => dirs, + Err(e) if e.kind() == ErrorKind::NotFound => { + // xdg-user-dirs not initialized on this system, skip the test + return; + } + Err(e) => panic!("failed to fetch xdg user dirs: {e:?}"), + }; + + // even when user-dirs.dirs is present, directories may be unset, + // so we can't assert anything about the dir paths here + let _ = dirs; + } + + #[test] + fn test_well_user_dirs_parsing() { + const TEST_USERDIRS: &'static str = r#" +# This file is written by xdg-user-dirs-update +# If you want to change or add directories, just edit the line you're +# interested in. All local changes will be retained on the next run. +# Format is XDG_xxx_DIR="$HOME/yyy", where yyy is a shell-escaped +# homedir-relative path, or XDG_xxx_DIR="/yyy", where /yyy is an +# absolute path. No other format is supported. +# +XDG_DESKTOP_DIR="$HOME/Desktop" +XDG_DOWNLOAD_DIR="$HOME/Downloads" +XDG_TEMPLATES_DIR="/pub/Templates" +XDG_PUBLICSHARE_DIR="/pub" +XDG_DOCUMENTS_DIR="$HOME/Documents" +XDG_MUSIC_DIR="$HOME/My \"Music\"" +XDG_PICTURES_DIR="$HOME/Pictures" +XDG_VIDEOS_DIR="$HOME" +"#; + + let dirs = xdg::parse_user_dirs(TEST_USERDIRS.as_bytes(), Path::new("/home/ferris")); + + assert_eq!(dirs.desktop(), Some("/home/ferris/Desktop".as_ref())); + assert_eq!(dirs.downloads(), Some("/home/ferris/Downloads".as_ref())); + assert_eq!(dirs.templates(), Some("/pub/Templates".as_ref())); + assert_eq!(dirs.documents(), Some("/home/ferris/Documents".as_ref())); + assert_eq!(dirs.music(), Some("/home/ferris/My \"Music\"".as_ref())); + assert_eq!(dirs.pictures(), Some("/home/ferris/Pictures".as_ref())); + assert_eq!(dirs.videos(), None); + } + + #[test] + fn test_ill_user_dirs_parsing() { + const TEST_USERDIRS: &'static str = r#" +# unquoted +XDG_DESKTOP_DIR=$HOME/Desktop +# mixed quoting +XDG_DOWNLOAD_DIR="$HOME"/"Downloads" +# non-HOME env +XDG_TEMPLATES_DIR="/home/$USER/Templates" +# missing PUBLICSHARE +# command substitution +XDG_DOCUMENTS_DIR="`pwd`/Documents" +# non-symbolic homedir disabled +XDG_MUSIC_DIR="/home/ferris" +# set multiple times +XDG_PICTURES_DIR="$HOME/Pictures1" +XDG_PICTURES_DIR="$HOME/Pictures2" +# relative path +XDG_VIDEOS_DIR="Videos" +"#; + + let dirs = xdg::parse_user_dirs(TEST_USERDIRS.as_bytes(), Path::new("/home/ferris")); + + assert_eq!(dirs.desktop(), None); + assert_eq!(dirs.downloads(), None); + assert_eq!(dirs.templates(), None); + assert_eq!(dirs.documents(), None); + assert_eq!(dirs.music(), None); + assert_eq!(dirs.pictures(), Some("/home/ferris/Pictures2".as_ref())); + assert_eq!(dirs.videos(), None); + } +} diff --git a/library/std/src/os/unix/net/addr.rs b/library/std/src/os/unix/net/addr.rs index 3daddc2d34323..0520da62ffae9 100644 --- a/library/std/src/os/unix/net/addr.rs +++ b/library/std/src/os/unix/net/addr.rs @@ -87,7 +87,7 @@ enum AddressKind<'a> { /// return /// } /// }; -/// let addr = socket.local_addr().expect("Couldn't get local address"); +/// let addr = socket.local_addr().expect("`UnixListener::local_addr` should not fail"); /// ``` #[derive(Clone)] #[stable(feature = "unix_socket", since = "1.10.0")] @@ -123,6 +123,8 @@ impl SocketAddr { .map_or(len, |new_len| (new_len + SUN_PATH_OFFSET) as libc::socklen_t); } + len = len.min(size_of::() as libc::socklen_t); + if len == 0 { // When there is a datagram from unnamed unix socket // linux returns zero bytes of address @@ -186,7 +188,7 @@ impl SocketAddr { /// /// fn main() -> std::io::Result<()> { /// let socket = UnixListener::bind("/tmp/sock")?; - /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// let addr = socket.local_addr().expect("`UnixListener::local_addr` should not fail"); /// assert_eq!(addr.is_unnamed(), false); /// Ok(()) /// } @@ -200,7 +202,7 @@ impl SocketAddr { /// /// fn main() -> std::io::Result<()> { /// let socket = UnixDatagram::unbound()?; - /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// let addr = socket.local_addr().expect("`UnixListener::local_addr` should not fail"); /// assert_eq!(addr.is_unnamed(), true); /// Ok(()) /// } @@ -224,7 +226,7 @@ impl SocketAddr { /// /// fn main() -> std::io::Result<()> { /// let socket = UnixListener::bind("/tmp/sock")?; - /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// let addr = socket.local_addr().expect("`UnixListener::local_addr` should not fail"); /// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/sock"))); /// Ok(()) /// } @@ -238,7 +240,7 @@ impl SocketAddr { /// /// fn main() -> std::io::Result<()> { /// let socket = UnixDatagram::unbound()?; - /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// let addr = socket.local_addr().expect("`UnixListener::local_addr` should not fail"); /// assert_eq!(addr.as_pathname(), None); /// Ok(()) /// } diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs index e03ecd7eed9ea..548dca3592063 100644 --- a/library/std/src/os/unix/net/datagram.rs +++ b/library/std/src/os/unix/net/datagram.rs @@ -112,13 +112,12 @@ impl UnixDatagram { /// #[cfg_attr(target_family = "unix", doc = "```no_run")] #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] - /// use std::os::unix::net::{UnixDatagram}; + /// use std::os::unix::net::{SocketAddr, UnixDatagram}; /// /// fn main() -> std::io::Result<()> { - /// let sock1 = UnixDatagram::bind("path/to/socket")?; - /// let addr = sock1.local_addr()?; + /// let addr = SocketAddr::from_pathname("path/to/socket")?; /// - /// let sock2 = match UnixDatagram::bind_addr(&addr) { + /// let sock = match UnixDatagram::bind_addr(&addr) { /// Ok(sock) => sock, /// Err(err) => { /// println!("Couldn't bind: {err:?}"); @@ -273,7 +272,7 @@ impl UnixDatagram { /// /// fn main() -> std::io::Result<()> { /// let sock = UnixDatagram::bind("/path/to/the/socket")?; - /// let sock_copy = sock.try_clone().expect("try_clone failed"); + /// let sock_copy = sock.try_clone().expect("`UnixDatagram::try_clone` should not fail"); /// Ok(()) /// } /// ``` @@ -292,7 +291,7 @@ impl UnixDatagram { /// /// fn main() -> std::io::Result<()> { /// let sock = UnixDatagram::bind("/path/to/the/socket")?; - /// let addr = sock.local_addr().expect("Couldn't get local address"); + /// let addr = sock.local_addr().expect("`UnixDatagram::local_addr` should not fail"); /// Ok(()) /// } /// ``` @@ -317,7 +316,7 @@ impl UnixDatagram { /// let sock = UnixDatagram::unbound()?; /// sock.connect("/path/to/the/socket")?; /// - /// let addr = sock.peer_addr().expect("Couldn't get peer address"); + /// let addr = sock.peer_addr().expect("`UnixDatagram::peer_addr` should not fail"); /// Ok(()) /// } /// ``` @@ -390,7 +389,7 @@ impl UnixDatagram { /// fn main() -> std::io::Result<()> { /// let sock = UnixDatagram::bind("/path/to/the/socket")?; /// let mut buf = vec![0; 10]; - /// sock.recv(buf.as_mut_slice()).expect("recv function failed"); + /// sock.recv(buf.as_mut_slice()).expect("`UnixDatagram::recv` should not fail"); /// Ok(()) /// } /// ``` @@ -523,7 +522,7 @@ impl UnixDatagram { /// /// fn main() -> std::io::Result<()> { /// let sock = UnixDatagram::unbound()?; - /// sock.send_to(b"omelette au fromage", "/some/sock").expect("send_to function failed"); + /// sock.send_to(b"omelette au fromage", "/some/sock").expect("`UnixDatagram::send_to` should not fail"); /// Ok(()) /// } /// ``` @@ -561,7 +560,7 @@ impl UnixDatagram { /// let addr = bound.local_addr()?; /// /// let sock = UnixDatagram::unbound()?; - /// sock.send_to_addr(b"bacon egg and cheese", &addr).expect("send_to_addr function failed"); + /// sock.send_to_addr(b"bacon egg and cheese", &addr).expect("`UnixDatagram::send_to_addr` should not fail"); /// Ok(()) /// } /// ``` @@ -595,8 +594,8 @@ impl UnixDatagram { /// /// fn main() -> std::io::Result<()> { /// let sock = UnixDatagram::unbound()?; - /// sock.connect("/some/sock").expect("Couldn't connect"); - /// sock.send(b"omelette au fromage").expect("send_to function failed"); + /// sock.connect("/some/sock").expect("`UnixDatagram::connect` should not fail"); + /// sock.send(b"omelette au fromage").expect("`UnixDatagram` is not connected"); /// Ok(()) /// } /// ``` @@ -638,7 +637,7 @@ impl UnixDatagram { /// let mut ancillary = SocketAncillary::new(&mut ancillary_buffer[..]); /// ancillary.add_fds(&fds[..]); /// sock.send_vectored_with_ancillary_to(bufs, &mut ancillary, "/some/sock") - /// .expect("send_vectored_with_ancillary_to function failed"); + /// .expect("`UnixDatagram::send_vectored_with_ancillary_to` should not fail"); /// Ok(()) /// } /// ``` @@ -686,7 +685,7 @@ impl UnixDatagram { /// let mut ancillary = SocketAncillary::new(&mut ancillary_buffer[..]); /// ancillary.add_fds(&fds[..]); /// sock.send_vectored_with_ancillary(bufs, &mut ancillary) - /// .expect("send_vectored_with_ancillary function failed"); + /// .expect("`UnixDatagram::send_vectored_with_ancillary` should not fail"); /// Ok(()) /// } /// ``` @@ -719,7 +718,7 @@ impl UnixDatagram { /// fn main() -> std::io::Result<()> { /// let sock = UnixDatagram::unbound()?; /// sock.set_read_timeout(Some(Duration::new(1, 0))) - /// .expect("set_read_timeout function failed"); + /// .expect("`UnixDatagram::set_read_timeout` should not fail"); /// Ok(()) /// } /// ``` @@ -765,7 +764,7 @@ impl UnixDatagram { /// fn main() -> std::io::Result<()> { /// let sock = UnixDatagram::unbound()?; /// sock.set_write_timeout(Some(Duration::new(1, 0))) - /// .expect("set_write_timeout function failed"); + /// .expect("`UnixDatagram::set_write_timeout` should not fail"); /// Ok(()) /// } /// ``` @@ -804,7 +803,7 @@ impl UnixDatagram { /// fn main() -> std::io::Result<()> { /// let sock = UnixDatagram::unbound()?; /// sock.set_read_timeout(Some(Duration::new(1, 0))) - /// .expect("set_read_timeout function failed"); + /// .expect("`UnixDatagram::set_read_timeout` should not fail"); /// assert_eq!(sock.read_timeout()?, Some(Duration::new(1, 0))); /// Ok(()) /// } @@ -826,7 +825,7 @@ impl UnixDatagram { /// fn main() -> std::io::Result<()> { /// let sock = UnixDatagram::unbound()?; /// sock.set_write_timeout(Some(Duration::new(1, 0))) - /// .expect("set_write_timeout function failed"); + /// .expect("`UnixDatagram::set_write_timeout` should not fail"); /// assert_eq!(sock.write_timeout()?, Some(Duration::new(1, 0))); /// Ok(()) /// } @@ -846,7 +845,7 @@ impl UnixDatagram { /// /// fn main() -> std::io::Result<()> { /// let sock = UnixDatagram::unbound()?; - /// sock.set_nonblocking(true).expect("set_nonblocking function failed"); + /// sock.set_nonblocking(true).expect("`UnixDatagram::set_nonblocking` should not fail"); /// Ok(()) /// } /// ``` @@ -914,7 +913,7 @@ impl UnixDatagram { /// /// fn main() -> std::io::Result<()> { /// let sock = UnixDatagram::unbound()?; - /// sock.shutdown(Shutdown::Both).expect("shutdown function failed"); + /// sock.shutdown(Shutdown::Both).expect("`UnixDatagram::shutdown` should not fail"); /// Ok(()) /// } /// ``` @@ -941,7 +940,7 @@ impl UnixDatagram { /// fn main() -> std::io::Result<()> { /// let socket = UnixDatagram::bind("/tmp/sock")?; /// let mut buf = [0; 10]; - /// let len = socket.peek(&mut buf).expect("peek failed"); + /// let len = socket.peek(&mut buf).expect("`UnixDatagram::peek` should not fail"); /// Ok(()) /// } /// ``` diff --git a/library/std/src/os/unix/net/listener.rs b/library/std/src/os/unix/net/listener.rs index b7f8d25a85ae5..2e47a4b23a1cb 100644 --- a/library/std/src/os/unix/net/listener.rs +++ b/library/std/src/os/unix/net/listener.rs @@ -119,13 +119,12 @@ impl UnixListener { /// #[cfg_attr(target_family = "unix", doc = "```no_run")] #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] - /// use std::os::unix::net::{UnixListener}; + /// use std::os::unix::net::{SocketAddr, UnixListener}; /// /// fn main() -> std::io::Result<()> { - /// let listener1 = UnixListener::bind("path/to/socket")?; - /// let addr = listener1.local_addr()?; + /// let addr = SocketAddr::from_pathname("path/to/socket")?; /// - /// let listener2 = match UnixListener::bind_addr(&addr) { + /// let listener = match UnixListener::bind_addr(&addr) { /// Ok(sock) => sock, /// Err(err) => { /// println!("Couldn't bind: {err:?}"); @@ -200,7 +199,7 @@ impl UnixListener { /// /// fn main() -> std::io::Result<()> { /// let listener = UnixListener::bind("/path/to/the/socket")?; - /// let listener_copy = listener.try_clone().expect("try_clone failed"); + /// let listener_copy = listener.try_clone().expect("`UnixListener::try_clone` should not fail"); /// Ok(()) /// } /// ``` @@ -219,7 +218,7 @@ impl UnixListener { /// /// fn main() -> std::io::Result<()> { /// let listener = UnixListener::bind("/path/to/the/socket")?; - /// let addr = listener.local_addr().expect("Couldn't get local address"); + /// let addr = listener.local_addr().expect("`UnixListener::local_addr` should not fail"); /// Ok(()) /// } /// ``` @@ -244,7 +243,7 @@ impl UnixListener { /// /// fn main() -> std::io::Result<()> { /// let listener = UnixListener::bind("/path/to/the/socket")?; - /// listener.set_nonblocking(true).expect("Couldn't set non blocking"); + /// listener.set_nonblocking(true).expect("`UnixListener::set_nonblocking` should not fail"); /// Ok(()) /// } /// ``` diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index 31a908dfe187e..dba49d0406bf1 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -199,7 +199,7 @@ impl UnixStream { /// /// fn main() -> std::io::Result<()> { /// let socket = UnixStream::connect("/tmp/sock")?; - /// let sock_copy = socket.try_clone().expect("Couldn't clone socket"); + /// let sock_copy = socket.try_clone().expect("`UnixStream::try_clone` should not fail"); /// Ok(()) /// } /// ``` @@ -218,7 +218,7 @@ impl UnixStream { /// /// fn main() -> std::io::Result<()> { /// let socket = UnixStream::connect("/tmp/sock")?; - /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// let addr = socket.local_addr().expect("`UnixStream::local_addr` should not fail"); /// Ok(()) /// } /// ``` @@ -237,7 +237,7 @@ impl UnixStream { /// /// fn main() -> std::io::Result<()> { /// let socket = UnixStream::connect("/tmp/sock")?; - /// let addr = socket.peer_addr().expect("Couldn't get peer address"); + /// let addr = socket.peer_addr().expect("`UnixStream::peer_addr` should not fail"); /// Ok(()) /// } /// ``` @@ -257,7 +257,7 @@ impl UnixStream { /// /// fn main() -> std::io::Result<()> { /// let socket = UnixStream::connect("/tmp/sock")?; - /// let peer_cred = socket.peer_cred().expect("Couldn't get peer credentials"); + /// let peer_cred = socket.peer_cred().expect("`UnixStream::peer_cred` should not fail"); /// Ok(()) /// } /// ``` @@ -295,7 +295,7 @@ impl UnixStream { /// /// fn main() -> std::io::Result<()> { /// let socket = UnixStream::connect("/tmp/sock")?; - /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout"); + /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("`UnixStream::set_read_timeout` should not fail"); /// Ok(()) /// } /// ``` @@ -340,7 +340,7 @@ impl UnixStream { /// fn main() -> std::io::Result<()> { /// let socket = UnixStream::connect("/tmp/sock")?; /// socket.set_write_timeout(Some(Duration::new(1, 0))) - /// .expect("Couldn't set write timeout"); + /// .expect("`UnixStream::connect` should not fail"); /// Ok(()) /// } /// ``` @@ -378,7 +378,7 @@ impl UnixStream { /// /// fn main() -> std::io::Result<()> { /// let socket = UnixStream::connect("/tmp/sock")?; - /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout"); + /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("`UnixStream::set_read_timeout` should not fail"); /// assert_eq!(socket.read_timeout()?, Some(Duration::new(1, 0))); /// Ok(()) /// } @@ -400,7 +400,7 @@ impl UnixStream { /// fn main() -> std::io::Result<()> { /// let socket = UnixStream::connect("/tmp/sock")?; /// socket.set_write_timeout(Some(Duration::new(1, 0))) - /// .expect("Couldn't set write timeout"); + /// .expect("`UnixStream::set_write_timeout` should not fail"); /// assert_eq!(socket.write_timeout()?, Some(Duration::new(1, 0))); /// Ok(()) /// } @@ -420,7 +420,7 @@ impl UnixStream { /// /// fn main() -> std::io::Result<()> { /// let socket = UnixStream::connect("/tmp/sock")?; - /// socket.set_nonblocking(true).expect("Couldn't set nonblocking"); + /// socket.set_nonblocking(true).expect("`UnixStream::set_nonblocking` should not fail"); /// Ok(()) /// } /// ``` @@ -493,7 +493,7 @@ impl UnixStream { /// /// fn main() -> std::io::Result<()> { /// let socket = UnixStream::connect("/tmp/sock")?; - /// socket.shutdown(Shutdown::Both).expect("shutdown function failed"); + /// socket.shutdown(Shutdown::Both).expect("`UnixStream::shutdown` should not fail"); /// Ok(()) /// } /// ``` @@ -520,7 +520,7 @@ impl UnixStream { /// fn main() -> std::io::Result<()> { /// let socket = UnixStream::connect("/tmp/sock")?; /// let mut buf = [0; 10]; - /// let len = socket.peek(&mut buf).expect("peek failed"); + /// let len = socket.peek(&mut buf).expect("`UnixStream::peek` should not fail"); /// Ok(()) /// } /// ``` diff --git a/library/std/src/os/unix/net/tests.rs b/library/std/src/os/unix/net/tests.rs index 9c3119e787b33..4014767461389 100644 --- a/library/std/src/os/unix/net/tests.rs +++ b/library/std/src/os/unix/net/tests.rs @@ -52,6 +52,25 @@ fn sock_addr_without_trailing_nul() { assert_eq!(address.as_pathname(), Some(Path::new("/path/to/socket"))); } +#[test] +#[cfg(any(target_os = "android", target_os = "linux"))] +fn sock_addr_pathname_fills_sun_path() { + use crate::ffi::OsStr; + use crate::os::unix::ffi::OsStrExt; + + let mut addr: libc::sockaddr_un = unsafe { crate::mem::zeroed() }; + addr.sun_family = libc::AF_UNIX as libc::sa_family_t; + let mut path = vec![b'a'; addr.sun_path.len()]; + path[0] = b'/'; + for (dst, &src) in addr.sun_path.iter_mut().zip(&path) { + *dst = src as _; + } + let offset = crate::mem::offset_of!(libc::sockaddr_un, sun_path); + + let address = or_panic!(SocketAddr::from_parts(addr, (offset + path.len() + 1) as _)); + assert_eq!(address.as_pathname(), Some(Path::new(OsStr::from_bytes(&path)))); +} + #[test] #[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets #[cfg_attr(target_os = "vxworks", ignore = "Unix sockets are not implemented in VxWorks")] diff --git a/library/std/src/os/wasip2/mod.rs b/library/std/src/os/wasip2/mod.rs index 809a288f20d04..6932bb577cff2 100644 --- a/library/std/src/os/wasip2/mod.rs +++ b/library/std/src/os/wasip2/mod.rs @@ -3,4 +3,5 @@ //! This module is currently empty, but will be filled over time as wasi-libc support for WASI Preview 2 is stabilized. #![forbid(unsafe_op_in_unsafe_fn)] -#![stable(feature = "raw_ext", since = "1.1.0")] +#![unstable(feature = "wasip2", issue = "none")] +#![doc(cfg(all(target_os = "wasi", target_env = "p2")))] diff --git a/library/std/src/os/windows/fs.rs b/library/std/src/os/windows/fs.rs index 21560638c1d0f..c10766470e873 100644 --- a/library/std/src/os/windows/fs.rs +++ b/library/std/src/os/windows/fs.rs @@ -11,6 +11,13 @@ use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner}; use crate::time::SystemTime; use crate::{io, sys}; +mod dirs; + +#[unstable(feature = "fs_home_dirs", issue = "162082")] +pub use dirs::HomeDirsExt; +#[unstable(feature = "fs_media_dirs", issue = "162083")] +pub use dirs::MediaDirsExt; + /// Windows-specific extensions to [`fs::File`]. #[stable(feature = "file_offset", since = "1.15.0")] pub trait FileExt { @@ -50,6 +57,69 @@ pub trait FileExt { #[stable(feature = "file_offset", since = "1.15.0")] fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result; + /// Seeks to a given position and reads the exact number of bytes required to fill `buf`. + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. The current cursor **is** affected by this + /// function, it is set to the end of the read. + /// + /// Similar to [`io::Read::read_exact`] but uses [`seek_read`] instead of `read`. + /// + /// [`seek_read`]: FileExt::seek_read + /// + /// # Errors + /// + /// If this function encounters an error of the kind + /// [`io::ErrorKind::Interrupted`] then the error is ignored and the operation + /// will continue. + /// + /// If this function encounters an "end of file" before completely filling + /// the buffer, it returns an error of the kind [`io::ErrorKind::UnexpectedEof`]. + /// The contents of `buf` are unspecified in this case. + /// + /// If any other read error is encountered then this function immediately + /// returns. The contents of `buf` are unspecified in this case. + /// + /// If this function returns an error, it is unspecified how many bytes it + /// has read, but it will never read more than would be necessary to + /// completely fill the buffer. + /// + /// # Examples + /// + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] + /// #![feature(seek_read_exact_seek_write_all)] + /// + /// use std::io; + /// use std::fs::File; + /// use std::os::windows::prelude::*; + /// + /// fn main() -> io::Result<()> { + /// let mut file = File::open("foo.txt")?; + /// let mut buffer = [0; 10]; + /// + /// // Read 10 bytes, starting 72 bytes from the + /// // start of the file. + /// file.seek_read_exact(&mut buffer[..], 72)?; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "seek_read_exact_seek_write_all", issue = "162868")] + fn seek_read_exact(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> { + while !buf.is_empty() { + match self.seek_read(buf, offset) { + Ok(0) => break, + Ok(n) => { + buf = &mut buf[n..]; + offset += n as u64; + } + Err(ref e) if e.is_interrupted() => {} + Err(e) => return Err(e), + } + } + if !buf.is_empty() { Err(io::Error::READ_EXACT_EOF) } else { Ok(()) } + } + /// Seeks to a given position and reads some bytes into the buffer. /// /// This is equivalent to the [`seek_read`](FileExt::seek_read) method, except that it is passed @@ -89,6 +159,62 @@ pub trait FileExt { io::default_read_buf(|b| self.seek_read(b, offset), buf) } + /// Seeks to a given position and reads the exact number of bytes required to fill `buf`. + /// + /// This is equivalent to the [`seek_read_exact`](FileExt::seek_read_exact) method, except + /// that it is passed a [`BorrowedCursor`] rather than `&mut [u8]` to allow use with + /// uninitialized buffers. The new data will be appended to any existing contents of `buf`. + /// + /// Reading beyond the end of the file will always succeed without reading any bytes. + /// + /// # Examples + /// + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] + /// #![feature(core_io_borrowed_buf)] + /// #![feature(seek_read_exact_seek_write_all)] + /// + /// use std::io; + /// use std::io::BorrowedBuf; + /// use std::fs::File; + /// use std::mem::MaybeUninit; + /// use std::os::windows::prelude::*; + /// + /// fn main() -> io::Result<()> { + /// let mut file = File::open("pi.txt")?; + /// + /// // Read some bytes starting from offset 2 + /// let mut buf: [MaybeUninit; 10] = [MaybeUninit::uninit(); 10]; + /// let mut buf = BorrowedBuf::from(buf.as_mut_slice()); + /// file.seek_read_buf_exact(buf.unfilled(), 2)?; + /// + /// assert!(buf.filled().starts_with(b"1")); + /// + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "seek_read_exact_seek_write_all", issue = "162868")] + fn seek_read_buf_exact( + &self, + mut buf: BorrowedCursor<'_, u8>, + mut offset: u64, + ) -> io::Result<()> { + while buf.capacity() > 0 { + let prev_written = buf.written(); + match self.seek_read_buf(buf.reborrow(), offset) { + Ok(()) => {} + Err(e) if e.is_interrupted() => {} + Err(e) => return Err(e), + } + let n = buf.written() - prev_written; + offset += n as u64; + if n == 0 { + return Err(io::Error::READ_EXACT_EOF); + } + } + Ok(()) + } + /// Seeks to a given position and writes a number of bytes. /// /// Returns the number of bytes written. @@ -122,6 +248,62 @@ pub trait FileExt { /// ``` #[stable(feature = "file_offset", since = "1.15.0")] fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result; + + /// Seeks to a given position and attempts to write an entire buffer. + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. The current cursor **is** affected by this + /// function, it is set to the end of the write. + /// + /// This method will continuously call [`seek_write`] until there is no more data + /// to be written or an error of non-[`io::ErrorKind::Interrupted`] kind is + /// returned. This method will not return until the entire buffer has been + /// successfully written or such an error occurs. The first error that is + /// not of [`io::ErrorKind::Interrupted`] kind generated from this method will be + /// returned. + /// + /// # Errors + /// + /// This function will return the first error of + /// non-[`io::ErrorKind::Interrupted`] kind that [`seek_write`] returns. + /// + /// [`seek_write`]: FileExt::seek_write + /// + /// # Examples + /// + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] + /// #![feature(seek_read_exact_seek_write_all)] + /// + /// use std::fs::File; + /// use std::os::windows::prelude::*; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = File::create("foo.txt")?; + /// + /// // Write a byte string starting 72 bytes from + /// // the start of the file. + /// buffer.seek_write_all(b"some bytes", 72)?; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "seek_read_exact_seek_write_all", issue = "162868")] + fn seek_write_all(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> { + while !buf.is_empty() { + match self.seek_write(buf, offset) { + Ok(0) => { + return Err(io::Error::WRITE_ALL_EOF); + } + Ok(n) => { + buf = &buf[n..]; + offset += n as u64 + } + Err(ref e) if e.is_interrupted() => {} + Err(e) => return Err(e), + } + } + Ok(()) + } } #[stable(feature = "file_offset", since = "1.15.0")] diff --git a/library/std/src/os/windows/fs/dirs.rs b/library/std/src/os/windows/fs/dirs.rs new file mode 100644 index 0000000000000..52710375e6eb2 --- /dev/null +++ b/library/std/src/os/windows/fs/dirs.rs @@ -0,0 +1,309 @@ +use crate::fs::{HomeDirs, MediaDirs}; +use crate::io::{ErrorKind, const_error}; +use crate::path::PathBuf; +use crate::{env, io}; + +/// Windows-specific extensions to [`fs::HomeDirs`](HomeDirs). +#[unstable(feature = "fs_home_dirs", issue = "162082")] +pub impl(self) trait HomeDirsExt: Sized { + /// Load the known user folder paths from environment variables. + /// + /// The loaded known folders are: + /// + /// | `HomeDirs` | Environment Variable | + /// | ---------- | -------------------- | + /// | [`cache_home`] | `%LOCALAPPDATA%` (`%USERPROFILE%\AppData\Local`) | + /// | [`config_home`] | `%APPDATA%` (`%USERPROFILE%\AppData\Roaming`) | + /// | [`data_home`] | `%APPDATA%` (`%USERPROFILE%\AppData\Roaming`) | + /// | [`state_home`] | `%LOCALAPPDATA%` (`%USERPROFILE%\AppData\Local`) | + /// + /// Note that caches/state are both put in `AppData\Local`, and config/data + /// in `AppData\Roaming`. It is always possible for multiple user directories + /// to be configured to the same path, but this is the common configuration + /// on Windows platforms, making it even more important to not assume files + /// in different user directories cannot alias each other. + /// + /// # Errors + /// + /// Errors if `%APPDATA%` or `%LOCALAPPDATA%` are not set to absolute paths. + /// + /// # Implementation-specific behavior + /// + /// Windows keeps these environment variables updated to contain the paths + /// to the configured folder path, but it is possible for the environment + /// variables to not match the underlying system, such as when the user or + /// a program modifies the environment directly, or if the configuration + /// changed after the environment block was copied from the system. + /// + /// Unlike [`known_folders`](Self::known_folders), this does not require + /// `Shell32.dll` and thus does not require the overhead of linking in + /// DLLs that may result in Windows considering the application as a + /// graphical application. + /// + /// This behavior may change in the future. One example change that we + /// explicitly reserve the right to make is to load additional common + /// directories not currently in this list. + /// + /// [`cache_home`]: HomeDirs::cache_home + /// [`config_home`]: HomeDirs::config_home + /// [`data_home`]: HomeDirs::data_home + /// [`state_home`]: HomeDirs::state_home + #[unstable(feature = "fs_home_dirs", issue = "162082")] + fn appdata_env() -> io::Result; + + /// Load the known user folder paths using the [Known Folders] API. + /// + /// The loaded known folders are: + /// + /// | `HomeDirs` | [`KNOWNFOLDERID`] | + /// | ---------- | ----------------- | + /// | [`cache_home`] | [`FOLDERID_LocalAppData`] (`%LOCALAPPDATA%`) | + /// | [`config_home`] | [`FOLDERID_RoamingAppData`] (`%APPDATA%`) | + /// | [`data_home`] | [`FOLDERID_RoamingAppData`] (`%APPDATA%`) | + /// | [`state_home`] | [`FOLDERID_LocalAppData`] (`%LOCALAPPDATA%`) | + /// + /// Note that caches/state are both put in LocalAppData, and config/data + /// in RoamingAppData. It is always possible for multiple user directories + /// to be configured to the same path, but this is the common configuration + /// on Windows platforms, making it even more important to not assume files + /// in different user directories cannot alias each other. + /// + /// # Errors + /// + /// Errors if the underlying system discovery API returns an error. The + /// lack of a configured path is not considered an error and results in a + /// `None` value. + /// + /// # Implementation-specific behavior + /// + /// Calls [`SHGetKnownFolderPath`] for the current user once for each known + /// folder. Does not create the folder if missing. + /// + /// COM should be initialized on the thread that calls this function or + /// else it may return unexpected errors. + /// + /// This behavior may change in the future. One example change that we + /// explicitly reserve the right to make is to load additional common + /// directories not currently in this list. + /// + /// [Known Folders]: https://learn.microsoft.com/en-us/windows/win32/shell/known-folders + /// [`SHGetKnownFolderPath`]: https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath + /// [`KNOWNFOLDERID`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid + /// [`FOLDERID_LocalAppData`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_localappdata + /// [`FOLDERID_RoamingAppData`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_roamingappdata + /// [`cache_home`]: HomeDirs::cache_home + /// [`config_home`]: HomeDirs::config_home + /// [`data_home`]: HomeDirs::data_home + /// [`state_home`]: HomeDirs::state_home + #[unstable(feature = "fs_home_dirs", issue = "162082")] + fn known_folders() -> io::Result; +} + +/// Windows-specific extensions to [`fs::MediaDirs`](MediaDirs). +#[unstable(feature = "fs_media_dirs", issue = "162083")] +pub impl(self) trait MediaDirsExt: Sized { + /// Load the known user folder paths using the [Known Folders] API. + /// + /// The loaded known folders are: + /// + /// | `MediaDirs` | [`KNOWNFOLDERID`] | + /// | ---------- | ----------------- | + /// | [`desktop`] | [`FOLDERID_Desktop`] (`%USERPROFILE%\Desktop`) | + /// | [`documents`] | [`FOLDERID_Documents`] (`%USERPROFILE%\Documents`) | + /// | [`downloads`] | [`FOLDERID_Downloads`] (`%USERPROFILE%\Downloads`) | + /// | [`music`] | [`FOLDERID_Music`] (`%USERPROFILE%\Music`) | + /// | [`pictures`] | [`FOLDERID_Pictures`] (`%USERPROFILE%\Pictures`) | + /// | [`videos`] | [`FOLDERID_Videos`] (`%USERPROFILE%\Videos`) | + /// + /// # Errors + /// + /// Errors if the underlying system discovery API returns an error. The + /// lack of a configured path is not considered an error and results in a + /// `None` value. + /// + /// # Implementation-specific behavior + /// + /// Calls [`SHGetKnownFolderPath`] for the current user once for each known + /// folder. Does not create the folder if missing. + /// + /// COM should be initialized on the thread that calls this function or + /// else it may return unexpected errors. + /// + /// This behavior may change in the future. One example change that we + /// explicitly reserve the right to make is to load additional common + /// directories not currently in this list. + /// + /// [Known Folders]: https://learn.microsoft.com/en-us/windows/win32/shell/known-folders + /// [`SHGetKnownFolderPath`]: https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath + /// [`KNOWNFOLDERID`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid + /// [`FOLDERID_Desktop`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_desktop + /// [`FOLDERID_Documents`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_documents + /// [`FOLDERID_Downloads`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_downloads + /// [`FOLDERID_Music`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_music + /// [`FOLDERID_Pictures`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_pictures + /// [`FOLDERID_Videos`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_videos + /// [`desktop`]: MediaDirs::desktop + /// [`documents`]: MediaDirs::documents + /// [`downloads`]: MediaDirs::downloads + /// [`music`]: MediaDirs::music + /// [`pictures`]: MediaDirs::pictures + /// [`videos`]: MediaDirs::videos + #[unstable(feature = "fs_media_dirs", issue = "162083")] + fn known_folders() -> io::Result; +} + +#[cfg(windows)] +#[unstable(feature = "fs_home_dirs", issue = "162082")] +impl HomeDirsExt for HomeDirs { + fn appdata_env() -> io::Result { + let roaming_app_data = env::var_os("APPDATA") + .map(PathBuf::from) + .filter(|p| p.is_absolute()) + .ok_or(const_error!(ErrorKind::InvalidData, "non-absolute %APPDATA%"))?; + let local_app_data = env::var_os("LOCALAPPDATA") + .map(PathBuf::from) + .filter(|p| p.is_absolute()) + .ok_or(const_error!(ErrorKind::InvalidData, "non-absolute %LOCALAPPDATA%"))?; + + // AppData/Local -- system-local, doesn't make sense to sync to another + // AppData/Roaming -- data that makes sense to sync across machines + + let mut dirs = HomeDirs::empty(); + + dirs.cache = Some(local_app_data.clone()); + dirs.config = Some(roaming_app_data.clone()); + dirs.data = Some(roaming_app_data); + dirs.state = Some(local_app_data); + + Ok(dirs) + } + + fn known_folders() -> io::Result { + use crate::sys::c; + + let local_app_data = sys::get_known_folder_path(&c::FOLDERID_LocalAppData)?; + let roaming_app_data = sys::get_known_folder_path(&c::FOLDERID_RoamingAppData)?; + + // AppData/Local -- system-local, doesn't make sense to sync to another + // AppData/Roaming -- data that makes sense to sync across machines + + let mut dirs = HomeDirs::empty(); + + dirs.cache = local_app_data.clone(); + dirs.config = roaming_app_data.clone(); + dirs.data = roaming_app_data; + dirs.state = local_app_data; + + Ok(dirs) + } +} + +#[cfg(windows)] +#[unstable(feature = "fs_media_dirs", issue = "162083")] +impl MediaDirsExt for MediaDirs { + fn known_folders() -> io::Result { + use crate::sys::c; + + let desktop = sys::get_known_folder_path(&c::FOLDERID_Desktop)?; + let documents = sys::get_known_folder_path(&c::FOLDERID_Documents)?; + let downloads = sys::get_known_folder_path(&c::FOLDERID_Downloads)?; + let music = sys::get_known_folder_path(&c::FOLDERID_Music)?; + let pictures = sys::get_known_folder_path(&c::FOLDERID_Pictures)?; + let videos = sys::get_known_folder_path(&c::FOLDERID_Videos)?; + + let mut dirs = MediaDirs::empty(); + + dirs.desktop = desktop; + dirs.documents = documents; + dirs.downloads = downloads; + dirs.music = music; + dirs.pictures = pictures; + dirs.videos = videos; + + Ok(dirs) + } +} + +#[cfg(windows)] +mod sys { + use crate::io::{self, ErrorKind, const_error}; + use crate::path::PathBuf; + use crate::sys::{c, os2path}; + use crate::{ptr, slice}; + + /// Retrieve a known folder path from the Windows API. + pub fn get_known_folder_path(id: &c::GUID) -> io::Result> { + // Get the known folder path. hToken = NULL requests the current user + // scope, and we set KF_FLAG_DONT_VERIFY because it's a bit faster and + // we don't guarantee that the directories at the paths exist. + let mut pszPath = ptr::null_mut(); + // SAFETY: rfid/ppszPath are valid pointers, flags are appropriate, and + // a NULL hToken is supported by SHGetKnownFolderPath. + let hr = unsafe { + c::SHGetKnownFolderPath( + /* rfid */ id, + /* dwFlags */ c::KF_FLAG_DONT_VERIFY as _, + /* hToken */ ptr::null_mut(), + /* ppszPath */ &mut pszPath, + ) + }; + + let result = match hr { + c::S_OK => { + // SAFETY: pszPath was populated by a successful call to SHGetKnownFolderPath + // and is valid up to and including its nul terminator + let len = unsafe { c::lstrlenW(pszPath) }; + // SAFETY: *pszPath is valid up to and including its nul terminator + Ok(Some(os2path(unsafe { slice::from_raw_parts(pszPath, len as usize) }))) + } + c::E_FAIL => { + // This known folder id exists but does not have a path + if cfg!(debug_assertions) { + unreachable!("should not call get_known_folder_path on a virtual folder"); + } else { + Err(const_error!( + ErrorKind::InvalidInput, + "virtual known folders do not have paths" + )) + } + } + c::E_INVALIDARG => { + // This known folder id is not present on the system + Ok(None) + } + _ => { + // Miscellaneous error + Err(io::Error::from_raw_os_error(hr)) + } + }; + + // SAFETY: The caller is responsible for freeing the path returned by + // SHGetKnownFolderPath by calling CoTaskMemFree, whether it + // succeeds or not. + unsafe { c::CoTaskMemFree(pszPath.cast()) }; + + result + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_fetch_known_folder_paths() { + let dirs = HomeDirs::known_folders().unwrap(); + assert!(dirs.cache_home().is_some()); + assert!(dirs.config_home().is_some()); + assert!(dirs.data_home().is_some()); + assert!(dirs.state_home().is_some()); + + let dirs = MediaDirs::known_folders().unwrap(); + assert!(dirs.desktop().is_some()); + assert!(dirs.documents().is_some()); + assert!(dirs.downloads().is_some()); + assert!(dirs.music().is_some()); + assert!(dirs.pictures().is_some()); + assert!(dirs.videos().is_some()); + } +} diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index 3e1fe13ee6fc2..971d88c65ea75 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -188,7 +188,7 @@ pub impl(self) trait CommandExt { /// /// /// [1]: - #[stable(feature = "windows_process_extensions_show_window", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "windows_process_extensions_show_window", since = "1.100.0")] fn show_window(&mut self, cmd_show: u16) -> &mut process::Command; /// Forces all arguments to be wrapped in quote (`"`) characters. @@ -451,23 +451,14 @@ impl CommandExt for process::Command { } } -#[stable( - feature = "windows_process_extensions_main_thread_handle", - since = "CURRENT_RUSTC_VERSION" -)] +#[stable(feature = "windows_process_extensions_main_thread_handle", since = "1.100.0")] pub impl(self) trait ChildExt { /// Extracts the main thread raw handle, without taking ownership - #[stable( - feature = "windows_process_extensions_main_thread_handle", - since = "CURRENT_RUSTC_VERSION" - )] + #[stable(feature = "windows_process_extensions_main_thread_handle", since = "1.100.0")] fn main_thread_handle(&self) -> BorrowedHandle<'_>; } -#[stable( - feature = "windows_process_extensions_main_thread_handle", - since = "CURRENT_RUSTC_VERSION" -)] +#[stable(feature = "windows_process_extensions_main_thread_handle", since = "1.100.0")] impl ChildExt for process::Child { fn main_thread_handle(&self) -> BorrowedHandle<'_> { self.handle.main_thread_handle() diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index 3ef786c294187..2bc6da94423c3 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -151,7 +151,7 @@ impl<'a> PanicHookInfo<'a> { /// location.line(), /// ); /// } else { - /// println!("panic occurred but can't get location information..."); + /// println!("panicked at unknown location"); /// } /// })); /// @@ -162,7 +162,7 @@ impl<'a> PanicHookInfo<'a> { #[stable(feature = "panic_hooks", since = "1.10.0")] pub fn location(&self) -> Option<&'static Location<'static>> { // NOTE: If this is changed to sometimes return None, - // deal with that case in std::panicking::default_hook and core::panicking::panic_fmt. + // deal with that case in std::panicking::default_hook and std::panicking::panic_with_hook. Some(self.location) } diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index f69e0a749f579..4a66d2c1d55f5 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -706,7 +706,7 @@ pub(crate) fn panic_handler(info: &core::panic::PanicInfo<'_>) -> ! { #[rustc_do_not_const_check] // hooked by const-eval pub const fn begin_panic(msg: M) -> ! { if cfg!(panic = "immediate-abort") { - intrinsics::abort() + intrinsics::abort_immediate() } struct Payload { @@ -887,5 +887,5 @@ fn rust_panic(msg: &mut dyn PanicPayload) -> ! { #[cfg_attr(not(test), rustc_std_internal_symbol)] #[cfg(panic = "immediate-abort")] fn rust_panic(_: &mut dyn PanicPayload) -> ! { - crate::intrinsics::abort(); + crate::intrinsics::abort_immediate(); } diff --git a/library/std/src/path.rs b/library/std/src/path.rs index fe72ad4692c02..4eb3796ac026d 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -83,6 +83,7 @@ use core::clone::CloneToUninit; +use crate::alloc::Allocator; use crate::borrow::{Borrow, Cow}; use crate::collections::TryReserveError; use crate::error::Error; @@ -1972,10 +1973,10 @@ impl From for Box { } #[stable(feature = "more_box_slice_clone", since = "1.29.0")] -impl Clone for Box { +impl Clone for Box { #[inline] fn clone(&self) -> Self { - self.to_path_buf().into_boxed_path() + Box::clone_from_ref_in(&**self, Self::allocator(self).clone()) } } @@ -2769,7 +2770,7 @@ impl Path { /// assert_eq!(path.trim_prefix("/haha"), path); /// ``` #[must_use = "this returns the remaining path as a new path, without modifying the original"] - #[stable(feature = "trim_prefix_suffix", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "trim_prefix_suffix", since = "1.100.0")] pub fn trim_prefix

    (&self, base: P) -> &Path where P: AsRef, diff --git a/library/std/src/sync/lazy_lock.rs b/library/std/src/sync/lazy_lock.rs index f150d42a3137c..d1e5fd0af66f1 100644 --- a/library/std/src/sync/lazy_lock.rs +++ b/library/std/src/sync/lazy_lock.rs @@ -197,14 +197,14 @@ impl T> LazyLock { guard.0.once.set_state(OnceExclusiveState::Complete); core::mem::forget(guard); // SAFETY: We put the value there above. - unsafe { &mut this.data.get_mut().value } + unsafe { LazyLock::get_unchecked_mut(this) } } let state = this.once.state(); match state { OnceExclusiveState::Poisoned => panic_poisoned(), // SAFETY: The `Once` states we completed the initialization. - OnceExclusiveState::Complete => unsafe { &mut this.data.get_mut().value }, + OnceExclusiveState::Complete => unsafe { LazyLock::get_unchecked_mut(this) }, // SAFETY: The state is `Incomplete`. OnceExclusiveState::Incomplete => unsafe { really_init_mut(this) }, } @@ -258,7 +258,7 @@ impl T> LazyLock { // * the closure was not called, but a previous call initialized `value`. // * the closure was not called because the Once is poisoned, which we handled above. // So `value` has definitely been initialized and will not be modified again. - unsafe { &(*this.data.get()).value } + unsafe { LazyLock::get_unchecked(this) } } } @@ -286,7 +286,7 @@ impl LazyLock { match state { // SAFETY: // The closure has been run successfully, so `value` has been initialized. - OnceExclusiveState::Complete => Some(unsafe { &mut this.data.get_mut().value }), + OnceExclusiveState::Complete => Some(unsafe { LazyLock::get_unchecked_mut(this) }), _ => None, } } @@ -313,11 +313,71 @@ impl LazyLock { // SAFETY: // The closure has been run successfully, so `value` has been initialized // and will not be modified again. - Some(unsafe { &(*this.data.get()).value }) + Some(unsafe { LazyLock::get_unchecked(this) }) } else { None } } + + /// Returns a shared reference to the value stored in the `LazyLock` without + /// checking whether it has been initialized. + /// + /// # Safety + /// + /// The lazy value must be initialized before calling this function. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_lazy_lock_get_unchecked)] + /// + /// use std::sync::LazyLock; + /// + /// let lazy = LazyLock::new(|| 42); + /// + /// // Initialize the lazy. + /// let _ = &*lazy; + /// + /// let value = unsafe { LazyLock::get_unchecked(&lazy) }; + /// assert_eq!(*value, 42); + /// ``` + #[inline] + #[unstable(feature = "once_lazy_lock_get_unchecked", issue = "162716")] + pub unsafe fn get_unchecked(this: &LazyLock) -> &T { + debug_assert!(this.once.is_completed()); + unsafe { &(*this.data.get()).value } + } + + /// Returns a mutable reference to the value stored in the `LazyLock` without + /// checking whether it has been initialized. + /// + /// # Safety + /// + /// The lazy value must be initialized before calling this function. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_lazy_lock_get_unchecked)] + /// + /// use std::sync::LazyLock; + /// + /// let mut lazy = LazyLock::new(|| 42); + /// + /// // Initialize the lazy. + /// let _ = &*lazy; + /// + /// let value = unsafe { LazyLock::get_unchecked_mut(&mut lazy) }; + /// *value = 100; + /// + /// assert_eq!(*lazy, 100); + /// ``` + #[inline] + #[unstable(feature = "once_lazy_lock_get_unchecked", issue = "162716")] + pub unsafe fn get_unchecked_mut(this: &mut LazyLock) -> &mut T { + debug_assert!(this.once.is_completed()); + unsafe { &mut this.data.get_mut().value } + } } #[stable(feature = "lazy_cell", since = "1.80.0")] diff --git a/library/std/src/sync/mod.rs b/library/std/src/sync/mod.rs index 9426d9a684ef6..b138c1b946c92 100644 --- a/library/std/src/sync/mod.rs +++ b/library/std/src/sync/mod.rs @@ -179,7 +179,7 @@ #![cfg_attr(rustfmt, rustfmt::skip)] // These come from `core` & `alloc` and only in one flavor: no poisoning. -#[unstable(feature = "exclusive_wrapper", issue = "98407")] +#[stable(feature = "exclusive_wrapper", since = "CURRENT_RUSTC_VERSION")] pub use core::sync::SyncView; #[stable(feature = "rust1", since = "1.0.0")] pub use core::sync::atomic; diff --git a/library/std/src/sync/once.rs b/library/std/src/sync/once.rs index 9b555c32df99d..9b7dd3b78eac7 100644 --- a/library/std/src/sync/once.rs +++ b/library/std/src/sync/once.rs @@ -348,7 +348,7 @@ impl fmt::Debug for Once { } } -#[stable(feature = "once_default", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "once_default", since = "1.100.0")] #[rustc_const_unstable(feature = "const_default", issue = "143894")] const impl Default for Once { /// Creates a new `Once` value, same as [`Once::new`]. diff --git a/library/std/src/sync/once_lock.rs b/library/std/src/sync/once_lock.rs index 4b41fc4587829..e8e8f869dd038 100644 --- a/library/std/src/sync/once_lock.rs +++ b/library/std/src/sync/once_lock.rs @@ -585,20 +585,58 @@ impl OnceLock { res } + /// Returns a reference to the value in the `OnceLock` without checking + /// whether it has been initialized. + /// /// # Safety /// - /// The cell must be initialized + /// The `OnceLock` must be initialized before calling this function. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_lazy_lock_get_unchecked)] + /// + /// use std::sync::OnceLock; + /// + /// let cell = OnceLock::new(); + /// cell.set(42).unwrap(); + /// + /// let value = unsafe { cell.get_unchecked() }; + /// assert_eq!(*value, 42); + /// ``` #[inline] - unsafe fn get_unchecked(&self) -> &T { + #[unstable(feature = "once_lazy_lock_get_unchecked", issue = "162716")] + pub unsafe fn get_unchecked(&self) -> &T { debug_assert!(self.initialized()); unsafe { (&*self.value.get()).assume_init_ref() } } + /// Returns a mutable reference to the value in the `OnceLock` without + /// checking whether it has been initialized. + /// /// # Safety /// - /// The cell must be initialized + /// The `OnceLock` must be initialized before calling this function. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_lazy_lock_get_unchecked)] + /// + /// use std::sync::OnceLock; + /// + /// let mut cell = OnceLock::new(); + /// cell.set(42).unwrap(); + /// + /// let value = unsafe { cell.get_unchecked_mut() }; + /// *value = 100; + /// + /// assert_eq!(*cell.get().unwrap(), 100); + /// ``` #[inline] - unsafe fn get_unchecked_mut(&mut self) -> &mut T { + #[unstable(feature = "once_lazy_lock_get_unchecked", issue = "162716")] + pub unsafe fn get_unchecked_mut(&mut self) -> &mut T { debug_assert!(self.initialized_mut()); unsafe { self.value.get_mut().assume_init_mut() } } diff --git a/library/std/src/sys/exit.rs b/library/std/src/sys/exit.rs index b9ebe2d974fec..1ca2bd8f6b514 100644 --- a/library/std/src/sys/exit.rs +++ b/library/std/src/sys/exit.rs @@ -127,7 +127,7 @@ pub fn exit(code: i32) -> ! { ) }; } - crate::intrinsics::abort() + crate::intrinsics::abort_immediate() } any(target_family = "unix", target_os = "wasi") => unsafe { libc::exit(code as crate::ffi::c_int) @@ -147,7 +147,7 @@ pub fn exit(code: i32) -> ! { target_os = "xous" => crate::os::xous::ffi::exit(code as u32), _ => { let _ = code; - crate::intrinsics::abort() + crate::intrinsics::abort_immediate() } } } diff --git a/library/std/src/sys/fd/unix.rs b/library/std/src/sys/fd/unix.rs index aa84f6bc28023..6ebbc6afc1661 100644 --- a/library/std/src/sys/fd/unix.rs +++ b/library/std/src/sys/fd/unix.rs @@ -72,12 +72,16 @@ pub struct FileDesc(OwnedFd); // // On Apple targets however, apparently the 64-bit libc is either buggy or // intentionally showing odd behavior by rejecting any read with a size -// larger than INT_MAX. To handle both of these the read size is capped on -// both platforms. -const READ_LIMIT: usize = if cfg!(target_vendor = "apple") { - libc::c_int::MAX as usize -} else { - libc::ssize_t::MAX as usize +// larger than INT_MAX. +// +// Meanwhile on QNX, reads/writes/sends larger than INT_MAX return the wrong +// number of bytes written (eg, writing 2^31 bytes returns (2^64 - 2^31) instead +// of the correct byte count). +const READ_LIMIT: usize = cfg_select! { + any(target_vendor = "apple", target_os = "nto", target_os = "qnx") => { + libc::c_int::MAX as usize + } + _ => libc::ssize_t::MAX as usize, }; #[cfg(any( diff --git a/library/std/src/sys/fs/common.rs b/library/std/src/sys/fs/common.rs index 17b98a4506544..6a7a1f52c15ac 100644 --- a/library/std/src/sys/fs/common.rs +++ b/library/std/src/sys/fs/common.rs @@ -77,11 +77,15 @@ impl Dir { Self::open(path, &opts) } + pub fn duplicate(&self) -> io::Result { + Ok(Self { path: self.path.clone() }) + } + pub fn open_file(&self, path: &Path, opts: &OpenOptions) -> io::Result { File::open(&self.path.join(path), opts) } - pub fn metadata(&self) -> io::Result { + pub fn self_metadata(&self) -> io::Result { self.path.metadata().map(|m| m.into_inner()) } @@ -102,7 +106,15 @@ impl Dir { } pub fn remove_dir(&self, path: &Path) -> io::Result<()> { - remove_dir(path) + remove_dir(self.path.join(path)) + } + + pub fn metadata(&self, path: &Path) -> io::Result { + self.path.join(path).metadata().map(|m| m.into_inner()) + } + + pub fn symlink_metadata(&self, path: &Path) -> io::Result { + self.path.join(path).symlink_metadata().map(|m| m.into_inner()) } } @@ -111,3 +123,11 @@ impl fmt::Debug for Dir { f.debug_struct("Dir").field("path", &self.path).finish() } } + +#[derive(Debug, Clone, Default)] +#[non_exhaustive] +pub struct ExtraHomeDirs {} + +#[derive(Debug, Clone, Default)] +#[non_exhaustive] +pub struct ExtraMediaDirs {} diff --git a/library/std/src/sys/fs/hermit.rs b/library/std/src/sys/fs/hermit.rs index 33feee4fc1c97..89f6989b9adbe 100644 --- a/library/std/src/sys/fs/hermit.rs +++ b/library/std/src/sys/fs/hermit.rs @@ -11,7 +11,7 @@ use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, Raw use crate::path::{Path, PathBuf}; use crate::sync::Arc; use crate::sys::fd::FileDesc; -pub use crate::sys::fs::common::{Dir, copy, exists}; +pub use crate::sys::fs::common::{Dir, ExtraHomeDirs, ExtraMediaDirs, copy, exists}; use crate::sys::helpers::run_path_with_cstr; use crate::sys::io::DEFAULT_BUF_SIZE; use crate::sys::time::SystemTime; diff --git a/library/std/src/sys/fs/mod.rs b/library/std/src/sys/fs/mod.rs index b2666eb2a3da9..ca5c28dcd88bd 100644 --- a/library/std/src/sys/fs/mod.rs +++ b/library/std/src/sys/fs/mod.rs @@ -61,8 +61,8 @@ pub fn with_native_path(path: &Path, f: &dyn Fn(&Path) -> io::Result) -> i } pub use imp::{ - Dir, DirBuilder, DirEntry, File, FileAttr, FilePermissions, FileTimes, FileType, OpenOptions, - ReadDir, + Dir, DirBuilder, DirEntry, ExtraHomeDirs, ExtraMediaDirs, File, FileAttr, FilePermissions, + FileTimes, FileType, OpenOptions, ReadDir, }; pub fn read_dir(path: &Path) -> io::Result { diff --git a/library/std/src/sys/fs/motor.rs b/library/std/src/sys/fs/motor.rs index 938d1537790a1..91cd90a6b0cb3 100644 --- a/library/std/src/sys/fs/motor.rs +++ b/library/std/src/sys/fs/motor.rs @@ -4,7 +4,7 @@ use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::path::{Path, PathBuf}; use crate::sys::fd::FileDesc; -pub use crate::sys::fs::common::{Dir, exists}; +pub use crate::sys::fs::common::{Dir, ExtraHomeDirs, ExtraMediaDirs, exists}; use crate::sys::time::SystemTime; use crate::sys::{ AsInner, AsInnerMut, FromInner, IntoInner, io_slices, io_slices_mut, map_motor_error, diff --git a/library/std/src/sys/fs/solid.rs b/library/std/src/sys/fs/solid.rs index bd963b1d2f038..dfdcacdcdf277 100644 --- a/library/std/src/sys/fs/solid.rs +++ b/library/std/src/sys/fs/solid.rs @@ -9,7 +9,7 @@ use crate::os::raw::{c_int, c_short}; use crate::os::solid::ffi::OsStrExt; use crate::path::{Path, PathBuf}; use crate::sync::Arc; -pub use crate::sys::fs::common::{Dir, exists}; +pub use crate::sys::fs::common::{Dir, ExtraHomeDirs, ExtraMediaDirs, exists}; use crate::sys::helpers::ignore_notfound; use crate::sys::pal::{abi, error}; use crate::sys::time::SystemTime; diff --git a/library/std/src/sys/fs/uefi.rs b/library/std/src/sys/fs/uefi.rs index 08473e245cc8e..5d0c219586e0e 100644 --- a/library/std/src/sys/fs/uefi.rs +++ b/library/std/src/sys/fs/uefi.rs @@ -6,7 +6,7 @@ use crate::fs::TryLockError; use crate::hash::Hash; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; use crate::path::{Path, PathBuf}; -pub use crate::sys::fs::common::{Dir, copy, remove_dir_all}; +pub use crate::sys::fs::common::{Dir, ExtraHomeDirs, ExtraMediaDirs, copy, remove_dir_all}; use crate::sys::pal::{helpers, unsupported}; use crate::sys::time::SystemTime; diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 46d5d33d23ac2..0adc0b9136f63 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1,11 +1,42 @@ #![allow(nonstandard_style)] #![allow(unsafe_op_in_unsafe_fn)] -// miri has some special hacks here that make things unused. -#![cfg_attr(miri, allow(unused))] #[cfg(test)] mod tests; +// Import the file operations under consistent names. +cfg_select! { + not(any( + all(target_os = "linux", not(target_env = "musl")), + target_os = "android", + target_os = "hurd", + target_os = "l4re", + )) => { + use libc::{ + dirent as dirent64, fstat as fstat64, ftruncate as ftruncate64, lseek as lseek64, + lstat as lstat64, off_t as off64_t, open as open64, stat as stat64, + }; + } + target_os = "android" => { + // Android's `stat`, `dirent`, and related functions are always 64-bit LFS compatible, and + // `open` already implies `O_LARGEFILE`, so all those don't need to follow Linux. + // However, we still need off64_t, ftruncate64, and lseek64. + use libc::{ + dirent as dirent64, fstat as fstat64, ftruncate64, lseek64, lstat as lstat64, off64_t, + open as open64, stat as stat64, + }; + } + target_os = "l4re" => { + use libc::{ + dirent64, fstat as fstat64, ftruncate as ftruncate64, lseek as lseek64, + lstat as lstat64, off_t as off64_t, open as open64, stat as stat64, + }; + } + _ => { + use libc::{dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, stat64}; + } +} + #[cfg(all(target_os = "linux", target_env = "gnu"))] use libc::c_char; #[cfg(any( @@ -17,39 +48,15 @@ use libc::c_char; target_vendor = "apple", ))] use libc::dirfd; -#[cfg(any(target_os = "fuchsia", target_os = "illumos", target_vendor = "apple"))] -use libc::fstatat as fstatat64; -#[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))] -use libc::fstatat64; use libc::{c_int, mode_t}; -#[cfg(target_os = "android")] -use libc::{ - dirent as dirent64, fstat as fstat64, fstatat as fstatat64, ftruncate64, lseek64, - lstat as lstat64, off64_t, open as open64, stat as stat64, -}; -#[cfg(not(any( - all(target_os = "linux", not(target_env = "musl")), - target_os = "android", - target_os = "hurd", - target_os = "l4re", -)))] -use libc::{ - dirent as dirent64, fstat as fstat64, ftruncate as ftruncate64, lseek as lseek64, - lstat as lstat64, off_t as off64_t, open as open64, stat as stat64, -}; -#[cfg(target_os = "l4re")] -use libc::{ - dirent64, fstat as fstat64, ftruncate as ftruncate64, lseek as lseek64, lstat as lstat64, - off_t as off64_t, open as open64, stat as stat64, -}; -#[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))] -use libc::{dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, stat64}; use crate::ffi::{CStr, OsStr, OsString}; use crate::fmt::{self, Write as _}; use crate::fs::TryLockError; use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd}; +#[cfg(not(target_os = "wasi"))] +pub use crate::os::unix::fs::dirs::{ExtraHomeDirs, ExtraMediaDirs}; #[cfg(target_family = "unix")] use crate::os::unix::prelude::*; #[cfg(target_os = "wasi")] @@ -58,6 +65,8 @@ use crate::path::{Path, PathBuf}; use crate::sync::Arc; use crate::sys::fd::FileDesc; pub use crate::sys::fs::common::exists; +#[cfg(target_os = "wasi")] +pub use crate::sys::fs::common::{ExtraHomeDirs, ExtraMediaDirs}; use crate::sys::helpers::run_path_with_cstr; use crate::sys::time::SystemTime; #[cfg(all(target_os = "linux", target_env = "gnu"))] @@ -977,7 +986,6 @@ impl Drop for DirStream { fn drop(&mut self) { // dirfd isn't supported everywhere #[cfg(not(any( - miri, target_os = "redox", target_os = "nto", target_os = "qnx", @@ -1016,89 +1024,58 @@ impl DirEntry { self.file_name_os_str().to_os_string() } - #[cfg(all( - any( - all(target_os = "linux", not(target_env = "musl")), - target_os = "android", - target_os = "fuchsia", - target_os = "hurd", - target_os = "illumos", - target_vendor = "apple", - ), - not(miri) // no dirfd on Miri - ))] pub fn metadata(&self) -> io::Result { - let fd = cvt(unsafe { dirfd(self.dir.dirp.0) })?; - let name = self.name.as_ptr(); - - cfg_has_statx! { - if let Some(ret) = unsafe { try_statx( - fd, - name, - libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT, - libc::STATX_BASIC_STATS | libc::STATX_BTIME, - ) } { - return ret; - } - } + cfg_select! { + // Use directory handle where possible + any( + all(target_os = "linux", not(target_env = "musl")), + target_os = "android", + target_os = "fuchsia", + target_os = "hurd", + target_os = "illumos", + target_vendor = "apple", + ) => { + let fd = cvt(unsafe { dirfd(self.dir.dirp.0) })?; - let mut stat: stat64 = unsafe { mem::zeroed() }; - cvt(unsafe { fstatat64(fd, name, &mut stat, libc::AT_SYMLINK_NOFOLLOW) })?; - Ok(FileAttr::from_stat64(stat)) - } + // Make this FD into a directory handle. We don't actually drop it, + // so having an `OwnedFd` is fine. + let dir_handle = + mem::ManuallyDrop::new(dir::Dir(unsafe { OwnedFd::from_raw_fd(fd) })); - #[cfg(any( - not(any( - all(target_os = "linux", not(target_env = "musl")), - target_os = "android", - target_os = "fuchsia", - target_os = "hurd", - target_os = "illumos", - target_vendor = "apple", - )), - miri // no dirfd on Miri - ))] - pub fn metadata(&self) -> io::Result { - run_path_with_cstr(&self.path(), &lstat) - } + dir_handle.metadata_c(&self.name, /* symlink_nofollow */ true) + } - #[cfg(any( - target_os = "solaris", - target_os = "illumos", - target_os = "haiku", - target_os = "vxworks", - target_os = "aix", - target_os = "nto", - target_os = "qnx", - target_os = "vita", - target_os = "l4re", - ))] - pub fn file_type(&self) -> io::Result { - self.metadata().map(|m| m.file_type()) + // Fallback based on path + _ => run_path_with_cstr(&self.path(), &lstat), + } } - #[cfg(not(any( - target_os = "solaris", - target_os = "illumos", - target_os = "haiku", - target_os = "vxworks", - target_os = "aix", - target_os = "nto", - target_os = "qnx", - target_os = "vita", - target_os = "l4re", - )))] pub fn file_type(&self) -> io::Result { + // Use `entry.d_type` if available. + #[cfg(not(any( + target_os = "solaris", + target_os = "illumos", + target_os = "haiku", + target_os = "vxworks", + target_os = "aix", + target_os = "nto", + target_os = "qnx", + target_os = "vita", + target_os = "l4re", + )))] match self.entry.d_type { - libc::DT_CHR => Ok(FileType { mode: libc::S_IFCHR }), - libc::DT_FIFO => Ok(FileType { mode: libc::S_IFIFO }), - libc::DT_LNK => Ok(FileType { mode: libc::S_IFLNK }), - libc::DT_REG => Ok(FileType { mode: libc::S_IFREG }), - libc::DT_SOCK => Ok(FileType { mode: libc::S_IFSOCK }), - libc::DT_DIR => Ok(FileType { mode: libc::S_IFDIR }), - libc::DT_BLK => Ok(FileType { mode: libc::S_IFBLK }), - _ => self.metadata().map(|m| m.file_type()), + libc::DT_CHR => return Ok(FileType { mode: libc::S_IFCHR }), + libc::DT_FIFO => return Ok(FileType { mode: libc::S_IFIFO }), + libc::DT_LNK => return Ok(FileType { mode: libc::S_IFLNK }), + libc::DT_REG => return Ok(FileType { mode: libc::S_IFREG }), + libc::DT_SOCK => return Ok(FileType { mode: libc::S_IFSOCK }), + libc::DT_DIR => return Ok(FileType { mode: libc::S_IFDIR }), + libc::DT_BLK => return Ok(FileType { mode: libc::S_IFBLK }), + _ => {} } + + // Fall back to loading the metadata. + self.metadata().map(|m| m.file_type()) } pub fn ino(&self) -> u64 { @@ -1896,22 +1873,11 @@ pub fn set_perm_nofollow(_p: &CStr, _perm: FilePermissions) -> io::Result<()> { Err(crate::io::ErrorKind::Unsupported.into()) } -#[cfg(target_os = "android")] -pub fn set_perm_nofollow(_p: &CStr, _perm: FilePermissions) -> io::Result<()> { - // Currently Android seems to be having inconsistent behavior with fchmodat - // with `AT_SYMLINK_NOFOLLOW` or openat with `O_NOFOLLOW` + fchmod. - // See this issue here mentioning inconsistent behavior on fchmodat: - // https://github.com/android/ndk/issues/1258 - // On the arm-android CI job, using fchmodat with `AT_SYMLINK_NOFOLLOW` + - // fallback behavior on a symlink sets the target file's permissions, - // which is incorrect behavior. - Err(crate::io::ErrorKind::Unsupported.into()) -} - -#[cfg(not(any(target_os = "android", target_os = "vxworks")))] +#[cfg(not(target_os = "vxworks"))] pub fn set_perm_nofollow(p: &CStr, perm: FilePermissions) -> io::Result<()> { + /// Helper function for fallback open with `O_NOFOLLOW` + `fchmod` behavior. This will + /// successfully change the permissions of non-symlinks and fail when there's a symlink. #[inline] - /// Helper function for fallback open with `O_NOFOLLOW` + `fchmod` behavior fn open_and_set_permissions(p: &CStr, perm: FilePermissions) -> io::Result<()> { use crate::fs::{OpenOptions, Permissions}; @@ -1926,6 +1892,7 @@ pub fn set_perm_nofollow(p: &CStr, perm: FilePermissions) -> io::Result<()> { use crate::os::unix::fs::OpenOptionsExt; #[cfg(target_os = "wasi")] use crate::os::wasi::fs::OpenOptionsExt; + options.custom_flags(libc::O_NOFOLLOW); } @@ -1940,7 +1907,8 @@ pub fn set_perm_nofollow(p: &CStr, perm: FilePermissions) -> io::Result<()> { #[allow(unused)] let mut res: Result<(), core::io::Error> = Err(crate::io::ErrorKind::Unsupported.into()); - // These platforms support `fchmodat`, so utilize this syscall over `open` + `fchmod` + // These platforms support `fchmodat`, so utilize this syscall over `open` + `fchmod`. + // We can *not* use this on Android due to . #[cfg(any( target_os = "linux", target_os = "macos", diff --git a/library/std/src/sys/fs/unix/dir.rs b/library/std/src/sys/fs/unix/dir.rs index 3fe952d942927..dd01db58c600d 100644 --- a/library/std/src/sys/fs/unix/dir.rs +++ b/library/std/src/sys/fs/unix/dir.rs @@ -4,16 +4,16 @@ cfg_select! { not(any( all(target_os = "linux", not(target_env = "musl")), target_os = "l4re", - target_os = "android", target_os = "hurd", )) => { - use libc::{open as open64, openat as openat64}; + use libc::{fstatat as fstatat64, openat as openat64}; } _ => { - use libc::{open64, openat64}; + use libc::{fstatat64, openat64}; } } +use super::{open64, stat64}; use crate::ffi::CStr; use crate::os::fd::{AsFd, BorrowedFd, IntoRawFd, OwnedFd, RawFd}; #[cfg(target_family = "unix")] @@ -26,7 +26,7 @@ use crate::sys::fs::OpenOptions; use crate::sys::fs::unix::{File, FileAttr, debug_path_fd}; use crate::sys::helpers::run_path_with_cstr; use crate::sys::{AsInner, FromInner, IntoInner, cvt, cvt_r}; -use crate::{fmt, fs, io}; +use crate::{fmt, fs, io, mem}; const TRAVERSE_DIRECTORY: i32 = cfg_select! { @@ -36,7 +36,7 @@ const TRAVERSE_DIRECTORY: i32 = _ => libc::O_RDONLY, }; -pub struct Dir(OwnedFd); +pub struct Dir(pub(super) OwnedFd); impl Dir { pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { @@ -47,13 +47,17 @@ impl Dir { run_path_with_cstr(path, &|path| Self::open_traversal_c(path)) } + pub fn duplicate(&self) -> io::Result { + Ok(Self(self.0.try_clone()?)) + } + pub fn open_file(&self, path: &Path, opts: &OpenOptions) -> io::Result { run_path_with_cstr(path.as_ref(), &|path| self.open_file_c(path, opts, 0)) .map(FileDesc::from_inner) .map(File) } - pub fn metadata(&self) -> io::Result { + pub fn self_metadata(&self) -> io::Result { // Reuse the implementation for files, which should work for all FDs. let fd = self.0.as_raw_fd(); let f = core::mem::ManuallyDrop::new(File( @@ -64,7 +68,7 @@ impl Dir { } pub fn remove_file(&self, path: &Path) -> io::Result<()> { - run_path_with_cstr(path, &|path| self.remove_c(path, false)) + run_path_with_cstr(path, &|path| self.remove_c(path, /* remove_dir */ false)) } pub fn rename(&self, from: &Path, to_dir: &Self, to: &Path) -> io::Result<()> { @@ -82,7 +86,17 @@ impl Dir { } pub fn remove_dir(&self, path: &Path) -> io::Result<()> { - run_path_with_cstr(path, &|path| self.remove_c(path, true)) + run_path_with_cstr(path, &|path| self.remove_c(path, /* remove_dir */ true)) + } + + pub fn metadata(&self, path: &Path) -> io::Result { + run_path_with_cstr(path, &|path| { + self.metadata_c(path, /* symlink_nofollow */ false) + }) + } + + pub fn symlink_metadata(&self, path: &Path) -> io::Result { + run_path_with_cstr(path, &|path| self.metadata_c(path, /* symlink_nofollow */ true)) } fn open_with_c(path: &CStr, opts: &OpenOptions) -> io::Result { @@ -139,6 +153,26 @@ impl Dir { fn create_dir_c(&self, path: &CStr) -> io::Result<()> { cvt(unsafe { mkdirat(self.0.as_raw_fd(), path.as_ptr(), 0o777) }).map(|_| ()) } + + pub(super) fn metadata_c(&self, path: &CStr, symlink_nofollow: bool) -> io::Result { + let fd = self.0.as_raw_fd(); + let flag = if symlink_nofollow { libc::AT_SYMLINK_NOFOLLOW } else { 0 }; + + cfg_has_statx! { + if let Some(ret) = unsafe { super::try_statx( + fd, + path.as_ptr(), + flag | libc::AT_STATX_SYNC_AS_STAT, + libc::STATX_BASIC_STATS | libc::STATX_BTIME, + ) } { + return ret; + } + } + + let mut stat: stat64 = unsafe { mem::zeroed() }; + cvt(unsafe { fstatat64(fd, path.as_ptr(), &mut stat, flag) })?; + Ok(FileAttr::from_stat64(stat)) + } } impl fmt::Debug for Dir { diff --git a/library/std/src/sys/fs/unsupported.rs b/library/std/src/sys/fs/unsupported.rs index 512af27401dfe..26677c7c7410a 100644 --- a/library/std/src/sys/fs/unsupported.rs +++ b/library/std/src/sys/fs/unsupported.rs @@ -4,7 +4,7 @@ use crate::fs::TryLockError; use crate::hash::{Hash, Hasher}; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; use crate::path::{Path, PathBuf}; -pub use crate::sys::fs::common::Dir; +pub use crate::sys::fs::common::{Dir, ExtraHomeDirs, ExtraMediaDirs}; use crate::sys::time::SystemTime; use crate::sys::unsupported; diff --git a/library/std/src/sys/fs/vexos.rs b/library/std/src/sys/fs/vexos.rs index 1357787c44c77..5043a4daa83b4 100644 --- a/library/std/src/sys/fs/vexos.rs +++ b/library/std/src/sys/fs/vexos.rs @@ -16,6 +16,8 @@ pub use unsupported_fs::{ symlink, unlink, }; +pub use crate::sys::fs::common::{ExtraHomeDirs, ExtraMediaDirs}; + /// VEXos file descriptor. /// /// This stores an opaque pointer to a [FatFs file object structure] managed by VEXos diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs index b703ad9bd0ae8..c4e0bb6ecf51f 100644 --- a/library/std/src/sys/fs/windows.rs +++ b/library/std/src/sys/fs/windows.rs @@ -11,6 +11,7 @@ use crate::os::windows::io::{AsHandle, BorrowedHandle}; use crate::os::windows::prelude::*; use crate::path::{Path, PathBuf}; use crate::sync::Arc; +pub use crate::sys::fs::common::{ExtraHomeDirs, ExtraMediaDirs}; use crate::sys::handle::Handle; use crate::sys::pal::api::{self, WinError, set_file_information_by_handle}; use crate::sys::pal::{IoResult, fill_utf16_buf, to_u16s, truncate_utf16_at_nul}; @@ -263,54 +264,34 @@ impl OpenOptions { } fn get_access_mode(&self) -> io::Result { - match (self.read, self.write, self.append, self.access_mode) { - (.., Some(mode)) => Ok(mode), - (true, false, false, None) => Ok(c::GENERIC_READ), - (false, true, false, None) => Ok(c::GENERIC_WRITE), - (true, true, false, None) => Ok(c::GENERIC_READ | c::GENERIC_WRITE), - (false, _, true, None) => Ok(c::FILE_GENERIC_WRITE & !c::FILE_WRITE_DATA), - (true, _, true, None) => { - Ok(c::GENERIC_READ | (c::FILE_GENERIC_WRITE & !c::FILE_WRITE_DATA)) - } - (false, false, false, None) => { - // If no access mode is set, check if any creation flags are set - // to provide a more descriptive error message - if self.create || self.create_new || self.truncate { - Err(io::Error::new( - io::ErrorKind::InvalidInput, - "creating or truncating a file requires write or append access", - )) - } else { - Err(io::Error::new( - io::ErrorKind::InvalidInput, - "must specify at least one of read, write, or append access", - )) - } - } + if let Some(access) = self.access_mode { + return Ok(access); } - } - fn get_cmode_disposition(&self) -> io::Result<(u32, u32)> { - match (self.write, self.append) { - (true, false) => {} - (false, false) => { - if self.truncate || self.create || self.create_new { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "creating or truncating a file requires write or append access", - )); - } - } - (_, true) => { - if self.truncate && !self.create_new { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "append and truncate cannot both be enabled", - )); - } + match (self.read, self.write, self.append) { + (_, false, false) if self.truncate || self.create || self.create_new => { + Err(io::Error::new( + io::ErrorKind::InvalidInput, + "creating or truncating a file requires write or append access", + )) } + (_, _, true) if self.truncate && !self.create_new => Err(io::Error::new( + io::ErrorKind::InvalidInput, + "append and truncate cannot both be enabled", + )), + (true, false, false) => Ok(c::GENERIC_READ), + (false, true, false) => Ok(c::GENERIC_WRITE), + (true, true, false) => Ok(c::GENERIC_READ | c::GENERIC_WRITE), + (false, _, true) => Ok(c::FILE_GENERIC_WRITE & !c::FILE_WRITE_DATA), + (true, _, true) => Ok(c::GENERIC_READ | (c::FILE_GENERIC_WRITE & !c::FILE_WRITE_DATA)), + (false, false, false) => Err(io::Error::new( + io::ErrorKind::InvalidInput, + "must specify at least one of read, write, or append access", + )), } + } + fn get_cmode_disposition(&self) -> io::Result<(u32, u32)> { Ok(match (self.create, self.truncate, self.create_new) { (false, false, false) => (c::OPEN_EXISTING, c::FILE_OPEN), (true, false, false) => (c::OPEN_ALWAYS, c::FILE_OPEN_IF), @@ -1474,7 +1455,9 @@ pub fn link(_original: &WCStr, _link: &WCStr) -> io::Result<()> { pub fn stat(path: &WCStr) -> io::Result { match metadata(path, ReparsePoint::Follow) { Err(err) if err.raw_os_error() == Some(c::ERROR_CANT_ACCESS_FILE as i32) => { - if let Ok(attrs) = lstat(path) { + // Fallback to opening reparse points when following fails. Needed for UNIX domain + // sockets. See . + if let Ok(attrs) = metadata(path, ReparsePoint::Open) { if !attrs.file_type().is_symlink() { return Ok(attrs); } diff --git a/library/std/src/sys/fs/windows/dir.rs b/library/std/src/sys/fs/windows/dir.rs index 70bade84f58fd..60f8c2522de26 100644 --- a/library/std/src/sys/fs/windows/dir.rs +++ b/library/std/src/sys/fs/windows/dir.rs @@ -7,7 +7,7 @@ use crate::os::windows::io::{ }; use crate::path::Path; use crate::sys::api::{UnicodeStrRef, WinError}; -use crate::sys::fs::windows::debug_path_handle; +use crate::sys::fs::windows::{ReparsePoint, debug_path_handle}; use crate::sys::fs::{File, FileAttr, OpenOptions}; use crate::sys::handle::Handle; use crate::sys::path::{WCStr, with_native_path}; @@ -72,25 +72,28 @@ impl Dir { with_native_path(path, &|path| Self::open_with_native(path, &opts)) } + pub fn duplicate(&self) -> io::Result { + Ok(Self { handle: self.handle.try_clone()? }) + } + pub fn open_file(&self, path: &Path, opts: &OpenOptions) -> io::Result { // NtCreateFile will fail if given an absolute path and a non-null RootDirectory if path.is_absolute() { return File::open(path, opts); } let path = to_u16s_without_nul(path)?; - self.open_file_native(&path, opts, false).map(|handle| File { handle }) + self.open_file_native(&path, opts, c::FILE_NON_DIRECTORY_FILE).map(|handle| File { handle }) } pub fn remove_file(&self, path: &Path) -> io::Result<()> { let path = to_u16s_without_nul(path)?; - self.remove_native(&path, false) + self.remove_native(&path, /* dir */ false) } pub fn rename(&self, from: &Path, to_dir: &Self, to: &Path) -> io::Result<()> { - let is_dir = from.is_dir(); let from = to_u16s_without_nul(from)?; let to = to_u16s_without_nul(to)?; - self.rename_native(&from, to_dir, &to, is_dir) + self.rename_native(&from, to_dir, &to) } pub fn create_dir(&self, path: &Path) -> io::Result<()> { @@ -103,12 +106,12 @@ impl Dir { pub fn open_dir(&self, path: &Path, opts: &OpenOptions) -> io::Result { let path = to_u16s_without_nul(&path)?; - self.open_file_native(&path, &opts, true).map(|handle| Self { handle }) + self.open_file_native(&path, &opts, c::FILE_DIRECTORY_FILE).map(|handle| Self { handle }) } pub fn remove_dir(&self, path: &Path) -> io::Result<()> { let path = to_u16s_without_nul(&path)?; - self.remove_native(&path, true) + self.remove_native(&path, /* dir */ true) } fn open_with_native(path: &WCStr, opts: &OpenOptions) -> io::Result { @@ -136,29 +139,35 @@ impl Dir { } } - fn open_file_native(&self, path: &[u16], opts: &OpenOptions, dir: bool) -> io::Result { + fn open_file_native( + &self, + path: &[u16], + opts: &OpenOptions, + create_opt: u32, + ) -> io::Result { let name = UnicodeStrRef::from_slice(path); let object_attributes = c::OBJECT_ATTRIBUTES { RootDirectory: self.handle.as_raw_handle(), ObjectName: name.as_ptr().cast_mut(), ..c::OBJECT_ATTRIBUTES::with_length() }; - let create_opt = if dir { c::FILE_DIRECTORY_FILE } else { c::FILE_NON_DIRECTORY_FILE }; unsafe { nt_create_file(opts, &object_attributes, create_opt) } } fn remove_native(&self, path: &[u16], dir: bool) -> io::Result<()> { let mut opts = OpenOptions::new(); opts.access_mode(c::DELETE); - let handle = self.open_file_native(path, &opts, dir)?; + let create_opt = if dir { c::FILE_DIRECTORY_FILE } else { c::FILE_NON_DIRECTORY_FILE }; + let handle = self.open_file_native(path, &opts, create_opt)?; File::from_inner(handle).delete().io_result() } - fn rename_native(&self, from: &[u16], to_dir: &Self, to: &[u16], dir: bool) -> io::Result<()> { + fn rename_native(&self, from: &[u16], to_dir: &Self, to: &[u16]) -> io::Result<()> { let mut opts = OpenOptions::new(); opts.access_mode(c::DELETE); + // FIXME: custom_flags is ignored by `open_file_native`! opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS); - let handle = self.open_file_native(from, &opts, dir)?; + let handle = self.open_file_native(from, &opts, /* create_opt */ 0)?; // Calculate the layout of the `FILE_RENAME_INFORMATION` we pass to `NtSetInformationFile` // This is a dynamically sized struct so we need to get the position of the last field to calculate the actual size. const too_long_err: io::Error = @@ -214,7 +223,7 @@ impl Dir { .io_result() } - pub fn metadata(&self) -> io::Result { + pub fn self_metadata(&self) -> io::Result { // Reuse the implementation for files, which should work for all handles. let handle = self.handle.as_raw_handle(); let f = core::mem::ManuallyDrop::new(File { @@ -223,6 +232,45 @@ impl Dir { }); f.file_attr() } + + pub fn metadata(&self, path: &Path) -> io::Result { + let path = to_u16s_without_nul(path)?; + // Same as the `stat` logic used for `fs::metadata` + match self.metadata_native(&path, ReparsePoint::Follow) { + Err(err) if err.raw_os_error() == Some(c::ERROR_CANT_ACCESS_FILE as i32) => { + // Fallback to opening reparse points when following fails. Needed for UNIX domain + // sockets. See . + if let Ok(attrs) = self.metadata_native(&path, ReparsePoint::Open) { + if !attrs.file_type().is_symlink() { + return Ok(attrs); + } + } + Err(err) + } + result => result, + } + } + + pub fn symlink_metadata(&self, path: &Path) -> io::Result { + let path = to_u16s_without_nul(path)?; + self.metadata_native(&path, ReparsePoint::Open) + } + + fn metadata_native(&self, path: &[u16], reparse: ReparsePoint) -> io::Result { + let mut opts = OpenOptions::new(); + // the NT functions need at least c::FILE_READ_ATTRIBUTES + opts.access_mode(c::FILE_READ_ATTRIBUTES); + let create_opt = if reparse == ReparsePoint::Open { c::FILE_OPEN_REPARSE_POINT } else { 0 }; + + let name = UnicodeStrRef::from_slice(path); + let object_attributes = c::OBJECT_ATTRIBUTES { + RootDirectory: self.handle.as_raw_handle(), + ObjectName: name.as_ptr().cast_mut(), + ..c::OBJECT_ATTRIBUTES::with_length() + }; + let handle = unsafe { nt_create_file(&opts, &object_attributes, create_opt)? }; + File { handle }.file_attr() + } } impl fmt::Debug for Dir { diff --git a/library/std/src/sys/net/connection/socket/mod.rs b/library/std/src/sys/net/connection/socket/mod.rs index 4e5de0209ad24..55db60fde694d 100644 --- a/library/std/src/sys/net/connection/socket/mod.rs +++ b/library/std/src/sys/net/connection/socket/mod.rs @@ -36,8 +36,26 @@ cfg_select! { use netc as c; +// Similarly to reads (see READ_LIMIT), the `send` syscall on most platforms +// takes a `size_t` length, but returns an `ssize_t` of bytes written. So the +// actual maximum number of bytes we can send in one call is SSIZE_MAX. +// +// On Apple targets however, apparently the 64-bit libc is either buggy or +// intentionally showing odd behavior by rejecting send calls with a size +// larger than INT_MAX. So cap the send size to INT_MAX. +// +// Meanwhile on QNX, reads/writes/sends larger than INT_MAX return the wrong +// number of bytes written (eg, writing 2^31 bytes returns (2^64 - 2^31) instead +// of the correct byte count). +// +// On Windows, the relevant syscall takes an `i32` (unlike for read/write!), +// so we need to clamp to i32::MAX. const MAX_SEND_LEN: usize = - if cfg!(target_vendor = "apple") { c_int::MAX as usize } else { ::MAX as usize }; + cfg_select! { + any(target_vendor = "apple", target_os = "nto", target_os = "qnx") => c_int::MAX as usize, + target_os = "windows" => i32::MAX as usize, + _ => libc::ssize_t::MAX as usize, + }; cfg_select! { any( @@ -717,8 +735,6 @@ impl UdpSocket { self.inner.peek_from(buf) } - // `MAX_SEND_LEN` is `usize::MAX` off Apple/Windows, where the guard is a no-op. - #[allow(clippy::absurd_extreme_comparisons)] pub fn send_to(&self, buf: &[u8], dst: &SocketAddr) -> io::Result { if buf.len() > MAX_SEND_LEN { return Err(io::Error::from_raw_os_error(c::EMSGSIZE)); @@ -872,8 +888,6 @@ impl UdpSocket { self.inner.peek(buf) } - // `MAX_SEND_LEN` is `usize::MAX` off Apple/Windows, where the guard is a no-op. - #[allow(clippy::absurd_extreme_comparisons)] pub fn send(&self, buf: &[u8]) -> io::Result { if buf.len() > MAX_SEND_LEN { return Err(io::Error::from_raw_os_error(c::EMSGSIZE)); diff --git a/library/std/src/sys/net/connection/socket/tests.rs b/library/std/src/sys/net/connection/socket/tests.rs index e6f02d7a93859..cc072420b754b 100644 --- a/library/std/src/sys/net/connection/socket/tests.rs +++ b/library/std/src/sys/net/connection/socket/tests.rs @@ -18,13 +18,32 @@ fn no_lookup_host_duplicates() { ); } -// #115325: on Apple, `send` rejects a length > `c_int::MAX` with `EINVAL`, so -// the clamp must not regress to the unbounded `wrlen_t::MAX`. +// On non-Windows platforms, the maximum valid length to pass into `send` is +// not the same as the maximum of the *type* used for the length. Ensure that +// the proper maximum is used, and that we do not regress to simply clamping to +// `wrlen_t::MAX` +// +// On Apple (per 115325), `send` takes a `size_t` length, but rejects any +// length > `c_int::MAX` with `EINVAL`. +// +// On QNX, `send` with length > `c_int::MAX` returns an incorrect count of bytes +// written. +// +// On Windows, `send` takes an `i32` length and returns an `i32`. +// +// On other platforms, `send` takes a `size_t` and returns an `ssize_t`, so +// sends larger then `ssize_t::MAX` will (maybe silently) return bad lengths. #[test] fn max_send_len_within_platform_limit() { - if cfg!(target_vendor = "apple") { - assert_eq!(MAX_SEND_LEN, c_int::MAX as usize); - } else { - assert_eq!(MAX_SEND_LEN, ::MAX as usize); + cfg_select! { + any(target_vendor = "apple", target_os = "nto", target_os = "qnx") => { + assert_eq!(MAX_SEND_LEN, c_int::MAX as usize); + } + target_os = "windows" => { + assert_eq!(MAX_SEND_LEN, i32::MAX as usize); + } + _ => { + assert_eq!(MAX_SEND_LEN, libc::ssize_t::MAX as usize); + } } } diff --git a/library/std/src/sys/net/connection/socket/windows.rs b/library/std/src/sys/net/connection/socket/windows.rs index 8b98d0d31e507..e9d5336001fd8 100644 --- a/library/std/src/sys/net/connection/socket/windows.rs +++ b/library/std/src/sys/net/connection/socket/windows.rs @@ -309,7 +309,7 @@ impl Socket { ) -> io::Result<(usize, SocketAddr)> { let mut storage = unsafe { mem::zeroed::() }; let mut addrlen = size_of_val(&storage) as netc::socklen_t; - let length = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; + let length = cmp::min(buf.len(), super::MAX_SEND_LEN) as wrlen_t; // On unix when a socket is shut down all further reads return 0, so we // do the same on windows to map a shut down socket to returning EOF. diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs index 67499d2c6f17c..bb771c232f2de 100644 --- a/library/std/src/sys/pal/uefi/mod.rs +++ b/library/std/src/sys/pal/uefi/mod.rs @@ -103,8 +103,8 @@ pub fn abort_internal() -> ! { }; } - // In case SystemTable and ImageHandle cannot be reached, use `core::intrinsics::abort` - core::intrinsics::abort(); + // In case SystemTable and ImageHandle cannot be reached, use `abort_immediate` + core::intrinsics::abort_immediate(); } /// Disable access to BootServices if `EVT_SIGNAL_EXIT_BOOT_SERVICES` is signaled diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index e4a18794f7823..292c02f3d1f3e 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -267,8 +267,8 @@ pub fn cvt_nz(error: libc::c_int) -> io::Result<()> { // Current glibc's abort() function unblocks SIGABRT, raises SIGABRT, clears the // SIGABRT handler and raises it again, and then starts to get creative. // -// See the public documentation for `intrinsics::abort()` and `process::abort()` -// for further discussion. +// See the public documentation for `intrinsics::abort_immediate()` and +// `process::abort()` for further discussion. // // There is confusion about whether libc::abort() flushes stdio streams. // libc::abort() is required by ISO C 99 (7.14.1.1p5) to be async-signal-safe, diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs deleted file mode 100644 index 5604e3e6dbf42..0000000000000 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ /dev/null @@ -1,844 +0,0 @@ -#![cfg_attr(test, allow(dead_code))] -#![forbid(unsafe_op_in_unsafe_fn)] - -pub use self::imp::init; -use self::imp::{drop_handler, make_handler}; - -pub struct Handler { - data: *mut libc::c_void, -} - -impl Handler { - pub unsafe fn new() -> Handler { - make_handler(false) - } - - fn null() -> Handler { - Handler { data: crate::ptr::null_mut() } - } -} - -impl Drop for Handler { - fn drop(&mut self) { - unsafe { - drop_handler(self.data); - } - } -} - -#[cfg(all( - not(miri), - any( - target_os = "linux", - target_os = "freebsd", - target_os = "hurd", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris", - target_os = "illumos", - ), -))] -mod thread_info; - -// miri doesn't model signals nor stack overflows and this code has some -// synchronization properties that we don't want to expose to user code, -// hence we disable it on miri. -#[cfg(all( - not(miri), - any( - target_os = "linux", - target_os = "freebsd", - target_os = "hurd", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris", - target_os = "illumos", - ) -))] -mod imp { - use libc::{ - MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE, SA_ONSTACK, - SA_SIGINFO, SIG_DFL, SIGBUS, SIGSEGV, SS_DISABLE, sigaction, sigaltstack, sighandler_t, - }; - #[cfg(not(all(target_os = "linux", target_env = "gnu")))] - use libc::{mmap as mmap64, mprotect, munmap}; - #[cfg(all(target_os = "linux", target_env = "gnu"))] - use libc::{mmap64, mprotect, munmap}; - - use super::Handler; - use super::thread_info::{delete_current_info, set_current_info, with_current_info}; - use crate::ops::Range; - use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr, AtomicUsize, Ordering}; - use crate::sys::pal::unix::conf; - use crate::{io, mem, ptr}; - - /// Signal handler for the SIGSEGV and SIGBUS handlers. - /// - /// We've got guard pages (unmapped pages) at the end of every thread's - /// stack, so if a thread ends up running into the guard page it'll trigger - /// this handler. We want to detect these cases and print out a helpful error - /// saying that the stack has overflowed. All other signals, however, should - /// go back to what they were originally supposed to do. - /// - /// This handler currently exists purely to print an informative message - /// whenever a thread overflows its stack. We then abort to exit and - /// indicate a crash, but to avoid a misleading SIGSEGV that might lead - /// users to believe that unsafe code has accessed an invalid pointer; the - /// SIGSEGV encountered when overflowing the stack is expected and - /// well-defined. - /// - /// If this is not a stack overflow, the handler un-registers itself and - /// then returns (to allow the original signal to be delivered again). - /// Returning from this kind of signal handler is technically not defined - /// to work when reading the POSIX spec strictly, but in practice it turns - /// out many large systems and all implementations allow returning from a - /// signal handler to work. For a more detailed explanation see the - /// comments on #26458. - /// - /// # Safety - /// Rust doesn't call this, it *gets called* by the kernel, which we expect - /// to provide valid parameters. Apart from that, this function does not - /// have any other preconditions. - unsafe extern "C" fn signal_handler( - signum: libc::c_int, - info: *mut libc::siginfo_t, - _data: *mut libc::c_void, - ) { - // SAFETY: this pointer is provided by the system and will always point to a valid `siginfo_t`. - let fault_addr = unsafe { (*info).si_addr().addr() }; - - // `with_current_info` expects that the process aborts after it is - // called. If the signal was not caused by a memory access, this might - // not be true. We detect this by noticing that the `si_addr` field is - // zero if the signal is synthetic. - if fault_addr != 0 { - with_current_info(|thread_info| { - // If the faulting address is within the guard page, then we print a - // message saying so and abort. - if let Some(thread_info) = thread_info - && thread_info.guard_page_range.contains(&fault_addr) - { - // Hey you! Yes, you modifying the stack overflow message! - // Please make sure that all functions called here are - // actually async-signal-safe. If they're not, try retrieving - // the information beforehand and storing it in `ThreadInfo`. - // Thank you! - // - says Jonas after having had to watch his carefully - // written code get made unsound again. - let tid = thread_info.tid; - let name = thread_info.name.as_deref().unwrap_or(""); - rtprintpanic!("\nthread '{name}' ({tid}) has overflowed its stack\n"); - rtabort!("stack overflow"); - } - }) - } - - // Unregister ourselves by reverting back to the default behavior. - // SAFETY: assuming all platforms define struct sigaction as "zero-initializable" - let mut action: sigaction = unsafe { mem::zeroed() }; - action.sa_sigaction = SIG_DFL; - // SAFETY: pray this is a well-behaved POSIX implementation of fn sigaction - unsafe { sigaction(signum, &action, ptr::null_mut()) }; - - // See comment above for why this function returns. - } - - static PAGE_SIZE: Atomic = AtomicUsize::new(0); - // Store a pointer to the allocation for the main thread's altstack so that - // tools like valgrind don't complain about a leaked unreachable allocation. - // - // If the main thread exits, the process will terminate so there's no use in - // freeing resources. It also means that the altstack is still installed - // while TLS destructors are run on the main thread (c.f. #111272). - static MAIN_ALTSTACK: Atomic<*mut libc::c_void> = AtomicPtr::new(ptr::null_mut()); - static NEED_ALTSTACK: Atomic = AtomicBool::new(false); - - /// # Safety - /// Must be called only once, on the main thread, during program startup. - pub unsafe fn init() { - PAGE_SIZE.store(conf::page_size(), Ordering::Relaxed); - - // SAFETY: - // This is only called on the main thread, and since it is still early - // in the programs lifetime there is (almost) certainly enough stack - // space left to install the guard page. - let mut guard_page_range = unsafe { install_main_guard() }; - - // Even for panic=immediate-abort, installing the guard pages is important for soundness. - // That said, we do not care about giving nice stackoverflow messages via our custom - // signal handler, just exit early and let the user enjoy the segfault. - if cfg!(panic = "immediate-abort") { - return; - } - - // SAFETY: C structures are always zero-initializable. - let mut action: sigaction = unsafe { mem::zeroed() }; - for &signal in &[SIGSEGV, SIGBUS] { - // SAFETY: just fetches the current signal handler into action - unsafe { sigaction(signal, ptr::null_mut(), &mut action) }; - // We assume that overriding the signal handler is always safe, - // which might conflict with certain libraries that rely on a - // specific signal behaviour. To prevent problems, we only - // override the handler if it has not been set yet. - if action.sa_sigaction == SIG_DFL { - if !NEED_ALTSTACK.load(Ordering::Relaxed) { - // haven't set up our sigaltstack yet - NEED_ALTSTACK.store(true, Ordering::Release); - let handler = make_handler(true); - MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed); - mem::forget(handler); - - if let Some(guard_page_range) = guard_page_range.take() { - set_current_info(guard_page_range); - } - } - - action.sa_flags = SA_SIGINFO | SA_ONSTACK; - action.sa_sigaction = signal_handler - as unsafe extern "C" fn(i32, *mut libc::siginfo_t, *mut libc::c_void) - as sighandler_t; - // SAFETY: - // `&action` describes a valid `sigaction` and `signal_handler` - // is safe to use as a signal handler for `SIGSEGV` and `SIGBUS`. - unsafe { sigaction(signal, &action, ptr::null_mut()) }; - } - } - } - - fn get_stack() -> libc::stack_t { - // OpenBSD requires this flag for stack mapping - // otherwise the said mapping will fail as a no-op on most systems - // and has a different meaning on FreeBSD - #[cfg(any( - target_os = "openbsd", - target_os = "netbsd", - target_os = "linux", - target_os = "dragonfly", - ))] - let flags = MAP_PRIVATE | MAP_ANON | libc::MAP_STACK; - #[cfg(not(any( - target_os = "openbsd", - target_os = "netbsd", - target_os = "linux", - target_os = "dragonfly", - )))] - let flags = MAP_PRIVATE | MAP_ANON; - - let sigstack_size = sigstack_size(); - let page_size = PAGE_SIZE.load(Ordering::Relaxed); - - // SAFETY: this does not unmap any existing pages. - let stackp = unsafe { - mmap64(ptr::null_mut(), sigstack_size + page_size, PROT_READ | PROT_WRITE, flags, -1, 0) - }; - if stackp == MAP_FAILED { - panic!("failed to allocate an alternative stack: {}", io::Error::last_os_error()); - } - // SAFETY: this only affects the memory we just allocated. - let guard_result = unsafe { libc::mprotect(stackp, page_size, PROT_NONE) }; - if guard_result != 0 { - panic!("failed to set up alternative stack guard page: {}", io::Error::last_os_error()); - } - // SAFETY: - // The region was allocated with a larger size than `page_size`, so this - // addition is within bounds. - let stackp = unsafe { stackp.add(page_size) }; - - libc::stack_t { ss_sp: stackp, ss_flags: 0, ss_size: sigstack_size } - } - - pub fn make_handler(main_thread: bool) -> Handler { - if cfg!(panic = "immediate-abort") || !NEED_ALTSTACK.load(Ordering::Acquire) { - return Handler::null(); - } - - if !main_thread { - if let Some(guard_page_range) = current_guard() { - set_current_info(guard_page_range); - } - } - - // Load the current alternate signal stack to see if we need to install - // our own. - // - // SAFETY: C structures are always zero-initializable. - let mut stack = unsafe { mem::zeroed() }; - // SAFETY: `&mut stack` is valid for writing a `stack_t`. - unsafe { sigaltstack(ptr::null(), &mut stack) }; - - // Configure alternate signal stack, if one is not already set. - if stack.ss_flags & SS_DISABLE != 0 { - let stack = get_stack(); - // SAFETY: - // `stack_t` is a freshly allocated stack that's not used anywhere - // else. It contains a guard page, so stack overflows in signal - // handlers will not cause undefined behaviour. We must make the - // fundamental runtime assumption that it is safe to install an - // alternate signal stack if there is none currently installed. - // This might conflict with foreign libraries that use the existence - // of an alternate signal stack as indication that certain runtime - // initialisation by the library has been performed (e.g. old - // versions of `std` assumed that certain thread-locals were already - // accessed and thus initialized in the thread if the stack overflow - // signal was successfully delivered). Such assumptions in other - // libraries are fundamentally flawed, so we pay no regard to them. - unsafe { sigaltstack(&stack, ptr::null_mut()) }; - Handler { data: stack.ss_sp as *mut libc::c_void } - } else { - Handler::null() - } - } - - /// # Safety - /// Must only be called with a pointer returned by `make_handler`, and only - /// once per `Handler`. - pub unsafe fn drop_handler(data: *mut libc::c_void) { - if !data.is_null() { - let sigstack_size = sigstack_size(); - let page_size = PAGE_SIZE.load(Ordering::Relaxed); - let disabling_stack = libc::stack_t { - ss_sp: ptr::null_mut(), - ss_flags: SS_DISABLE, - // Workaround for bug in macOS implementation of sigaltstack - // UNIX2003 which returns ENOMEM when disabling a stack while - // passing ss_size smaller than MINSIGSTKSZ. According to POSIX - // both ss_sp and ss_size should be ignored in this case. - ss_size: sigstack_size, - }; - // SAFETY: - // We assume that disabling the alternate signal stack is always - // sound, even if the current alternate signal stack is not the one - // we installed in `make_handler`. Any stack overflows from this - // point on will abort the program when the kernel tries to write - // the signal information to the guard page. - // - // FIXME: detect if the stack has changed, and only uninstall if it hasn't. - unsafe { sigaltstack(&disabling_stack, ptr::null_mut()) }; - // The stack returned by `get_stack` is part of a mapping that - // started one page earlier, so walk back a page and unmap from - // there. - // - // SAFETY: - // This allocation was created by us in `get_stack` and, as the - // alternate signal stack is now disabled, is no longer in use. - unsafe { munmap(data.sub(page_size), sigstack_size + page_size) }; - } - - delete_current_info(); - } - - /// Modern kernels on modern hardware can have dynamic signal stack sizes. - #[cfg(all(any(target_os = "linux", target_os = "android"), not(target_env = "uclibc")))] - fn sigstack_size() -> usize { - // SAFETY: `getauxval` is always safe to call. - let dynamic_sigstksz = unsafe { libc::getauxval(libc::AT_MINSIGSTKSZ) }; - // If getauxval couldn't find the entry, it returns 0, - // so take the higher of the "constant" and auxval. - // This transparently supports older kernels which don't provide AT_MINSIGSTKSZ - libc::SIGSTKSZ.max(dynamic_sigstksz as _) - } - - /// Not all OS support hardware where this is needed. - #[cfg(not(all(any(target_os = "linux", target_os = "android"), not(target_env = "uclibc"))))] - fn sigstack_size() -> usize { - libc::SIGSTKSZ - } - - #[cfg(any(target_os = "solaris", target_os = "illumos"))] - fn get_stack_start() -> Option<*mut libc::c_void> { - // SAFETY: C types are always zero-initializable. - let mut current_stack: libc::stack_t = unsafe { crate::mem::zeroed() }; - // SAFETY: - // The pointer is valid for writing a `stack_t`. - assert_eq!(unsafe { libc::stack_getbounds(&mut current_stack) }, 0); - Some(current_stack.ss_sp) - } - - #[cfg(target_os = "macos")] - fn get_stack_start() -> Option<*mut libc::c_void> { - // SAFETY: always safe to call. - let th = unsafe { libc::pthread_self() }; - // SAFETY: `th` is a valid `pthread_t`. - unsafe { - let stackptr = libc::pthread_get_stackaddr_np(th); - let stacksize = libc::pthread_get_stacksize_np(th); - Some(stackptr.map_addr(|addr| addr - stacksize)) - } - } - - #[cfg(target_os = "openbsd")] - fn get_stack_start() -> Option<*mut libc::c_void> { - // SAFETY: C types are always zero-initializable. - let mut current_stack: libc::stack_t = unsafe { crate::mem::zeroed() }; - // SAFETY: - // * calling `pthread_self` is always valid and returns a valid `pthread_t`. - // * `&mut current_stack` is coerced to a pointer that is valid for writing - // a `stack_t`. - assert_eq!( - unsafe { libc::pthread_stackseg_np(libc::pthread_self(), &mut current_stack) }, - 0 - ); - - let stack_ptr = current_stack.ss_sp; - // SAFETY: this is always safe to call. - let stackaddr = if unsafe { libc::pthread_main_np() } == 1 { - // main thread - stack_ptr.addr() - current_stack.ss_size + PAGE_SIZE.load(Ordering::Relaxed) - } else { - // new thread - stack_ptr.addr() - current_stack.ss_size - }; - Some(stack_ptr.with_addr(stackaddr)) - } - - #[cfg(any( - target_os = "android", - target_os = "freebsd", - target_os = "netbsd", - target_os = "hurd", - target_os = "linux", - target_os = "l4re" - ))] - fn get_stack_start() -> Option<*mut libc::c_void> { - use crate::pin::pin; - use crate::sys::helpers::COpaque; - - let mut ret = None; - let mut attr: COpaque = COpaque::uninit(); - if !cfg!(target_os = "freebsd") { - attr = COpaque::zeroed(); - } - let attr = pin!(attr); - // FIXME(pin-ergonomics): remove the next line. - let attr = attr.into_ref(); - - // SAFETY: - // The attributes object has not been initialized yet and will not be - // moved until destroyed. - #[cfg(target_os = "freebsd")] - assert_eq!(unsafe { libc::pthread_attr_init(attr.get()) }, 0); - // SAFETY: - // * calling `pthread_self` is always valid and returns a valid `pthread_t` - // * `attr` is an initialized attribute object that can be written to. - #[cfg(target_os = "freebsd")] - let e = unsafe { libc::pthread_attr_get_np(libc::pthread_self(), attr.get()) }; - // SAFETY: - // * calling `pthread_self` is always valid and returns a valid `pthread_t` - // * `attr` can be written to, and will be initialized by this call. - #[cfg(not(target_os = "freebsd"))] - let e = unsafe { libc::pthread_getattr_np(libc::pthread_self(), attr.get()) }; - if e == 0 { - let mut stackaddr = crate::ptr::null_mut(); - let mut stacksize = 0; - // SAFETY: - // `attr` is an initialized attribute object and both the pointers - // are valid for writing. - assert_eq!( - unsafe { libc::pthread_attr_getstack(attr.get(), &mut stackaddr, &mut stacksize) }, - 0 - ); - ret = Some(stackaddr); - } - if e == 0 || cfg!(target_os = "freebsd") { - // SAFETY: - // `attr` was initialized either by `pthread_attr_init` (FreeBSD) or - // by `pthread_attr_get_np`, and is not used after this point. - assert_eq!(unsafe { libc::pthread_attr_destroy(attr.get()) }, 0); - } - ret - } - - fn stack_start_aligned(page_size: usize) -> Option<*mut libc::c_void> { - let stackptr = get_stack_start()?; - let stackaddr = stackptr.addr(); - - // Ensure stackaddr is page aligned! A parent process might - // have reset RLIMIT_STACK to be non-page aligned. The - // pthread_attr_getstack() reports the usable stack area - // stackaddr < stackaddr + stacksize, so if stackaddr is not - // page-aligned, calculate the fix such that stackaddr < - // new_page_aligned_stackaddr < stackaddr + stacksize - let remainder = stackaddr % page_size; - Some(if remainder == 0 { - stackptr - } else { - stackptr.with_addr(stackaddr + page_size - remainder) - }) - } - - /// # Safety - /// This function must only be called from the main thread, and there must - /// be sufficient stack space remaining to place a stack guard. - unsafe fn install_main_guard() -> Option> { - let page_size = PAGE_SIZE.load(Ordering::Relaxed); - - // this way someone on any unix-y OS can check that all these compile - if cfg!(all(target_os = "linux", not(target_env = "musl"))) { - install_main_guard_linux(page_size) - } else if cfg!(all(target_os = "linux", target_env = "musl")) { - install_main_guard_linux_musl(page_size) - } else if cfg!(target_os = "freebsd") { - #[cfg(not(target_os = "freebsd"))] - return None; - // The FreeBSD code cannot be checked on non-BSDs. - #[cfg(target_os = "freebsd")] - install_main_guard_freebsd(page_size) - } else if cfg!(any(target_os = "netbsd", target_os = "openbsd")) { - install_main_guard_bsds(page_size) - } else { - // SAFETY: guaranteed by caller. - unsafe { install_main_guard_default(page_size) } - } - } - - fn install_main_guard_linux(page_size: usize) -> Option> { - // See the corresponding conditional in init(). - // Avoid stack_start_aligned, which makes slow syscalls to read /proc/self/maps - if cfg!(panic = "immediate-abort") { - return None; - } - // Linux doesn't allocate the whole stack right away, and - // the kernel has its own stack-guard mechanism to fault - // when growing too close to an existing mapping. If we map - // our own guard, then the kernel starts enforcing a rather - // large gap above that, rendering much of the possible - // stack space useless. See #43052. - // - // Instead, we'll just note where we expect rlimit to start - // faulting, so our handler can report "stack overflow", and - // trust that the kernel's own stack guard will work. - let stackptr = stack_start_aligned(page_size)?; - let stackaddr = stackptr.addr(); - Some(stackaddr - page_size..stackaddr) - } - - fn install_main_guard_linux_musl(_page_size: usize) -> Option> { - // For the main thread, the musl's pthread_attr_getstack - // returns the current stack size, rather than maximum size - // it can eventually grow to. It cannot be used to determine - // the position of kernel's stack guard. - None - } - - #[cfg(target_os = "freebsd")] - fn install_main_guard_freebsd(page_size: usize) -> Option> { - // See the corresponding conditional in install_main_guard_linux(). - if cfg!(panic = "immediate-abort") { - return None; - } - // FreeBSD's stack autogrows, and optionally includes a guard page - // at the bottom. If we try to remap the bottom of the stack - // ourselves, FreeBSD's guard page moves upwards. So we'll just use - // the builtin guard page. - let stackptr = stack_start_aligned(page_size)?; - let guardaddr = stackptr.addr(); - // Technically the number of guard pages is tunable and controlled - // by the security.bsd.stack_guard_page sysctl. - // By default it is 1, checking once is enough since it is - // a boot time config value. - // FIXME(joboet): this function is only called once, remove the caching. - static PAGES: crate::sync::OnceLock = crate::sync::OnceLock::new(); - - let pages = PAGES.get_or_init(|| { - let mut guard: usize = 0; - let mut size = size_of_val(&guard); - let oid = c"security.bsd.stack_guard_page"; - - let r = unsafe { - libc::sysctlbyname( - oid.as_ptr(), - (&raw mut guard).cast(), - &raw mut size, - ptr::null_mut(), - 0, - ) - }; - if r == 0 { guard } else { 1 } - }); - Some(guardaddr..guardaddr + pages * page_size) - } - - fn install_main_guard_bsds(page_size: usize) -> Option> { - // See the corresponding conditional in install_main_guard_linux(). - if cfg!(panic = "immediate-abort") { - return None; - } - // OpenBSD stack already includes a guard page, and stack is - // immutable. - // NetBSD stack includes the guard page. - // - // We'll just note where we expect rlimit to start - // faulting, so our handler can report "stack overflow", and - // trust that the kernel's own stack guard will work. - let stackptr = stack_start_aligned(page_size)?; - let stackaddr = stackptr.addr(); - Some(stackaddr - page_size..stackaddr) - } - - /// # Safety - /// This function must only be called from the main thread, and there must - /// be sufficient stack space remaining to place a stack guard. - unsafe fn install_main_guard_default(page_size: usize) -> Option> { - // Reallocate the last page of the stack. - // This ensures SIGBUS will be raised on - // stack overflow. - // Systems which enforce strict PAX MPROTECT do not allow - // to mprotect() a mapping with less restrictive permissions - // than the initial mmap() used, so we mmap() here with - // read/write permissions and only then mprotect() it to - // no permissions at all. See issue #50313. - let stackptr = stack_start_aligned(page_size)?; - // SAFETY: - // The memory region from `stackptr..stackptr + page_size` belongs to - // the current thread's stack, and the caller has asserted that there - // is sufficient stack space, which means that this will not overwrite - // any existing allocations. - let result = unsafe { - mmap64( - stackptr, - page_size, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANON | MAP_FIXED, - -1, - 0, - ) - }; - if result != stackptr || result == MAP_FAILED { - panic!("failed to allocate a guard page: {}", io::Error::last_os_error()); - } - - // SAFETY: - // Since this function is only called on the main thread, the stack will - // not be reused until program exit, so the runtime will never observe - // that part of the stack has been made unusable in this way. - let result = unsafe { mprotect(stackptr, page_size, PROT_NONE) }; - if result != 0 { - panic!("failed to protect the guard page: {}", io::Error::last_os_error()); - } - - let guardaddr = stackptr.addr(); - - Some(guardaddr..guardaddr + page_size) - } - - #[cfg(any( - target_os = "macos", - target_os = "openbsd", - target_os = "solaris", - target_os = "illumos", - ))] - fn current_guard() -> Option> { - let stackptr = get_stack_start()?; - let stackaddr = stackptr.addr(); - Some(stackaddr - PAGE_SIZE.load(Ordering::Relaxed)..stackaddr) - } - - #[cfg(any( - target_os = "android", - target_os = "freebsd", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "l4re" - ))] - fn current_guard() -> Option> { - use crate::pin::pin; - use crate::sys::helpers::COpaque; - - let mut ret = None; - - let mut attr: COpaque = COpaque::uninit(); - if !cfg!(target_os = "freebsd") { - attr = COpaque::zeroed(); - } - let attr = pin!(attr); - // FIXME(pin-ergonomics): remove the next line. - let attr = attr.into_ref(); - - // SAFETY: - // The attributes object has not been initialized yet and will not be - // moved until destroyed. - #[cfg(target_os = "freebsd")] - assert_eq!(unsafe { libc::pthread_attr_init(attr.get()) }, 0); - // SAFETY: - // * calling `pthread_self` is always valid and returns a valid `pthread_t` - // * `attr` is an initialized attribute object that can be written to. - #[cfg(target_os = "freebsd")] - let e = unsafe { libc::pthread_attr_get_np(libc::pthread_self(), attr.get()) }; - // SAFETY: - // * calling `pthread_self` is always valid and returns a valid `pthread_t` - // * `attr` can be written to, and will be initialized by this call. - #[cfg(not(target_os = "freebsd"))] - let e = unsafe { libc::pthread_getattr_np(libc::pthread_self(), attr.get()) }; - if e == 0 { - let mut guardsize = 0; - // SAFETY: - // `attr` is an initialized attribute object and the pointer is valid - // for writing. - assert_eq!(unsafe { libc::pthread_attr_getguardsize(attr.get(), &mut guardsize) }, 0); - if guardsize == 0 { - if cfg!(all(target_os = "linux", target_env = "musl")) { - // musl versions before 1.1.19 always reported guard - // size obtained from pthread_attr_get_np as zero. - // Use page size as a fallback. - guardsize = PAGE_SIZE.load(Ordering::Relaxed); - } else { - panic!("there is no guard page"); - } - } - let mut stackptr = crate::ptr::null_mut::(); - let mut size = 0; - // SAFETY: - // `attr` is an initialized attribute object and both the pointers - // are valid for writing. - assert_eq!( - unsafe { libc::pthread_attr_getstack(attr.get(), &mut stackptr, &mut size) }, - 0 - ); - - let stackaddr = stackptr.addr(); - ret = if cfg!(any(target_os = "freebsd", target_os = "netbsd", target_os = "hurd")) { - Some(stackaddr - guardsize..stackaddr) - } else if cfg!(all(target_os = "linux", target_env = "musl")) { - Some(stackaddr - guardsize..stackaddr) - } else if cfg!(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc"))) - { - // glibc used to include the guard area within the stack, as noted in the BUGS - // section of `man pthread_attr_getguardsize`. This has been corrected starting - // with glibc 2.27, and in some distro backports, so the guard is now placed at the - // end (below) the stack. There's no easy way for us to know which we have at - // runtime, so we'll just match any fault in the range right above or below the - // stack base to call that fault a stack overflow. - Some(stackaddr - guardsize..stackaddr + guardsize) - } else { - Some(stackaddr..stackaddr + guardsize) - }; - } - if e == 0 || cfg!(target_os = "freebsd") { - // SAFETY: - // `attr` was initialized either by `pthread_attr_init` (FreeBSD) or - // by `pthread_attr_get_np`, and is not used after this point. - assert_eq!(unsafe { libc::pthread_attr_destroy(attr.get()) }, 0); - } - ret - } -} - -// This is intentionally not enabled on iOS/tvOS/watchOS/visionOS, as it uses -// several symbols that might lead to rejections from the App Store, namely -// `sigaction`, `sigaltstack`, `sysctlbyname`, `mmap`, `munmap` and `mprotect`. -// -// This might be overly cautious, though it is also what Swift does (and they -// usually have fewer qualms about forwards compatibility, since the runtime -// is shipped with the OS): -// -#[cfg(any( - miri, - not(any( - target_os = "linux", - target_os = "freebsd", - target_os = "hurd", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris", - target_os = "illumos", - target_os = "cygwin", - )) -))] -mod imp { - pub unsafe fn init() {} - - pub fn make_handler(_main_thread: bool) -> super::Handler { - super::Handler::null() - } - - pub unsafe fn drop_handler(_data: *mut libc::c_void) {} -} - -#[cfg(target_os = "cygwin")] -mod imp { - mod c { - pub type PVECTORED_EXCEPTION_HANDLER = - Option i32>; - pub type NTSTATUS = i32; - pub type BOOL = i32; - - unsafe extern "system" { - pub fn AddVectoredExceptionHandler( - first: u32, - handler: PVECTORED_EXCEPTION_HANDLER, - ) -> *mut core::ffi::c_void; - pub fn SetThreadStackGuarantee(stacksizeinbytes: *mut u32) -> BOOL; - } - - pub const EXCEPTION_STACK_OVERFLOW: NTSTATUS = 0xC00000FD_u32 as _; - pub const EXCEPTION_CONTINUE_SEARCH: i32 = 1i32; - - #[repr(C)] - #[derive(Clone, Copy)] - pub struct EXCEPTION_POINTERS { - pub ExceptionRecord: *mut EXCEPTION_RECORD, - // We don't need this field here - // pub Context: *mut CONTEXT, - } - #[repr(C)] - #[derive(Clone, Copy)] - pub struct EXCEPTION_RECORD { - pub ExceptionCode: NTSTATUS, - pub ExceptionFlags: u32, - pub ExceptionRecord: *mut EXCEPTION_RECORD, - pub ExceptionAddress: *mut core::ffi::c_void, - pub NumberParameters: u32, - pub ExceptionInformation: [usize; 15], - } - } - - /// Reserve stack space for use in stack overflow exceptions. - fn reserve_stack() { - let result = unsafe { c::SetThreadStackGuarantee(&mut 0x5000) }; - // Reserving stack space is not critical so we allow it to fail in the released build of libstd. - // We still use debug assert here so that CI will test that we haven't made a mistake calling the function. - debug_assert_ne!(result, 0, "failed to reserve stack space for exception handling"); - } - - unsafe extern "system" fn vectored_handler(ExceptionInfo: *mut c::EXCEPTION_POINTERS) -> i32 { - // SAFETY: It's up to the caller (which in this case is the OS) to ensure that `ExceptionInfo` is valid. - unsafe { - let rec = &(*(*ExceptionInfo).ExceptionRecord); - let code = rec.ExceptionCode; - - if code == c::EXCEPTION_STACK_OVERFLOW { - crate::thread::with_current_name(|name| { - let name = name.unwrap_or(""); - let tid = crate::thread::current_os_id(); - rtprintpanic!("\nthread '{name}' ({tid}) has overflowed its stack\n"); - }); - } - c::EXCEPTION_CONTINUE_SEARCH - } - } - - pub unsafe fn init() { - // SAFETY: `vectored_handler` has the correct ABI and is safe to call during exception handling. - unsafe { - let result = c::AddVectoredExceptionHandler(0, Some(vectored_handler)); - // Similar to the above, adding the stack overflow handler is allowed to fail - // but a debug assert is used so CI will still test that it normally works. - debug_assert!(!result.is_null(), "failed to install exception handler"); - } - // Set the thread stack guarantee for the main thread. - reserve_stack(); - } - - pub fn make_handler(main_thread: bool) -> super::Handler { - if !main_thread { - reserve_stack(); - } - super::Handler::null() - } - - pub unsafe fn drop_handler(_data: *mut libc::c_void) {} -} diff --git a/library/std/src/sys/pal/unix/stack_overflow/handler_cygwin.rs b/library/std/src/sys/pal/unix/stack_overflow/handler_cygwin.rs new file mode 100644 index 0000000000000..3289efe05b068 --- /dev/null +++ b/library/std/src/sys/pal/unix/stack_overflow/handler_cygwin.rs @@ -0,0 +1,81 @@ +mod c { + pub type PVECTORED_EXCEPTION_HANDLER = + Option i32>; + pub type NTSTATUS = i32; + pub type BOOL = i32; + + unsafe extern "system" { + pub fn AddVectoredExceptionHandler( + first: u32, + handler: PVECTORED_EXCEPTION_HANDLER, + ) -> *mut core::ffi::c_void; + pub fn SetThreadStackGuarantee(stacksizeinbytes: *mut u32) -> BOOL; + } + + pub const EXCEPTION_STACK_OVERFLOW: NTSTATUS = 0xC00000FD_u32 as _; + pub const EXCEPTION_CONTINUE_SEARCH: i32 = 1i32; + + #[repr(C)] + #[derive(Clone, Copy)] + pub struct EXCEPTION_POINTERS { + pub ExceptionRecord: *mut EXCEPTION_RECORD, + // We don't need this field here + // pub Context: *mut CONTEXT, + } + #[repr(C)] + #[derive(Clone, Copy)] + pub struct EXCEPTION_RECORD { + pub ExceptionCode: NTSTATUS, + pub ExceptionFlags: u32, + pub ExceptionRecord: *mut EXCEPTION_RECORD, + pub ExceptionAddress: *mut core::ffi::c_void, + pub NumberParameters: u32, + pub ExceptionInformation: [usize; 15], + } +} + +/// Reserve stack space for use in stack overflow exceptions. +fn reserve_stack() { + let result = unsafe { c::SetThreadStackGuarantee(&mut 0x5000) }; + // Reserving stack space is not critical so we allow it to fail in the released build of libstd. + // We still use debug assert here so that CI will test that we haven't made a mistake calling the function. + debug_assert_ne!(result, 0, "failed to reserve stack space for exception handling"); +} + +unsafe extern "system" fn vectored_handler(ExceptionInfo: *mut c::EXCEPTION_POINTERS) -> i32 { + // SAFETY: It's up to the caller (which in this case is the OS) to ensure that `ExceptionInfo` is valid. + unsafe { + let rec = &(*(*ExceptionInfo).ExceptionRecord); + let code = rec.ExceptionCode; + + if code == c::EXCEPTION_STACK_OVERFLOW { + crate::thread::with_current_name(|name| { + let name = name.unwrap_or(""); + let tid = crate::thread::current_os_id(); + rtprintpanic!("\nthread '{name}' ({tid}) has overflowed its stack\n"); + }); + } + c::EXCEPTION_CONTINUE_SEARCH + } +} + +pub unsafe fn init() { + // SAFETY: `vectored_handler` has the correct ABI and is safe to call during exception handling. + unsafe { + let result = c::AddVectoredExceptionHandler(0, Some(vectored_handler)); + // Similar to the above, adding the stack overflow handler is allowed to fail + // but a debug assert is used so CI will still test that it normally works. + debug_assert!(!result.is_null(), "failed to install exception handler"); + } + // Set the thread stack guarantee for the main thread. + reserve_stack(); +} + +pub fn make_handler(main_thread: bool) -> super::Handler { + if !main_thread { + reserve_stack(); + } + super::Handler::null() +} + +pub unsafe fn drop_handler(_data: *mut libc::c_void) {} diff --git a/library/std/src/sys/pal/unix/stack_overflow/handler_none.rs b/library/std/src/sys/pal/unix/stack_overflow/handler_none.rs new file mode 100644 index 0000000000000..a0e2dfa941867 --- /dev/null +++ b/library/std/src/sys/pal/unix/stack_overflow/handler_none.rs @@ -0,0 +1,7 @@ +pub unsafe fn init() {} + +pub fn make_handler(_main_thread: bool) -> super::Handler { + super::Handler::null() +} + +pub unsafe fn drop_handler(_data: *mut libc::c_void) {} diff --git a/library/std/src/sys/pal/unix/stack_overflow/handler_signal.rs b/library/std/src/sys/pal/unix/stack_overflow/handler_signal.rs new file mode 100644 index 0000000000000..54e94e0f22578 --- /dev/null +++ b/library/std/src/sys/pal/unix/stack_overflow/handler_signal.rs @@ -0,0 +1,659 @@ +use libc::{ + MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE, SA_ONSTACK, + SA_SIGINFO, SIG_DFL, SIGBUS, SIGSEGV, SS_DISABLE, sigaction, sigaltstack, sighandler_t, +}; +#[cfg(not(all(target_os = "linux", target_env = "gnu")))] +use libc::{mmap as mmap64, mprotect, munmap}; +#[cfg(all(target_os = "linux", target_env = "gnu"))] +use libc::{mmap64, mprotect, munmap}; + +use super::Handler; +use super::thread_info::{delete_current_info, set_current_info, with_current_info}; +use crate::ops::Range; +use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr, AtomicUsize, Ordering}; +use crate::sys::pal::unix::conf; +use crate::{io, mem, ptr}; + +/// Signal handler for the SIGSEGV and SIGBUS handlers. +/// +/// We've got guard pages (unmapped pages) at the end of every thread's +/// stack, so if a thread ends up running into the guard page it'll trigger +/// this handler. We want to detect these cases and print out a helpful error +/// saying that the stack has overflowed. All other signals, however, should +/// go back to what they were originally supposed to do. +/// +/// This handler currently exists purely to print an informative message +/// whenever a thread overflows its stack. We then abort to exit and +/// indicate a crash, but to avoid a misleading SIGSEGV that might lead +/// users to believe that unsafe code has accessed an invalid pointer; the +/// SIGSEGV encountered when overflowing the stack is expected and +/// well-defined. +/// +/// If this is not a stack overflow, the handler un-registers itself and +/// then returns (to allow the original signal to be delivered again). +/// Returning from this kind of signal handler is technically not defined +/// to work when reading the POSIX spec strictly, but in practice it turns +/// out many large systems and all implementations allow returning from a +/// signal handler to work. For a more detailed explanation see the +/// comments on #26458. +/// +/// # Safety +/// Rust doesn't call this, it *gets called* by the kernel, which we expect +/// to provide valid parameters. Apart from that, this function does not +/// have any other preconditions. +unsafe extern "C" fn signal_handler( + signum: libc::c_int, + info: *mut libc::siginfo_t, + _data: *mut libc::c_void, +) { + // SAFETY: this pointer is provided by the system and will always point to a valid `siginfo_t`. + let fault_addr = unsafe { (*info).si_addr().addr() }; + + // `with_current_info` expects that the process aborts after it is + // called. If the signal was not caused by a memory access, this might + // not be true. We detect this by noticing that the `si_addr` field is + // zero if the signal is synthetic. + if fault_addr != 0 { + with_current_info(|thread_info| { + // If the faulting address is within the guard page, then we print a + // message saying so and abort. + if let Some(thread_info) = thread_info + && thread_info.guard_page_range.contains(&fault_addr) + { + // Hey you! Yes, you modifying the stack overflow message! + // Please make sure that all functions called here are + // actually async-signal-safe. If they're not, try retrieving + // the information beforehand and storing it in `ThreadInfo`. + // Thank you! + // - says Jonas after having had to watch his carefully + // written code get made unsound again. + let tid = thread_info.tid; + let name = thread_info.name.as_deref().unwrap_or(""); + rtprintpanic!("\nthread '{name}' ({tid}) has overflowed its stack\n"); + rtabort!("stack overflow"); + } + }) + } + + // Unregister ourselves by reverting back to the default behavior. + // SAFETY: assuming all platforms define struct sigaction as "zero-initializable" + let mut action: sigaction = unsafe { mem::zeroed() }; + action.sa_sigaction = SIG_DFL; + // SAFETY: pray this is a well-behaved POSIX implementation of fn sigaction + unsafe { sigaction(signum, &action, ptr::null_mut()) }; + + // See comment above for why this function returns. +} + +static PAGE_SIZE: Atomic = AtomicUsize::new(0); +// Store a pointer to the allocation for the main thread's altstack so that +// tools like valgrind don't complain about a leaked unreachable allocation. +// +// If the main thread exits, the process will terminate so there's no use in +// freeing resources. It also means that the altstack is still installed +// while TLS destructors are run on the main thread (c.f. #111272). +static MAIN_ALTSTACK: Atomic<*mut libc::c_void> = AtomicPtr::new(ptr::null_mut()); +static NEED_ALTSTACK: Atomic = AtomicBool::new(false); + +/// # Safety +/// Must be called only once, on the main thread, during program startup. +pub unsafe fn init() { + PAGE_SIZE.store(conf::page_size(), Ordering::Relaxed); + + // SAFETY: + // This is only called on the main thread, and since it is still early + // in the programs lifetime there is (almost) certainly enough stack + // space left to install the guard page. + let mut guard_page_range = unsafe { install_main_guard() }; + + // Even for panic=immediate-abort, installing the guard pages is important for soundness. + // That said, we do not care about giving nice stackoverflow messages via our custom + // signal handler, just exit early and let the user enjoy the segfault. + if cfg!(panic = "immediate-abort") { + return; + } + + // SAFETY: C structures are always zero-initializable. + let mut action: sigaction = unsafe { mem::zeroed() }; + for &signal in &[SIGSEGV, SIGBUS] { + // SAFETY: just fetches the current signal handler into action + unsafe { sigaction(signal, ptr::null_mut(), &mut action) }; + // We assume that overriding the signal handler is always safe, + // which might conflict with certain libraries that rely on a + // specific signal behaviour. To prevent problems, we only + // override the handler if it has not been set yet. + if action.sa_sigaction == SIG_DFL { + if !NEED_ALTSTACK.load(Ordering::Relaxed) { + // haven't set up our sigaltstack yet + NEED_ALTSTACK.store(true, Ordering::Release); + let handler = make_handler(true); + MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed); + mem::forget(handler); + + if let Some(guard_page_range) = guard_page_range.take() { + set_current_info(guard_page_range); + } + } + + action.sa_flags = SA_SIGINFO | SA_ONSTACK; + action.sa_sigaction = signal_handler + as unsafe extern "C" fn(i32, *mut libc::siginfo_t, *mut libc::c_void) + as sighandler_t; + // SAFETY: + // `&action` describes a valid `sigaction` and `signal_handler` + // is safe to use as a signal handler for `SIGSEGV` and `SIGBUS`. + unsafe { sigaction(signal, &action, ptr::null_mut()) }; + } + } +} + +fn get_stack() -> libc::stack_t { + // OpenBSD requires this flag for stack mapping + // otherwise the said mapping will fail as a no-op on most systems + // and has a different meaning on FreeBSD + #[cfg(any( + target_os = "openbsd", + target_os = "netbsd", + target_os = "linux", + target_os = "dragonfly", + ))] + let flags = MAP_PRIVATE | MAP_ANON | libc::MAP_STACK; + #[cfg(not(any( + target_os = "openbsd", + target_os = "netbsd", + target_os = "linux", + target_os = "dragonfly", + )))] + let flags = MAP_PRIVATE | MAP_ANON; + + let sigstack_size = sigstack_size(); + let page_size = PAGE_SIZE.load(Ordering::Relaxed); + + // SAFETY: this does not unmap any existing pages. + let stackp = unsafe { + mmap64(ptr::null_mut(), sigstack_size + page_size, PROT_READ | PROT_WRITE, flags, -1, 0) + }; + if stackp == MAP_FAILED { + panic!("failed to allocate an alternative stack: {}", io::Error::last_os_error()); + } + // SAFETY: this only affects the memory we just allocated. + let guard_result = unsafe { libc::mprotect(stackp, page_size, PROT_NONE) }; + if guard_result != 0 { + panic!("failed to set up alternative stack guard page: {}", io::Error::last_os_error()); + } + // SAFETY: + // The region was allocated with a larger size than `page_size`, so this + // addition is within bounds. + let stackp = unsafe { stackp.add(page_size) }; + + libc::stack_t { ss_sp: stackp, ss_flags: 0, ss_size: sigstack_size } +} + +pub fn make_handler(main_thread: bool) -> Handler { + if cfg!(panic = "immediate-abort") || !NEED_ALTSTACK.load(Ordering::Acquire) { + return Handler::null(); + } + + if !main_thread { + if let Some(guard_page_range) = current_guard() { + set_current_info(guard_page_range); + } + } + + // Load the current alternate signal stack to see if we need to install + // our own. + // + // SAFETY: C structures are always zero-initializable. + let mut stack = unsafe { mem::zeroed() }; + // SAFETY: `&mut stack` is valid for writing a `stack_t`. + unsafe { sigaltstack(ptr::null(), &mut stack) }; + + // Configure alternate signal stack, if one is not already set. + if stack.ss_flags & SS_DISABLE != 0 { + let stack = get_stack(); + // SAFETY: + // `stack_t` is a freshly allocated stack that's not used anywhere + // else. It contains a guard page, so stack overflows in signal + // handlers will not cause undefined behaviour. We must make the + // fundamental runtime assumption that it is safe to install an + // alternate signal stack if there is none currently installed. + // This might conflict with foreign libraries that use the existence + // of an alternate signal stack as indication that certain runtime + // initialisation by the library has been performed (e.g. old + // versions of `std` assumed that certain thread-locals were already + // accessed and thus initialized in the thread if the stack overflow + // signal was successfully delivered). Such assumptions in other + // libraries are fundamentally flawed, so we pay no regard to them. + unsafe { sigaltstack(&stack, ptr::null_mut()) }; + Handler { data: stack.ss_sp as *mut libc::c_void } + } else { + Handler::null() + } +} + +/// # Safety +/// Must only be called with a pointer returned by `make_handler`, and only +/// once per `Handler`. +pub unsafe fn drop_handler(data: *mut libc::c_void) { + if !data.is_null() { + let sigstack_size = sigstack_size(); + let page_size = PAGE_SIZE.load(Ordering::Relaxed); + let disabling_stack = libc::stack_t { + ss_sp: ptr::null_mut(), + ss_flags: SS_DISABLE, + // Workaround for bug in macOS implementation of sigaltstack + // UNIX2003 which returns ENOMEM when disabling a stack while + // passing ss_size smaller than MINSIGSTKSZ. According to POSIX + // both ss_sp and ss_size should be ignored in this case. + ss_size: sigstack_size, + }; + // SAFETY: + // We assume that disabling the alternate signal stack is always + // sound, even if the current alternate signal stack is not the one + // we installed in `make_handler`. Any stack overflows from this + // point on will abort the program when the kernel tries to write + // the signal information to the guard page. + // + // FIXME: detect if the stack has changed, and only uninstall if it hasn't. + unsafe { sigaltstack(&disabling_stack, ptr::null_mut()) }; + // The stack returned by `get_stack` is part of a mapping that + // started one page earlier, so walk back a page and unmap from + // there. + // + // SAFETY: + // This allocation was created by us in `get_stack` and, as the + // alternate signal stack is now disabled, is no longer in use. + unsafe { munmap(data.sub(page_size), sigstack_size + page_size) }; + } + + delete_current_info(); +} + +/// Modern kernels on modern hardware can have dynamic signal stack sizes. +#[cfg(all(any(target_os = "linux", target_os = "android"), not(target_env = "uclibc")))] +fn sigstack_size() -> usize { + // SAFETY: `getauxval` is always safe to call. + let dynamic_sigstksz = unsafe { libc::getauxval(libc::AT_MINSIGSTKSZ) }; + // If getauxval couldn't find the entry, it returns 0, + // so take the higher of the "constant" and auxval. + // This transparently supports older kernels which don't provide AT_MINSIGSTKSZ + libc::SIGSTKSZ.max(dynamic_sigstksz as _) +} + +/// Not all OS support hardware where this is needed. +#[cfg(not(all(any(target_os = "linux", target_os = "android"), not(target_env = "uclibc"))))] +fn sigstack_size() -> usize { + libc::SIGSTKSZ +} + +#[cfg(any(target_os = "solaris", target_os = "illumos"))] +fn get_stack_start() -> Option<*mut libc::c_void> { + // SAFETY: C types are always zero-initializable. + let mut current_stack: libc::stack_t = unsafe { crate::mem::zeroed() }; + // SAFETY: + // The pointer is valid for writing a `stack_t`. + assert_eq!(unsafe { libc::stack_getbounds(&mut current_stack) }, 0); + Some(current_stack.ss_sp) +} + +#[cfg(target_os = "macos")] +fn get_stack_start() -> Option<*mut libc::c_void> { + // SAFETY: always safe to call. + let th = unsafe { libc::pthread_self() }; + // SAFETY: `th` is a valid `pthread_t`. + unsafe { + let stackptr = libc::pthread_get_stackaddr_np(th); + let stacksize = libc::pthread_get_stacksize_np(th); + Some(stackptr.map_addr(|addr| addr - stacksize)) + } +} + +#[cfg(target_os = "openbsd")] +fn get_stack_start() -> Option<*mut libc::c_void> { + // SAFETY: C types are always zero-initializable. + let mut current_stack: libc::stack_t = unsafe { crate::mem::zeroed() }; + // SAFETY: + // * calling `pthread_self` is always valid and returns a valid `pthread_t`. + // * `&mut current_stack` is coerced to a pointer that is valid for writing + // a `stack_t`. + assert_eq!(unsafe { libc::pthread_stackseg_np(libc::pthread_self(), &mut current_stack) }, 0); + + let stack_ptr = current_stack.ss_sp; + // SAFETY: this is always safe to call. + let stackaddr = if unsafe { libc::pthread_main_np() } == 1 { + // main thread + stack_ptr.addr() - current_stack.ss_size + PAGE_SIZE.load(Ordering::Relaxed) + } else { + // new thread + stack_ptr.addr() - current_stack.ss_size + }; + Some(stack_ptr.with_addr(stackaddr)) +} + +#[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", + target_os = "hurd", + target_os = "linux", + target_os = "l4re" +))] +fn get_stack_start() -> Option<*mut libc::c_void> { + use crate::pin::pin; + use crate::sys::helpers::COpaque; + + let mut ret = None; + let mut attr: COpaque = COpaque::uninit(); + if !cfg!(target_os = "freebsd") { + attr = COpaque::zeroed(); + } + let attr = pin!(attr); + // FIXME(pin-ergonomics): remove the next line. + let attr = attr.into_ref(); + + // SAFETY: + // The attributes object has not been initialized yet and will not be + // moved until destroyed. + #[cfg(target_os = "freebsd")] + assert_eq!(unsafe { libc::pthread_attr_init(attr.get()) }, 0); + // SAFETY: + // * calling `pthread_self` is always valid and returns a valid `pthread_t` + // * `attr` is an initialized attribute object that can be written to. + #[cfg(target_os = "freebsd")] + let e = unsafe { libc::pthread_attr_get_np(libc::pthread_self(), attr.get()) }; + // SAFETY: + // * calling `pthread_self` is always valid and returns a valid `pthread_t` + // * `attr` can be written to, and will be initialized by this call. + #[cfg(not(target_os = "freebsd"))] + let e = unsafe { libc::pthread_getattr_np(libc::pthread_self(), attr.get()) }; + if e == 0 { + let mut stackaddr = crate::ptr::null_mut(); + let mut stacksize = 0; + // SAFETY: + // `attr` is an initialized attribute object and both the pointers + // are valid for writing. + assert_eq!( + unsafe { libc::pthread_attr_getstack(attr.get(), &mut stackaddr, &mut stacksize) }, + 0 + ); + ret = Some(stackaddr); + } + if e == 0 || cfg!(target_os = "freebsd") { + // SAFETY: + // `attr` was initialized either by `pthread_attr_init` (FreeBSD) or + // by `pthread_attr_get_np`, and is not used after this point. + assert_eq!(unsafe { libc::pthread_attr_destroy(attr.get()) }, 0); + } + ret +} + +fn stack_start_aligned(page_size: usize) -> Option<*mut libc::c_void> { + let stackptr = get_stack_start()?; + let stackaddr = stackptr.addr(); + + // Ensure stackaddr is page aligned! A parent process might + // have reset RLIMIT_STACK to be non-page aligned. The + // pthread_attr_getstack() reports the usable stack area + // stackaddr < stackaddr + stacksize, so if stackaddr is not + // page-aligned, calculate the fix such that stackaddr < + // new_page_aligned_stackaddr < stackaddr + stacksize + let remainder = stackaddr % page_size; + Some(if remainder == 0 { + stackptr + } else { + stackptr.with_addr(stackaddr + page_size - remainder) + }) +} + +/// # Safety +/// This function must only be called from the main thread, and there must +/// be sufficient stack space remaining to place a stack guard. +unsafe fn install_main_guard() -> Option> { + let page_size = PAGE_SIZE.load(Ordering::Relaxed); + + // this way someone on any unix-y OS can check that all these compile + if cfg!(all(target_os = "linux", not(target_env = "musl"))) { + install_main_guard_linux(page_size) + } else if cfg!(all(target_os = "linux", target_env = "musl")) { + install_main_guard_linux_musl(page_size) + } else if cfg!(target_os = "freebsd") { + #[cfg(not(target_os = "freebsd"))] + return None; + // The FreeBSD code cannot be checked on non-BSDs. + #[cfg(target_os = "freebsd")] + install_main_guard_freebsd(page_size) + } else if cfg!(any(target_os = "netbsd", target_os = "openbsd")) { + install_main_guard_bsds(page_size) + } else { + // SAFETY: guaranteed by caller. + unsafe { install_main_guard_default(page_size) } + } +} + +fn install_main_guard_linux(page_size: usize) -> Option> { + // See the corresponding conditional in init(). + // Avoid stack_start_aligned, which makes slow syscalls to read /proc/self/maps + if cfg!(panic = "immediate-abort") { + return None; + } + // Linux doesn't allocate the whole stack right away, and + // the kernel has its own stack-guard mechanism to fault + // when growing too close to an existing mapping. If we map + // our own guard, then the kernel starts enforcing a rather + // large gap above that, rendering much of the possible + // stack space useless. See #43052. + // + // Instead, we'll just note where we expect rlimit to start + // faulting, so our handler can report "stack overflow", and + // trust that the kernel's own stack guard will work. + let stackptr = stack_start_aligned(page_size)?; + let stackaddr = stackptr.addr(); + Some(stackaddr - page_size..stackaddr) +} + +fn install_main_guard_linux_musl(_page_size: usize) -> Option> { + // For the main thread, the musl's pthread_attr_getstack + // returns the current stack size, rather than maximum size + // it can eventually grow to. It cannot be used to determine + // the position of kernel's stack guard. + None +} + +#[cfg(target_os = "freebsd")] +fn install_main_guard_freebsd(page_size: usize) -> Option> { + // See the corresponding conditional in install_main_guard_linux(). + if cfg!(panic = "immediate-abort") { + return None; + } + // FreeBSD's stack autogrows, and optionally includes a guard page + // at the bottom. If we try to remap the bottom of the stack + // ourselves, FreeBSD's guard page moves upwards. So we'll just use + // the builtin guard page. + let stackptr = stack_start_aligned(page_size)?; + let guardaddr = stackptr.addr(); + // Technically the number of guard pages is tunable and controlled + // by the security.bsd.stack_guard_page sysctl. + // By default it is 1, checking once is enough since it is + // a boot time config value. + // FIXME(joboet): this function is only called once, remove the caching. + static PAGES: crate::sync::OnceLock = crate::sync::OnceLock::new(); + + let pages = PAGES.get_or_init(|| { + let mut guard: usize = 0; + let mut size = size_of_val(&guard); + let oid = c"security.bsd.stack_guard_page"; + + let r = unsafe { + libc::sysctlbyname( + oid.as_ptr(), + (&raw mut guard).cast(), + &raw mut size, + ptr::null_mut(), + 0, + ) + }; + if r == 0 { guard } else { 1 } + }); + Some(guardaddr..guardaddr + pages * page_size) +} + +fn install_main_guard_bsds(page_size: usize) -> Option> { + // See the corresponding conditional in install_main_guard_linux(). + if cfg!(panic = "immediate-abort") { + return None; + } + // OpenBSD stack already includes a guard page, and stack is + // immutable. + // NetBSD stack includes the guard page. + // + // We'll just note where we expect rlimit to start + // faulting, so our handler can report "stack overflow", and + // trust that the kernel's own stack guard will work. + let stackptr = stack_start_aligned(page_size)?; + let stackaddr = stackptr.addr(); + Some(stackaddr - page_size..stackaddr) +} + +/// # Safety +/// This function must only be called from the main thread, and there must +/// be sufficient stack space remaining to place a stack guard. +unsafe fn install_main_guard_default(page_size: usize) -> Option> { + // Reallocate the last page of the stack. + // This ensures SIGBUS will be raised on + // stack overflow. + // Systems which enforce strict PAX MPROTECT do not allow + // to mprotect() a mapping with less restrictive permissions + // than the initial mmap() used, so we mmap() here with + // read/write permissions and only then mprotect() it to + // no permissions at all. See issue #50313. + let stackptr = stack_start_aligned(page_size)?; + // SAFETY: + // The memory region from `stackptr..stackptr + page_size` belongs to + // the current thread's stack, and the caller has asserted that there + // is sufficient stack space, which means that this will not overwrite + // any existing allocations. + let result = unsafe { + mmap64( + stackptr, + page_size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED, + -1, + 0, + ) + }; + if result != stackptr || result == MAP_FAILED { + panic!("failed to allocate a guard page: {}", io::Error::last_os_error()); + } + + // SAFETY: + // Since this function is only called on the main thread, the stack will + // not be reused until program exit, so the runtime will never observe + // that part of the stack has been made unusable in this way. + let result = unsafe { mprotect(stackptr, page_size, PROT_NONE) }; + if result != 0 { + panic!("failed to protect the guard page: {}", io::Error::last_os_error()); + } + + let guardaddr = stackptr.addr(); + + Some(guardaddr..guardaddr + page_size) +} + +#[cfg(any( + target_os = "macos", + target_os = "openbsd", + target_os = "solaris", + target_os = "illumos", +))] +fn current_guard() -> Option> { + let stackptr = get_stack_start()?; + let stackaddr = stackptr.addr(); + Some(stackaddr - PAGE_SIZE.load(Ordering::Relaxed)..stackaddr) +} + +#[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "hurd", + target_os = "linux", + target_os = "netbsd", + target_os = "l4re" +))] +fn current_guard() -> Option> { + use crate::pin::pin; + use crate::sys::helpers::COpaque; + + let mut ret = None; + + let mut attr: COpaque = COpaque::uninit(); + if !cfg!(target_os = "freebsd") { + attr = COpaque::zeroed(); + } + let attr = pin!(attr); + // FIXME(pin-ergonomics): remove the next line. + let attr = attr.into_ref(); + + // SAFETY: + // The attributes object has not been initialized yet and will not be + // moved until destroyed. + #[cfg(target_os = "freebsd")] + assert_eq!(unsafe { libc::pthread_attr_init(attr.get()) }, 0); + // SAFETY: + // * calling `pthread_self` is always valid and returns a valid `pthread_t` + // * `attr` is an initialized attribute object that can be written to. + #[cfg(target_os = "freebsd")] + let e = unsafe { libc::pthread_attr_get_np(libc::pthread_self(), attr.get()) }; + // SAFETY: + // * calling `pthread_self` is always valid and returns a valid `pthread_t` + // * `attr` can be written to, and will be initialized by this call. + #[cfg(not(target_os = "freebsd"))] + let e = unsafe { libc::pthread_getattr_np(libc::pthread_self(), attr.get()) }; + if e == 0 { + let mut guardsize = 0; + // SAFETY: + // `attr` is an initialized attribute object and the pointer is valid + // for writing. + assert_eq!(unsafe { libc::pthread_attr_getguardsize(attr.get(), &mut guardsize) }, 0); + if guardsize == 0 { + if cfg!(all(target_os = "linux", target_env = "musl")) { + // musl versions before 1.1.19 always reported guard + // size obtained from pthread_attr_get_np as zero. + // Use page size as a fallback. + guardsize = PAGE_SIZE.load(Ordering::Relaxed); + } else { + panic!("there is no guard page"); + } + } + let mut stackptr = crate::ptr::null_mut::(); + let mut size = 0; + // SAFETY: + // `attr` is an initialized attribute object and both the pointers + // are valid for writing. + assert_eq!(unsafe { libc::pthread_attr_getstack(attr.get(), &mut stackptr, &mut size) }, 0); + + let stackaddr = stackptr.addr(); + ret = if cfg!(any(target_os = "freebsd", target_os = "netbsd", target_os = "hurd")) { + Some(stackaddr - guardsize..stackaddr) + } else if cfg!(all(target_os = "linux", target_env = "musl")) { + Some(stackaddr - guardsize..stackaddr) + } else if cfg!(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc"))) { + // glibc used to include the guard area within the stack, as noted in the BUGS + // section of `man pthread_attr_getguardsize`. This has been corrected starting + // with glibc 2.27, and in some distro backports, so the guard is now placed at the + // end (below) the stack. There's no easy way for us to know which we have at + // runtime, so we'll just match any fault in the range right above or below the + // stack base to call that fault a stack overflow. + Some(stackaddr - guardsize..stackaddr + guardsize) + } else { + Some(stackaddr..stackaddr + guardsize) + }; + } + if e == 0 || cfg!(target_os = "freebsd") { + // SAFETY: + // `attr` was initialized either by `pthread_attr_init` (FreeBSD) or + // by `pthread_attr_get_np`, and is not used after this point. + assert_eq!(unsafe { libc::pthread_attr_destroy(attr.get()) }, 0); + } + ret +} diff --git a/library/std/src/sys/pal/unix/stack_overflow/mod.rs b/library/std/src/sys/pal/unix/stack_overflow/mod.rs new file mode 100644 index 0000000000000..d60fe4b5bbec8 --- /dev/null +++ b/library/std/src/sys/pal/unix/stack_overflow/mod.rs @@ -0,0 +1,67 @@ +#![cfg_attr(test, allow(dead_code))] +#![forbid(unsafe_op_in_unsafe_fn)] + +cfg_select! { + // This is intentionally not enabled on iOS/tvOS/watchOS/visionOS, as it uses + // several symbols that might lead to rejections from the App Store, namely + // `sigaction`, `sigaltstack`, `sysctlbyname`, `mmap`, `munmap` and `mprotect`. + // + // This might be overly cautious, though it is also what Swift does (and they + // usually have fewer qualms about forwards compatibility, since the runtime + // is shipped with the OS): + // + // + // miri doesn't model signals nor stack overflows and this code has some + // synchronization properties that we don't want to expose to user code, + // hence we disable it on miri. + all( + not(miri), + any( + target_os = "linux", + target_os = "freebsd", + target_os = "hurd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + target_os = "illumos", + ) + ) => { + mod handler_signal; + mod thread_info; + use handler_signal as imp; + } + target_os = "cygwin" => { + mod handler_cygwin; + use handler_cygwin as imp; + } + _ => { + mod handler_none; + use handler_none as imp; + } +} + +pub use self::imp::init; +use self::imp::{drop_handler, make_handler}; + +pub struct Handler { + data: *mut libc::c_void, +} + +impl Handler { + pub unsafe fn new() -> Handler { + make_handler(false) + } + + fn null() -> Handler { + Handler { data: crate::ptr::null_mut() } + } +} + +impl Drop for Handler { + fn drop(&mut self) { + unsafe { + drop_handler(self.data); + } + } +} diff --git a/library/std/src/sys/pal/unsupported/common.rs b/library/std/src/sys/pal/unsupported/common.rs index d94b9015d0f5f..8e53c3212b64b 100644 --- a/library/std/src/sys/pal/unsupported/common.rs +++ b/library/std/src/sys/pal/unsupported/common.rs @@ -17,5 +17,5 @@ pub fn unsupported_err() -> std_io::Error { } pub fn abort_internal() -> ! { - core::intrinsics::abort(); + core::intrinsics::abort_immediate(); } diff --git a/library/std/src/sys/pal/windows/c.rs b/library/std/src/sys/pal/windows/c.rs index 432cd0425ac11..25031968a7e02 100644 --- a/library/std/src/sys/pal/windows/c.rs +++ b/library/std/src/sys/pal/windows/c.rs @@ -154,6 +154,9 @@ pub const MB_ERR_INVALID_CHARS: u32 = cu(windows_sys::MB_ERR_INVALID_CHARS); pub const CREATE_WAITABLE_TIMER_HIGH_RESOLUTION: u32 = cu(windows_sys::CREATE_WAITABLE_TIMER_HIGH_RESOLUTION); +// dwFlags --LoadLibraryExA +pub const LOAD_LIBRARY_SEARCH_SYSTEM32: u32 = cu(windows_sys::LOAD_LIBRARY_SEARCH_SYSTEM32); + // dwCreationDisposition -- CreateFile pub const CREATE_NEW: u32 = cu(windows_sys::CREATE_NEW); pub const OPEN_ALWAYS: u32 = cu(windows_sys::OPEN_ALWAYS); @@ -197,6 +200,16 @@ pub const WAIT_OBJECT_0: u32 = windows_sys::WAIT_OBJECT_0.cast_unsigned(); // LPPROGRESS_ROUTINE return value (DWORD) pub const PROGRESS_CONTINUE: u32 = cu(windows_sys::PROGRESS_CONTINUE); +// KNOWNFOLDERIDs (GUID) +pub const FOLDERID_Desktop: GUID = GUID::from_u128(0xb4bfcc3a_db2c_424c_b029_7fe99a87c641); +pub const FOLDERID_Documents: GUID = GUID::from_u128(0xfdd39ad0_238f_46af_adb4_6c85480369c7); +pub const FOLDERID_Downloads: GUID = GUID::from_u128(0x374de290_123f_4565_9164_39c4925e467b); +pub const FOLDERID_LocalAppData: GUID = GUID::from_u128(0xf1b32785_6fba_4fcf_9d55_7b8e7f157091); +pub const FOLDERID_Music: GUID = GUID::from_u128(0x4bd8d571_6d19_48d3_be97_422220080e43); +pub const FOLDERID_Pictures: GUID = GUID::from_u128(0x33e28130_4e1e_4676_835a_98395c3bc3bb); +pub const FOLDERID_RoamingAppData: GUID = GUID::from_u128(0x3eb685db_65f9_4cf6_a03a_e3ef65729f3d); +pub const FOLDERID_Videos: GUID = GUID::from_u128(0x18989b1d_99b5_455b_841c_ab7c74e4ddfc); + // Errors pub const ERROR_SUCCESS: u32 = cu(windows_sys::ERROR_SUCCESS); pub const ERROR_ACCESS_DENIED: u32 = cu(windows_sys::ERROR_ACCESS_DENIED); @@ -280,6 +293,17 @@ pub fn nt_success(status: NTSTATUS) -> bool { status >= 0 } +impl GUID { + pub const fn from_u128(uuid: u128) -> Self { + Self { + data1: (uuid >> 96) as u32, + data2: (uuid >> 80 & 0xffff) as u16, + data3: (uuid >> 64 & 0xffff) as u16, + data4: (uuid as u64).to_be_bytes(), + } + } +} + impl OBJECT_ATTRIBUTES { pub fn with_length() -> Self { Self { @@ -459,3 +483,24 @@ cfg_select! { unsafe extern "C" { pub fn atexit(cb: unsafe extern "C" fn()) -> c_int; } + +// Functions in DLLs that we want to lazily load +compat_fn_with_fallback! { + // Avoid eagerly linking shell32.dll, which marks the loading application as graphical. + #[lazy] + pub static SHELL32: &CStr = c"shell32"; + + pub fn SHGetKnownFolderPath(rfid : *const KNOWNFOLDERID, dwflags : u32, htoken : HANDLE, ppszpath : *mut PWSTR) -> HRESULT { + unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as u32); E_NOTIMPL } + } +} + +compat_fn_with_fallback! { + // Only used with SHGetKnownFolderPath, so avoid eager load overhead. + #[lazy] + pub static OLE32: &CStr = c"ole32"; + + pub fn CoTaskMemFree(pv : *mut core::ffi::c_void) -> () { + // without OLE32 there's no COM alloc, so no-op COM free is fine + } +} diff --git a/library/std/src/sys/pal/windows/c/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt index ebe4b8e128352..70af36f27907e 100644 --- a/library/std/src/sys/pal/windows/c/bindings.txt +++ b/library/std/src/sys/pal/windows/c/bindings.txt @@ -252,6 +252,8 @@ DNS_ERROR_ZONESCOPE_IS_REFERENCED DUPLICATE_CLOSE_SOURCE DUPLICATE_SAME_ACCESS DuplicateHandle +E_FAIL +E_INVALIDARG E_NOTIMPL ENABLE_AUTO_POSITION ENABLE_ECHO_INPUT @@ -2260,8 +2262,12 @@ IPV6_MREQ IPV6_MULTICAST_LOOP IPV6_V6ONLY IsThreadAFiber +KF_FLAG_DONT_VERIFY +KNOWNFOLDERID LINGER listen +LOAD_LIBRARY_SEARCH_SYSTEM32 +LoadLibraryExA LocalFree LOCKFILE_EXCLUSIVE_LOCK LOCKFILE_FAIL_IMMEDIATELY @@ -2350,6 +2356,7 @@ ReleaseSRWLockShared RemoveDirectoryW RtlGenRandom RtlNtStatusToDosError +S_OK SD_BOTH SD_RECEIVE SD_SEND diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs index 34c4407296dcf..0c865494a6e7c 100644 --- a/library/std/src/sys/pal/windows/c/windows_sys.rs +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -71,6 +71,7 @@ windows_link::link!("kernel32.dll" "system" fn InitOnceBeginInitialize(lpinitonc windows_link::link!("kernel32.dll" "system" fn InitOnceComplete(lpinitonce : LPINIT_ONCE, dwflags : u32, lpcontext : *const core::ffi::c_void) -> BOOL); windows_link::link!("kernel32.dll" "system" fn InitializeProcThreadAttributeList(lpattributelist : *mut _PROC_THREAD_ATTRIBUTE_LIST, dwattributecount : u32, dwflags : u32, lpsize : *mut usize) -> BOOL); windows_link::link!("kernel32.dll" "system" fn IsThreadAFiber() -> BOOL); +windows_link::link!("kernel32.dll" "system" fn LoadLibraryExA(lplibfilename : PCSTR, hfile : HANDLE, dwflags : u32) -> HMODULE); windows_link::link!("kernel32.dll" "system" fn LocalFree(hmem : HLOCAL) -> HLOCAL); windows_link::link!("kernel32.dll" "system" fn LockFileEx(hfile : HANDLE, dwflags : u32, dwreserved : u32, nnumberofbytestolocklow : u32, nnumberofbytestolockhigh : u32, lpoverlapped : *mut OVERLAPPED) -> BOOL); windows_link::link!("kernel32.dll" "system" fn MoveFileExW(lpexistingfilename : PCWSTR, lpnewfilename : PCWSTR, dwflags : u32) -> BOOL); @@ -2433,6 +2434,8 @@ impl Default for EXCEPTION_RECORD { } pub const EXCEPTION_STACK_OVERFLOW: i32 = -1073741571; pub const EXTENDED_STARTUPINFO_PRESENT: i32 = 524288; +pub const E_FAIL: HRESULT = 0x80004005_u32 as _; +pub const E_INVALIDARG: HRESULT = 0x80070057_u32 as _; pub const E_NOTIMPL: HRESULT = 0x80004001_u32 as _; pub const ExceptionCollidedUnwind: EXCEPTION_DISPOSITION = 3; pub const ExceptionContinueExecution: EXCEPTION_DISPOSITION = 0; @@ -2925,7 +2928,11 @@ impl Default for IP_MREQ { pub const IP_MULTICAST_LOOP: i32 = 11; pub const IP_MULTICAST_TTL: i32 = 10; pub const IP_TTL: i32 = 4; +pub const KF_FLAG_DONT_VERIFY: KNOWN_FOLDER_FLAG = 16384; +pub type KNOWNFOLDERID = GUID; +pub type KNOWN_FOLDER_FLAG = u32; pub type LINGER = linger; +pub const LOAD_LIBRARY_SEARCH_SYSTEM32: i32 = 2048; pub const LOCKFILE_EXCLUSIVE_LOCK: i32 = 2; pub const LOCKFILE_FAIL_IMMEDIATELY: i32 = 1; pub type LPBYTE = *mut u8; @@ -3295,6 +3302,7 @@ pub struct SYSTEM_INFO_0_0 { pub wProcessorArchitecture: u16, pub wReserved: u16, } +pub const S_OK: HRESULT = 0x0_u32 as _; pub const TCP_NODELAY: i32 = 1; pub const TIMER_ALL_ACCESS: i32 = 2031619; pub const TIMER_MODIFY_STATE: i32 = 2; diff --git a/library/std/src/sys/pal/windows/compat.rs b/library/std/src/sys/pal/windows/compat.rs index c465ceb2301ce..7e9f5bd108729 100644 --- a/library/std/src/sys/pal/windows/compat.rs +++ b/library/std/src/sys/pal/windows/compat.rs @@ -20,7 +20,8 @@ //! importing the same function unnecessarily. use crate::ffi::{CStr, c_void}; -use crate::ptr::NonNull; +use crate::ptr::{self, NonNull}; +use crate::sync::LazyLock; use crate::sys::c; // This uses a static initializer to preload some imported functions. @@ -118,6 +119,23 @@ impl Module { } } + /// Try to load a module handle. + /// + /// # SAFETY + /// + /// This should only be use for modules that exist for the lifetime of std. + pub unsafe fn load(name: &CStr) -> Option { + // SAFETY: A CStr is always null terminated. + unsafe { + let module = c::LoadLibraryExA( + /* lpLibFileName */ name.as_ptr().cast::(), + /* hReserved */ ptr::null_mut(), + /* dwFlags */ c::LOAD_LIBRARY_SEARCH_SYSTEM32, + ); + NonNull::new(module).map(Self) + } + } + // Try to get the address of a function. pub fn proc_address(self, name: &CStr) -> Option> { unsafe { @@ -131,6 +149,33 @@ impl Module { } } +unsafe impl Send for Module {} +unsafe impl Sync for Module {} + +type LazyLoadModule = impl FnOnce() -> Option + Send + Sync + 'static; + +#[define_opaque(LazyLoadModule)] +const fn lazy_load_module(name: &'static CStr) -> LazyLoadModule { + move || unsafe { Module::load(name) } +} + +/// Represents a loaded module. +/// +/// Note that the modules std depends on must not be unloaded. +/// Therefore a `Module` is always valid for the lifetime of std. +pub(in crate::sys) struct LazyModule(LazyLock, LazyLoadModule>); +impl LazyModule { + /// Create a lazy-loaded handle to a module. + pub const unsafe fn new(name: &'static CStr) -> Self { + Self(LazyLock::new(lazy_load_module(name))) + } + + /// Get the module handle, loading it if necessary. + pub fn load(&self) -> Option { + *LazyLock::force(&self.0) + } +} + /// Load a function or use a fallback implementation if that fails. macro_rules! compat_fn_with_fallback { (pub static $module:ident: &CStr = $name:expr; $( @@ -193,6 +238,67 @@ macro_rules! compat_fn_with_fallback { #[allow(unused)] $(#[$meta])* $vis use $symbol::call as $symbol; + )*); + (#[lazy] pub static $module:ident: &CStr = $name:expr; $( + $(#[$meta:meta])* + $vis:vis fn $symbol:ident($($argname:ident: $argtype:ty),*) -> $rettype:ty $fallback_body:block + )*) => ( + pub static $module: &CStr = $name; + $( + $(#[$meta])* + pub mod $symbol { + #[allow(unused_imports)] + use super::*; + use crate::mem; + use crate::ffi::CStr; + use crate::sync::atomic::{Atomic, AtomicPtr, Ordering}; + use crate::sys::compat::{LazyModule, Module}; + + type F = unsafe extern "system" fn($($argtype),*) -> $rettype; + + /// `PTR` contains a function pointer to one of three functions. + /// It starts with the `load` function. + /// When that is called it attempts to load the requested symbol. + /// If it succeeds, `PTR` is set to the address of that symbol. + /// If it fails, then `PTR` is set to `fallback`. + static PTR: Atomic<*mut c_void> = AtomicPtr::new(load as unsafe extern "system" fn($($argname: $argtype),*) -> $rettype as *mut _); + + unsafe extern "system" fn load($($argname: $argtype),*) -> $rettype { + static MODULE: LazyModule = unsafe { LazyModule::new($module) }; + unsafe { + let func = load_from_module(MODULE.load()); + func($($argname),*) + } + } + + fn load_from_module(module: Option) -> F { + unsafe { + static SYMBOL_NAME: &CStr = ansi_str!(sym $symbol); + if let Some(f) = module.and_then(|m| m.proc_address(SYMBOL_NAME)) { + PTR.store(f.as_ptr(), Ordering::Relaxed); + mem::transmute(f) + } else { + PTR.store(fallback as unsafe extern "system" fn($($argname: $argtype),*) -> $rettype as *mut _, Ordering::Relaxed); + fallback + } + } + } + + #[allow(unused_variables)] + unsafe extern "system" fn fallback($($argname: $argtype),*) -> $rettype { + $fallback_body + } + + #[inline(always)] + pub unsafe fn call($($argname: $argtype),*) -> $rettype { + unsafe { + let func: F = mem::transmute(PTR.load(Ordering::Relaxed)); + func($($argname),*) + } + } + } + $(#[$meta])* + $vis use $symbol::call as $symbol; )*) } diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 2a8ffaa0baab8..a9f3cdbdd9d3e 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -275,7 +275,7 @@ pub fn abort_internal() -> ! { core::arch::asm!("brk 0xF003", in("x0") c::FAST_FAIL_FATAL_APP_EXIT, options(noreturn, nostack)); } _ => { - core::intrinsics::abort(); + core::intrinsics::abort_immediate(); } } } @@ -284,7 +284,7 @@ pub fn abort_internal() -> ! { #[cfg(miri)] #[track_caller] // even without panics, this helps for Miri backtraces pub fn abort_internal() -> ! { - crate::intrinsics::abort(); + crate::intrinsics::abort_immediate(); } /// Align the inner value to 8 bytes. diff --git a/library/std/src/sys/pal/zkvm/mod.rs b/library/std/src/sys/pal/zkvm/mod.rs index 1a64d3c93d701..3a21e6f66c4e2 100644 --- a/library/std/src/sys/pal/zkvm/mod.rs +++ b/library/std/src/sys/pal/zkvm/mod.rs @@ -31,5 +31,5 @@ pub fn unsupported_err() -> std_io::Error { } pub fn abort_internal() -> ! { - core::intrinsics::abort(); + core::intrinsics::abort_immediate(); } diff --git a/library/std/src/sys/paths/mod.rs b/library/std/src/sys/paths/mod.rs index 69c814ef06c06..865d06ae8644e 100644 --- a/library/std/src/sys/paths/mod.rs +++ b/library/std/src/sys/paths/mod.rs @@ -6,7 +6,8 @@ cfg_select! { mod imp { pub use super::hermit::{getcwd, temp_dir}; pub use super::unsupported::{ - JoinPathsError, SplitPaths, chdir, current_exe, home_dir, join_paths, split_paths, + JoinPathsError, SplitPaths, SplitPathsRef, chdir, current_exe, home_dir, + join_paths, split_paths, split_paths_ref, }; } } @@ -16,8 +17,8 @@ cfg_select! { mod unsupported; mod imp { pub use super::motor::{ - JoinPathsError, SplitPaths, chdir, current_exe, getcwd, home_dir, join_paths, - split_paths, temp_dir, + JoinPathsError, SplitPaths, SplitPathsRef, chdir, current_exe, getcwd, home_dir, + join_paths, split_paths, split_paths_ref, temp_dir, }; } } @@ -28,8 +29,8 @@ cfg_select! { mod imp { pub use super::sgx::chdir; pub use super::unsupported::{ - JoinPathsError, SplitPaths, current_exe, getcwd, home_dir, join_paths, split_paths, - temp_dir, + JoinPathsError, SplitPaths, SplitPathsRef, current_exe, getcwd, home_dir, + join_paths, split_paths, split_paths_ref, temp_dir, }; } } @@ -47,7 +48,8 @@ cfg_select! { mod unsupported; mod imp { pub use super::unsupported::{ - JoinPathsError, SplitPaths, current_exe, home_dir, join_paths, split_paths, + JoinPathsError, SplitPaths, SplitPathsRef, current_exe, home_dir, join_paths, + split_paths, split_paths_ref, }; pub use super::wasi::{chdir, getcwd, temp_dir}; } @@ -63,6 +65,6 @@ cfg_select! { } pub use imp::{ - JoinPathsError, SplitPaths, chdir, current_exe, getcwd, home_dir, join_paths, split_paths, - temp_dir, + JoinPathsError, SplitPaths, SplitPathsRef, chdir, current_exe, getcwd, home_dir, join_paths, + split_paths, split_paths_ref, temp_dir, }; diff --git a/library/std/src/sys/paths/motor.rs b/library/std/src/sys/paths/motor.rs index 82f8a35e50db6..cd7605d6f2662 100644 --- a/library/std/src/sys/paths/motor.rs +++ b/library/std/src/sys/paths/motor.rs @@ -15,6 +15,16 @@ pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> { unparsed.as_str().split(PATH_SEPARATOR).map(into_pathbuf as fn(&str) -> PathBuf) } +pub type SplitPathsRef<'a> = iter::Map, fn(&str) -> &path::Path>; + +pub fn split_paths_ref(unparsed: &OsStr) -> Option> { + fn as_path(part: &str) -> &path::Path { + part.as_ref() + } + + Some(unparsed.as_str().split(PATH_SEPARATOR).map(as_path as fn(&str) -> &path::Path)) +} + #[derive(Debug)] pub struct JoinPathsError; diff --git a/library/std/src/sys/paths/uefi.rs b/library/std/src/sys/paths/uefi.rs index 7fddcfdff7724..d9dcd45267935 100644 --- a/library/std/src/sys/paths/uefi.rs +++ b/library/std/src/sys/paths/uefi.rs @@ -1,6 +1,7 @@ use r_efi::efi::protocols::{device_path, loaded_image_device_path}; use crate::ffi::{OsStr, OsString}; +use crate::marker::PhantomData; use crate::os::uefi::ffi::{OsStrExt, OsStringExt}; use crate::path::{self, PathBuf}; use crate::sys::pal::{helpers, unsupported_err}; @@ -71,6 +72,19 @@ impl<'a> Iterator for SplitPaths<'a> { } } +pub struct SplitPathsRef<'a>(!, PhantomData<&'a ()>); + +pub fn split_paths_ref(_unparsed: &OsStr) -> Option> { + None +} + +impl<'a> Iterator for SplitPathsRef<'a> { + type Item = &'a path::Path; + fn next(&mut self) -> Option<&'a path::Path> { + self.0 + } +} + #[derive(Debug)] pub struct JoinPathsError; diff --git a/library/std/src/sys/paths/unix.rs b/library/std/src/sys/paths/unix.rs index e023451b13c8c..cee799c36c79b 100644 --- a/library/std/src/sys/paths/unix.rs +++ b/library/std/src/sys/paths/unix.rs @@ -6,7 +6,7 @@ use libc::{c_char, c_int, c_void}; use crate::ffi::{CStr, OsStr, OsString}; use crate::os::unix::prelude::*; -use crate::path::{self, PathBuf}; +use crate::path::{self, Path, PathBuf}; use crate::sys::helpers::run_path_with_cstr; use crate::sys::pal::cvt; use crate::{fmt, io, iter, mem, ptr, slice, str}; @@ -76,6 +76,28 @@ pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> { unparsed.as_bytes().split(is_separator).map(into_pathbuf) } +pub type SplitPathsRef<'a> = iter::Map< + slice::Split<'a, u8, impl FnMut(&u8) -> bool + 'static>, + impl FnMut(&[u8]) -> &'_ Path + 'static, +>; + +pub fn split_paths_ref(unparsed: &OsStr) -> Option> { + #[define_opaque(SplitPathsRef)] + fn split_paths_ref(unparsed: &OsStr) -> SplitPathsRef<'_> { + fn is_separator(&b: &u8) -> bool { + b == PATH_SEPARATOR + } + + fn into_path(part: &[u8]) -> &Path { + Path::new(OsStr::from_bytes(part)) + } + + unparsed.as_bytes().split(is_separator).map(into_path) + } + + Some(split_paths_ref(unparsed)) +} + #[derive(Debug)] pub struct JoinPathsError; diff --git a/library/std/src/sys/paths/unsupported.rs b/library/std/src/sys/paths/unsupported.rs index 024830a254fd7..204d6b0897e20 100644 --- a/library/std/src/sys/paths/unsupported.rs +++ b/library/std/src/sys/paths/unsupported.rs @@ -1,6 +1,6 @@ use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; -use crate::path::{self, PathBuf}; +use crate::path::{self, Path, PathBuf}; use crate::sys::pal::unsupported; use crate::{fmt, io}; @@ -13,11 +13,16 @@ pub fn chdir(_: &path::Path) -> io::Result<()> { } pub struct SplitPaths<'a>(!, PhantomData<&'a ()>); +pub struct SplitPathsRef<'a>(!, PhantomData<&'a ()>); pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> { panic!("unsupported") } +pub fn split_paths_ref(_unparsed: &OsStr) -> Option> { + None +} + impl<'a> Iterator for SplitPaths<'a> { type Item = PathBuf; fn next(&mut self) -> Option { @@ -25,6 +30,13 @@ impl<'a> Iterator for SplitPaths<'a> { } } +impl<'a> Iterator for SplitPathsRef<'a> { + type Item = &'a Path; + fn next(&mut self) -> Option<&'a Path> { + self.0 + } +} + #[derive(Debug)] pub struct JoinPathsError; diff --git a/library/std/src/sys/paths/windows.rs b/library/std/src/sys/paths/windows.rs index 9b9316b2658c0..350a942124694 100644 --- a/library/std/src/sys/paths/windows.rs +++ b/library/std/src/sys/paths/windows.rs @@ -3,6 +3,7 @@ #![allow(nonstandard_style)] use crate::ffi::{OsStr, OsString}; +use crate::marker::PhantomData; use crate::os::windows::ffi::EncodeWide; use crate::os::windows::prelude::*; use crate::path::{self, PathBuf}; @@ -56,6 +57,19 @@ impl<'a> Iterator for SplitPaths<'a> { } } +pub struct SplitPathsRef<'a>(!, PhantomData<&'a ()>); + +pub fn split_paths_ref(_unparsed: &OsStr) -> Option> { + None +} + +impl<'a> Iterator for SplitPathsRef<'a> { + type Item = &'a path::Path; + fn next(&mut self) -> Option<&'a path::Path> { + self.0 + } +} + #[derive(Debug)] pub struct JoinPathsError; diff --git a/library/std/src/sys/personality/mod.rs b/library/std/src/sys/personality/mod.rs index 31bd457adbe6b..be79643bdf846 100644 --- a/library/std/src/sys/personality/mod.rs +++ b/library/std/src/sys/personality/mod.rs @@ -22,7 +22,7 @@ cfg_select! { // aborting stub. #[lang = "eh_personality"] fn rust_eh_personality() { - core::intrinsics::abort() + core::intrinsics::abort_immediate() } } any( diff --git a/library/std/src/sys/process/unix/unsupported/wait_status.rs b/library/std/src/sys/process/unix/unsupported/wait_status.rs index ac54824875813..b77e7ae835656 100644 --- a/library/std/src/sys/process/unix/unsupported/wait_status.rs +++ b/library/std/src/sys/process/unix/unsupported/wait_status.rs @@ -1,4 +1,4 @@ -//! Emulated wait status for non-Unix #[cfg(unix) platforms +//! Emulated wait status for non-Unix `#[cfg(unix)]` platforms //! //! Separate module to facilitate testing against a real Unix implementation. diff --git a/library/std/src/sys/thread_local/native/mod.rs b/library/std/src/sys/thread_local/native/mod.rs index 4dad81685a94e..209a8790def08 100644 --- a/library/std/src/sys/thread_local/native/mod.rs +++ b/library/std/src/sys/thread_local/native/mod.rs @@ -81,8 +81,12 @@ pub macro thread_local_inner { // used to generate the `LocalKey` value for `thread_local!` (@key $t:ty, $(#[$align_attr:meta])*, $init:expr) => {{ + // We intentionally have an argument-position `'static` lifetime so that elided lifetimes in `$t` + // become `'static` like they do for `const`s and `static`s, including in the other two + // `thread_local!` implementations. + #[allow(mismatched_lifetime_syntaxes)] #[inline] - fn __rust_std_internal_init_fn() -> $t { + fn __rust_std_internal_init_fn(_lifetime_elision: $crate::marker::PhantomData<&'static ()>) -> $t { $init } @@ -94,7 +98,7 @@ pub macro thread_local_inner { $(#[$align_attr])* static __RUST_STD_INTERNAL_VAL: $crate::thread::local_impl::LazyStorage<$t, ()> = $crate::thread::local_impl::LazyStorage::new(); - __RUST_STD_INTERNAL_VAL.get_or_init(__rust_std_internal_init, __rust_std_internal_init_fn) + __RUST_STD_INTERNAL_VAL.get_or_init(__rust_std_internal_init, || __rust_std_internal_init_fn($crate::marker::PhantomData)) } } else { |__rust_std_internal_init| { @@ -102,7 +106,7 @@ pub macro thread_local_inner { $(#[$align_attr])* static __RUST_STD_INTERNAL_VAL: $crate::thread::local_impl::LazyStorage<$t, !> = $crate::thread::local_impl::LazyStorage::new(); - __RUST_STD_INTERNAL_VAL.get_or_init(__rust_std_internal_init, __rust_std_internal_init_fn) + __RUST_STD_INTERNAL_VAL.get_or_init(__rust_std_internal_init, || __rust_std_internal_init_fn($crate::marker::PhantomData)) } } }) diff --git a/library/std/src/sys/thread_local/no_threads.rs b/library/std/src/sys/thread_local/no_threads.rs index 9f4e7710dffb7..a3d50d05eeec8 100644 --- a/library/std/src/sys/thread_local/no_threads.rs +++ b/library/std/src/sys/thread_local/no_threads.rs @@ -31,14 +31,18 @@ pub macro thread_local_inner { // used to generate the `LocalKey` value for `thread_local!` (@key $t:ty, $(#[$align_attr:meta])*, $init:expr) => {{ + // We intentionally have an argument-position `'static` lifetime so that elided lifetimes in `$t` + // become `'static` like they do for `const`s and `static`s, including in the other two + // `thread_local!` implementations. + #[allow(mismatched_lifetime_syntaxes)] #[inline] - fn __rust_std_internal_init_fn() -> $t { $init } + fn __rust_std_internal_init_fn(_lifetime_elision: $crate::marker::PhantomData<&'static ()>) -> $t { $init } unsafe { $crate::thread::LocalKey::new(|__rust_std_internal_init| { $(#[$align_attr])* static __RUST_STD_INTERNAL_VAL: $crate::thread::local_impl::LazyStorage<$t> = $crate::thread::local_impl::LazyStorage::new(); - __RUST_STD_INTERNAL_VAL.get(__rust_std_internal_init, __rust_std_internal_init_fn) + __RUST_STD_INTERNAL_VAL.get(__rust_std_internal_init, || __rust_std_internal_init_fn($crate::marker::PhantomData)) }) } }}, diff --git a/library/std/src/sys/thread_local/os.rs b/library/std/src/sys/thread_local/os.rs index 428339a16f4ea..8588fc994350b 100644 --- a/library/std/src/sys/thread_local/os.rs +++ b/library/std/src/sys/thread_local/os.rs @@ -20,8 +20,12 @@ pub macro thread_local_inner { // used to generate the `LocalKey` value for `thread_local!`. (@key $t:ty, $($(#[$($align_attr:tt)*])+)?, $init:expr) => {{ + // We intentionally have an argument-position `'static` lifetime so that elided lifetimes in `$t` + // become `'static` like they do for `const`s and `static`s, including in the other two + // `thread_local!` implementations. + #[allow(mismatched_lifetime_syntaxes)] #[inline] - fn __rust_std_internal_init_fn() -> $t { $init } + fn __rust_std_internal_init_fn(_lifetime_elision: $crate::marker::PhantomData<&'static ()>) -> $t { $init } // NOTE: this cannot import `LocalKey` or `Storage` with a `use` because that can shadow // user provided type or type alias with a matching name. Please update the shadowing test @@ -43,7 +47,7 @@ pub macro thread_local_inner { final_align }> = $crate::thread::local_impl::Storage::new(); - __RUST_STD_INTERNAL_VAL.get(__rust_std_internal_init, __rust_std_internal_init_fn) + __RUST_STD_INTERNAL_VAL.get(__rust_std_internal_init, || __rust_std_internal_init_fn($crate::marker::PhantomData)) }) } }}, diff --git a/library/std/src/thread/lifecycle.rs b/library/std/src/thread/lifecycle.rs index c22b97c39ecd5..b435f7185f043 100644 --- a/library/std/src/thread/lifecycle.rs +++ b/library/std/src/thread/lifecycle.rs @@ -7,6 +7,7 @@ use super::thread::Thread; use super::{Result, spawnhook}; use crate::cell::UnsafeCell; use crate::marker::PhantomData; +use crate::mem::MaybeDangling; use crate::sync::Arc; use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; use crate::sys::{AsInner, IntoInner, thread as imp}; @@ -56,9 +57,14 @@ where Arc::new(Packet { scope: scope_data, result: UnsafeCell::new(None), _marker: PhantomData }); let their_packet = my_packet.clone(); + // Pass `f` in `MaybeDangling` because actually that closure might *run longer than the lifetime of `F`*. + // See for more details. + let f = MaybeDangling::new(f); + // The entrypoint of the Rust thread, after platform-specific thread // initialization is done. let rust_start = move || { + let f = f.into_inner(); let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| { crate::sys::backtrace::__rust_begin_short_backtrace(|| hooks.inherit_and_run()); crate::sys::backtrace::__rust_begin_short_backtrace(f) diff --git a/library/std/tests/sync_view.rs b/library/std/tests/sync_view.rs new file mode 100644 index 0000000000000..1a21130f73fa2 --- /dev/null +++ b/library/std/tests/sync_view.rs @@ -0,0 +1,93 @@ +use core::sync::SyncView; +use std::cell::Cell; +use std::future::Future; +use std::marker::PhantomPinned; +use std::pin::{Pin, pin}; +use std::task::{Context, Poll, Waker}; + +const EMPTY: Vec = SyncView::new(Vec::new()).into_inner(); +const PROJECTED: u32 = { + let mut value = 41; + let view = SyncView::from_mut(&mut value); + *Pin::into_inner(Pin::new(view).as_pin_mut()) += 1; + let view = SyncView::from_pin_mut(Pin::new(&mut value)).get_mut(); + *Pin::new(&*view).as_pin_ref().get_ref() +}; + +fn require_sync(_: &T) {} + +fn call_mut(mut f: impl FnMut() -> u32) -> u32 { + f() +} + +fn call_shared(f: &impl Fn() -> u32) -> u32 { + f() +} + +fn complete(future: impl Future) -> u32 { + let mut context = Context::from_waker(Waker::noop()); + match pin!(future).poll(&mut context) { + Poll::Ready(value) => value, + Poll::Pending => panic!("expected an immediately ready future"), + } +} + +async fn call_async_once(f: impl AsyncFnOnce() -> u32) -> u32 { + f().await +} + +struct Pinned { + value: Cell, + _pin: PhantomPinned, +} + +#[test] +fn sync_view_const() { + assert!(EMPTY.is_empty()); + assert_eq!(PROJECTED, 42); +} + +#[test] +fn sync_view_access() { + let mut value = std::sync::SyncView::new(Cell::new(1)); + require_sync(&value); + value.as_mut().set(2); + assert_eq!(value.into_inner().get(), 2); + + let mut values = [1, 2]; + let view = SyncView::from_mut(&mut values[..]); + view.as_mut()[0] = 3; + assert_eq!(view.as_ref(), &[3, 2]); +} + +#[test] +fn sync_view_pinned_projection() { + let mut pinned = pin!(Pinned { value: Cell::new(4), _pin: PhantomPinned }); + let address = std::ptr::from_ref(pinned.as_ref().get_ref()); + let view = SyncView::from_pin_mut(pinned.as_mut()); + require_sync(view.as_ref().get_ref()); + let inner = view.as_pin_mut(); + assert_eq!(std::ptr::from_ref(inner.as_ref().get_ref()), address); + inner.value.set(5); + assert_eq!(pinned.value.get(), 5); +} + +#[test] +fn sync_view_forwarding() { + let counter = Cell::new(0); + let increment = SyncView::new(|| { + counter.set(counter.get() + 1); + counter.get() + }); + require_sync(&increment); + assert_eq!(call_mut(increment), 1); + assert_eq!(call_shared(&SyncView::new(|| 7)), 7); + + let future = SyncView::new(async { + std::future::ready(()).await; + counter.get() + }); + require_sync(&future); + assert_eq!(complete(future), 1); + assert_eq!(complete(call_async_once(SyncView::new(async || counter.get()))), 1); +} diff --git a/library/std_detect/Cargo.toml b/library/std_detect/Cargo.toml index a6785bb20555d..c03fff827d5cd 100644 --- a/library/std_detect/Cargo.toml +++ b/library/std_detect/Cargo.toml @@ -24,3 +24,7 @@ libc = { version = "0.2.0", default-features = false } [features] std_detect_env_override = [] + +[lints.cargo] +# libc is definitely used in code but the lint doesn't realize that (unclear why) +unused_dependencies = "allow" diff --git a/library/std_detect/src/detect/arch/x86.rs b/library/std_detect/src/detect/arch/x86.rs index e054546afcb19..408b6ceaaa00b 100644 --- a/library/std_detect/src/detect/arch/x86.rs +++ b/library/std_detect/src/detect/arch/x86.rs @@ -111,6 +111,8 @@ features! { /// * `"adx"` /// * `"rtm"` /// * `"movbe"` + /// * `"movdir64b"` + /// * `"movdiri"` /// * `"ermsb"` /// * `"movrs"` /// * `"xop"` @@ -271,6 +273,10 @@ features! { /// RTM, Intel (Restricted Transactional Memory) @FEATURE: #[stable(feature = "movbe_target_feature", since = "1.67.0")] movbe: "movbe"; /// MOVBE (Move Data After Swapping Bytes) + @FEATURE: #[unstable(feature = "movdir64b_target_feature", issue = "163741")] movdir64b: "movdir64b"; + /// MOVDIR64B (Move 64 Bytes as Direct Store) + @FEATURE: #[unstable(feature = "movdiri_target_feature", issue = "163741")] movdiri: "movdiri"; + /// MOVDIRI (Move Doubleword as Direct Store) @FEATURE: #[unstable(feature = "movrs_target_feature", issue = "137976")] movrs: "movrs"; /// MOVRS (Move data with the read-shared hint) @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] ermsb: "ermsb"; diff --git a/library/std_detect/src/detect/os/x86.rs b/library/std_detect/src/detect/os/x86.rs index 4e739fb38d56e..3d348fe2ac439 100644 --- a/library/std_detect/src/detect/os/x86.rs +++ b/library/std_detect/src/detect/os/x86.rs @@ -121,6 +121,8 @@ pub(crate) fn detect_features() -> cache::Initializer { enable(extended_features_ecx, 8, Feature::gfni); enable(extended_features_ecx, 9, Feature::vaes); enable(extended_features_ecx, 10, Feature::vpclmulqdq); + enable(extended_features_ecx, 27, Feature::movdiri); + enable(extended_features_ecx, 28, Feature::movdir64b); enable(extended_features_ebx, 3, Feature::bmi1); enable(extended_features_ebx, 8, Feature::bmi2); diff --git a/library/std_detect/tests/x86-specific.rs b/library/std_detect/tests/x86-specific.rs index 117182558f46a..ff88e25073d34 100644 --- a/library/std_detect/tests/x86-specific.rs +++ b/library/std_detect/tests/x86-specific.rs @@ -5,7 +5,9 @@ x86_amx_intrinsics, xop_target_feature, movrs_target_feature, - clflushopt_target_feature + clflushopt_target_feature, + movdir64b_target_feature, + movdiri_target_feature )] #[macro_use] @@ -68,6 +70,8 @@ fn dump() { println!("adx: {:?}", is_x86_feature_detected!("adx")); println!("rtm: {:?}", is_x86_feature_detected!("rtm")); println!("movbe: {:?}", is_x86_feature_detected!("movbe")); + println!("movdir64b: {:?}", is_x86_feature_detected!("movdir64b")); + println!("movdiri: {:?}", is_x86_feature_detected!("movdiri")); println!("avxvnni: {:?}", is_x86_feature_detected!("avxvnni")); println!("avxvnniint8: {:?}", is_x86_feature_detected!("avxvnniint8")); println!("avxneconvert: {:?}", is_x86_feature_detected!("avxneconvert")); diff --git a/library/stdarch/.github/workflows/main.yml b/library/stdarch/.github/workflows/main.yml index 5c2e2ae4a8e21..d7539fe2e8ebd 100644 --- a/library/stdarch/.github/workflows/main.yml +++ b/library/stdarch/.github/workflows/main.yml @@ -8,208 +8,208 @@ jobs: name: Check Style runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 - - name: Install Rust - run: rustup update nightly --no-self-update && rustup default nightly - - run: ci/style.sh + - uses: actions/checkout@v6 + - name: Install Rust + run: rustup update nightly --no-self-update && rustup default nightly + - run: ci/style.sh docs: name: Build Documentation - needs: [style] + needs: [ style ] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 - - name: Install Rust - run: rustup update nightly --no-self-update && rustup default nightly - - run: ci/dox.sh - env: - CI: 1 + - uses: actions/checkout@v6 + - name: Install Rust + run: rustup update nightly --no-self-update && rustup default nightly + - run: ci/dox.sh + env: + CI: 1 verify: name: Automatic intrinsic verification - needs: [style] + needs: [ style ] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 - - name: Install Rust - run: rustup update nightly --no-self-update && rustup default nightly - - run: cargo test --manifest-path crates/stdarch-verify/Cargo.toml + - uses: actions/checkout@v6 + - name: Install Rust + run: rustup update nightly --no-self-update && rustup default nightly + - run: cargo test --manifest-path crates/stdarch-verify/Cargo.toml test: - needs: [style] + needs: [ style ] name: Test runs-on: ${{ matrix.target.os }} strategy: matrix: profile: - - dev - - release + - dev + - release target: - # Dockers that are run through docker on linux - - tuple: i686-unknown-linux-gnu - os: ubuntu-latest - - tuple: x86_64-unknown-linux-gnu - os: ubuntu-latest - - tuple: arm-unknown-linux-gnueabihf - os: ubuntu-latest - - tuple: armv7-unknown-linux-gnueabihf - os: ubuntu-latest - - tuple: aarch64-unknown-linux-gnu - os: ubuntu-latest - - tuple: aarch64_be-unknown-linux-gnu - os: ubuntu-latest - - tuple: riscv32gc-unknown-linux-gnu - os: ubuntu-latest - - tuple: riscv64gc-unknown-linux-gnu - os: ubuntu-latest - - tuple: powerpc-unknown-linux-gnu - os: ubuntu-latest - - tuple: powerpc64-unknown-linux-gnu - os: ubuntu-latest - - tuple: powerpc64le-unknown-linux-gnu - os: ubuntu-latest - # MIPS targets disabled since they are dropped to tier 3. - # See https://github.com/rust-lang/compiler-team/issues/648 - #- tuple: mips-unknown-linux-gnu - # os: ubuntu-latest - #- tuple: mips64-unknown-linux-gnuabi64 - # os: ubuntu-latest - #- tuple: mips64el-unknown-linux-gnuabi64 - # os: ubuntu-latest - #- tuple: mipsel-unknown-linux-musl - # os: ubuntu-latest - - tuple: s390x-unknown-linux-gnu - os: ubuntu-latest - - tuple: i586-unknown-linux-gnu - os: ubuntu-latest - - tuple: nvptx64-nvidia-cuda - os: ubuntu-latest - - tuple: amdgcn-amd-amdhsa - os: ubuntu-latest - - tuple: thumbv6m-none-eabi - os: ubuntu-latest - - tuple: thumbv7m-none-eabi - os: ubuntu-latest - - tuple: thumbv7em-none-eabi - os: ubuntu-latest - - tuple: thumbv7em-none-eabihf - os: ubuntu-latest - - tuple: loongarch64-unknown-linux-gnu - os: ubuntu-latest - # hexagon doesn't build at the moment due to a libc issue. - # - tuple: hexagon-unknown-linux-musl - # os: ubuntu-latest - - tuple: wasm32-wasip1 - os: ubuntu-latest - - # macOS targets - - tuple: x86_64-apple-darwin - os: macos-15-intel - - tuple: x86_64-apple-ios-macabi - os: macos-15-intel - - tuple: aarch64-apple-darwin - os: macos-15 - - tuple: aarch64-apple-ios-macabi - os: macos-15 - # FIXME: gh-actions build environment doesn't have linker support - # - tuple: i686-apple-darwin - # os: macos-13 - - # Windows targets - - tuple: x86_64-pc-windows-msvc - os: windows-2025 - - tuple: i686-pc-windows-msvc - os: windows-2025 - - tuple: aarch64-pc-windows-msvc - os: windows-11-arm - - tuple: arm64ec-pc-windows-msvc - os: windows-11-arm - - tuple: x86_64-pc-windows-gnu - os: windows-2025 - # - tuple: i686-pc-windows-gnu - # os: windows-latest - - # Add additional variables to the matrix variations generated above using `include`: - include: - # `TEST_EVERYTHING` setups - there should be at least 1 for each architecture - - target: - tuple: aarch64-unknown-linux-gnu + # Dockers that are run through docker on linux + - tuple: i686-unknown-linux-gnu + os: ubuntu-latest + - tuple: x86_64-unknown-linux-gnu + os: ubuntu-latest + - tuple: arm-unknown-linux-gnueabihf os: ubuntu-latest - test_everything: true - - target: - tuple: aarch64_be-unknown-linux-gnu + - tuple: armv7-unknown-linux-gnueabihf os: ubuntu-latest - test_everything: true - build_std: true - - target: - tuple: armv7-unknown-linux-gnueabihf + - tuple: aarch64-unknown-linux-gnu os: ubuntu-latest - test_everything: true - - target: - tuple: loongarch64-unknown-linux-gnu + - tuple: aarch64_be-unknown-linux-gnu os: ubuntu-latest - test_everything: true - - target: - tuple: powerpc-unknown-linux-gnu + - tuple: riscv32gc-unknown-linux-gnu os: ubuntu-latest - disable_assert_instr: true - test_everything: true - - target: - tuple: powerpc64-unknown-linux-gnu + - tuple: riscv64gc-unknown-linux-gnu os: ubuntu-latest - disable_assert_instr: true - test_everything: true - - target: - tuple: powerpc64le-unknown-linux-gnu + - tuple: powerpc-unknown-linux-gnu os: ubuntu-latest - test_everything: true - - target: - tuple: riscv32gc-unknown-linux-gnu + - tuple: powerpc64-unknown-linux-gnu os: ubuntu-latest - test_everything: true - build_std: true - - target: - tuple: riscv64gc-unknown-linux-gnu + - tuple: powerpc64le-unknown-linux-gnu os: ubuntu-latest - test_everything: true - - target: - tuple: s390x-unknown-linux-gnu + # MIPS targets disabled since they are dropped to tier 3. + # See https://github.com/rust-lang/compiler-team/issues/648 + #- tuple: mips-unknown-linux-gnu + # os: ubuntu-latest + #- tuple: mips64-unknown-linux-gnuabi64 + # os: ubuntu-latest + #- tuple: mips64el-unknown-linux-gnuabi64 + # os: ubuntu-latest + #- tuple: mipsel-unknown-linux-musl + # os: ubuntu-latest + - tuple: s390x-unknown-linux-gnu os: ubuntu-latest - test_everything: true - - target: - tuple: x86_64-unknown-linux-gnu + - tuple: i586-unknown-linux-gnu os: ubuntu-latest - test_everything: true - # MIPS targets disabled since they are dropped to tier 3. - # See https://github.com/rust-lang/compiler-team/issues/648 - #- target: - # tuple: mips-unknown-linux-gnu - # os: ubuntu-latest - # norun: true - #- target: - # tuple: mips64-unknown-linux-gnuabi64 - # os: ubuntu-latest - # norun: true - #- target: - # tuple: mips64el-unknown-linux-gnuabi64 - # os: ubuntu-latest - # norun: true - #- target: - # tuple: mipsel-unknown-linux-musl - # os: ubuntu-latest - # norun: true - - target: - tuple: aarch64-apple-darwin + - tuple: nvptx64-nvidia-cuda + os: ubuntu-latest + - tuple: amdgcn-amd-amdhsa + os: ubuntu-latest + - tuple: thumbv6m-none-eabi + os: ubuntu-latest + - tuple: thumbv7m-none-eabi + os: ubuntu-latest + - tuple: thumbv7em-none-eabi + os: ubuntu-latest + - tuple: thumbv7em-none-eabihf + os: ubuntu-latest + - tuple: loongarch64-unknown-linux-gnu + os: ubuntu-latest + # hexagon doesn't build at the moment due to a libc issue. + # - tuple: hexagon-unknown-linux-musl + # os: ubuntu-latest + - tuple: wasm32-wasip1 + os: ubuntu-latest + + # macOS targets + - tuple: x86_64-apple-darwin + os: macos-15-intel + - tuple: x86_64-apple-ios-macabi + os: macos-15-intel + - tuple: aarch64-apple-darwin os: macos-15 - norun: true # https://github.com/rust-lang/stdarch/issues/1206 - - target: - tuple: aarch64-apple-ios-macabi + - tuple: aarch64-apple-ios-macabi os: macos-15 - norun: true # https://github.com/rust-lang/stdarch/issues/1206 - - target: - tuple: amdgcn-amd-amdhsa - os: ubuntu-latest - norun: true + # FIXME: gh-actions build environment doesn't have linker support + # - tuple: i686-apple-darwin + # os: macos-13 + + # Windows targets + - tuple: x86_64-pc-windows-msvc + os: windows-2025 + - tuple: i686-pc-windows-msvc + os: windows-2025 + - tuple: aarch64-pc-windows-msvc + os: windows-11-arm + - tuple: arm64ec-pc-windows-msvc + os: windows-11-arm + - tuple: x86_64-pc-windows-gnu + os: windows-2025 + # - tuple: i686-pc-windows-gnu + # os: windows-latest + + # Add additional variables to the matrix variations generated above using `include`: + include: + # `TEST_EVERYTHING` setups - there should be at least 1 for each architecture + - target: + tuple: aarch64-unknown-linux-gnu + os: ubuntu-latest + test_everything: true + - target: + tuple: aarch64_be-unknown-linux-gnu + os: ubuntu-latest + test_everything: true + build_std: true + - target: + tuple: armv7-unknown-linux-gnueabihf + os: ubuntu-latest + test_everything: true + - target: + tuple: loongarch64-unknown-linux-gnu + os: ubuntu-latest + test_everything: true + - target: + tuple: powerpc-unknown-linux-gnu + os: ubuntu-latest + disable_assert_instr: true + test_everything: true + - target: + tuple: powerpc64-unknown-linux-gnu + os: ubuntu-latest + disable_assert_instr: true + test_everything: true + - target: + tuple: powerpc64le-unknown-linux-gnu + os: ubuntu-latest + test_everything: true + - target: + tuple: riscv32gc-unknown-linux-gnu + os: ubuntu-latest + test_everything: true + build_std: true + - target: + tuple: riscv64gc-unknown-linux-gnu + os: ubuntu-latest + test_everything: true + - target: + tuple: s390x-unknown-linux-gnu + os: ubuntu-latest + test_everything: true + - target: + tuple: x86_64-unknown-linux-gnu + os: ubuntu-latest + test_everything: true + # MIPS targets disabled since they are dropped to tier 3. + # See https://github.com/rust-lang/compiler-team/issues/648 + #- target: + # tuple: mips-unknown-linux-gnu + # os: ubuntu-latest + # norun: true + #- target: + # tuple: mips64-unknown-linux-gnuabi64 + # os: ubuntu-latest + # norun: true + #- target: + # tuple: mips64el-unknown-linux-gnuabi64 + # os: ubuntu-latest + # norun: true + #- target: + # tuple: mipsel-unknown-linux-musl + # os: ubuntu-latest + # norun: true + - target: + tuple: aarch64-apple-darwin + os: macos-15 + norun: true # https://github.com/rust-lang/stdarch/issues/1206 + - target: + tuple: aarch64-apple-ios-macabi + os: macos-15 + norun: true # https://github.com/rust-lang/stdarch/issues/1206 + - target: + tuple: amdgcn-amd-amdhsa + os: ubuntu-latest + norun: true # hexagon doesn't build at the moment due to a libc issue. # - target: # tuple: hexagon-unknown-linux-musl @@ -218,59 +218,61 @@ jobs: # build_std: true steps: - - uses: actions/checkout@v6 - - name: Install Rust - run: | - rustup update nightly --no-self-update - rustup default nightly - shell: bash + - uses: actions/checkout@v6 + - name: Install Rust + run: | + rustup update nightly --no-self-update + rustup default nightly + shell: bash + + - run: rustup component add rustfmt - - run: rustup target add ${{ matrix.target.tuple }} - shell: bash - if: matrix.build_std == '' && matrix.target.tuple != 'amdgcn-amd-amdhsa' - - run: | - rustup component add rust-src - echo "CARGO_UNSTABLE_BUILD_STD=std" >> $GITHUB_ENV - shell: bash - if: matrix.build_std != '' - - run: | - rustup component add rust-src - echo "CARGO_UNSTABLE_BUILD_STD=core,alloc" >> $GITHUB_ENV - shell: bash - if: matrix.target.tuple == 'amdgcn-amd-amdhsa' + - run: rustup target add ${{ matrix.target.tuple }} + shell: bash + if: matrix.build_std == '' && matrix.target.tuple != 'amdgcn-amd-amdhsa' + - run: | + rustup component add rust-src + echo "CARGO_UNSTABLE_BUILD_STD=std" >> $GITHUB_ENV + shell: bash + if: matrix.build_std != '' + - run: | + rustup component add rust-src + echo "CARGO_UNSTABLE_BUILD_STD=core,alloc" >> $GITHUB_ENV + shell: bash + if: matrix.target.tuple == 'amdgcn-amd-amdhsa' - # Configure some env vars based on matrix configuration - - run: echo "PROFILE=${{matrix.profile}}" >> $GITHUB_ENV - shell: bash - - run: echo "NORUN=1" >> $GITHUB_ENV - shell: bash - if: matrix.norun != '' || startsWith(matrix.target.tuple, 'thumb') || matrix.target.tuple == 'nvptx64-nvidia-cuda' - - run: echo "STDARCH_TEST_EVERYTHING=1" >> $GITHUB_ENV - shell: bash - if: matrix.test_everything != '' - - run: echo "STDARCH_DISABLE_ASSERT_INSTR=1" >> $GITHUB_ENV - shell: bash - if: matrix.disable_assert_instr != '' - - run: echo "NOSTD=1" >> $GITHUB_ENV - shell: bash - if: startsWith(matrix.target.tuple, 'thumb') || matrix.target.tuple == 'nvptx64-nvidia-cuda' || matrix.target.tuple == 'amdgcn-amd-amdhsa' + # Configure some env vars based on matrix configuration + - run: echo "PROFILE=${{matrix.profile}}" >> $GITHUB_ENV + shell: bash + - run: echo "NORUN=1" >> $GITHUB_ENV + shell: bash + if: matrix.norun != '' || startsWith(matrix.target.tuple, 'thumb') || matrix.target.tuple == 'nvptx64-nvidia-cuda' + - run: echo "STDARCH_TEST_EVERYTHING=1" >> $GITHUB_ENV + shell: bash + if: matrix.test_everything != '' + - run: echo "STDARCH_DISABLE_ASSERT_INSTR=1" >> $GITHUB_ENV + shell: bash + if: matrix.disable_assert_instr != '' + - run: echo "NOSTD=1" >> $GITHUB_ENV + shell: bash + if: startsWith(matrix.target.tuple, 'thumb') || matrix.target.tuple == 'nvptx64-nvidia-cuda' || matrix.target.tuple == 'amdgcn-amd-amdhsa' - # Windows & OSX go straight to `run.sh` ... - - run: ./ci/run.sh - shell: bash - if: matrix.target.os != 'ubuntu-latest' || startsWith(matrix.target.tuple, 'thumb') - env: - TARGET: ${{ matrix.target.tuple }} + # Windows & OSX go straight to `run.sh` ... + - run: ./ci/run.sh + shell: bash + if: matrix.target.os != 'ubuntu-latest' || startsWith(matrix.target.tuple, 'thumb') + env: + TARGET: ${{ matrix.target.tuple }} - # ... while Linux goes to `run-docker.sh` - - run: ./ci/run-docker.sh ${{ matrix.target.tuple }} - shell: bash - if: matrix.target.os == 'ubuntu-latest' && !startsWith(matrix.target.tuple, 'thumb') - env: - TARGET: ${{ matrix.target.tuple }} + # ... while Linux goes to `run-docker.sh` + - run: ./ci/run-docker.sh ${{ matrix.target.tuple }} + shell: bash + if: matrix.target.os == 'ubuntu-latest' && !startsWith(matrix.target.tuple, 'thumb') + env: + TARGET: ${{ matrix.target.tuple }} intrinsic-test: - needs: [style] + needs: [ style ] name: Intrinsic Test runs-on: ubuntu-latest strategy: @@ -279,9 +281,10 @@ jobs: - aarch64-unknown-linux-gnu - aarch64_be-unknown-linux-gnu - armv7-unknown-linux-gnueabihf + - loongarch64-unknown-linux-gnu - x86_64-unknown-linux-gnu - profile: [dev, release] - cc: [clang, gcc] + profile: [ dev, release ] + cc: [ clang, gcc ] include: - target: aarch64_be-unknown-linux-gnu build_std: true @@ -295,74 +298,71 @@ jobs: - target: armv7-unknown-linux-gnueabihf cc: gcc steps: - - uses: actions/checkout@v6 - - name: Install Rust - run: | - rustup update nightly --no-self-update - rustup default nightly - - run: rustup target add ${{ matrix.target }} - if: ${{ (matrix.build_std || false) == false }} - - run: | - rustup component add rust-src - echo "CARGO_UNSTABLE_BUILD_STD=std" >> $GITHUB_ENV - if: ${{ matrix.build_std }} - - run: rustup component add rustfmt + - uses: actions/checkout@v6 + - name: Install Rust + run: | + rustup update nightly --no-self-update + rustup default nightly + - run: rustup target add ${{ matrix.target }} + if: ${{ (matrix.build_std || false) == false }} + - run: | + rustup component add rust-src + echo "CARGO_UNSTABLE_BUILD_STD=std" >> $GITHUB_ENV + if: ${{ matrix.build_std }} + - run: rustup component add rustfmt - # Configure some env vars based on matrix configuration - - run: echo "PROFILE=${{ matrix.profile }}" >> $GITHUB_ENV - - run: ./ci/intrinsic-test-docker.sh ${{ matrix.target }} ${{ matrix.cc }} - if: ${{ !startsWith(matrix.target, 'thumb') }} - env: - TARGET: ${{ matrix.target }} + # Configure some env vars based on matrix configuration + - run: echo "PROFILE=${{ matrix.profile }}" >> $GITHUB_ENV + - run: ./ci/intrinsic-test-docker.sh ${{ matrix.target }} ${{ matrix.cc }} + if: ${{ !startsWith(matrix.target, 'thumb') }} + env: + TARGET: ${{ matrix.target }} # Check that the generated files agree with the checked-in versions. check-stdarch-gen: - needs: [style] + needs: [ style ] name: Check stdarch-gen-{arm, loongarch, hexagon} output runs-on: ubuntu-latest env: STDARCH_GEN_MODE: check steps: - - uses: actions/checkout@v6 - - name: Install Rust - run: rustup update nightly && rustup default nightly && rustup component add rustfmt - - name: Check arm spec - run: | - cargo run --bin=stdarch-gen-arm --release -- crates/stdarch-gen-arm/spec - - name: Check loongarch lsx - run: | - cargo run -p stdarch-gen-loongarch --release -- lsx - git diff --exit-code - - name: Check loongarch lasx - run: | - cargo run -p stdarch-gen-loongarch --release -- lasx - git diff --exit-code - - name: Check hexagon - run: | - cargo run -p stdarch-gen-hexagon --release - git diff --exit-code - + - uses: actions/checkout@v6 + - name: Install Rust + run: rustup update nightly && rustup default nightly && rustup component add rustfmt + - name: Check arm spec + run: | + cargo run --bin=stdarch-gen-arm --release -- crates/stdarch-gen-arm/spec + - name: Check loongarch lsx + run: | + cargo run -p stdarch-gen-loongarch --release -- lsx + - name: Check loongarch lasx + run: | + cargo run -p stdarch-gen-loongarch --release -- lasx + - name: Check hexagon + run: | + cargo run -p stdarch-gen-hexagon --release + # Run some tests with Miri. Most stdarch functions use platform-specific intrinsics # that Miri does not support. Also Miri is reltively slow. # # Below we run some tests where Miri might catch UB, for instance on intrinsics that read from # or write to pointers. miri: - needs: [style] + needs: [ style ] name: Run some tests with miri runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 - - name: Install Rust - run: rustup update nightly && rustup default nightly && rustup component add miri - - name: Run miri tests - env: - TARGET: "aarch64-unknown-linux-gnu" - RUSTFLAGS: "-Ctarget-cpu=neoverse-v3" - run: | - # read filters and join them with a space. - FILTERS=$(cat aarch64-miri-tests.txt | tr '\n' ' ') - cargo miri test -p core_arch --target aarch64-unknown-linux-gnu -- $FILTERS + - uses: actions/checkout@v6 + - name: Install Rust + run: rustup update nightly && rustup default nightly && rustup component add miri + - name: Run miri tests + env: + TARGET: "aarch64-unknown-linux-gnu" + RUSTFLAGS: "-Ctarget-cpu=neoverse-v3" + run: | + # read filters and join them with a space. + FILTERS=$(cat aarch64-miri-tests.txt | tr '\n' ' ') + cargo miri test -p core_arch --target aarch64-unknown-linux-gnu -- $FILTERS conclusion: needs: diff --git a/library/stdarch/Cargo.lock b/library/stdarch/Cargo.lock index 9ec3227f49e7d..490161626c164 100644 --- a/library/stdarch/Cargo.lock +++ b/library/stdarch/Cargo.lock @@ -88,6 +88,16 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +[[package]] +name = "bstr" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb31b46c14244e20ee9984b11bf5c992b91fb6939fea616e3512c8baecdbe5f" +dependencies = [ + "memchr", + "serde_core", +] + [[package]] name = "cc" version = "1.2.59" @@ -800,10 +810,20 @@ dependencies = [ "syn", ] +[[package]] +name = "similar" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f66ca1f7aca2474dc10c942eb22feffc897735f54cd1db90138c2fddb490987" +dependencies = [ + "bstr", +] + [[package]] name = "stdarch-gen-arm" version = "0.1.0" dependencies = [ + "clap", "itertools", "proc-macro2", "quote", @@ -819,6 +839,7 @@ dependencies = [ name = "stdarch-gen-common" version = "0.1.0" dependencies = [ + "similar", "tempfile", ] @@ -826,6 +847,7 @@ dependencies = [ name = "stdarch-gen-hexagon" version = "0.1.0" dependencies = [ + "clap", "regex", "stdarch-gen-common", ] @@ -834,6 +856,7 @@ dependencies = [ name = "stdarch-gen-loongarch" version = "0.1.0" dependencies = [ + "clap", "rand 0.9.4", "stdarch-gen-common", ] diff --git a/library/stdarch/ci/docker/loongarch64-unknown-linux-gnu/Dockerfile b/library/stdarch/ci/docker/loongarch64-unknown-linux-gnu/Dockerfile index e803dae1006a0..8cbd2d8080002 100644 --- a/library/stdarch/ci/docker/loongarch64-unknown-linux-gnu/Dockerfile +++ b/library/stdarch/ci/docker/loongarch64-unknown-linux-gnu/Dockerfile @@ -3,8 +3,15 @@ FROM ubuntu:25.10 RUN apt-get update && \ apt-get install -y --no-install-recommends \ gcc libc6-dev qemu-user ca-certificates \ - gcc-loongarch64-linux-gnu libc6-dev-loong64-cross + gcc-loongarch64-linux-gnu libc6-dev-loong64-cross \ + wget +RUN wget https://ci-mirrors.rust-lang.org/llvm/llvm-22.1.4-x86_64.tar.gz -O llvm.tar.xz +RUN mkdir llvm +RUN tar -xvf llvm.tar.xz --strip-components=1 -C llvm + +ENV CLANG_PATH="/llvm/bin/clang" +ENV GCC_PATH=loongarch64-linux-gnu-gcc ENV CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNU_LINKER=loongarch64-linux-gnu-gcc \ CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNU_RUNNER="qemu-loongarch64 -cpu max -L /usr/loongarch64-linux-gnu" \ diff --git a/library/stdarch/ci/intrinsic-test.sh b/library/stdarch/ci/intrinsic-test.sh index 3966aa88d8f41..4b7a3d27ea4e2 100755 --- a/library/stdarch/ci/intrinsic-test.sh +++ b/library/stdarch/ci/intrinsic-test.sh @@ -65,6 +65,12 @@ case ${TARGET} in ARCH=x86 RUNTIME_RUSTFLAGS= ;; + + loongarch64*) + export CFLAGS="-I/usr/loongarch64-linux-gnu/include/" + ARCH=loongarch64 + RUNTIME_RUSTFLAGS=-Ctarget-feature=+lsx,+lasx,+frecipe + ;; *) ;; @@ -80,6 +86,14 @@ case "${TARGET}" in --target "${TARGET}" \ --cc-arg-style "${CC_ARG_STYLE}" ;; + loongarch64*) + cargo run "${INTRINSIC_TEST}" --release \ + --bin intrinsic-test -- crates/stdarch-gen-loongarch \ + --skip "crates/intrinsic-test/missing_${ARCH}_common.txt" \ + --skip "crates/intrinsic-test/missing_${ARCH}_${CC_KIND}.txt" \ + --target "${TARGET}" \ + --cc-arg-style "${CC_ARG_STYLE}" + ;; *) cargo run "${INTRINSIC_TEST}" --release \ --bin intrinsic-test -- intrinsics_data/arm_intrinsics.json \ diff --git a/library/stdarch/crates/core_arch/Cargo.toml b/library/stdarch/crates/core_arch/Cargo.toml index ab8b9bcd9fcef..ee3eff825c790 100644 --- a/library/stdarch/crates/core_arch/Cargo.toml +++ b/library/stdarch/crates/core_arch/Cargo.toml @@ -24,6 +24,9 @@ stdarch-test = { version = "0.*", path = "../stdarch-test" } [target.'cfg(all(target_arch = "x86_64", target_os = "linux"))'.dev-dependencies] syscalls = { version = "0.6.18", default-features = false } +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(target_has_reliable_f16)'] } + [lints.clippy] too_long_first_doc_paragraph = "allow" missing_transmute_annotations = "allow" diff --git a/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs b/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs index 9fb0e1464683e..72ce377acc9fb 100644 --- a/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs +++ b/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs @@ -25081,7 +25081,7 @@ pub unsafe fn vst1q_f64_x4(a: *mut f64, b: float64x2x4_t) { #[stable(feature = "neon_intrinsics", since = "1.59.0")] pub unsafe fn vst1_lane_f64(a: *mut f64, b: float64x1_t) { static_assert!(LANE == 0); - *a = simd_extract!(b, LANE as u32); + core::ptr::write_unaligned(a, simd_extract!(b, LANE as u32)) } #[doc = "Store multiple single-element structures from one, two, three, or four registers"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vst1q_lane_f64)"] @@ -25094,7 +25094,7 @@ pub unsafe fn vst1_lane_f64(a: *mut f64, b: float64x1_t) { #[stable(feature = "neon_intrinsics", since = "1.59.0")] pub unsafe fn vst1q_lane_f64(a: *mut f64, b: float64x2_t) { static_assert_uimm_bits!(LANE, 1); - *a = simd_extract!(b, LANE as u32); + core::ptr::write_unaligned(a, simd_extract!(b, LANE as u32)) } #[doc = "Store multiple 2-element structures from two registers"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vst2_f64)"] diff --git a/library/stdarch/crates/core_arch/src/aarch64/neon/mod.rs b/library/stdarch/crates/core_arch/src/aarch64/neon/mod.rs index c66702814cfb2..e04c93ac1acae 100644 --- a/library/stdarch/crates/core_arch/src/aarch64/neon/mod.rs +++ b/library/stdarch/crates/core_arch/src/aarch64/neon/mod.rs @@ -120,7 +120,7 @@ pub unsafe fn vld1q_dup_f64(ptr: *const f64) -> float64x2_t { #[stable(feature = "neon_intrinsics", since = "1.59.0")] pub unsafe fn vld1_lane_f64(ptr: *const f64, src: float64x1_t) -> float64x1_t { static_assert!(LANE == 0); - simd_insert!(src, LANE as u32, *ptr) + simd_insert!(src, LANE as u32, crate::ptr::read_unaligned(ptr)) } /// Load one single-element structure to one lane of one register. @@ -131,7 +131,7 @@ pub unsafe fn vld1_lane_f64(ptr: *const f64, src: float64x1_t) #[stable(feature = "neon_intrinsics", since = "1.59.0")] pub unsafe fn vld1q_lane_f64(ptr: *const f64, src: float64x2_t) -> float64x2_t { static_assert_uimm_bits!(LANE, 1); - simd_insert!(src, LANE as u32, *ptr) + simd_insert!(src, LANE as u32, crate::ptr::read_unaligned(ptr)) } /// Bitwise Select instructions. This instruction sets each bit in the destination SIMD&FP register diff --git a/library/stdarch/crates/core_arch/src/aarch64/sve/generated.rs b/library/stdarch/crates/core_arch/src/aarch64/sve/generated.rs index 324b5bccf3777..75393f594ad64 100644 --- a/library/stdarch/crates/core_arch/src/aarch64/sve/generated.rs +++ b/library/stdarch/crates/core_arch/src/aarch64/sve/generated.rs @@ -11,8 +11,8 @@ use stdarch_test::assert_instr; use super::*; -use crate::core_arch::arch::aarch64::*; use super::{AsSigned, AsUnsigned}; +use crate::core_arch::arch::aarch64::*; #[doc = "Absolute difference"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svabd[_f32]_m)"] diff --git a/library/stdarch/crates/core_arch/src/arm_shared/hints.rs b/library/stdarch/crates/core_arch/src/arm_shared/hints.rs index ce64e8341e7eb..f60c107a2aaf3 100644 --- a/library/stdarch/crates/core_arch/src/arm_shared/hints.rs +++ b/library/stdarch/crates/core_arch/src/arm_shared/hints.rs @@ -17,7 +17,7 @@ ))] #[inline(always)] #[unstable(feature = "stdarch_arm_hints", issue = "117218")] -pub unsafe fn __wfi() { +pub fn __wfi() { hint(HINT_WFI); } @@ -36,7 +36,7 @@ pub unsafe fn __wfi() { ))] #[inline(always)] #[unstable(feature = "stdarch_arm_hints", issue = "117218")] -pub unsafe fn __wfe() { +pub fn __wfe() { hint(HINT_WFE); } @@ -54,7 +54,7 @@ pub unsafe fn __wfe() { ))] #[inline(always)] #[unstable(feature = "stdarch_arm_hints", issue = "117218")] -pub unsafe fn __sev() { +pub fn __sev() { hint(HINT_SEV); } @@ -72,7 +72,7 @@ pub unsafe fn __sev() { ))] #[inline(always)] #[unstable(feature = "stdarch_arm_hints", issue = "117218")] -pub unsafe fn __sevl() { +pub fn __sevl() { hint(HINT_SEVL); } @@ -94,7 +94,7 @@ pub unsafe fn __sevl() { ))] #[inline(always)] #[unstable(feature = "stdarch_arm_hints", issue = "117218")] -pub unsafe fn __yield() { +pub fn __yield() { hint(HINT_YIELD); } @@ -106,7 +106,7 @@ pub unsafe fn __yield() { /// will increase execution time. #[inline(always)] #[unstable(feature = "stdarch_arm_hints", issue = "117218")] -pub unsafe fn __nop() { +pub fn __nop() { crate::arch::asm!("nop", options(nomem, nostack, preserves_flags)); } diff --git a/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs b/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs index 094b13ca8023a..237f8fb413c99 100644 --- a/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs +++ b/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs @@ -18265,7 +18265,7 @@ pub unsafe fn vld1q_dup_f16(ptr: *const f16) -> float16x8_t { #[inline] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr("vld1.32"))] +#[cfg_attr(all(test, target_arch = "arm"), assert_instr("ldr"))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), assert_instr(ld1r) @@ -18279,7 +18279,7 @@ pub unsafe fn vld1q_dup_f16(ptr: *const f16) -> float16x8_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub unsafe fn vld1_dup_f32(ptr: *const f32) -> float32x2_t { - transmute(f32x2::splat(*ptr)) + transmute(f32x2::splat(crate::ptr::read_unaligned(ptr))) } #[doc = "Load one single-element structure and Replicate to all lanes (of one register)."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1_dup_p16)"] @@ -18302,7 +18302,7 @@ pub unsafe fn vld1_dup_f32(ptr: *const f32) -> float32x2_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub unsafe fn vld1_dup_p16(ptr: *const p16) -> poly16x4_t { - transmute(u16x4::splat(*ptr)) + transmute(u16x4::splat(crate::ptr::read_unaligned(ptr))) } #[doc = "Load one single-element structure and Replicate to all lanes (of one register)."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1_dup_p8)"] @@ -18325,7 +18325,7 @@ pub unsafe fn vld1_dup_p16(ptr: *const p16) -> poly16x4_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub unsafe fn vld1_dup_p8(ptr: *const p8) -> poly8x8_t { - transmute(u8x8::splat(*ptr)) + transmute(u8x8::splat(crate::ptr::read_unaligned(ptr))) } #[doc = "Load one single-element structure and Replicate to all lanes (of one register)."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1_dup_s16)"] @@ -18348,7 +18348,7 @@ pub unsafe fn vld1_dup_p8(ptr: *const p8) -> poly8x8_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub unsafe fn vld1_dup_s16(ptr: *const i16) -> int16x4_t { - transmute(i16x4::splat(*ptr)) + transmute(i16x4::splat(crate::ptr::read_unaligned(ptr))) } #[doc = "Load one single-element structure and Replicate to all lanes (of one register)."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1_dup_s32)"] @@ -18371,7 +18371,7 @@ pub unsafe fn vld1_dup_s16(ptr: *const i16) -> int16x4_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub unsafe fn vld1_dup_s32(ptr: *const i32) -> int32x2_t { - transmute(i32x2::splat(*ptr)) + transmute(i32x2::splat(crate::ptr::read_unaligned(ptr))) } #[doc = "Load one single-element structure and Replicate to all lanes (of one register)."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1_dup_s8)"] @@ -18394,7 +18394,7 @@ pub unsafe fn vld1_dup_s32(ptr: *const i32) -> int32x2_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub unsafe fn vld1_dup_s8(ptr: *const i8) -> int8x8_t { - transmute(i8x8::splat(*ptr)) + transmute(i8x8::splat(crate::ptr::read_unaligned(ptr))) } #[doc = "Load one single-element structure and Replicate to all lanes (of one register)."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1_dup_u16)"] @@ -18417,7 +18417,7 @@ pub unsafe fn vld1_dup_s8(ptr: *const i8) -> int8x8_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub unsafe fn vld1_dup_u16(ptr: *const u16) -> uint16x4_t { - transmute(u16x4::splat(*ptr)) + transmute(u16x4::splat(crate::ptr::read_unaligned(ptr))) } #[doc = "Load one single-element structure and Replicate to all lanes (of one register)."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1_dup_u32)"] @@ -18440,7 +18440,7 @@ pub unsafe fn vld1_dup_u16(ptr: *const u16) -> uint16x4_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub unsafe fn vld1_dup_u32(ptr: *const u32) -> uint32x2_t { - transmute(u32x2::splat(*ptr)) + transmute(u32x2::splat(crate::ptr::read_unaligned(ptr))) } #[doc = "Load one single-element structure and Replicate to all lanes (of one register)."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1_dup_u8)"] @@ -18463,7 +18463,7 @@ pub unsafe fn vld1_dup_u32(ptr: *const u32) -> uint32x2_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub unsafe fn vld1_dup_u8(ptr: *const u8) -> uint8x8_t { - transmute(u8x8::splat(*ptr)) + transmute(u8x8::splat(crate::ptr::read_unaligned(ptr))) } #[doc = "Load one single-element structure and Replicate to all lanes (of one register)."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1q_dup_f32)"] @@ -18472,7 +18472,7 @@ pub unsafe fn vld1_dup_u8(ptr: *const u8) -> uint8x8_t { #[inline] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr("vld1.32"))] +#[cfg_attr(all(test, target_arch = "arm"), assert_instr("ldr"))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), assert_instr(ld1r) @@ -18486,7 +18486,7 @@ pub unsafe fn vld1_dup_u8(ptr: *const u8) -> uint8x8_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub unsafe fn vld1q_dup_f32(ptr: *const f32) -> float32x4_t { - transmute(f32x4::splat(*ptr)) + transmute(f32x4::splat(crate::ptr::read_unaligned(ptr))) } #[doc = "Load one single-element structure and Replicate to all lanes (of one register)."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1q_dup_p16)"] @@ -18509,7 +18509,7 @@ pub unsafe fn vld1q_dup_f32(ptr: *const f32) -> float32x4_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub unsafe fn vld1q_dup_p16(ptr: *const p16) -> poly16x8_t { - transmute(u16x8::splat(*ptr)) + transmute(u16x8::splat(crate::ptr::read_unaligned(ptr))) } #[doc = "Load one single-element structure and Replicate to all lanes (of one register)."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1q_dup_p8)"] @@ -18532,7 +18532,7 @@ pub unsafe fn vld1q_dup_p16(ptr: *const p16) -> poly16x8_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub unsafe fn vld1q_dup_p8(ptr: *const p8) -> poly8x16_t { - transmute(u8x16::splat(*ptr)) + transmute(u8x16::splat(crate::ptr::read_unaligned(ptr))) } #[doc = "Load one single-element structure and Replicate to all lanes (of one register)."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1q_dup_s16)"] @@ -18555,7 +18555,7 @@ pub unsafe fn vld1q_dup_p8(ptr: *const p8) -> poly8x16_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub unsafe fn vld1q_dup_s16(ptr: *const i16) -> int16x8_t { - transmute(i16x8::splat(*ptr)) + transmute(i16x8::splat(crate::ptr::read_unaligned(ptr))) } #[doc = "Load one single-element structure and Replicate to all lanes (of one register)."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1q_dup_s32)"] @@ -18578,7 +18578,7 @@ pub unsafe fn vld1q_dup_s16(ptr: *const i16) -> int16x8_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub unsafe fn vld1q_dup_s32(ptr: *const i32) -> int32x4_t { - transmute(i32x4::splat(*ptr)) + transmute(i32x4::splat(crate::ptr::read_unaligned(ptr))) } #[doc = "Load one single-element structure and Replicate to all lanes (of one register)."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1q_dup_s64)"] @@ -18587,7 +18587,7 @@ pub unsafe fn vld1q_dup_s32(ptr: *const i32) -> int32x4_t { #[inline] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr("vldr"))] +#[cfg_attr(all(test, target_arch = "arm"), assert_instr("vld1.8"))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), assert_instr(ld1r) @@ -18601,7 +18601,7 @@ pub unsafe fn vld1q_dup_s32(ptr: *const i32) -> int32x4_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub unsafe fn vld1q_dup_s64(ptr: *const i64) -> int64x2_t { - transmute(i64x2::splat(*ptr)) + transmute(i64x2::splat(crate::ptr::read_unaligned(ptr))) } #[doc = "Load one single-element structure and Replicate to all lanes (of one register)."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1q_dup_s8)"] @@ -18624,7 +18624,7 @@ pub unsafe fn vld1q_dup_s64(ptr: *const i64) -> int64x2_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub unsafe fn vld1q_dup_s8(ptr: *const i8) -> int8x16_t { - transmute(i8x16::splat(*ptr)) + transmute(i8x16::splat(crate::ptr::read_unaligned(ptr))) } #[doc = "Load one single-element structure and Replicate to all lanes (of one register)."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1q_dup_u16)"] @@ -18647,7 +18647,7 @@ pub unsafe fn vld1q_dup_s8(ptr: *const i8) -> int8x16_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub unsafe fn vld1q_dup_u16(ptr: *const u16) -> uint16x8_t { - transmute(u16x8::splat(*ptr)) + transmute(u16x8::splat(crate::ptr::read_unaligned(ptr))) } #[doc = "Load one single-element structure and Replicate to all lanes (of one register)."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1q_dup_u32)"] @@ -18670,7 +18670,7 @@ pub unsafe fn vld1q_dup_u16(ptr: *const u16) -> uint16x8_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub unsafe fn vld1q_dup_u32(ptr: *const u32) -> uint32x4_t { - transmute(u32x4::splat(*ptr)) + transmute(u32x4::splat(crate::ptr::read_unaligned(ptr))) } #[doc = "Load one single-element structure and Replicate to all lanes (of one register)."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1q_dup_u64)"] @@ -18679,7 +18679,7 @@ pub unsafe fn vld1q_dup_u32(ptr: *const u32) -> uint32x4_t { #[inline] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr("vldr"))] +#[cfg_attr(all(test, target_arch = "arm"), assert_instr("vld1.8"))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), assert_instr(ld1r) @@ -18693,7 +18693,7 @@ pub unsafe fn vld1q_dup_u32(ptr: *const u32) -> uint32x4_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub unsafe fn vld1q_dup_u64(ptr: *const u64) -> uint64x2_t { - transmute(u64x2::splat(*ptr)) + transmute(u64x2::splat(crate::ptr::read_unaligned(ptr))) } #[doc = "Load one single-element structure and Replicate to all lanes (of one register)."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1q_dup_u8)"] @@ -18716,7 +18716,7 @@ pub unsafe fn vld1q_dup_u64(ptr: *const u64) -> uint64x2_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub unsafe fn vld1q_dup_u8(ptr: *const u8) -> uint8x16_t { - transmute(u8x16::splat(*ptr)) + transmute(u8x16::splat(crate::ptr::read_unaligned(ptr))) } #[doc = "Load one single-element structure and Replicate to all lanes (of one register)."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1_dup_p64)"] @@ -19307,7 +19307,7 @@ pub unsafe fn vld1q_f32_x4(a: *const f32) -> float32x4x4_t { #[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld1_lane_f16(ptr: *const f16, src: float16x4_t) -> float16x4_t { static_assert_uimm_bits!(LANE, 2); - simd_insert!(src, LANE as u32, *ptr) + simd_insert!(src, LANE as u32, crate::ptr::read_unaligned(ptr)) } #[doc = "Load one single-element structure to one lane of one register"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1q_lane_f16)"] @@ -19327,7 +19327,7 @@ pub unsafe fn vld1_lane_f16(ptr: *const f16, src: float16x4_t) #[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld1q_lane_f16(ptr: *const f16, src: float16x8_t) -> float16x8_t { static_assert_uimm_bits!(LANE, 3); - simd_insert!(src, LANE as u32, *ptr) + simd_insert!(src, LANE as u32, crate::ptr::read_unaligned(ptr)) } #[doc = "Load one single-element structure to one lane of one register."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1_lane_f32)"] @@ -19337,7 +19337,7 @@ pub unsafe fn vld1q_lane_f16(ptr: *const f16, src: float16x8_t) #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[rustc_legacy_const_generics(2)] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr("vld1.32", LANE = 1))] +#[cfg_attr(all(test, target_arch = "arm"), assert_instr(ldr, LANE = 1))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), assert_instr(ld1, LANE = 1) @@ -19352,7 +19352,7 @@ pub unsafe fn vld1q_lane_f16(ptr: *const f16, src: float16x8_t) )] pub unsafe fn vld1_lane_f32(ptr: *const f32, src: float32x2_t) -> float32x2_t { static_assert_uimm_bits!(LANE, 1); - simd_insert!(src, LANE as u32, *ptr) + simd_insert!(src, LANE as u32, crate::ptr::read_unaligned(ptr)) } #[doc = "Load one single-element structure to one lane of one register."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1_lane_p16)"] @@ -19377,7 +19377,7 @@ pub unsafe fn vld1_lane_f32(ptr: *const f32, src: float32x2_t) )] pub unsafe fn vld1_lane_p16(ptr: *const p16, src: poly16x4_t) -> poly16x4_t { static_assert_uimm_bits!(LANE, 2); - simd_insert!(src, LANE as u32, *ptr) + simd_insert!(src, LANE as u32, crate::ptr::read_unaligned(ptr)) } #[doc = "Load one single-element structure to one lane of one register."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1_lane_p8)"] @@ -19402,7 +19402,7 @@ pub unsafe fn vld1_lane_p16(ptr: *const p16, src: poly16x4_t) - )] pub unsafe fn vld1_lane_p8(ptr: *const p8, src: poly8x8_t) -> poly8x8_t { static_assert_uimm_bits!(LANE, 3); - simd_insert!(src, LANE as u32, *ptr) + simd_insert!(src, LANE as u32, crate::ptr::read_unaligned(ptr)) } #[doc = "Load one single-element structure to one lane of one register."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1_lane_s16)"] @@ -19427,7 +19427,7 @@ pub unsafe fn vld1_lane_p8(ptr: *const p8, src: poly8x8_t) -> p )] pub unsafe fn vld1_lane_s16(ptr: *const i16, src: int16x4_t) -> int16x4_t { static_assert_uimm_bits!(LANE, 2); - simd_insert!(src, LANE as u32, *ptr) + simd_insert!(src, LANE as u32, crate::ptr::read_unaligned(ptr)) } #[doc = "Load one single-element structure to one lane of one register."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1_lane_s32)"] @@ -19452,7 +19452,7 @@ pub unsafe fn vld1_lane_s16(ptr: *const i16, src: int16x4_t) -> )] pub unsafe fn vld1_lane_s32(ptr: *const i32, src: int32x2_t) -> int32x2_t { static_assert_uimm_bits!(LANE, 1); - simd_insert!(src, LANE as u32, *ptr) + simd_insert!(src, LANE as u32, crate::ptr::read_unaligned(ptr)) } #[doc = "Load one single-element structure to one lane of one register."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1_lane_s64)"] @@ -19462,7 +19462,7 @@ pub unsafe fn vld1_lane_s32(ptr: *const i32, src: int32x2_t) -> #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[rustc_legacy_const_generics(2)] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(vldr, LANE = 0))] +#[cfg_attr(all(test, target_arch = "arm"), assert_instr("vld1.8", LANE = 0))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), assert_instr(ldr, LANE = 0) @@ -19477,7 +19477,7 @@ pub unsafe fn vld1_lane_s32(ptr: *const i32, src: int32x2_t) -> )] pub unsafe fn vld1_lane_s64(ptr: *const i64, src: int64x1_t) -> int64x1_t { static_assert!(LANE == 0); - simd_insert!(src, LANE as u32, *ptr) + simd_insert!(src, LANE as u32, crate::ptr::read_unaligned(ptr)) } #[doc = "Load one single-element structure to one lane of one register."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1_lane_s8)"] @@ -19502,7 +19502,7 @@ pub unsafe fn vld1_lane_s64(ptr: *const i64, src: int64x1_t) -> )] pub unsafe fn vld1_lane_s8(ptr: *const i8, src: int8x8_t) -> int8x8_t { static_assert_uimm_bits!(LANE, 3); - simd_insert!(src, LANE as u32, *ptr) + simd_insert!(src, LANE as u32, crate::ptr::read_unaligned(ptr)) } #[doc = "Load one single-element structure to one lane of one register."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1_lane_u16)"] @@ -19527,7 +19527,7 @@ pub unsafe fn vld1_lane_s8(ptr: *const i8, src: int8x8_t) -> in )] pub unsafe fn vld1_lane_u16(ptr: *const u16, src: uint16x4_t) -> uint16x4_t { static_assert_uimm_bits!(LANE, 2); - simd_insert!(src, LANE as u32, *ptr) + simd_insert!(src, LANE as u32, crate::ptr::read_unaligned(ptr)) } #[doc = "Load one single-element structure to one lane of one register."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1_lane_u32)"] @@ -19552,7 +19552,7 @@ pub unsafe fn vld1_lane_u16(ptr: *const u16, src: uint16x4_t) - )] pub unsafe fn vld1_lane_u32(ptr: *const u32, src: uint32x2_t) -> uint32x2_t { static_assert_uimm_bits!(LANE, 1); - simd_insert!(src, LANE as u32, *ptr) + simd_insert!(src, LANE as u32, crate::ptr::read_unaligned(ptr)) } #[doc = "Load one single-element structure to one lane of one register."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1_lane_u64)"] @@ -19562,7 +19562,7 @@ pub unsafe fn vld1_lane_u32(ptr: *const u32, src: uint32x2_t) - #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[rustc_legacy_const_generics(2)] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(vldr, LANE = 0))] +#[cfg_attr(all(test, target_arch = "arm"), assert_instr("vld1.8", LANE = 0))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), assert_instr(ldr, LANE = 0) @@ -19577,7 +19577,7 @@ pub unsafe fn vld1_lane_u32(ptr: *const u32, src: uint32x2_t) - )] pub unsafe fn vld1_lane_u64(ptr: *const u64, src: uint64x1_t) -> uint64x1_t { static_assert!(LANE == 0); - simd_insert!(src, LANE as u32, *ptr) + simd_insert!(src, LANE as u32, crate::ptr::read_unaligned(ptr)) } #[doc = "Load one single-element structure to one lane of one register."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1_lane_u8)"] @@ -19602,7 +19602,7 @@ pub unsafe fn vld1_lane_u64(ptr: *const u64, src: uint64x1_t) - )] pub unsafe fn vld1_lane_u8(ptr: *const u8, src: uint8x8_t) -> uint8x8_t { static_assert_uimm_bits!(LANE, 3); - simd_insert!(src, LANE as u32, *ptr) + simd_insert!(src, LANE as u32, crate::ptr::read_unaligned(ptr)) } #[doc = "Load one single-element structure to one lane of one register."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1q_lane_f32)"] @@ -19612,7 +19612,7 @@ pub unsafe fn vld1_lane_u8(ptr: *const u8, src: uint8x8_t) -> u #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[rustc_legacy_const_generics(2)] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr("vld1.32", LANE = 3))] +#[cfg_attr(all(test, target_arch = "arm"), assert_instr(ldr, LANE = 3))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), assert_instr(ld1, LANE = 3) @@ -19627,7 +19627,7 @@ pub unsafe fn vld1_lane_u8(ptr: *const u8, src: uint8x8_t) -> u )] pub unsafe fn vld1q_lane_f32(ptr: *const f32, src: float32x4_t) -> float32x4_t { static_assert_uimm_bits!(LANE, 2); - simd_insert!(src, LANE as u32, *ptr) + simd_insert!(src, LANE as u32, crate::ptr::read_unaligned(ptr)) } #[doc = "Load one single-element structure to one lane of one register."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1q_lane_p16)"] @@ -19652,7 +19652,7 @@ pub unsafe fn vld1q_lane_f32(ptr: *const f32, src: float32x4_t) )] pub unsafe fn vld1q_lane_p16(ptr: *const p16, src: poly16x8_t) -> poly16x8_t { static_assert_uimm_bits!(LANE, 3); - simd_insert!(src, LANE as u32, *ptr) + simd_insert!(src, LANE as u32, crate::ptr::read_unaligned(ptr)) } #[doc = "Load one single-element structure to one lane of one register."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1q_lane_p8)"] @@ -19677,7 +19677,7 @@ pub unsafe fn vld1q_lane_p16(ptr: *const p16, src: poly16x8_t) )] pub unsafe fn vld1q_lane_p8(ptr: *const p8, src: poly8x16_t) -> poly8x16_t { static_assert_uimm_bits!(LANE, 4); - simd_insert!(src, LANE as u32, *ptr) + simd_insert!(src, LANE as u32, crate::ptr::read_unaligned(ptr)) } #[doc = "Load one single-element structure to one lane of one register."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1q_lane_s16)"] @@ -19702,7 +19702,7 @@ pub unsafe fn vld1q_lane_p8(ptr: *const p8, src: poly8x16_t) -> )] pub unsafe fn vld1q_lane_s16(ptr: *const i16, src: int16x8_t) -> int16x8_t { static_assert_uimm_bits!(LANE, 3); - simd_insert!(src, LANE as u32, *ptr) + simd_insert!(src, LANE as u32, crate::ptr::read_unaligned(ptr)) } #[doc = "Load one single-element structure to one lane of one register."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1q_lane_s32)"] @@ -19727,7 +19727,7 @@ pub unsafe fn vld1q_lane_s16(ptr: *const i16, src: int16x8_t) - )] pub unsafe fn vld1q_lane_s32(ptr: *const i32, src: int32x4_t) -> int32x4_t { static_assert_uimm_bits!(LANE, 2); - simd_insert!(src, LANE as u32, *ptr) + simd_insert!(src, LANE as u32, crate::ptr::read_unaligned(ptr)) } #[doc = "Load one single-element structure to one lane of one register."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1q_lane_s64)"] @@ -19737,7 +19737,7 @@ pub unsafe fn vld1q_lane_s32(ptr: *const i32, src: int32x4_t) - #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[rustc_legacy_const_generics(2)] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(vldr, LANE = 1))] +#[cfg_attr(all(test, target_arch = "arm"), assert_instr("vld1.8", LANE = 1))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), assert_instr(ld1, LANE = 1) @@ -19752,7 +19752,7 @@ pub unsafe fn vld1q_lane_s32(ptr: *const i32, src: int32x4_t) - )] pub unsafe fn vld1q_lane_s64(ptr: *const i64, src: int64x2_t) -> int64x2_t { static_assert_uimm_bits!(LANE, 1); - simd_insert!(src, LANE as u32, *ptr) + simd_insert!(src, LANE as u32, crate::ptr::read_unaligned(ptr)) } #[doc = "Load one single-element structure to one lane of one register."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1q_lane_s8)"] @@ -19777,7 +19777,7 @@ pub unsafe fn vld1q_lane_s64(ptr: *const i64, src: int64x2_t) - )] pub unsafe fn vld1q_lane_s8(ptr: *const i8, src: int8x16_t) -> int8x16_t { static_assert_uimm_bits!(LANE, 4); - simd_insert!(src, LANE as u32, *ptr) + simd_insert!(src, LANE as u32, crate::ptr::read_unaligned(ptr)) } #[doc = "Load one single-element structure to one lane of one register."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1q_lane_u16)"] @@ -19802,7 +19802,7 @@ pub unsafe fn vld1q_lane_s8(ptr: *const i8, src: int8x16_t) -> )] pub unsafe fn vld1q_lane_u16(ptr: *const u16, src: uint16x8_t) -> uint16x8_t { static_assert_uimm_bits!(LANE, 3); - simd_insert!(src, LANE as u32, *ptr) + simd_insert!(src, LANE as u32, crate::ptr::read_unaligned(ptr)) } #[doc = "Load one single-element structure to one lane of one register."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1q_lane_u32)"] @@ -19827,7 +19827,7 @@ pub unsafe fn vld1q_lane_u16(ptr: *const u16, src: uint16x8_t) )] pub unsafe fn vld1q_lane_u32(ptr: *const u32, src: uint32x4_t) -> uint32x4_t { static_assert_uimm_bits!(LANE, 2); - simd_insert!(src, LANE as u32, *ptr) + simd_insert!(src, LANE as u32, crate::ptr::read_unaligned(ptr)) } #[doc = "Load one single-element structure to one lane of one register."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1q_lane_u64)"] @@ -19837,7 +19837,7 @@ pub unsafe fn vld1q_lane_u32(ptr: *const u32, src: uint32x4_t) #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[rustc_legacy_const_generics(2)] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(vldr, LANE = 1))] +#[cfg_attr(all(test, target_arch = "arm"), assert_instr("vld1.8", LANE = 1))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), assert_instr(ld1, LANE = 1) @@ -19852,7 +19852,7 @@ pub unsafe fn vld1q_lane_u32(ptr: *const u32, src: uint32x4_t) )] pub unsafe fn vld1q_lane_u64(ptr: *const u64, src: uint64x2_t) -> uint64x2_t { static_assert_uimm_bits!(LANE, 1); - simd_insert!(src, LANE as u32, *ptr) + simd_insert!(src, LANE as u32, crate::ptr::read_unaligned(ptr)) } #[doc = "Load one single-element structure to one lane of one register."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1q_lane_u8)"] @@ -19877,7 +19877,7 @@ pub unsafe fn vld1q_lane_u64(ptr: *const u64, src: uint64x2_t) )] pub unsafe fn vld1q_lane_u8(ptr: *const u8, src: uint8x16_t) -> uint8x16_t { static_assert_uimm_bits!(LANE, 4); - simd_insert!(src, LANE as u32, *ptr) + simd_insert!(src, LANE as u32, crate::ptr::read_unaligned(ptr)) } #[doc = "Load one single-element structure to one lane of one register."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1_lane_p64)"] @@ -19887,7 +19887,7 @@ pub unsafe fn vld1q_lane_u8(ptr: *const u8, src: uint8x16_t) -> #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[rustc_legacy_const_generics(2)] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(vldr, LANE = 0))] +#[cfg_attr(all(test, target_arch = "arm"), assert_instr("vld1.8", LANE = 0))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), assert_instr(ldr, LANE = 0) @@ -19902,7 +19902,7 @@ pub unsafe fn vld1q_lane_u8(ptr: *const u8, src: uint8x16_t) -> )] pub unsafe fn vld1_lane_p64(ptr: *const p64, src: poly64x1_t) -> poly64x1_t { static_assert!(LANE == 0); - simd_insert!(src, LANE as u32, *ptr) + simd_insert!(src, LANE as u32, crate::ptr::read_unaligned(ptr)) } #[doc = "Load one single-element structure to one lane of one register."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1q_lane_p64)"] @@ -19912,7 +19912,7 @@ pub unsafe fn vld1_lane_p64(ptr: *const p64, src: poly64x1_t) - #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[rustc_legacy_const_generics(2)] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(vldr, LANE = 1))] +#[cfg_attr(all(test, target_arch = "arm"), assert_instr("vld1.8", LANE = 1))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), assert_instr(ld1, LANE = 1) @@ -19927,7 +19927,7 @@ pub unsafe fn vld1_lane_p64(ptr: *const p64, src: poly64x1_t) - )] pub unsafe fn vld1q_lane_p64(ptr: *const p64, src: poly64x2_t) -> poly64x2_t { static_assert_uimm_bits!(LANE, 1); - simd_insert!(src, LANE as u32, *ptr) + simd_insert!(src, LANE as u32, crate::ptr::read_unaligned(ptr)) } #[doc = "Load multiple single-element structures to one, two, three, or four registers."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vld1_p64)"] @@ -21734,7 +21734,7 @@ unsafe fn vld1q_v8f16(a: *const i8, b: i32) -> float16x8_t { #[inline] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(vldr))] +#[cfg_attr(all(test, target_arch = "arm"), assert_instr("vld1.8"))] #[cfg_attr( all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), assert_instr(ld1r) @@ -58579,7 +58579,7 @@ pub unsafe fn vst1q_f32_x4(a: *mut f32, b: float32x4x4_t) { #[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst1_lane_f16(a: *mut f16, b: float16x4_t) { static_assert_uimm_bits!(LANE, 2); - *a = simd_extract!(b, LANE as u32); + crate::ptr::write_unaligned(a, simd_extract!(b, LANE as u32)) } #[doc = "Store multiple single-element structures from one, two, three, or four registers"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vst1q_lane_f16)"] @@ -58599,7 +58599,7 @@ pub unsafe fn vst1_lane_f16(a: *mut f16, b: float16x4_t) { #[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst1q_lane_f16(a: *mut f16, b: float16x8_t) { static_assert_uimm_bits!(LANE, 3); - *a = simd_extract!(b, LANE as u32); + crate::ptr::write_unaligned(a, simd_extract!(b, LANE as u32)) } #[doc = "Store multiple single-element structures from one, two, three, or four registers"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vst1_lane_f32)"] @@ -58624,7 +58624,7 @@ pub unsafe fn vst1q_lane_f16(a: *mut f16, b: float16x8_t) { )] pub unsafe fn vst1_lane_f32(a: *mut f32, b: float32x2_t) { static_assert_uimm_bits!(LANE, 1); - *a = simd_extract!(b, LANE as u32); + crate::ptr::write_unaligned(a, simd_extract!(b, LANE as u32)) } #[doc = "Store multiple single-element structures from one, two, three, or four registers"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vst1q_lane_f32)"] @@ -58649,7 +58649,7 @@ pub unsafe fn vst1_lane_f32(a: *mut f32, b: float32x2_t) { )] pub unsafe fn vst1q_lane_f32(a: *mut f32, b: float32x4_t) { static_assert_uimm_bits!(LANE, 2); - *a = simd_extract!(b, LANE as u32); + crate::ptr::write_unaligned(a, simd_extract!(b, LANE as u32)) } #[doc = "Store multiple single-element structures from one, two, three, or four registers"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vst1_lane_s8)"] @@ -58674,7 +58674,7 @@ pub unsafe fn vst1q_lane_f32(a: *mut f32, b: float32x4_t) { )] pub unsafe fn vst1_lane_s8(a: *mut i8, b: int8x8_t) { static_assert_uimm_bits!(LANE, 3); - *a = simd_extract!(b, LANE as u32); + crate::ptr::write_unaligned(a, simd_extract!(b, LANE as u32)) } #[doc = "Store multiple single-element structures from one, two, three, or four registers"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vst1q_lane_s8)"] @@ -58699,7 +58699,7 @@ pub unsafe fn vst1_lane_s8(a: *mut i8, b: int8x8_t) { )] pub unsafe fn vst1q_lane_s8(a: *mut i8, b: int8x16_t) { static_assert_uimm_bits!(LANE, 4); - *a = simd_extract!(b, LANE as u32); + crate::ptr::write_unaligned(a, simd_extract!(b, LANE as u32)) } #[doc = "Store multiple single-element structures from one, two, three, or four registers"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vst1_lane_s16)"] @@ -58724,7 +58724,7 @@ pub unsafe fn vst1q_lane_s8(a: *mut i8, b: int8x16_t) { )] pub unsafe fn vst1_lane_s16(a: *mut i16, b: int16x4_t) { static_assert_uimm_bits!(LANE, 2); - *a = simd_extract!(b, LANE as u32); + crate::ptr::write_unaligned(a, simd_extract!(b, LANE as u32)) } #[doc = "Store multiple single-element structures from one, two, three, or four registers"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vst1q_lane_s16)"] @@ -58749,7 +58749,7 @@ pub unsafe fn vst1_lane_s16(a: *mut i16, b: int16x4_t) { )] pub unsafe fn vst1q_lane_s16(a: *mut i16, b: int16x8_t) { static_assert_uimm_bits!(LANE, 3); - *a = simd_extract!(b, LANE as u32); + crate::ptr::write_unaligned(a, simd_extract!(b, LANE as u32)) } #[doc = "Store multiple single-element structures from one, two, three, or four registers"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vst1_lane_s32)"] @@ -58774,7 +58774,7 @@ pub unsafe fn vst1q_lane_s16(a: *mut i16, b: int16x8_t) { )] pub unsafe fn vst1_lane_s32(a: *mut i32, b: int32x2_t) { static_assert_uimm_bits!(LANE, 1); - *a = simd_extract!(b, LANE as u32); + crate::ptr::write_unaligned(a, simd_extract!(b, LANE as u32)) } #[doc = "Store multiple single-element structures from one, two, three, or four registers"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vst1q_lane_s32)"] @@ -58799,7 +58799,7 @@ pub unsafe fn vst1_lane_s32(a: *mut i32, b: int32x2_t) { )] pub unsafe fn vst1q_lane_s32(a: *mut i32, b: int32x4_t) { static_assert_uimm_bits!(LANE, 2); - *a = simd_extract!(b, LANE as u32); + crate::ptr::write_unaligned(a, simd_extract!(b, LANE as u32)) } #[doc = "Store multiple single-element structures from one, two, three, or four registers"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vst1q_lane_s64)"] @@ -58824,7 +58824,7 @@ pub unsafe fn vst1q_lane_s32(a: *mut i32, b: int32x4_t) { )] pub unsafe fn vst1q_lane_s64(a: *mut i64, b: int64x2_t) { static_assert_uimm_bits!(LANE, 1); - *a = simd_extract!(b, LANE as u32); + crate::ptr::write_unaligned(a, simd_extract!(b, LANE as u32)) } #[doc = "Store multiple single-element structures from one, two, three, or four registers"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vst1_lane_u8)"] @@ -58849,7 +58849,7 @@ pub unsafe fn vst1q_lane_s64(a: *mut i64, b: int64x2_t) { )] pub unsafe fn vst1_lane_u8(a: *mut u8, b: uint8x8_t) { static_assert_uimm_bits!(LANE, 3); - *a = simd_extract!(b, LANE as u32); + crate::ptr::write_unaligned(a, simd_extract!(b, LANE as u32)) } #[doc = "Store multiple single-element structures from one, two, three, or four registers"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vst1q_lane_u8)"] @@ -58874,7 +58874,7 @@ pub unsafe fn vst1_lane_u8(a: *mut u8, b: uint8x8_t) { )] pub unsafe fn vst1q_lane_u8(a: *mut u8, b: uint8x16_t) { static_assert_uimm_bits!(LANE, 4); - *a = simd_extract!(b, LANE as u32); + crate::ptr::write_unaligned(a, simd_extract!(b, LANE as u32)) } #[doc = "Store multiple single-element structures from one, two, three, or four registers"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vst1_lane_u16)"] @@ -58899,7 +58899,7 @@ pub unsafe fn vst1q_lane_u8(a: *mut u8, b: uint8x16_t) { )] pub unsafe fn vst1_lane_u16(a: *mut u16, b: uint16x4_t) { static_assert_uimm_bits!(LANE, 2); - *a = simd_extract!(b, LANE as u32); + crate::ptr::write_unaligned(a, simd_extract!(b, LANE as u32)) } #[doc = "Store multiple single-element structures from one, two, three, or four registers"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vst1q_lane_u16)"] @@ -58924,7 +58924,7 @@ pub unsafe fn vst1_lane_u16(a: *mut u16, b: uint16x4_t) { )] pub unsafe fn vst1q_lane_u16(a: *mut u16, b: uint16x8_t) { static_assert_uimm_bits!(LANE, 3); - *a = simd_extract!(b, LANE as u32); + crate::ptr::write_unaligned(a, simd_extract!(b, LANE as u32)) } #[doc = "Store multiple single-element structures from one, two, three, or four registers"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vst1_lane_u32)"] @@ -58949,7 +58949,7 @@ pub unsafe fn vst1q_lane_u16(a: *mut u16, b: uint16x8_t) { )] pub unsafe fn vst1_lane_u32(a: *mut u32, b: uint32x2_t) { static_assert_uimm_bits!(LANE, 1); - *a = simd_extract!(b, LANE as u32); + crate::ptr::write_unaligned(a, simd_extract!(b, LANE as u32)) } #[doc = "Store multiple single-element structures from one, two, three, or four registers"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vst1q_lane_u32)"] @@ -58974,7 +58974,7 @@ pub unsafe fn vst1_lane_u32(a: *mut u32, b: uint32x2_t) { )] pub unsafe fn vst1q_lane_u32(a: *mut u32, b: uint32x4_t) { static_assert_uimm_bits!(LANE, 2); - *a = simd_extract!(b, LANE as u32); + crate::ptr::write_unaligned(a, simd_extract!(b, LANE as u32)) } #[doc = "Store multiple single-element structures from one, two, three, or four registers"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vst1q_lane_u64)"] @@ -58999,7 +58999,7 @@ pub unsafe fn vst1q_lane_u32(a: *mut u32, b: uint32x4_t) { )] pub unsafe fn vst1q_lane_u64(a: *mut u64, b: uint64x2_t) { static_assert_uimm_bits!(LANE, 1); - *a = simd_extract!(b, LANE as u32); + crate::ptr::write_unaligned(a, simd_extract!(b, LANE as u32)) } #[doc = "Store multiple single-element structures from one, two, three, or four registers"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vst1_lane_p8)"] @@ -59024,7 +59024,7 @@ pub unsafe fn vst1q_lane_u64(a: *mut u64, b: uint64x2_t) { )] pub unsafe fn vst1_lane_p8(a: *mut p8, b: poly8x8_t) { static_assert_uimm_bits!(LANE, 3); - *a = simd_extract!(b, LANE as u32); + crate::ptr::write_unaligned(a, simd_extract!(b, LANE as u32)) } #[doc = "Store multiple single-element structures from one, two, three, or four registers"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vst1q_lane_p8)"] @@ -59049,7 +59049,7 @@ pub unsafe fn vst1_lane_p8(a: *mut p8, b: poly8x8_t) { )] pub unsafe fn vst1q_lane_p8(a: *mut p8, b: poly8x16_t) { static_assert_uimm_bits!(LANE, 4); - *a = simd_extract!(b, LANE as u32); + crate::ptr::write_unaligned(a, simd_extract!(b, LANE as u32)) } #[doc = "Store multiple single-element structures from one, two, three, or four registers"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vst1_lane_p16)"] @@ -59074,7 +59074,7 @@ pub unsafe fn vst1q_lane_p8(a: *mut p8, b: poly8x16_t) { )] pub unsafe fn vst1_lane_p16(a: *mut p16, b: poly16x4_t) { static_assert_uimm_bits!(LANE, 2); - *a = simd_extract!(b, LANE as u32); + crate::ptr::write_unaligned(a, simd_extract!(b, LANE as u32)) } #[doc = "Store multiple single-element structures from one, two, three, or four registers"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vst1q_lane_p16)"] @@ -59099,7 +59099,7 @@ pub unsafe fn vst1_lane_p16(a: *mut p16, b: poly16x4_t) { )] pub unsafe fn vst1q_lane_p16(a: *mut p16, b: poly16x8_t) { static_assert_uimm_bits!(LANE, 3); - *a = simd_extract!(b, LANE as u32); + crate::ptr::write_unaligned(a, simd_extract!(b, LANE as u32)) } #[doc = "Store multiple single-element structures from one, two, three, or four registers"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vst1_lane_p64)"] @@ -59124,7 +59124,7 @@ pub unsafe fn vst1q_lane_p16(a: *mut p16, b: poly16x8_t) { )] pub unsafe fn vst1_lane_p64(a: *mut p64, b: poly64x1_t) { static_assert!(LANE == 0); - *a = simd_extract!(b, LANE as u32); + crate::ptr::write_unaligned(a, simd_extract!(b, LANE as u32)) } #[doc = "Store multiple single-element structures from one, two, three, or four registers"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vst1_lane_s64)"] @@ -59149,7 +59149,7 @@ pub unsafe fn vst1_lane_p64(a: *mut p64, b: poly64x1_t) { )] pub unsafe fn vst1_lane_s64(a: *mut i64, b: int64x1_t) { static_assert!(LANE == 0); - *a = simd_extract!(b, LANE as u32); + crate::ptr::write_unaligned(a, simd_extract!(b, LANE as u32)) } #[doc = "Store multiple single-element structures from one, two, three, or four registers"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vst1_lane_u64)"] @@ -59174,7 +59174,7 @@ pub unsafe fn vst1_lane_s64(a: *mut i64, b: int64x1_t) { )] pub unsafe fn vst1_lane_u64(a: *mut u64, b: uint64x1_t) { static_assert!(LANE == 0); - *a = simd_extract!(b, LANE as u32); + crate::ptr::write_unaligned(a, simd_extract!(b, LANE as u32)) } #[doc = "Store multiple single-element structures to one, two, three, or four registers"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vst1_p64_x2)"] @@ -61181,7 +61181,7 @@ unsafe fn vst1q_v8f16(addr: *const i8, val: float16x8_t, align: i32) { )] pub unsafe fn vst1q_lane_p64(a: *mut p64, b: poly64x2_t) { static_assert_uimm_bits!(LANE, 1); - *a = simd_extract!(b, LANE as u32); + crate::ptr::write_unaligned(a, simd_extract!(b, LANE as u32)) } #[doc = "Store multiple 2-element structures from two registers"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vst2_f16)"] diff --git a/library/stdarch/crates/core_arch/src/arm_shared/neon/load_tests.rs b/library/stdarch/crates/core_arch/src/arm_shared/neon/load_tests.rs index 70a37f7c05dad..a4db10f7c1d26 100644 --- a/library/stdarch/crates/core_arch/src/arm_shared/neon/load_tests.rs +++ b/library/stdarch/crates/core_arch/src/arm_shared/neon/load_tests.rs @@ -223,3 +223,65 @@ fn test_vld1q_f32() { let r = unsafe { f32x4::from(vld1q_f32(a[1..].as_ptr())) }; assert_eq!(r, e) } + +#[simd_test(enable = "neon")] +fn test_vld1q_lane_u32_unaligned() { + // ld1 single-lane loads impose no alignment requirement: read from an odd byte offset. + let a: [u8; 5] = [0, 1, 2, 3, 4]; + let e = u32::from_ne_bytes([1, 2, 3, 4]); + let src = u32x4::new(10, 11, 12, 13); + let r = unsafe { + u32x4::from(vld1q_lane_u32::<2>( + a.as_ptr().add(1) as *const u32, + src.into(), + )) + }; + assert_eq!(r, u32x4::new(10, 11, e, 13)); +} + +#[simd_test(enable = "neon")] +fn test_vld1q_lane_u64_unaligned() { + let a: [u8; 9] = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + let e = u64::from_ne_bytes([1, 2, 3, 4, 5, 6, 7, 8]); + let src = u64x2::new(10, 11); + let r = unsafe { + u64x2::from(vld1q_lane_u64::<1>( + a.as_ptr().add(1) as *const u64, + src.into(), + )) + }; + assert_eq!(r, u64x2::new(10, e)); +} + +#[simd_test(enable = "neon")] +fn test_vld1q_lane_f32_unaligned() { + // Float loads legalize differently from integer ones under low alignment + // (armv7 uses ldr + vmov), so the type class needs its own coverage. + let a: [u8; 5] = [0, 1, 2, 3, 4]; + let e = f32::from_ne_bytes([1, 2, 3, 4]); + let src = f32x4::new(10., 11., 12., 13.); + let r = unsafe { + f32x4::from(vld1q_lane_f32::<2>( + a.as_ptr().add(1) as *const f32, + src.into(), + )) + }; + assert_eq!(r, f32x4::new(10., 11., e, 13.)); +} + +#[simd_test(enable = "neon")] +fn test_vld1q_dup_u32_unaligned() { + // ld1r replicate loads impose no alignment requirement either. + let a: [u8; 5] = [0, 1, 2, 3, 4]; + let e = u32::from_ne_bytes([1, 2, 3, 4]); + let r = unsafe { u32x4::from(vld1q_dup_u32(a.as_ptr().add(1) as *const u32)) }; + assert_eq!(r, u32x4::new(e, e, e, e)); +} + +#[simd_test(enable = "neon")] +fn test_vld1q_dup_u64_unaligned() { + let a: [u8; 9] = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + let e = u64::from_ne_bytes([1, 2, 3, 4, 5, 6, 7, 8]); + let r = unsafe { u64x2::from(vld1q_dup_u64(a.as_ptr().add(1) as *const u64)) }; + assert_eq!(r, u64x2::new(e, e)); +} diff --git a/library/stdarch/crates/core_arch/src/arm_shared/neon/store_tests.rs b/library/stdarch/crates/core_arch/src/arm_shared/neon/store_tests.rs index 6eb60e4c78bc8..68df3ec944abd 100644 --- a/library/stdarch/crates/core_arch/src/arm_shared/neon/store_tests.rs +++ b/library/stdarch/crates/core_arch/src/arm_shared/neon/store_tests.rs @@ -473,3 +473,27 @@ fn test_vst1q_f32() { assert_eq!(vals[3], 3.); assert_eq!(vals[4], 4.); } + +#[simd_test(enable = "neon")] +fn test_vst1q_lane_u32_unaligned() { + // st1 single-lane stores impose no alignment requirement: write to an odd byte offset. + let mut vals = [0_u8; 5]; + let a = u32x4::new(1, 2, 3, 4); + unsafe { + vst1q_lane_u32::<2>(vals.as_mut_ptr().add(1) as *mut u32, a.into()); + } + assert_eq!(vals[0], 0); + assert_eq!(u32::from_ne_bytes([vals[1], vals[2], vals[3], vals[4]]), 3); +} + +#[simd_test(enable = "neon")] +fn test_vst1q_lane_u64_unaligned() { + let mut vals = [0_u8; 9]; + let a = u64x2::new(1, 2); + unsafe { + vst1q_lane_u64::<1>(vals.as_mut_ptr().add(1) as *mut u64, a.into()); + } + assert_eq!(vals[0], 0); + let stored: [u8; 8] = vals[1..9].try_into().unwrap(); + assert_eq!(u64::from_ne_bytes(stored), 2); +} diff --git a/library/stdarch/crates/core_arch/src/lib.rs b/library/stdarch/crates/core_arch/src/lib.rs index 55163fffedd78..2b7fb159130d9 100644 --- a/library/stdarch/crates/core_arch/src/lib.rs +++ b/library/stdarch/crates/core_arch/src/lib.rs @@ -32,7 +32,6 @@ x86_amx_intrinsics, f16, aarch64_unstable_target_feature, - funnel_shifts, avx10_target_feature, const_trait_impl, const_cmp, @@ -42,7 +41,15 @@ clflushopt_target_feature, min_adt_const_params )] -#![cfg_attr(test, feature(test, abi_vectorcall, stdarch_internal))] +#![cfg_attr( + test, + feature( + test, + abi_vectorcall, + stdarch_internal, + cfg_target_has_reliable_f16_f128 + ) +)] #![deny(clippy::missing_inline_in_public_items)] #![allow( clippy::identity_op, diff --git a/library/stdarch/crates/core_arch/src/loongarch64/lasx/generated.rs b/library/stdarch/crates/core_arch/src/loongarch64/lasx/generated.rs index f481a159eb632..cfc4aced34173 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/lasx/generated.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/lasx/generated.rs @@ -6,8 +6,8 @@ // OUT_DIR=`pwd`/crates/core_arch cargo run -p stdarch-gen-loongarch -- crates/stdarch-gen-loongarch/lasx.spec // ``` -use crate::mem::transmute; use super::super::*; +use crate::mem::transmute; #[allow(improper_ctypes)] unsafe extern "llvm-intrinsic" { @@ -3033,168 +3033,312 @@ pub fn lasx_xvhsubw_qu_du(a: m256i, b: m256i) -> m256i { #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lasx_xvmaddwev_q_d(a: m256i, b: m256i, c: m256i) -> m256i { - unsafe { transmute(__lasx_xvmaddwev_q_d(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lasx_xvmaddwev_q_d( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lasx_xvmaddwev_d_w(a: m256i, b: m256i, c: m256i) -> m256i { - unsafe { transmute(__lasx_xvmaddwev_d_w(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lasx_xvmaddwev_d_w( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lasx_xvmaddwev_w_h(a: m256i, b: m256i, c: m256i) -> m256i { - unsafe { transmute(__lasx_xvmaddwev_w_h(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lasx_xvmaddwev_w_h( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lasx_xvmaddwev_h_b(a: m256i, b: m256i, c: m256i) -> m256i { - unsafe { transmute(__lasx_xvmaddwev_h_b(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lasx_xvmaddwev_h_b( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lasx_xvmaddwev_q_du(a: m256i, b: m256i, c: m256i) -> m256i { - unsafe { transmute(__lasx_xvmaddwev_q_du(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lasx_xvmaddwev_q_du( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lasx_xvmaddwev_d_wu(a: m256i, b: m256i, c: m256i) -> m256i { - unsafe { transmute(__lasx_xvmaddwev_d_wu(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lasx_xvmaddwev_d_wu( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lasx_xvmaddwev_w_hu(a: m256i, b: m256i, c: m256i) -> m256i { - unsafe { transmute(__lasx_xvmaddwev_w_hu(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lasx_xvmaddwev_w_hu( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lasx_xvmaddwev_h_bu(a: m256i, b: m256i, c: m256i) -> m256i { - unsafe { transmute(__lasx_xvmaddwev_h_bu(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lasx_xvmaddwev_h_bu( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lasx_xvmaddwod_q_d(a: m256i, b: m256i, c: m256i) -> m256i { - unsafe { transmute(__lasx_xvmaddwod_q_d(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lasx_xvmaddwod_q_d( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lasx_xvmaddwod_d_w(a: m256i, b: m256i, c: m256i) -> m256i { - unsafe { transmute(__lasx_xvmaddwod_d_w(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lasx_xvmaddwod_d_w( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lasx_xvmaddwod_w_h(a: m256i, b: m256i, c: m256i) -> m256i { - unsafe { transmute(__lasx_xvmaddwod_w_h(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lasx_xvmaddwod_w_h( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lasx_xvmaddwod_h_b(a: m256i, b: m256i, c: m256i) -> m256i { - unsafe { transmute(__lasx_xvmaddwod_h_b(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lasx_xvmaddwod_h_b( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lasx_xvmaddwod_q_du(a: m256i, b: m256i, c: m256i) -> m256i { - unsafe { transmute(__lasx_xvmaddwod_q_du(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lasx_xvmaddwod_q_du( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lasx_xvmaddwod_d_wu(a: m256i, b: m256i, c: m256i) -> m256i { - unsafe { transmute(__lasx_xvmaddwod_d_wu(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lasx_xvmaddwod_d_wu( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lasx_xvmaddwod_w_hu(a: m256i, b: m256i, c: m256i) -> m256i { - unsafe { transmute(__lasx_xvmaddwod_w_hu(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lasx_xvmaddwod_w_hu( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lasx_xvmaddwod_h_bu(a: m256i, b: m256i, c: m256i) -> m256i { - unsafe { transmute(__lasx_xvmaddwod_h_bu(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lasx_xvmaddwod_h_bu( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lasx_xvmaddwev_q_du_d(a: m256i, b: m256i, c: m256i) -> m256i { - unsafe { transmute(__lasx_xvmaddwev_q_du_d(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lasx_xvmaddwev_q_du_d( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lasx_xvmaddwev_d_wu_w(a: m256i, b: m256i, c: m256i) -> m256i { - unsafe { transmute(__lasx_xvmaddwev_d_wu_w(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lasx_xvmaddwev_d_wu_w( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lasx_xvmaddwev_w_hu_h(a: m256i, b: m256i, c: m256i) -> m256i { - unsafe { transmute(__lasx_xvmaddwev_w_hu_h(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lasx_xvmaddwev_w_hu_h( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lasx_xvmaddwev_h_bu_b(a: m256i, b: m256i, c: m256i) -> m256i { - unsafe { transmute(__lasx_xvmaddwev_h_bu_b(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lasx_xvmaddwev_h_bu_b( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lasx_xvmaddwod_q_du_d(a: m256i, b: m256i, c: m256i) -> m256i { - unsafe { transmute(__lasx_xvmaddwod_q_du_d(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lasx_xvmaddwod_q_du_d( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lasx_xvmaddwod_d_wu_w(a: m256i, b: m256i, c: m256i) -> m256i { - unsafe { transmute(__lasx_xvmaddwod_d_wu_w(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lasx_xvmaddwod_d_wu_w( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lasx_xvmaddwod_w_hu_h(a: m256i, b: m256i, c: m256i) -> m256i { - unsafe { transmute(__lasx_xvmaddwod_w_hu_h(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lasx_xvmaddwod_w_hu_h( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lasx_xvmaddwod_h_bu_b(a: m256i, b: m256i, c: m256i) -> m256i { - unsafe { transmute(__lasx_xvmaddwod_h_bu_b(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lasx_xvmaddwod_h_bu_b( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] diff --git a/library/stdarch/crates/core_arch/src/loongarch64/lsx/generated.rs b/library/stdarch/crates/core_arch/src/loongarch64/lsx/generated.rs index 7915ef07d68e7..aa4e31ce8da6c 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/lsx/generated.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/lsx/generated.rs @@ -6,8 +6,8 @@ // OUT_DIR=`pwd`/crates/core_arch cargo run -p stdarch-gen-loongarch -- crates/stdarch-gen-loongarch/lsx.spec // ``` -use crate::mem::transmute; use super::super::*; +use crate::mem::transmute; #[allow(improper_ctypes)] unsafe extern "llvm-intrinsic" { @@ -2747,21 +2747,39 @@ pub fn lsx_vmaddwev_h_b(a: m128i, b: m128i, c: m128i) -> m128i { #[target_feature(enable = "lsx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lsx_vmaddwev_d_wu(a: m128i, b: m128i, c: m128i) -> m128i { - unsafe { transmute(__lsx_vmaddwev_d_wu(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lsx_vmaddwev_d_wu( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lsx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lsx_vmaddwev_w_hu(a: m128i, b: m128i, c: m128i) -> m128i { - unsafe { transmute(__lsx_vmaddwev_w_hu(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lsx_vmaddwev_w_hu( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lsx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lsx_vmaddwev_h_bu(a: m128i, b: m128i, c: m128i) -> m128i { - unsafe { transmute(__lsx_vmaddwev_h_bu(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lsx_vmaddwev_h_bu( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] @@ -2789,63 +2807,117 @@ pub fn lsx_vmaddwod_h_b(a: m128i, b: m128i, c: m128i) -> m128i { #[target_feature(enable = "lsx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lsx_vmaddwod_d_wu(a: m128i, b: m128i, c: m128i) -> m128i { - unsafe { transmute(__lsx_vmaddwod_d_wu(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lsx_vmaddwod_d_wu( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lsx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lsx_vmaddwod_w_hu(a: m128i, b: m128i, c: m128i) -> m128i { - unsafe { transmute(__lsx_vmaddwod_w_hu(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lsx_vmaddwod_w_hu( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lsx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lsx_vmaddwod_h_bu(a: m128i, b: m128i, c: m128i) -> m128i { - unsafe { transmute(__lsx_vmaddwod_h_bu(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lsx_vmaddwod_h_bu( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lsx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lsx_vmaddwev_d_wu_w(a: m128i, b: m128i, c: m128i) -> m128i { - unsafe { transmute(__lsx_vmaddwev_d_wu_w(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lsx_vmaddwev_d_wu_w( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lsx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lsx_vmaddwev_w_hu_h(a: m128i, b: m128i, c: m128i) -> m128i { - unsafe { transmute(__lsx_vmaddwev_w_hu_h(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lsx_vmaddwev_w_hu_h( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lsx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lsx_vmaddwev_h_bu_b(a: m128i, b: m128i, c: m128i) -> m128i { - unsafe { transmute(__lsx_vmaddwev_h_bu_b(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lsx_vmaddwev_h_bu_b( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lsx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lsx_vmaddwod_d_wu_w(a: m128i, b: m128i, c: m128i) -> m128i { - unsafe { transmute(__lsx_vmaddwod_d_wu_w(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lsx_vmaddwod_d_wu_w( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lsx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lsx_vmaddwod_w_hu_h(a: m128i, b: m128i, c: m128i) -> m128i { - unsafe { transmute(__lsx_vmaddwod_w_hu_h(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lsx_vmaddwod_w_hu_h( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lsx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lsx_vmaddwod_h_bu_b(a: m128i, b: m128i, c: m128i) -> m128i { - unsafe { transmute(__lsx_vmaddwod_h_bu_b(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lsx_vmaddwod_h_bu_b( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] @@ -2866,28 +2938,52 @@ pub fn lsx_vmaddwod_q_d(a: m128i, b: m128i, c: m128i) -> m128i { #[target_feature(enable = "lsx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lsx_vmaddwev_q_du(a: m128i, b: m128i, c: m128i) -> m128i { - unsafe { transmute(__lsx_vmaddwev_q_du(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lsx_vmaddwev_q_du( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lsx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lsx_vmaddwod_q_du(a: m128i, b: m128i, c: m128i) -> m128i { - unsafe { transmute(__lsx_vmaddwod_q_du(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lsx_vmaddwod_q_du( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lsx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lsx_vmaddwev_q_du_d(a: m128i, b: m128i, c: m128i) -> m128i { - unsafe { transmute(__lsx_vmaddwev_q_du_d(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lsx_vmaddwev_q_du_d( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] #[target_feature(enable = "lsx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn lsx_vmaddwod_q_du_d(a: m128i, b: m128i, c: m128i) -> m128i { - unsafe { transmute(__lsx_vmaddwod_q_du_d(transmute(a), transmute(b), transmute(c))) } + unsafe { + transmute(__lsx_vmaddwod_q_du_d( + transmute(a), + transmute(b), + transmute(c), + )) + } } #[inline] diff --git a/library/stdarch/crates/core_arch/src/mips/mod.rs b/library/stdarch/crates/core_arch/src/mips/mod.rs index 1de3ffd03d1f0..84bfd1585e89b 100644 --- a/library/stdarch/crates/core_arch/src/mips/mod.rs +++ b/library/stdarch/crates/core_arch/src/mips/mod.rs @@ -16,5 +16,5 @@ use stdarch_test::assert_instr; #[inline] #[unstable(feature = "stdarch_mips", issue = "111198")] pub unsafe fn break_() -> ! { - crate::intrinsics::abort() + crate::intrinsics::abort_immediate() } diff --git a/library/stdarch/crates/core_arch/src/nvptx/mod.rs b/library/stdarch/crates/core_arch/src/nvptx/mod.rs index d382aff41f4bf..ea6fffee3897f 100644 --- a/library/stdarch/crates/core_arch/src/nvptx/mod.rs +++ b/library/stdarch/crates/core_arch/src/nvptx/mod.rs @@ -145,7 +145,7 @@ pub unsafe fn _thread_idx_z() -> u32 { #[inline] #[unstable(feature = "stdarch_nvptx", issue = "111199")] pub unsafe fn trap() -> ! { - crate::intrinsics::abort() + crate::intrinsics::abort_immediate() } // Basic CUDA syscall declarations. diff --git a/library/stdarch/crates/core_arch/src/powerpc/mod.rs b/library/stdarch/crates/core_arch/src/powerpc/mod.rs index 53227215d946c..9bb1bf22434dc 100644 --- a/library/stdarch/crates/core_arch/src/powerpc/mod.rs +++ b/library/stdarch/crates/core_arch/src/powerpc/mod.rs @@ -18,5 +18,5 @@ use stdarch_test::assert_instr; #[inline] #[unstable(feature = "stdarch_powerpc", issue = "111145")] pub unsafe fn trap() -> ! { - crate::intrinsics::abort() + crate::intrinsics::abort_immediate() } diff --git a/library/stdarch/crates/core_arch/src/s390x/vector.rs b/library/stdarch/crates/core_arch/src/s390x/vector.rs index 6703691f080b2..8fd9fd83a64cd 100644 --- a/library/stdarch/crates/core_arch/src/s390x/vector.rs +++ b/library/stdarch/crates/core_arch/src/s390x/vector.rs @@ -122,8 +122,6 @@ unsafe extern "llvm-intrinsic" { #[link_name = "llvm.s390.vsrlb"] fn vsrlb(a: vector_signed_char, b: vector_signed_char) -> vector_signed_char; #[link_name = "llvm.s390.vslb"] fn vslb(a: vector_signed_char, b: vector_signed_char) -> vector_signed_char; - #[link_name = "llvm.s390.vsrd"] fn vsrd(a: i8x16, b: i8x16, c: u32) -> i8x16; - #[link_name = "llvm.s390.verimb"] fn verimb(a: vector_signed_char, b: vector_signed_char, c: vector_signed_char, d: i32) -> vector_signed_char; #[link_name = "llvm.s390.verimh"] fn verimh(a: vector_signed_short, b: vector_signed_short, c: vector_signed_short, d: i32) -> vector_signed_short; #[link_name = "llvm.s390.verimf"] fn verimf(a: vector_signed_int, b: vector_signed_int, c: vector_signed_int, d: i32) -> vector_signed_int; @@ -140,9 +138,6 @@ unsafe extern "llvm-intrinsic" { #[link_name = "llvm.s390.vsumqf"] fn vsumqf(a: vector_unsigned_int, b: vector_unsigned_int) -> u128; #[link_name = "llvm.s390.vsumqg"] fn vsumqg(a: vector_unsigned_long_long, b: vector_unsigned_long_long) -> u128; - #[link_name = "llvm.s390.vaccq"] fn vaccq(a: u128, b: u128) -> u128; - #[link_name = "llvm.s390.vacccq"] fn vacccq(a: u128, b: u128, c: u128) -> u128; - #[link_name = "llvm.s390.vscbiq"] fn vscbiq(a: u128, b: u128) -> u128; #[link_name = "llvm.s390.vsbiq"] fn vsbiq(a: u128, b: u128, c: u128) -> u128; #[link_name = "llvm.s390.vsbcbiq"] fn vsbcbiq(a: u128, b: u128, c: u128) -> u128; @@ -3664,12 +3659,7 @@ mod sealed { #[target_feature(enable = "vector-enhancements-2")] unsafe fn vec_srdb(self, b: Self) -> Self { static_assert_uimm_bits!(C, 3); - transmute(vsrd(transmute(self), transmute(b), C)) - // FIXME(llvm): https://github.com/llvm/llvm-project/issues/129955#issuecomment-3207488190 - // LLVM currently rewrites `fshr` to `fshl`, and the logic in the s390x - // backend cannot deal with that yet. - // #[link_name = "llvm.fshr.i128"] fn fshr_i128(a: u128, b: u128, c: u128) -> u128; - // transmute(fshr_i128(transmute(self), transmute(b), const { C as u128 })) + transmute(u128::funnel_shr(transmute(self), transmute(b), C)) } } )* @@ -4849,9 +4839,7 @@ pub unsafe fn vec_addc_u128( ) -> vector_unsigned_char { let a: u128 = transmute(a); let b: u128 = transmute(b); - // FIXME(llvm) https://github.com/llvm/llvm-project/pull/153557 - // transmute(a.overflowing_add(b).1 as u128) - transmute(vaccq(a, b)) + transmute(a.overflowing_add(b).1 as u128) } /// Vector Add With Carry unsigned 128-bits @@ -4886,10 +4874,8 @@ pub unsafe fn vec_addec_u128( let a: u128 = transmute(a); let b: u128 = transmute(b); let c: u128 = transmute(c); - // FIXME(llvm) https://github.com/llvm/llvm-project/pull/153557 - // let (_d, carry) = a.carrying_add(b, c & 1 != 0); - // transmute(carry as u128) - transmute(vacccq(a, b, c)) + let (_d, carry) = a.carrying_add(b, c & 1 != 0); + transmute(carry as u128) } /// Vector Subtract with Carryout diff --git a/library/stdarch/crates/core_arch/src/wasm32/mod.rs b/library/stdarch/crates/core_arch/src/wasm32/mod.rs index 57c9157bede89..03986e7e08ec7 100644 --- a/library/stdarch/crates/core_arch/src/wasm32/mod.rs +++ b/library/stdarch/crates/core_arch/src/wasm32/mod.rs @@ -29,7 +29,7 @@ pub use self::memory::*; #[inline] #[stable(feature = "unreachable_wasm32", since = "1.37.0")] pub fn unreachable() -> ! { - crate::intrinsics::abort() + crate::intrinsics::abort_immediate() } /// Generates the [`f32.ceil`] instruction, returning the smallest integer greater than or equal to `a`. diff --git a/library/stdarch/crates/core_arch/src/x86/avx2.rs b/library/stdarch/crates/core_arch/src/x86/avx2.rs index 333f38e5474cc..7ad45421cf89f 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx2.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx2.rs @@ -5142,10 +5142,24 @@ mod tests { #[simd_test(enable = "avx2")] fn test_mm_sllv_epi64() { - let a = _mm_set1_epi64x(2); - let b = _mm_set1_epi64x(1); + let a = _mm_set_epi64x(2, 3); + let b = _mm_set_epi64x(1, 2); let r = _mm_sllv_epi64(a, b); - let e = _mm_set1_epi64x(4); + // Compare with the scalar version of the same computation. + let e = _mm_set_epi64x(2i64.unbounded_shl(1), 3i64.unbounded_shl(2)); + assert_eq_m128i(r, e); + // Compare with hardcoded output. + let e = _mm_set_epi64x(4, 12); + assert_eq_m128i(r, e); + + // The shift has unbounded semantics: if the shift amount + // is >= the number of bits the result is 0. + let a = _mm_set_epi64x(1, 2); + let b = _mm_set_epi64x(64, 65); + let r = _mm_sllv_epi64(a, b); + let e = _mm_set_epi64x(1i64.unbounded_shl(64), 2i64.unbounded_shl(65)); + assert_eq_m128i(r, e); + let e = _mm_set_epi64x(0, 0); assert_eq_m128i(r, e); } @@ -5192,10 +5206,27 @@ mod tests { #[simd_test(enable = "avx2")] fn test_mm_srav_epi32() { - let a = _mm_set1_epi32(4); - let count = _mm_set1_epi32(1); - let r = _mm_srav_epi32(a, count); - let e = _mm_set1_epi32(2); + let a = _mm_set_epi32(16, -32, 64, -128); + let b = _mm_set_epi32(4, 3, 2, 1); + let r = _mm_srav_epi32(a, b); + let e = _mm_set_epi32(1, -4, 16, -64); + assert_eq_m128i(r, e); + + // The shift has unbounded semantics: if the shift amount + // is >= the number of bits the result is -1. + let a = _mm_set_epi32(-16, -32, -64, -128); + let b = _mm_set_epi32(31, 32, 33, 0); + let r = _mm_srav_epi32(a, b); + // Compare with the scalar version of the same computation. + let e = _mm_set_epi32( + (-16i32).unbounded_shr(31), + (-32i32).unbounded_shr(32), + (-64i32).unbounded_shr(33), + (-128i32).unbounded_shr(0), + ); + assert_eq_m128i(r, e); + // Compare with hardcoded output. + let e = _mm_set_epi32(-1, -1, -1, -128); assert_eq_m128i(r, e); } @@ -5296,10 +5327,24 @@ mod tests { #[simd_test(enable = "avx2")] fn test_mm_srlv_epi64() { - let a = _mm_set1_epi64x(2); - let count = _mm_set1_epi64x(1); - let r = _mm_srlv_epi64(a, count); - let e = _mm_set1_epi64x(1); + let a = _mm_set_epi64x(4, 8); + let b = _mm_set_epi64x(2, 1); + let r = _mm_srlv_epi64(a, b); + // Compare with the scalar version of the same computation. + let e = _mm_set_epi64x(4i64.unbounded_shr(2), 8i64.unbounded_shr(1)); + assert_eq_m128i(r, e); + // Compare with hardcoded output. + let e = _mm_set_epi64x(1, 4); + assert_eq_m128i(r, e); + + // The shift has unbounded semantics: if the shift amount + // is >= the number of bits the result is 0. + let a = _mm_set_epi64x(i64::MAX, i64::MAX); + let b = _mm_set_epi64x(64, 65); + let r = _mm_sllv_epi64(a, b); + let e = _mm_set_epi64x(i64::MAX.unbounded_shr(64), i64::MAX.unbounded_shr(65)); + assert_eq_m128i(r, e); + let e = _mm_set_epi64x(0, 0); assert_eq_m128i(r, e); } diff --git a/library/stdarch/crates/core_arch/src/x86/avx512fp16.rs b/library/stdarch/crates/core_arch/src/x86/avx512fp16.rs index 869f577a55905..d54c222eb7ccf 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512fp16.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512fp16.rs @@ -16837,6 +16837,7 @@ unsafe extern "llvm-intrinsic" { } #[cfg(test)] +#[cfg(target_has_reliable_f16)] mod tests { use crate::core_arch::assert_eq_const as assert_eq; use crate::core_arch::x86::*; diff --git a/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs b/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs index 832384b11e68b..9c261784553bd 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs @@ -9583,7 +9583,7 @@ mod tests { let a = _mm_set_epi64x(-1, -2); let b = _mm_set_epi64x(64, 65); let r = _mm_srav_epi64(a, b); - let e = _mm_set_epi64x((-1i64).unbounded_shl(64), (-2i64).unbounded_shl(65)); + let e = _mm_set_epi64x((-1i64).unbounded_shr(64), (-2i64).unbounded_shr(65)); assert_eq_m128i(r, e); let e = _mm_set_epi64x(-1, -1); assert_eq_m128i(r, e); diff --git a/library/stdarch/crates/core_arch/src/x86_64/avx512fp16.rs b/library/stdarch/crates/core_arch/src/x86_64/avx512fp16.rs index f8d21e3f9d7fd..5b26fc9f45426 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/avx512fp16.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/avx512fp16.rs @@ -227,6 +227,7 @@ unsafe extern "llvm-intrinsic" { } #[cfg(test)] +#[cfg(target_has_reliable_f16)] mod tests { use crate::core_arch::{x86::*, x86_64::*}; use stdarch_test::simd_test; diff --git a/library/stdarch/crates/intrinsic-test/missing_loongarch64_clang.txt b/library/stdarch/crates/intrinsic-test/missing_loongarch64_clang.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/library/stdarch/crates/intrinsic-test/missing_loongarch64_common.txt b/library/stdarch/crates/intrinsic-test/missing_loongarch64_common.txt new file mode 100644 index 0000000000000..5817247c2f4d2 --- /dev/null +++ b/library/stdarch/crates/intrinsic-test/missing_loongarch64_common.txt @@ -0,0 +1,31 @@ +# Missing in old c compiler +lasx_concat_128 +lasx_concat_128_d +lasx_concat_128_s +lasx_extract_128_hi +lasx_extract_128_lo +lasx_extract_128_hi_d +lasx_extract_128_lo_d +lasx_extract_128_hi_s +lasx_extract_128_lo_s +lasx_insert_128_hi +lasx_insert_128_lo +lasx_insert_128_hi_d +lasx_insert_128_lo_d +lasx_insert_128_hi_s +lasx_insert_128_lo_s + +# Missing in old qemu +lasx_xvfrecipe_d +lasx_xvfrecipe_s +lasx_xvfrsqrte_d +lasx_xvfrsqrte_s +lsx_vfrecipe_d +lsx_vfrecipe_s +lsx_vfrsqrte_d +lsx_vfrsqrte_s + +# Top bits are undefined, unclear how to test these +lasx_cast_128 +lasx_cast_128_d +lasx_cast_128_s diff --git a/library/stdarch/crates/intrinsic-test/missing_loongarch64_gcc.txt b/library/stdarch/crates/intrinsic-test/missing_loongarch64_gcc.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs index 9c2d0f336a7aa..5e892c613464a 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs @@ -37,6 +37,8 @@ impl SupportedArchitecture for Arm { "#; const RUST_PRELUDE: &str = RUST_PRELUDE; + const C_NAME_PREFIX: &str = ""; + fn c_compiler_flags(&self, cli_options: &ProcessedCli) -> Vec<&str> { // GCC uses an extra `-` in the arch name let big_endian = cli_options.target.starts_with("aarch64_be"); diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs index bbff7a91b6c41..a749afd17d367 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs @@ -1,5 +1,6 @@ use itertools::Itertools; +use crate::common::imm_value_to_ident; use crate::common::{SupportedArchitecture, intrinsic::Intrinsic}; use super::intrinsic_helpers::TypeDefinition; @@ -37,13 +38,18 @@ pub fn write_wrapper_c( fmt(&format_args!( " void {name}_wrapper{imm_arglist}({return_ty}* __dst{arglist}) {{ - *__dst = {name}({params}); + *__dst = {prefix}{name}({params}); }}", return_ty = intrinsic.results.c_type(), + prefix = A::C_NAME_PREFIX, name = intrinsic.name, - imm_arglist = imm_values - .iter() - .format_with("", |i, fmt| fmt(&format_args!("_{i}"))), + imm_arglist = + imm_values + .iter() + .format_with("", |i, fmt| fmt(&format_args!( + "_{}", + imm_value_to_ident(i) + ))), arglist = intrinsic.arguments.as_non_imm_arglist_c(), params = intrinsic.arguments.as_call_params_c(&imm_values) )) diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs index bfe37edbcfe43..f319418b49c7f 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs @@ -4,6 +4,7 @@ use itertools::Itertools; use super::intrinsic_helpers::TypeDefinition; use crate::common::cli::{CcArgStyle, ProcessedCli}; +use crate::common::imm_value_to_ident; use crate::common::intrinsic::Intrinsic; use crate::common::intrinsic_helpers::TypeKind; use crate::common::values::{test_values_array_name, test_values_array_static}; @@ -276,7 +277,7 @@ for (id, rust, c) in specializations {{ } }) .join(","), - c_const_args = imm_values.iter().join("_"), + c_const_args = imm_values.iter().map(imm_value_to_ident).join("_"), )) } }), @@ -343,9 +344,13 @@ unsafe extern "C" {{ "fn {name}_wrapper{imm_arglist}(__dst: *mut {return_ty}{arglist});", return_ty = intrinsic.results.rust_type(), name = intrinsic.name, - imm_arglist = imm_values - .iter() - .format_with("", |i, fmt| fmt(&format_args!("_{i}"))), + imm_arglist = + imm_values + .iter() + .format_with("", |i, fmt| fmt(&format_args!( + "_{}", + imm_value_to_ident(i) + ))), arglist = intrinsic.arguments.as_non_imm_arglist_rust(), )) })) diff --git a/library/stdarch/crates/intrinsic-test/src/common/mod.rs b/library/stdarch/crates/intrinsic-test/src/common/mod.rs index b476ad477ca36..9cde80daff76c 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/mod.rs @@ -49,6 +49,10 @@ pub trait SupportedArchitecture: Sized { const C_PRELUDE: &str; const RUST_PRELUDE: &str; + /// Per-architecture prefix used to convert a Rust intrinsic name to the + /// corresponding C intrinsic name by prepending it to the Rust name. + const C_NAME_PREFIX: &str; + fn c_compiler_flags(&self, cli_options: &ProcessedCli) -> Vec<&str>; fn generate_c_file(&self) { @@ -138,3 +142,8 @@ pub fn manual_chunk(intrinsic_count: usize) -> (usize, usize) { let number_of_chunks = intrinsic_count.div_ceil(max_intrinsics_per_chunk); (max_intrinsics_per_chunk, number_of_chunks) } + +pub fn imm_value_to_ident(value: impl std::fmt::Display) -> String { + let value = value.to_string(); + value.replace('-', "neg") +} diff --git a/library/stdarch/crates/intrinsic-test/src/loongarch/intrinsic.rs b/library/stdarch/crates/intrinsic-test/src/loongarch/intrinsic.rs new file mode 100644 index 0000000000000..f8ebb3e7ab34c --- /dev/null +++ b/library/stdarch/crates/intrinsic-test/src/loongarch/intrinsic.rs @@ -0,0 +1,19 @@ +use crate::common::intrinsic_helpers::IntrinsicType; +use std::ops::{Deref, DerefMut}; + +#[derive(Debug, Clone, PartialEq)] +pub struct LoongArchType(pub IntrinsicType); + +impl Deref for LoongArchType { + type Target = IntrinsicType; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for LoongArchType { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} diff --git a/library/stdarch/crates/intrinsic-test/src/loongarch/mod.rs b/library/stdarch/crates/intrinsic-test/src/loongarch/mod.rs new file mode 100644 index 0000000000000..00cff20a0f083 --- /dev/null +++ b/library/stdarch/crates/intrinsic-test/src/loongarch/mod.rs @@ -0,0 +1,139 @@ +mod intrinsic; +mod parser; +mod types; + +use std::path::Path; + +use crate::common::SupportedArchitecture; +use crate::common::cli::ProcessedCli; +use crate::common::intrinsic::Intrinsic; +use crate::common::intrinsic_helpers::TypeKind; +use intrinsic::LoongArchType; +use parser::get_intrinsics; + +#[derive(PartialEq)] +pub struct LoongArch { + intrinsics: Vec>, +} + +impl SupportedArchitecture for LoongArch { + type Type = LoongArchType; + + fn intrinsics(&self) -> &[Intrinsic] { + &self.intrinsics + } + + const NOTICE: &str = r#" +// This is a transient test file, not intended for distribution. Some aspects of the +// test are derived from LoongArch specification files, published under the same license as the +// `intrinsic-test` crate. +"#; + + const C_PRELUDE: &str = r#" +#include +#include +"#; + const RUST_PRELUDE: &str = RUST_PRELUDE; + + const C_NAME_PREFIX: &str = "__"; + + fn c_compiler_flags(&self, _cli_options: &ProcessedCli) -> Vec<&str> { + let mut flags = vec!["-mlsx"]; + if self + .intrinsics + .iter() + .any(|intrinsic| intrinsic.extension == "LASX") + { + flags.push("-mlasx"); + } + if self.intrinsics.iter().any(|intrinsic| { + intrinsic.name.contains("frecipe") || intrinsic.name.contains("frsqrte") + }) { + flags.push("-mfrecipe"); + } + flags + } + + fn create(cli_options: &ProcessedCli) -> Self { + let mut intrinsics = + load_intrinsics(&cli_options.filename).expect("Error parsing input file"); + + intrinsics.sort_by(|a, b| a.name.cmp(&b.name)); + intrinsics.dedup_by(|a, b| { + a.name == b.name && a.results == b.results && a.arguments == b.arguments + }); + + let intrinsics = intrinsics + .into_iter() + // Skip intrinsics that don't return a value. + .filter(|intrinsic| intrinsic.results.kind() != TypeKind::Void) + .filter(|intrinsic| !intrinsic.arguments.args.is_empty()) + // Skip pointers for now, we would probably need to look at the return + // type to work out how many elements we need to point to. + .filter(|intrinsic| !intrinsic.arguments.iter().any(|arg| arg.is_ptr())) + // Skip intrinsics from `--skip` + .filter(|intrinsic| !cli_options.skip.contains(&intrinsic.name)) + .collect::>(); + + let sample_percentage: usize = cli_options.sample_percentage as usize; + let sample_size = (intrinsics.len() * sample_percentage) / 100; + let intrinsics = intrinsics.into_iter().take(sample_size).collect(); + + Self { intrinsics } + } + + fn predicate_function(_: u32) -> String { + unimplemented!("no scalable vectors on LoongArch") + } +} + +fn load_intrinsics(path: &Path) -> Result>, Box> { + if path.is_dir() { + let mut intrinsics = Vec::new(); + for spec in ["lsx.spec", "lasx.spec"] { + let spec_path = path.join(spec); + if spec_path.exists() { + intrinsics.extend(get_intrinsics(&spec_path)?); + } + } + return Ok(intrinsics); + } + + get_intrinsics(path) +} + +const RUST_PRELUDE: &str = r#" +#![feature(stdarch_loongarch)] + +use core_arch::arch::loongarch64::*; + +#[inline] +unsafe fn lsx_vld_to_m128i(mem_addr: *const i8) -> m128i { + lsx_vld::<0>(mem_addr) +} + +#[inline] +unsafe fn lsx_vld_to_m128(mem_addr: *const i8) -> m128 { + core::mem::transmute(lsx_vld::<0>(mem_addr)) +} + +#[inline] +unsafe fn lsx_vld_to_m128d(mem_addr: *const i8) -> m128d { + core::mem::transmute(lsx_vld::<0>(mem_addr)) +} + +#[inline] +unsafe fn lasx_xvld_to_m256i(mem_addr: *const i8) -> m256i { + lasx_xvld::<0>(mem_addr) +} + +#[inline] +unsafe fn lasx_xvld_to_m256(mem_addr: *const i8) -> m256 { + core::mem::transmute(lasx_xvld::<0>(mem_addr)) +} + +#[inline] +unsafe fn lasx_xvld_to_m256d(mem_addr: *const i8) -> m256d { + core::mem::transmute(lasx_xvld::<0>(mem_addr)) +} +"#; diff --git a/library/stdarch/crates/intrinsic-test/src/loongarch/parser.rs b/library/stdarch/crates/intrinsic-test/src/loongarch/parser.rs new file mode 100644 index 0000000000000..ecb5d1ab3e8df --- /dev/null +++ b/library/stdarch/crates/intrinsic-test/src/loongarch/parser.rs @@ -0,0 +1,179 @@ +use std::path::Path; + +use super::intrinsic::LoongArchType; +use super::types::parse_intrinsic_type; +use crate::common::argument::{Argument, ArgumentList}; +use crate::common::constraint::Constraint; +use crate::common::intrinsic::Intrinsic; +use crate::loongarch::LoongArch; + +pub fn get_intrinsics( + filename: &Path, +) -> Result>, Box> { + parse_spec_file(filename) +} + +fn parse_spec_file( + filename: &Path, +) -> Result>, Box> { + let contents = std::fs::read_to_string(filename)?; + parse_spec_contents(&contents) +} + +fn parse_spec_contents( + contents: &str, +) -> Result>, Box> { + let mut intrinsics = Vec::new(); + let mut record = Vec::new(); + + for line in contents.lines().chain(std::iter::once("")) { + let line = line.trim(); + if line.is_empty() { + if !record.is_empty() { + if let Some(intrinsic) = parse_record(&record)? { + intrinsics.push(intrinsic); + } + record.clear(); + } + continue; + } + record.push(line); + } + + Ok(intrinsics) +} + +fn parse_record( + record: &[&str], +) -> Result>, Box> { + let mut name = None; + let mut asm_formats = Vec::new(); + let mut data_types = None; + + for line in record { + if let Some(value) = line.strip_prefix("name = ") { + name = Some(value.to_string()); + } else if let Some(value) = line.strip_prefix("asm-fmts = ") { + asm_formats = value + .split(',') + .map(|part| part.trim().to_string()) + .collect(); + } else if let Some(value) = line.strip_prefix("data-types = ") { + data_types = Some(value); + } + } + + let Some(data_types) = data_types else { + return Ok(None); + }; + + let name = name.ok_or("missing name before data-types")?; + Ok(Some(parse_intrinsic(&name, &asm_formats, data_types)?)) +} + +fn parse_intrinsic( + name: &str, + asm_formats: &[String], + data_types: &str, +) -> Result, Box> { + let data_types = data_types + .split(',') + .map(|value| value.trim()) + .filter(|value| !value.is_empty()) + .collect::>(); + let Some((result_type, argument_types)) = data_types.split_first() else { + return Err("missing data-types for intrinsic".into()); + }; + let result = LoongArchType(parse_intrinsic_type(result_type)?); + let asm_offset = asm_formats + .len() + .checked_sub(argument_types.len()) + .ok_or_else(|| format!("{name}: fewer asm formats than arguments"))?; + let arguments = argument_types + .iter() + .enumerate() + .map(|(pos, data_type)| { + let constraint = asm_formats + .get(pos + asm_offset) + .and_then(|format| parse_constraint(name, format)); + let mut ty = LoongArchType(parse_intrinsic_type(data_type)?); + if constraint.is_some() { + ty.constant = true; + } + Ok(Argument::new( + pos, + format!("arg_{pos}"), + ty, + constraint, + false, + )) + }) + .collect::, String>>()?; + + Ok(Intrinsic { + name: name.to_string(), + arguments: ArgumentList { args: arguments }, + results: result, + arch_tags: Vec::new(), + extension: extension_for(name)?.to_string(), + }) +} + +fn parse_constraint(name: &str, asm_format: &str) -> Option { + if let Some(cons) = special_constraint(name) { + return Some(cons); + } + if let Some(bits) = asm_format.strip_prefix("ui") { + return Some(unsigned_constraint(bits.parse::().ok()?)); + } + if let Some(bits) = asm_format + .strip_prefix("si") + .or_else(|| asm_format.strip_prefix('i')) + { + return Some(signed_constraint(bits.parse::().ok()?)); + } + None +} + +fn special_constraint(name: &str) -> Option { + match name { + "lsx_vldi" | "lasx_xvldi" => { + // CC: imm13 only support 0000 ~ 1100 in bits 9 ~ 12 when bit ‘13’ is 1 + let values: Vec = (0i64..8192i64) + .filter(|&x| x < 4096 || ((x >> 8) & 0xf) < 13) + // sign extend + .map(|x| ((x << 51) as i64) >> 51) + .collect(); + Some(Constraint::Set(values)) + } + _ => None, + } +} + +fn unsigned_constraint(bits: u32) -> Constraint { + Constraint::Range(0..(1i64 << bits.min(63))) +} + +fn signed_constraint(bits: u32) -> Constraint { + let min = if bits == 64 { + i64::MIN + } else { + -(1i64 << (bits - 1)) + }; + let max = if bits == 64 { + i64::MAX + } else { + 1i64 << (bits - 1) + }; + Constraint::Range(min..max) +} + +fn extension_for(name: &str) -> Result<&'static str, Box> { + if name.starts_with("lasx_") { + Ok("LASX") + } else if name.starts_with("lsx_") { + Ok("LSX") + } else { + Err(format!("unsupported LoongArch intrinsic name {name}").into()) + } +} diff --git a/library/stdarch/crates/intrinsic-test/src/loongarch/types.rs b/library/stdarch/crates/intrinsic-test/src/loongarch/types.rs new file mode 100644 index 0000000000000..dd4e578e67a41 --- /dev/null +++ b/library/stdarch/crates/intrinsic-test/src/loongarch/types.rs @@ -0,0 +1,309 @@ +use super::intrinsic::LoongArchType; +use crate::common::intrinsic_helpers::{IntrinsicType, Sign, SimdLen, TypeDefinition, TypeKind}; + +impl TypeDefinition for LoongArchType { + fn c_type(&self) -> String { + if self.ptr { + return if self.ptr_constant { + "const void*".to_string() + } else { + "void*".to_string() + }; + } + + match (self.kind(), self.simd_len) { + (_, Some(SimdLen::Fixed(lanes))) => { + format!( + "__{}", + vector_type_name(lanes, self.inner_size(), self.kind()) + ) + } + (TypeKind::Int(Sign::Signed), None) => { + scalar_type_name(true, scalar_signature_bits(self.inner_size())).to_string() + } + (TypeKind::Int(Sign::Unsigned), None) => { + scalar_type_name(false, scalar_signature_bits(self.inner_size())).to_string() + } + (TypeKind::Float, None) => match self.inner_size() { + 32 => "float".to_string(), + 64 => "double".to_string(), + bits => unreachable!("unsupported scalar float width {bits}"), + }, + (TypeKind::Void, None) => "void".to_string(), + _ => unreachable!("unsupported LoongArch type {self:#?}"), + } + } + + fn rust_type(&self) -> String { + if self.ptr { + return format!( + "*{} core::ffi::c_void", + if self.ptr_constant { "const" } else { "mut" }, + ); + } + + match (self.kind(), self.simd_len) { + (_, Some(SimdLen::Fixed(lanes))) => { + vector_type_name(lanes, self.inner_size(), self.kind()).to_string() + } + (TypeKind::Int(Sign::Signed), None) => { + semantic_rust_scalar_type(true, scalar_signature_bits(self.inner_size())) + .to_string() + } + (TypeKind::Int(Sign::Unsigned), None) => { + semantic_rust_scalar_type(false, scalar_signature_bits(self.inner_size())) + .to_string() + } + (TypeKind::Float, None) => match self.inner_size() { + 32 => "f32".to_string(), + 64 => "f64".to_string(), + bits => unreachable!("unsupported scalar float width {bits}"), + }, + (TypeKind::Void, None) => "()".to_string(), + _ => unreachable!("unsupported LoongArch type {self:#?}"), + } + } + + fn rust_scalar_type(&self) -> String { + match self.kind() { + TypeKind::Int(Sign::Signed) => { + semantic_rust_scalar_type(true, self.inner_size()).to_string() + } + TypeKind::Int(Sign::Unsigned) => { + semantic_rust_scalar_type(false, self.inner_size()).to_string() + } + TypeKind::Float => match self.inner_size() { + 32 => "f32".to_string(), + 64 => "f64".to_string(), + bits => unreachable!("unsupported scalar float width {bits}"), + }, + _ => unreachable!("unsupported LoongArch scalar type {self:#?}"), + } + } + + fn load_function(&self) -> String { + let Some(SimdLen::Fixed(lanes)) = self.simd_len else { + unreachable!("LoongArch loads are only used for SIMD types") + }; + + match (lanes * self.inner_size(), self.kind()) { + (128, TypeKind::Float) if self.inner_size() == 64 => "lsx_vld_to_m128d".to_string(), + (128, TypeKind::Float) => "lsx_vld_to_m128".to_string(), + (128, _) => "lsx_vld_to_m128i".to_string(), + (256, TypeKind::Float) if self.inner_size() == 64 => "lasx_xvld_to_m256d".to_string(), + (256, TypeKind::Float) => "lasx_xvld_to_m256".to_string(), + (256, _) => "lasx_xvld_to_m256i".to_string(), + bits => unreachable!("unsupported LoongArch vector width {bits:?}"), + } + } +} + +fn vector_type_name(lanes: u32, bit_len: u32, kind: TypeKind) -> &'static str { + match (lanes * bit_len, kind, bit_len) { + (128, TypeKind::Float, 32) => "m128", + (128, TypeKind::Float, 64) => "m128d", + (128, _, _) => "m128i", + (256, TypeKind::Float, 32) => "m256", + (256, TypeKind::Float, 64) => "m256d", + (256, _, _) => "m256i", + _ => unreachable!("unsupported LoongArch vector shape {kind:?}x{bit_len}x{lanes}"), + } +} + +fn scalar_type_name(signed: bool, bit_len: u32) -> &'static str { + match (signed, bit_len) { + (true, 32) => "int32_t", + (true, 64) => "int64_t", + (false, 32) => "uint32_t", + (false, 64) => "uint64_t", + _ => unreachable!("unsupported LoongArch scalar width {bit_len}"), + } +} + +fn scalar_signature_bits(bit_len: u32) -> u32 { + match bit_len { + 8 | 16 | 32 => 32, + 64 => 64, + _ => unreachable!("unsupported LoongArch scalar width {bit_len}"), + } +} + +fn semantic_rust_scalar_type(signed: bool, bit_len: u32) -> &'static str { + match (signed, bit_len) { + (true, 8) => "i8", + (true, 16) => "i16", + (true, 32) => "i32", + (true, 64) => "i64", + (false, 8) => "u8", + (false, 16) => "u16", + (false, 32) => "u32", + (false, 64) => "u64", + _ => unreachable!("unsupported LoongArch scalar width {bit_len}"), + } +} + +pub fn parse_intrinsic_type(s: &str) -> Result { + let (kind, bit_len, simd_len, ptr, ptr_constant) = match s { + "V16QI" => ( + TypeKind::Int(Sign::Signed), + Some(8), + Some(SimdLen::Fixed(16)), + false, + false, + ), + "V32QI" => ( + TypeKind::Int(Sign::Signed), + Some(8), + Some(SimdLen::Fixed(32)), + false, + false, + ), + "V8HI" => ( + TypeKind::Int(Sign::Signed), + Some(16), + Some(SimdLen::Fixed(8)), + false, + false, + ), + "V16HI" => ( + TypeKind::Int(Sign::Signed), + Some(16), + Some(SimdLen::Fixed(16)), + false, + false, + ), + "V4SI" => ( + TypeKind::Int(Sign::Signed), + Some(32), + Some(SimdLen::Fixed(4)), + false, + false, + ), + "V8SI" => ( + TypeKind::Int(Sign::Signed), + Some(32), + Some(SimdLen::Fixed(8)), + false, + false, + ), + "V2DI" => ( + TypeKind::Int(Sign::Signed), + Some(64), + Some(SimdLen::Fixed(2)), + false, + false, + ), + "V4DI" => ( + TypeKind::Int(Sign::Signed), + Some(64), + Some(SimdLen::Fixed(4)), + false, + false, + ), + "UV16QI" => ( + TypeKind::Int(Sign::Unsigned), + Some(8), + Some(SimdLen::Fixed(16)), + false, + false, + ), + "UV32QI" => ( + TypeKind::Int(Sign::Unsigned), + Some(8), + Some(SimdLen::Fixed(32)), + false, + false, + ), + "UV8HI" => ( + TypeKind::Int(Sign::Unsigned), + Some(16), + Some(SimdLen::Fixed(8)), + false, + false, + ), + "UV16HI" => ( + TypeKind::Int(Sign::Unsigned), + Some(16), + Some(SimdLen::Fixed(16)), + false, + false, + ), + "UV4SI" => ( + TypeKind::Int(Sign::Unsigned), + Some(32), + Some(SimdLen::Fixed(4)), + false, + false, + ), + "UV8SI" => ( + TypeKind::Int(Sign::Unsigned), + Some(32), + Some(SimdLen::Fixed(8)), + false, + false, + ), + "UV2DI" => ( + TypeKind::Int(Sign::Unsigned), + Some(64), + Some(SimdLen::Fixed(2)), + false, + false, + ), + "UV4DI" => ( + TypeKind::Int(Sign::Unsigned), + Some(64), + Some(SimdLen::Fixed(4)), + false, + false, + ), + "V4SF" => ( + TypeKind::Float, + Some(32), + Some(SimdLen::Fixed(4)), + false, + false, + ), + "V8SF" => ( + TypeKind::Float, + Some(32), + Some(SimdLen::Fixed(8)), + false, + false, + ), + "V2DF" => ( + TypeKind::Float, + Some(64), + Some(SimdLen::Fixed(2)), + false, + false, + ), + "V4DF" => ( + TypeKind::Float, + Some(64), + Some(SimdLen::Fixed(4)), + false, + false, + ), + "QI" => (TypeKind::Int(Sign::Signed), Some(8), None, false, false), + "HI" => (TypeKind::Int(Sign::Signed), Some(16), None, false, false), + "SI" => (TypeKind::Int(Sign::Signed), Some(32), None, false, false), + "UQI" => (TypeKind::Int(Sign::Unsigned), Some(8), None, false, false), + "UHI" => (TypeKind::Int(Sign::Unsigned), Some(16), None, false, false), + "USI" => (TypeKind::Int(Sign::Unsigned), Some(32), None, false, false), + "DI" => (TypeKind::Int(Sign::Signed), Some(64), None, false, false), + "UDI" => (TypeKind::Int(Sign::Unsigned), Some(64), None, false, false), + "CVPOINTER" => (TypeKind::Int(Sign::Signed), Some(8), None, true, true), + "VOID" => (TypeKind::Void, None, None, false, false), + _ => return Err(format!("unsupported LoongArch type {s}")), + }; + + Ok(IntrinsicType { + constant: false, + ptr_constant, + ptr, + kind, + bit_len, + simd_len, + vec_len: None, + }) +} diff --git a/library/stdarch/crates/intrinsic-test/src/main.rs b/library/stdarch/crates/intrinsic-test/src/main.rs index e25eb48a456d1..91e76d8f5fa50 100644 --- a/library/stdarch/crates/intrinsic-test/src/main.rs +++ b/library/stdarch/crates/intrinsic-test/src/main.rs @@ -3,11 +3,13 @@ extern crate log; mod arm; mod common; +mod loongarch; mod x86; use arm::Arm; use common::SupportedArchitecture; use common::cli::{Cli, ProcessedCli}; +use loongarch::LoongArch; use x86::X86; fn main() { @@ -21,6 +23,11 @@ fn main() { run(Arm::create(&processed_cli_options), processed_cli_options) } else if processed_cli_options.target.starts_with("x86") { run(X86::create(&processed_cli_options), processed_cli_options) + } else if processed_cli_options.target.starts_with("loongarch64") { + run( + LoongArch::create(&processed_cli_options), + processed_cli_options, + ) } else { unimplemented!("Unsupported target {}", processed_cli_options.target) } diff --git a/library/stdarch/crates/intrinsic-test/src/x86/mod.rs b/library/stdarch/crates/intrinsic-test/src/x86/mod.rs index 36f4fee43741b..9eafcf1fc7b96 100644 --- a/library/stdarch/crates/intrinsic-test/src/x86/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/x86/mod.rs @@ -32,6 +32,8 @@ impl SupportedArchitecture for X86 { "#; const RUST_PRELUDE: &str = RUST_PRELUDE; + const C_NAME_PREFIX: &str = ""; + fn c_compiler_flags(&self, _cli_options: &ProcessedCli) -> Vec<&str> { vec![ "-maes", diff --git a/library/stdarch/crates/stdarch-gen-arm/Cargo.toml b/library/stdarch/crates/stdarch-gen-arm/Cargo.toml index cb284d5f2c57b..9d803538474c7 100644 --- a/library/stdarch/crates/stdarch-gen-arm/Cargo.toml +++ b/library/stdarch/crates/stdarch-gen-arm/Cargo.toml @@ -12,6 +12,7 @@ edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +clap = { version = "4", features = ["derive", "env"] } itertools = "0.15.0" proc-macro2 = "1.0" quote = "1.0" diff --git a/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml b/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml index e5ce77ed8b33f..01942246a0997 100644 --- a/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml +++ b/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml @@ -4321,10 +4321,7 @@ intrinsics: - ['*mut f64', float64x1_t] compose: - FnCall: [static_assert!, ['LANE == 0']] - - Assign: - - "*a" - - FnCall: [simd_extract!, [b, 'LANE as u32']] - - Identifier: [';', Symbol] + - FnCall: [core::ptr::write_unaligned, [a, {FnCall: [simd_extract!, [b, 'LANE as u32']]}]] - name: "vst1{neon_type[1].lane_nox}" doc: "Store multiple single-element structures from one, two, three, or four registers" @@ -4340,10 +4337,7 @@ intrinsics: - ['*mut f64', float64x2_t] compose: - FnCall: [static_assert_uimm_bits!, [LANE, '1']] - - Assign: - - "*a" - - FnCall: [simd_extract!, [b, 'LANE as u32']] - - Identifier: [';', Symbol] + - FnCall: [core::ptr::write_unaligned, [a, {FnCall: [simd_extract!, [b, 'LANE as u32']]}]] - name: "vst2{neon_type[1].nox}" doc: "Store multiple 2-element structures from two registers" diff --git a/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml b/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml index a4ade26e45c37..35c2a108c39ab 100644 --- a/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml +++ b/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml @@ -2868,7 +2868,7 @@ intrinsics: - ["*const f16", float16x8_t, 'q_lane', '3'] compose: - FnCall: [static_assert_uimm_bits!, [LANE, '{type[3]}']] - - FnCall: [simd_insert!, [src, "LANE as u32", "*ptr"]] + - FnCall: [simd_insert!, [src, "LANE as u32", {FnCall: ["crate::ptr::read_unaligned", [ptr]]}]] - name: "vld1{type[2]}_{neon_type[1]}" doc: "Load one single-element structure and replicate to all lanes of one register" @@ -4683,10 +4683,7 @@ intrinsics: - ['*mut u64', uint64x1_t] compose: - FnCall: [static_assert!, ['LANE == 0']] - - Assign: - - "*a" - - FnCall: [simd_extract!, [b, 'LANE as u32']] - - Identifier: [';', Symbol] + - FnCall: ['crate::ptr::write_unaligned', [a, {FnCall: [simd_extract!, [b, 'LANE as u32']]}]] - name: "vst1{neon_type[1].lane_nox}" doc: "Store multiple single-element structures from one, two, three, or four registers" @@ -4708,10 +4705,7 @@ intrinsics: - ['*mut p64', poly64x1_t] compose: - FnCall: [static_assert!, ['LANE == 0']] - - Assign: - - "*a" - - FnCall: [simd_extract!, [b, 'LANE as u32']] - - Identifier: [';', Symbol] + - FnCall: ['crate::ptr::write_unaligned', [a, {FnCall: [simd_extract!, [b, 'LANE as u32']]}]] - name: "vst1{neon_type[1].lane_nox}" doc: "Store multiple single-element structures from one, two, three, or four registers" @@ -4733,10 +4727,7 @@ intrinsics: - ['*mut p64', poly64x2_t] compose: - FnCall: [static_assert_uimm_bits!, [LANE, '1']] - - Assign: - - "*a" - - FnCall: [simd_extract!, [b, 'LANE as u32']] - - Identifier: [';', Symbol] + - FnCall: ['crate::ptr::write_unaligned', [a, {FnCall: [simd_extract!, [b, 'LANE as u32']]}]] - name: "vst1{neon_type[1].lane_nox}" doc: "Store multiple single-element structures from one, two, three, or four registers" @@ -4774,10 +4765,7 @@ intrinsics: - ['*mut f32', float32x4_t, '2'] compose: - FnCall: [static_assert_uimm_bits!, [LANE, "{type[2]}"]] - - Assign: - - "*a" - - FnCall: [simd_extract!, [b, 'LANE as u32']] - - Identifier: [';', Symbol] + - FnCall: ['crate::ptr::write_unaligned', [a, {FnCall: [simd_extract!, [b, 'LANE as u32']]}]] - name: "vst1{neon_type[1].lane_nox}" @@ -4799,10 +4787,7 @@ intrinsics: - ['*mut f16', float16x8_t, '3'] compose: - FnCall: [static_assert_uimm_bits!, [LANE, "{type[2]}"]] - - Assign: - - "*a" - - FnCall: [simd_extract!, [b, 'LANE as u32']] - - Identifier: [';', Symbol] + - FnCall: ['crate::ptr::write_unaligned', [a, {FnCall: [simd_extract!, [b, 'LANE as u32']]}]] - name: 'vst1{neon_type[1].no}' @@ -14310,17 +14295,17 @@ intrinsics: - ['vld1q_lane_p16', '*const p16', 'poly16x8_t', '"vld1.16"', '7', 'ld1', 'static_assert_uimm_bits!', 'LANE, 3'] - ['vld1_lane_s32', '*const i32', 'int32x2_t', '"vld1.32"', '1', 'ld1', 'static_assert_uimm_bits!', 'LANE, 1'] - ['vld1_lane_u32', '*const u32', 'uint32x2_t', '"vld1.32"', '1', 'ld1', 'static_assert_uimm_bits!', 'LANE, 1'] - - ['vld1_lane_f32', '*const f32', 'float32x2_t', '"vld1.32"', '1', 'ld1', 'static_assert_uimm_bits!', 'LANE, 1'] + - ['vld1_lane_f32', '*const f32', 'float32x2_t', 'ldr', '1', 'ld1', 'static_assert_uimm_bits!', 'LANE, 1'] - ['vld1q_lane_s32', '*const i32', 'int32x4_t', '"vld1.32"', '3', 'ld1', 'static_assert_uimm_bits!', 'LANE, 2'] - ['vld1q_lane_u32', '*const u32', 'uint32x4_t', '"vld1.32"', '3', 'ld1', 'static_assert_uimm_bits!', 'LANE, 2'] - - ['vld1q_lane_f32', '*const f32', 'float32x4_t', '"vld1.32"', '3', 'ld1', 'static_assert_uimm_bits!', 'LANE, 2'] - - ['vld1_lane_s64', '*const i64', 'int64x1_t', 'vldr', '0', 'ldr', 'static_assert!', 'LANE == 0'] - - ['vld1_lane_u64', '*const u64', 'uint64x1_t', 'vldr', '0', 'ldr', 'static_assert!', 'LANE == 0'] - - ['vld1q_lane_s64', '*const i64', 'int64x2_t', 'vldr', '1', 'ld1', 'static_assert_uimm_bits!', 'LANE, 1'] - - ['vld1q_lane_u64', '*const u64', 'uint64x2_t', 'vldr', '1', 'ld1', 'static_assert_uimm_bits!', 'LANE, 1'] + - ['vld1q_lane_f32', '*const f32', 'float32x4_t', 'ldr', '3', 'ld1', 'static_assert_uimm_bits!', 'LANE, 2'] + - ['vld1_lane_s64', '*const i64', 'int64x1_t', '"vld1.8"', '0', 'ldr', 'static_assert!', 'LANE == 0'] + - ['vld1_lane_u64', '*const u64', 'uint64x1_t', '"vld1.8"', '0', 'ldr', 'static_assert!', 'LANE == 0'] + - ['vld1q_lane_s64', '*const i64', 'int64x2_t', '"vld1.8"', '1', 'ld1', 'static_assert_uimm_bits!', 'LANE, 1'] + - ['vld1q_lane_u64', '*const u64', 'uint64x2_t', '"vld1.8"', '1', 'ld1', 'static_assert_uimm_bits!', 'LANE, 1'] compose: - FnCall: ["{type[6]}", ["{type[7]}"]] - - FnCall: [simd_insert!, [src, 'LANE as u32', '*ptr']] + - FnCall: [simd_insert!, [src, 'LANE as u32', {FnCall: ['crate::ptr::read_unaligned', [ptr]]}]] - name: "{type[0]}" doc: "Load one single-element structure to one lane of one register." @@ -14338,11 +14323,11 @@ intrinsics: safety: unsafe: [neon] types: - - ['vld1_lane_p64', '*const p64', 'poly64x1_t', 'vldr', '0', 'ldr', 'static_assert!', 'LANE == 0'] - - ['vld1q_lane_p64', '*const p64', 'poly64x2_t', 'vldr', '1', 'ld1', 'static_assert_uimm_bits!', 'LANE, 1'] + - ['vld1_lane_p64', '*const p64', 'poly64x1_t', '"vld1.8"', '0', 'ldr', 'static_assert!', 'LANE == 0'] + - ['vld1q_lane_p64', '*const p64', 'poly64x2_t', '"vld1.8"', '1', 'ld1', 'static_assert_uimm_bits!', 'LANE, 1'] compose: - FnCall: ["{type[6]}", ["{type[7]}"]] - - FnCall: [simd_insert!, [src, 'LANE as u32', '*ptr']] + - FnCall: [simd_insert!, [src, 'LANE as u32', {FnCall: ['crate::ptr::read_unaligned', [ptr]]}]] - name: "{type[0]}" doc: "Load one single-element structure and Replicate to all lanes (of one register)." @@ -14396,7 +14381,7 @@ intrinsics: safety: unsafe: [neon] types: - - ['vld1q_dup_p64', '*const p64', 'poly64x2_t', 'vldr', 'ld1r', 'vld1q_lane_p64::<0>', 'u64x2::splat(0)', '[0, 0]'] + - ['vld1q_dup_p64', '*const p64', 'poly64x2_t', '"vld1.8"', 'ld1r', 'vld1q_lane_p64::<0>', 'u64x2::splat(0)', '[0, 0]'] compose: - Let: - x @@ -14437,18 +14422,18 @@ intrinsics: - ['vld1_dup_s32', '*const i32', 'int32x2_t', 'vld1.32', 'ld1r', 'i32x2::splat'] - ['vld1_dup_u32', '*const u32', 'uint32x2_t', 'vld1.32', 'ld1r', 'u32x2::splat'] - - ['vld1_dup_f32', '*const f32', 'float32x2_t', 'vld1.32', 'ld1r', 'f32x2::splat'] + - ['vld1_dup_f32', '*const f32', 'float32x2_t', 'ldr', 'ld1r', 'f32x2::splat'] - ['vld1q_dup_s32', '*const i32', 'int32x4_t', 'vld1.32', 'ld1r', 'i32x4::splat'] - ['vld1q_dup_u32', '*const u32', 'uint32x4_t', 'vld1.32', 'ld1r', 'u32x4::splat'] - - ['vld1q_dup_f32', '*const f32', 'float32x4_t', 'vld1.32', 'ld1r', 'f32x4::splat'] + - ['vld1q_dup_f32', '*const f32', 'float32x4_t', 'ldr', 'ld1r', 'f32x4::splat'] - - ['vld1q_dup_s64', '*const i64', 'int64x2_t', 'vldr', 'ld1r', 'i64x2::splat'] - - ['vld1q_dup_u64', '*const u64', 'uint64x2_t', 'vldr', 'ld1r', 'u64x2::splat'] + - ['vld1q_dup_s64', '*const i64', 'int64x2_t', 'vld1.8', 'ld1r', 'i64x2::splat'] + - ['vld1q_dup_u64', '*const u64', 'uint64x2_t', 'vld1.8', 'ld1r', 'u64x2::splat'] compose: - FnCall: - transmute - - - FnCall: ['{type[5]}', ["*ptr"]] + - - FnCall: ['{type[5]}', [{FnCall: ['crate::ptr::read_unaligned', [ptr]]}]] - name: "{type[0]}" doc: "Absolute difference and accumulate (64-bit)" diff --git a/library/stdarch/crates/stdarch-gen-arm/src/load_store_tests.rs b/library/stdarch/crates/stdarch-gen-arm/src/load_store_tests.rs index 672047fc057b8..3e3c20c592be0 100644 --- a/library/stdarch/crates/stdarch-gen-arm/src/load_store_tests.rs +++ b/library/stdarch/crates/stdarch-gen-arm/src/load_store_tests.rs @@ -1,10 +1,9 @@ use std::fs::File; -use std::io::Write; -use std::path::PathBuf; +use std::io::{BufWriter, Write}; +use std::path::Path; use std::str::FromStr; use std::sync::LazyLock; -use crate::format_code; use crate::input::InputType; use crate::intrinsic::Intrinsic; use crate::typekinds::BaseType; @@ -38,15 +37,11 @@ const LEN_U64: usize = VL_MAX_BYTES / core::mem::size_of::(); pub fn generate_load_store_tests( load_intrinsics: Vec, store_intrinsics: Vec, - out_path: Option<&PathBuf>, + out_path: &Path, ) -> Result<(), String> { - let output = match out_path { - Some(out) => { - Box::new(File::create(out).map_err(|e| format!("couldn't create tests file: {e}"))?) - as Box - } - None => Box::new(std::io::stdout()) as Box, - }; + let mut output = BufWriter::new( + File::create(out_path).map_err(|e| format!("couldn't create tests file: {e}"))?, + ); let mut used_stores = vec![false; store_intrinsics.len()]; let tests: Vec<_> = load_intrinsics .iter() @@ -89,10 +84,9 @@ pub fn generate_load_store_tests( .map_err(|e| format!("Manual tests are invalid: {e}"))?, _ => quote!(), }; - format_code( + write!( output, - format!( - "// This code is automatically generated. DO NOT MODIFY. + "// This code is automatically generated. DO NOT MODIFY. // // Instead, modify `crates/stdarch-gen-arm/spec/sve` and run the following command to re-generate // this file: @@ -101,8 +95,7 @@ pub fn generate_load_store_tests( // cargo run --bin=stdarch-gen-arm -- crates/stdarch-gen-arm/spec // ``` {}", - quote! { #preamble #(#tests)* #manual_tests } - ), + quote! { #preamble #(#tests)* #manual_tests } ) .map_err(|e| format!("couldn't write tests: {e}")) } diff --git a/library/stdarch/crates/stdarch-gen-arm/src/main.rs b/library/stdarch/crates/stdarch-gen-arm/src/main.rs index 276cc5ba41b01..db1cfb2bb92b2 100644 --- a/library/stdarch/crates/stdarch-gen-arm/src/main.rs +++ b/library/stdarch/crates/stdarch-gen-arm/src/main.rs @@ -14,19 +14,51 @@ mod typekinds; mod wildcards; mod wildstring; +use clap::Parser; use intrinsic::Test; use itertools::Itertools; use quote::quote; use std::fs::File; use std::io::Write; use std::path::{Path, PathBuf}; -use std::process::{Command, Stdio}; -use stdarch_gen_common::{Mode, run_generator}; +use stdarch_gen_common::{GeneratorCtx, Mode, run_generator}; use walkdir::WalkDir; +#[derive(clap::Parser)] +struct Args { + /// Directory with spec files: //.spec.yml + input_dir: PathBuf, + /// Output directory to generate the files into, such as crates/core_arch/src + output_dir: Option, + /// Generation mode. + #[arg(long, env = "STDARCH_GEN_MODE")] + mode: Option, + /// Path to a rustfmt binary that will be used to reformat the generated code. + /// If unset, it will just use "rustfmt" from the environment. + #[arg(long)] + rustfmt_path: Option, +} + fn main() -> Result<(), String> { - let (in_path, out_base) = parse_args(); - let mode = Mode::from_env(); + let args = Args::parse(); + + let in_path = args.input_dir; + let out_base = args.output_dir.unwrap_or_else(|| { + std::env::current_exe() + .ok() + .map(|mut f| { + f.pop(); + f.push("../../crates/core_arch/src/"); + f + }) + .filter(|f| f.exists()) + .expect("could not locate crates/core_arch/src; pass OUTPUT_DIR command-line argument explicitly") + }); + assert!(in_path.exists()); + assert!(out_base.exists()); + + let mode = args.mode.unwrap_or_default(); + let ctx = GeneratorCtx::new(args.rustfmt_path); for filepath in WalkDir::new(&in_path) .into_iter() @@ -42,7 +74,7 @@ fn main() -> Result<(), String> { .expect("generated output path must have a parent directory") .to_path_buf(); - run_generator(&committed, mode, |scratch: &Path| { + run_generator(&ctx, &committed, mode, |scratch: &Path| { generate_spec(&filepath, scratch) }) .map_err(|e| e.to_string())?; @@ -92,7 +124,7 @@ fn generate_spec(filepath: &Path, out_dir: &Path) -> Result<(), String> { .expect("load/store test path must have a file name") .to_owned(); let tests_path = out_dir.join(tests_name); - load_store_tests::generate_load_store_tests(loads, stores, Some(&tests_path))?; + load_store_tests::generate_load_store_tests(loads, stores, &tests_path)?; } let generated = input::GeneratorInput { @@ -105,44 +137,6 @@ fn generate_spec(filepath: &Path, out_dir: &Path) -> Result<(), String> { .map_err(|e| format!("could not generate output file: {e}")) } -fn parse_args() -> (PathBuf, PathBuf) { - let mut args_it = std::env::args().skip(1); - assert!( - 1 <= args_it.len() && args_it.len() <= 2, - "Usage: cargo run -p stdarch-gen-arm -- INPUT_DIR [OUTPUT_DIR]\n\ - where:\n\ - - INPUT_DIR contains a tree like: INPUT_DIR//.spec.yml\n\ - - OUTPUT_DIR is a directory like: crates/core_arch/src/" - ); - - let in_path = Path::new(args_it.next().unwrap().as_str()).to_path_buf(); - assert!( - in_path.exists() && in_path.is_dir(), - "invalid path {in_path:#?} given" - ); - - let out_base = if let Some(dir) = args_it.next() { - let out_path = Path::new(dir.as_str()).to_path_buf(); - assert!( - out_path.exists() && out_path.is_dir(), - "invalid path {out_path:#?} given" - ); - out_path - } else { - std::env::current_exe() - .ok() - .map(|mut f| { - f.pop(); - f.push("../../crates/core_arch/src/"); - f - }) - .filter(|f| f.exists()) - .expect("could not locate crates/core_arch/src; pass OUTPUT_DIR command-line argument explicitly") - }; - - (in_path, out_base) -} - fn generate_file( generated_input: input::GeneratorInput, mut out: Box, @@ -171,25 +165,10 @@ use super::*;{uses_neon} }, )?; let intrinsics = generated_input.intrinsics; - format_code(out, quote! { #(#intrinsics)* })?; + write!(out, "{}", quote! { #(#intrinsics)* })?; Ok(()) } -pub fn format_code( - mut output: impl std::io::Write, - input: impl std::fmt::Display, -) -> std::io::Result<()> { - let proc = Command::new("rustfmt") - // Ensure that we generate the same file contents on both Linux and Windows. - .arg("--config") - .arg("newline_style=Unix") - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .spawn()?; - write!(proc.stdin.as_ref().unwrap(), "{input}")?; - output.write_all(proc.wait_with_output()?.stdout.as_slice()) -} - /// Derive an output file path from an input file path and an output directory. /// /// `in_filepath` is expected to have a structure like: diff --git a/library/stdarch/crates/stdarch-gen-common/Cargo.toml b/library/stdarch/crates/stdarch-gen-common/Cargo.toml index 691be14971a0d..fda09cb4c5994 100644 --- a/library/stdarch/crates/stdarch-gen-common/Cargo.toml +++ b/library/stdarch/crates/stdarch-gen-common/Cargo.toml @@ -4,4 +4,5 @@ version = "0.1.0" edition = "2024" [dependencies] -tempfile = "3" \ No newline at end of file +tempfile = "3" +similar = "3" diff --git a/library/stdarch/crates/stdarch-gen-common/src/lib.rs b/library/stdarch/crates/stdarch-gen-common/src/lib.rs index 13d788594ca55..5dbfc76f27dfe 100644 --- a/library/stdarch/crates/stdarch-gen-common/src/lib.rs +++ b/library/stdarch/crates/stdarch-gen-common/src/lib.rs @@ -1,11 +1,14 @@ //! Shared check/bless harness for stdarch generators. +use similar::TextDiff; use std::error::Error as StdError; use std::fmt; use std::fs; use std::io; use std::io::Read; use std::path::{Path, PathBuf}; +use std::process::{Command, Stdio}; +use std::str::FromStr; /// First-line marker identifying an auto-generated file. Generators emit this /// as the first line of every file they produce; the harness uses it to @@ -13,7 +16,7 @@ use std::path::{Path, PathBuf}; pub const GENERATED_MARKER: &str = "// This code is automatically generated. DO NOT MODIFY."; /// Controls what `run_generator` does with the generator's output. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] pub enum Mode { /// Verify that the `committed` matches the generator's output for owned files. /// @@ -26,23 +29,20 @@ pub enum Mode { /// into `committed`. If the generator no longer produces an owned file, the /// committed copy is deleted. Files in `committed` that are not owned /// are left untouched. + #[default] Bless, } -impl Mode { - /// Read the mode from the `STDARCH_GEN_MODE` environment variable. - /// - /// Recognized values: - /// - `"check"` → [`Mode::Check`] - /// - `"bless"` → [`Mode::Bless`] - /// - unset → [`Mode::Bless`] - /// - any other value → panic - pub fn from_env() -> Self { - match std::env::var("STDARCH_GEN_MODE").as_deref() { - Ok("check") => Mode::Check, - Ok("bless") => Mode::Bless, - Ok(other) => panic!("unknown STDARCH_GEN_MODE value: {other:?}"), - Err(_) => Mode::Bless, +impl FromStr for Mode { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + match s { + "check" => Ok(Mode::Check), + "bless" => Ok(Mode::Bless), + other => Err(format!( + "unknown stdarch generation mode: {other:?}. Possible values are `check` or `bless`." + )), } } } @@ -102,6 +102,18 @@ impl From for Error { pub type Result = std::result::Result; +pub struct GeneratorCtx { + rustfmt_path: PathBuf, +} + +impl GeneratorCtx { + pub fn new(rustfmt_path: Option) -> Self { + Self { + rustfmt_path: rustfmt_path.unwrap_or_else(|| PathBuf::from("rustfmt")), + } + } +} + /// Run a generator under the chosen `mode`, reconciling its output with `committed`. /// /// Arguments: @@ -120,7 +132,12 @@ pub type Result = std::result::Result; /// - [`Mode::Bless`]: runs the generator into a temp dir and copies owned /// files into `committed`, or removes `committed`'s copy if the generator no /// longer produces them. -pub fn run_generator(committed: &Path, mode: Mode, generate: F) -> Result<()> +pub fn run_generator( + ctx: &GeneratorCtx, + committed: &Path, + mode: Mode, + generate: F, +) -> Result<()> where F: FnOnce(&Path) -> std::result::Result<(), E>, E: Into>, @@ -131,6 +148,14 @@ where let owned = discover_owned(committed)?; let produced = discover_all(scratch.path())?; + // Format all generated Rust files + for file in &produced { + let fullpath = scratch.path().join(file); + if fullpath.extension().and_then(|s| s.to_str()) == Some("rs") { + reformat_file(ctx, &fullpath)?; + } + } + let mut names: Vec<&String> = owned.iter().chain(produced.iter()).collect(); names.sort(); names.dedup(); @@ -144,6 +169,34 @@ where Ok(()) } +fn reformat_file(ctx: &GeneratorCtx, path: &Path) -> std::io::Result<()> { + let file = std::fs::File::open(path)?; + let proc = Command::new(&ctx.rustfmt_path) + // Ensure that rustfmt config files in other directories won't interfere with the formatting + // This is important for usage within the rust-lang/rust repository + .arg("--config-path") + .arg( + Path::new(env!("CARGO_MANIFEST_DIR")) + .parent() + .unwrap() + .parent() + .unwrap() + .join("rustfmt.toml"), + ) + .stdin(Stdio::from(file)) + .stdout(Stdio::piped()) + .spawn()?; + + let output = proc.wait_with_output()?; + if !output.status.success() { + panic!( + "Running {:?} on {path:?} failed with exit code {:?}", + ctx.rustfmt_path, output.status + ); + } + std::fs::write(path, output.stdout) +} + /// Returns the names of files in `dir` whose first line begins with /// [`GENERATED_MARKER`]. Files without the marker are skipped. fn discover_owned(dir: &Path) -> Result> { @@ -199,7 +252,23 @@ fn compare(generated_dir: &Path, committed_dir: &Path, filename: &str) -> Result }), (false, false) => Ok(()), (true, true) => { - if fs::read(&gen_path)? != fs::read(&comm_path)? { + let generated = fs::read(&gen_path)?; + let committed = fs::read(&comm_path)?; + if generated != committed { + if let (Ok(committed), Ok(generated)) = + (str::from_utf8(&committed), str::from_utf8(&generated)) + { + eprintln!( + "{}", + TextDiff::from_lines(committed, generated) + .unified_diff() + .context_radius(3) + .header( + &format!("committed/{filename}"), + &format!("generated/{filename}"), + ) + ); + } Err(Error::Mismatch { path: rel_path, kind: MismatchKind::ContentsDiffer, @@ -249,7 +318,10 @@ mod tests { let tmp = tempfile::tempdir().unwrap(); let committed = tmp.path().join("c"); write_marker(&committed.join("a.txt"), b"hi"); + + let ctx = GeneratorCtx::new(None); let e = run_generator( + &ctx, &committed, Mode::Check, |out| -> std::result::Result<(), io::Error> { @@ -272,7 +344,10 @@ mod tests { let tmp = tempfile::tempdir().unwrap(); let committed = tmp.path().join("c"); write_marker(&committed.join("a.txt"), b"hi"); + + let ctx = GeneratorCtx::new(None); let e = run_generator( + &ctx, &committed, Mode::Check, |_| -> std::result::Result<(), io::Error> { Ok(()) }, @@ -292,7 +367,10 @@ mod tests { let tmp = tempfile::tempdir().unwrap(); let committed = tmp.path().join("c"); fs::create_dir_all(&committed).unwrap(); + + let ctx = GeneratorCtx::new(None); let e = run_generator( + &ctx, &committed, Mode::Check, |out| -> std::result::Result<(), io::Error> { @@ -316,7 +394,10 @@ mod tests { let committed = tmp.path().join("c"); write_marker(&committed.join("keep.txt"), b""); write_marker(&committed.join("stale.txt"), b""); + + let ctx = GeneratorCtx::new(None); run_generator( + &ctx, &committed, Mode::Bless, |out| -> std::result::Result<(), io::Error> { @@ -336,7 +417,9 @@ mod tests { fs::create_dir_all(&committed).unwrap(); fs::write(committed.join("mod.rs"), b"hand-written").unwrap(); fs::write(committed.join("old.txt"), b"old").unwrap(); + let ctx = GeneratorCtx::new(None); run_generator( + &ctx, &committed, Mode::Bless, |out| -> std::result::Result<(), io::Error> { @@ -349,4 +432,32 @@ mod tests { assert_eq!(fs::read(committed.join("old.txt")).unwrap(), b"old"); assert!(committed.join("new.txt").exists()); } + + #[test] + fn generation_reformats_files() { + let tmp = tempfile::tempdir().unwrap(); + let committed = tmp.path().join("c"); + let file = committed.join("a.rs"); + write_marker(&file, b"foo"); + + let ctx = GeneratorCtx::new(None); + run_generator( + &ctx, + &committed, + Mode::Bless, + |out| -> std::result::Result<(), io::Error> { + write_marker(&out.join("a.rs"), b"fn main() {}"); + Ok(()) + }, + ) + .unwrap(); + assert_eq!( + std::fs::read_to_string(file).unwrap(), + format!( + r#"{GENERATED_MARKER} +fn main() {{}} +"# + ) + ); + } } diff --git a/library/stdarch/crates/stdarch-gen-hexagon/Cargo.toml b/library/stdarch/crates/stdarch-gen-hexagon/Cargo.toml index c7dfce2c0fd7b..7d457868398ac 100644 --- a/library/stdarch/crates/stdarch-gen-hexagon/Cargo.toml +++ b/library/stdarch/crates/stdarch-gen-hexagon/Cargo.toml @@ -6,5 +6,6 @@ license = "MIT OR Apache-2.0" edition = "2021" [dependencies] +clap = { version = "4", features = ["derive", "env"] } regex = "1.10" stdarch-gen-common = { path = "../stdarch-gen-common" } diff --git a/library/stdarch/crates/stdarch-gen-hexagon/src/hvx.rs b/library/stdarch/crates/stdarch-gen-hexagon/src/hvx.rs index 537c59bbce861..95c77ae4700a4 100644 --- a/library/stdarch/crates/stdarch-gen-hexagon/src/hvx.rs +++ b/library/stdarch/crates/stdarch-gen-hexagon/src/hvx.rs @@ -19,7 +19,7 @@ use regex::Regex; use std::collections::{HashMap, HashSet}; use std::fs::File; -use std::io::Write; +use std::io::{BufWriter, Write}; use std::path::Path; use stdarch_gen_common::GENERATED_MARKER; @@ -1606,30 +1606,16 @@ fn generate_module_file( intrinsics: &[IntrinsicInfo], output_path: &Path, mode: VectorMode, -) -> Result<(), String> { - let mut output = - File::create(output_path).map_err(|e| format!("Failed to create output: {}", e))?; - - writeln!(output, "{}", GENERATED_MARKER).map_err(|e| e.to_string())?; - writeln!(output, "{}", generate_module_doc(mode)).map_err(|e| e.to_string())?; - writeln!(output, "{}", generate_types(mode)).map_err(|e| e.to_string())?; - writeln!(output, "{}", generate_extern_block(intrinsics, mode)).map_err(|e| e.to_string())?; - writeln!(output, "{}", generate_functions(intrinsics)).map_err(|e| e.to_string())?; - - // Ensure file is flushed before running rustfmt - drop(output); - - // Run rustfmt on the generated file - let status = std::process::Command::new("rustfmt") - .arg(output_path) - .status() - .map_err(|e| format!("Failed to run rustfmt: {}", e))?; - - if !status.success() { - return Err("rustfmt failed".to_string()); - } +) -> std::io::Result<()> { + let mut output = BufWriter::new(File::create(output_path)?); - Ok(()) + writeln!(output, "{}", GENERATED_MARKER)?; + writeln!(output, "{}", generate_module_doc(mode))?; + writeln!(output, "{}", generate_types(mode))?; + writeln!(output, "{}", generate_extern_block(intrinsics, mode))?; + writeln!(output, "{}", generate_functions(intrinsics))?; + + output.flush() } /// Parse the HVX header in `crate_dir` and write `v64.rs` and `v128.rs` into `out_dir`. @@ -1642,7 +1628,8 @@ pub fn generate(crate_dir: &std::path::Path, out_dir: &std::path::Path) -> Resul .collect(); for (filename, vmode) in [("v64.rs", VectorMode::V64), ("v128.rs", VectorMode::V128)] { let path = out_dir.join(filename); - generate_module_file(&intrinsics, &path, vmode)?; + generate_module_file(&intrinsics, &path, vmode) + .map_err(|e| format!("Cannot generate {path:?}: {e:?}"))?; } Ok(()) } diff --git a/library/stdarch/crates/stdarch-gen-hexagon/src/main.rs b/library/stdarch/crates/stdarch-gen-hexagon/src/main.rs index 88ef4fe11f332..1e23e993ef918 100644 --- a/library/stdarch/crates/stdarch-gen-hexagon/src/main.rs +++ b/library/stdarch/crates/stdarch-gen-hexagon/src/main.rs @@ -1,18 +1,31 @@ -//! Hexagon code generator. -//! -//! Single binary that produces every generated file under -//! `core_arch/src/hexagon/`: scalar.rs (scalar intrinsics) and -//! v64.rs / v128.rs (HVX intrinsics). -//! -//! Run in check or bless mode via `STDARCH_GEN_MODE`. - mod hvx; mod scalar; +use clap::Parser; use std::path::PathBuf; -use stdarch_gen_common::{run_generator, Mode}; +use stdarch_gen_common::{run_generator, GeneratorCtx, Mode}; + +/// Hexagon code generator. +/// +/// Produces every generated file under +/// `core_arch/src/hexagon/`: scalar.rs (scalar intrinsics) and +/// v64.rs / v128.rs (HVX intrinsics). +/// +/// Run in check or bless mode via `STDARCH_GEN_MODE`. +#[derive(clap::Parser)] +struct Args { + /// Generation mode. + #[arg(long, env = "STDARCH_GEN_MODE")] + mode: Option, + /// Path to a rustfmt binary that will be used to reformat the generated code. + /// If unset, it will just use "rustfmt" from the environment. + #[arg(long)] + rustfmt_path: Option, +} fn main() -> Result<(), String> { + let args = Args::parse(); + let crate_dir = std::env::var("CARGO_MANIFEST_DIR") .map(PathBuf::from) .unwrap_or_else(|_| std::env::current_dir().unwrap()); @@ -20,9 +33,10 @@ fn main() -> Result<(), String> { let hexagon_dir = crate_dir.join("../core_arch/src/hexagon"); // Either "check" to check the output versus the committed output, or "bless" // to update the output. - let mode = Mode::from_env(); + let mode = args.mode.unwrap_or_default(); + let ctx = GeneratorCtx::new(args.rustfmt_path); - run_generator(&hexagon_dir, mode, |out_dir| -> Result<(), String> { + run_generator(&ctx, &hexagon_dir, mode, |out_dir| -> Result<(), String> { // Here scalar::generate writes scalar.rs . scalar::generate(&crate_dir, out_dir)?; // Here hvx::generate writes v64.rs and v128.rs . diff --git a/library/stdarch/crates/stdarch-gen-hexagon/src/scalar.rs b/library/stdarch/crates/stdarch-gen-hexagon/src/scalar.rs index 2a64798d097d1..8c7880b58a023 100644 --- a/library/stdarch/crates/stdarch-gen-hexagon/src/scalar.rs +++ b/library/stdarch/crates/stdarch-gen-hexagon/src/scalar.rs @@ -19,7 +19,7 @@ use regex::Regex; use std::collections::HashMap; use std::fs::File; -use std::io::Write; +use std::io::{BufWriter, Write}; use std::path::Path; use stdarch_gen_common::GENERATED_MARKER; @@ -627,30 +627,16 @@ fn generate_functions(intrinsics: &[ScalarIntrinsic]) -> String { } /// Generate the complete scalar.rs file -fn generate_scalar_file(intrinsics: &[ScalarIntrinsic], output_path: &Path) -> Result<(), String> { - let mut output = - File::create(output_path).map_err(|e| format!("Failed to create output: {}", e))?; - - writeln!(output, "{}", GENERATED_MARKER).map_err(|e| e.to_string())?; - writeln!(output, "{}", generate_module_doc()).map_err(|e| e.to_string())?; - writeln!(output, "").map_err(|e| e.to_string())?; - writeln!(output, "{}", generate_extern_block(intrinsics)).map_err(|e| e.to_string())?; - writeln!(output, "{}", generate_functions(intrinsics)).map_err(|e| e.to_string())?; - - // Flush before running rustfmt - drop(output); - - // Run rustfmt on the generated file - let status = std::process::Command::new("rustfmt") - .arg(output_path) - .status() - .map_err(|e| format!("Failed to run rustfmt: {}", e))?; - - if !status.success() { - return Err("rustfmt failed".to_string()); - } +fn generate_scalar_file(intrinsics: &[ScalarIntrinsic], output_path: &Path) -> std::io::Result<()> { + let mut output = BufWriter::new(File::create(output_path)?); - Ok(()) + writeln!(output, "{}", GENERATED_MARKER)?; + writeln!(output, "{}", generate_module_doc())?; + writeln!(output, "")?; + writeln!(output, "{}", generate_extern_block(intrinsics))?; + writeln!(output, "{}", generate_functions(intrinsics))?; + + output.flush() } /// Parse the scalar header in `crate_dir` and write `scalar.rs` into `out_dir`. @@ -659,6 +645,6 @@ pub fn generate(crate_dir: &std::path::Path, out_dir: &std::path::Path) -> Resul let intrinsics = parse_header(&header_content); std::fs::create_dir_all(out_dir).map_err(|e| e.to_string())?; let scalar_path = out_dir.join("scalar.rs"); - generate_scalar_file(&intrinsics, &scalar_path)?; + generate_scalar_file(&intrinsics, &scalar_path).map_err(|e| e.to_string())?; Ok(()) } diff --git a/library/stdarch/crates/stdarch-gen-loongarch/Cargo.toml b/library/stdarch/crates/stdarch-gen-loongarch/Cargo.toml index a9ddc123b3688..d174b844b430a 100644 --- a/library/stdarch/crates/stdarch-gen-loongarch/Cargo.toml +++ b/library/stdarch/crates/stdarch-gen-loongarch/Cargo.toml @@ -7,5 +7,6 @@ edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +clap = { version = "4", features = ["derive", "env"] } rand = "0.9.3" stdarch-gen-common = { path = "../stdarch-gen-common" } diff --git a/library/stdarch/crates/stdarch-gen-loongarch/src/main.rs b/library/stdarch/crates/stdarch-gen-loongarch/src/main.rs index 86fa8956e6ed1..1fed0b96f9426 100644 --- a/library/stdarch/crates/stdarch-gen-loongarch/src/main.rs +++ b/library/stdarch/crates/stdarch-gen-loongarch/src/main.rs @@ -1,3 +1,4 @@ +use clap::Parser; use std::collections::HashSet; use std::env; use std::fmt; @@ -6,7 +7,7 @@ use std::io::prelude::*; use std::io::{self, BufReader}; use std::path::Path; use std::path::PathBuf; -use stdarch_gen_common::{Mode, run_generator}; +use stdarch_gen_common::{GeneratorCtx, Mode, run_generator}; /// Complete lines of generated source. /// @@ -1602,51 +1603,64 @@ static void {current_name}(void) /// Runs the check/bless harness for `lsx`/`lasx` when invoked with /// no args or a bare ext name. +#[derive(clap::Parser, Debug)] +struct Args { + /// Either: + /// - The extension (lsx/lasx) to generate, or: + /// - A path to a intrin.h file to generate the spec file from. Optionally followed + /// by "test". + arguments: Vec, + /// Generation mode. + #[arg(long, env = "STDARCH_GEN_MODE")] + mode: Option, + /// Path to a rustfmt binary that will be used to reformat the generated code. + /// If unset, it will just use "rustfmt" from the environment. + #[arg(long)] + rustfmt_path: Option, +} + pub fn main() -> Result<(), String> { - let args: Vec = env::args().collect(); - let arg_strs: Vec<&str> = args.iter().map(String::as_str).collect(); - let harness_exts: Option<&[&str]> = match arg_strs.as_slice() { - [_] => Some(&["lsx", "lasx"]), - [_, "lsx"] => Some(&["lsx"]), - [_, "lasx"] => Some(&["lasx"]), - _ => None, - }; - if let Some(exts) = harness_exts { - let crate_dir = - PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set")); - let core_arch_src = crate_dir.join("../core_arch/src"); - let mode = Mode::from_env(); - for ext in exts { - let spec_rel = format!("crates/stdarch-gen-loongarch/{ext}.spec"); - let committed = core_arch_src.join("loongarch64").join(ext); - run_generator(&committed, mode, |out_dir| { - gen_bind(&spec_rel, ext, out_dir) - }) - .map_err(|e| e.to_string())?; - } - return Ok(()); - } + let args = Args::parse(); + + let crate_dir = + PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set")); + let core_arch_src = crate_dir.join("../core_arch/src"); + let mode = args.mode.unwrap_or_default(); + let ctx = GeneratorCtx::new(args.rustfmt_path); - let in_file = args[1].clone(); - let in_file_name = PathBuf::from(&in_file) - .file_name() - .unwrap() - .to_string_lossy() - .into_owned(); - let ext_name = if in_file_name.starts_with("lasx") { - "lasx" + let arguments = &args.arguments; + if arguments.len() == 1 && (arguments[0] == "lsx" || arguments[0] == "lasx") { + let extension = arguments[0].as_str(); + let spec_rel = format!("crates/stdarch-gen-loongarch/{extension}.spec"); + let committed = core_arch_src.join("loongarch64").join(extension); + run_generator(&ctx, &committed, mode, |out_dir| { + gen_bind(&spec_rel, extension, out_dir) + }) + .map_err(|e| e.to_string())?; } else { - "lsx" - }; - if in_file_name.ends_with(".h") { - return gen_spec(in_file, ext_name).map_err(|e| e.to_string()); - } - if let [_, _lsx_or_lasx, "test"] = arg_strs.as_slice() { - return gen_test(in_file, ext_name).map_err(|e| e.to_string()); + let in_file = arguments[0].clone(); + let in_file_name = PathBuf::from(&in_file) + .file_name() + .unwrap() + .to_string_lossy() + .into_owned(); + let ext_name = if in_file_name.starts_with("lasx") { + "lasx" + } else { + "lsx" + }; + if in_file_name.ends_with(".h") { + return gen_spec(in_file, ext_name).map_err(|e| e.to_string()); + } + if arguments.last().map(|s| s.as_str()) == Some("test") { + return gen_test(in_file, ext_name).map_err(|e| e.to_string()); + } + // Note: this does not apply rustfmt formatting + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap_or("crates/core_arch".to_string())) + .join("src") + .join("loongarch64") + .join(ext_name); + gen_bind(&in_file, ext_name, &out_path).map_err(|e| e.to_string())?; } - let out_path = PathBuf::from(env::var("OUT_DIR").unwrap_or("crates/core_arch".to_string())) - .join("src") - .join("loongarch64") - .join(ext_name); - gen_bind(&in_file, ext_name, &out_path).map_err(|e| e.to_string()) + Ok(()) } diff --git a/library/stdarch/rust-version b/library/stdarch/rust-version index 18fea436747c7..368cecc0b82f9 100644 --- a/library/stdarch/rust-version +++ b/library/stdarch/rust-version @@ -1 +1 @@ -32d94cc9be3f6e6c3fa1deaea9e0ab93c4980dba +220b36c420c49c59923f54cd4a76634fac98a067 diff --git a/library/stdarch/rustfmt.toml b/library/stdarch/rustfmt.toml index e69de29bb2d1d..3dd85bb2defbf 100644 --- a/library/stdarch/rustfmt.toml +++ b/library/stdarch/rustfmt.toml @@ -0,0 +1,2 @@ +# Ensure that we generate the same file contents on both Linux and Windows. +newline_style = "Unix" diff --git a/library/stdarch/triagebot.toml b/library/stdarch/triagebot.toml index 5b178f0cdf456..149646aa65f01 100644 --- a/library/stdarch/triagebot.toml +++ b/library/stdarch/triagebot.toml @@ -1,3 +1,35 @@ +[relabel] +allow-unauthenticated = [ + "A-*", + "B-*", + "C-*", + "D-*", + "E-*", + "F-*", + "I-*", + "L-*", + "NLL-*", + "O-*", + "PG-*", + "S-*", + "T-*", + "WG-*", + "-Z*", + "beta-nominated", + "CI-spurious-*", + "const-hack", + "llvm-*", + "needs-fcp", + "relnotes", + "release-blog-post", + "requires-*", + "regression-*", + "rla-*", + "perf-*", + "AsyncAwait-OnDeck", + "needs-triage", + "has-merge-commits",] + [assign] [assign.owners] diff --git a/library/sysroot/Cargo.toml b/library/sysroot/Cargo.toml index 10562389d62d3..cef5532857b5e 100644 --- a/library/sysroot/Cargo.toml +++ b/library/sysroot/Cargo.toml @@ -32,3 +32,8 @@ optimize_for_size = ["std/optimize_for_size"] panic-unwind = ["std/panic-unwind"] profiler = ["dep:profiler_builtins"] windows_raw_dylib = ["std/windows_raw_dylib"] + +[lints.cargo] +# We expect some of these dependencies to not actually be used since this is +# just an aggregator crate. +unused_dependencies = "allow" diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index 043f854ea79fd..fb0acdab835a9 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -204,11 +204,7 @@ unsafe extern "C" {} #[link(name = "gcc_s", cfg(not(target_feature = "crt-static")))] unsafe extern "C" {} -#[cfg(all(target_os = "openbsd", target_arch = "sparc64"))] -#[link(name = "gcc")] -unsafe extern "C" {} - -#[cfg(all(target_os = "openbsd", not(target_arch = "sparc64")))] +#[cfg(target_os = "openbsd")] #[link(name = "c++abi")] unsafe extern "C" {} diff --git a/package.json b/package.json index d9a3ced805ba5..876d81c1d60ef 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "dependencies": { - "browser-ui-test": "^0.25.0", + "browser-ui-test": "^0.25.2", "es-check": "^9.4.4", "eslint": "^8.57.1", "typescript": "^5.8.3" diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index b1157ef381013..3aded58123525 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1251,31 +1251,7 @@ pub fn rustc_cargo( cargo.rustflag("-Zdefault-visibility=protected"); } - if is_lto_stage(build_compiler) { - match builder.config.rust_lto { - RustcLto::Thin | RustcLto::Fat => { - // Since using LTO for optimizing dylibs is currently experimental, - // we need to pass -Zdylib-lto. - cargo.rustflag("-Zdylib-lto"); - // Cargo by default passes `-Cembed-bitcode=no` and doesn't pass `-Clto` when - // compiling dylibs (and their dependencies), even when LTO is enabled for the - // crate. Therefore, we need to override `-Clto` and `-Cembed-bitcode` here. - let lto_type = match builder.config.rust_lto { - RustcLto::Thin => "thin", - RustcLto::Fat => "fat", - _ => unreachable!(), - }; - cargo.rustflag(&format!("-Clto={lto_type}")); - cargo.rustflag("-Cembed-bitcode=yes"); - } - RustcLto::ThinLocal => { /* Do nothing, this is the default */ } - RustcLto::Off => { - cargo.rustflag("-Clto=off"); - } - } - } else if builder.config.rust_lto == RustcLto::Off { - cargo.rustflag("-Clto=off"); - } + apply_dylib_lto(builder, build_compiler, cargo); // With LLD, we can use ICF (identical code folding) to reduce the executable size // of librustc_driver/rustc and to improve i-cache utilization. @@ -1308,6 +1284,34 @@ pub fn rustc_cargo( rustc_cargo_env(builder, cargo, target); } +fn apply_dylib_lto(builder: &Builder<'_>, build_compiler: &Compiler, cargo: &mut Cargo) { + if is_lto_stage(build_compiler) { + match builder.config.rust_lto { + RustcLto::Thin | RustcLto::Fat => { + // Since using LTO for optimizing dylibs is currently experimental, + // we need to pass -Zdylib-lto. + cargo.rustflag("-Zdylib-lto"); + // Cargo by default passes `-Cembed-bitcode=no` and doesn't pass `-Clto` when + // compiling dylibs (and their dependencies), even when LTO is enabled for the + // crate. Therefore, we need to override `-Clto` and `-Cembed-bitcode` here. + let lto_type = match builder.config.rust_lto { + RustcLto::Thin => "thin", + RustcLto::Fat => "fat", + _ => unreachable!(), + }; + cargo.rustflag(&format!("-Clto={lto_type}")); + cargo.rustflag("-Cembed-bitcode=yes"); + } + RustcLto::ThinLocal => { /* Do nothing, this is the default */ } + RustcLto::Off => { + cargo.rustflag("-Clto=off"); + } + } + } else if builder.config.rust_lto == RustcLto::Off { + cargo.rustflag("-Clto=off"); + } +} + fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelection) { // Set some configuration variables picked up by build scripts and // the compiler alike @@ -1720,6 +1724,7 @@ impl CommandLineStep for GccCodegenBackend { Kind::Build, ); cargo.arg("--manifest-path").arg(builder.src.join("compiler/rustc_codegen_gcc/Cargo.toml")); + apply_dylib_lto(builder, &build_compiler, &mut cargo); let _guard = builder.msg(Kind::Build, "codegen backend gcc", Mode::Codegen, build_compiler, host); @@ -1790,6 +1795,8 @@ impl CommandLineStep for CraneliftCodegenBackend { cargo .arg("--manifest-path") .arg(builder.src.join("compiler/rustc_codegen_cranelift/Cargo.toml")); + apply_dylib_lto(builder, &build_compiler, &mut cargo); + apply_pgo(builder, &mut cargo, build_compiler, &builder.config.cranelift_pgo); let _guard = builder.msg( Kind::Build, @@ -2669,7 +2676,7 @@ pub fn run_cargo( let (filenames_vec, crate_types) = match msg { CargoMessage::CompilerArtifact { filenames, - target: CargoTarget { crate_types }, + target: CargoTarget { crate_types, .. }, .. } => { let mut f: Vec = filenames.into_iter().map(|s| s.into_owned()).collect(); @@ -2876,12 +2883,14 @@ pub fn stream_cargo( status.success() } -#[derive(Deserialize)] +#[derive(Deserialize, Debug)] pub struct CargoTarget<'a> { - crate_types: Vec>, + pub crate_types: Vec>, + #[serde(default)] + pub doc: bool, } -#[derive(Deserialize)] +#[derive(Deserialize, Debug)] #[serde(tag = "reason", rename_all = "kebab-case")] pub enum CargoMessage<'a> { CompilerArtifact { filenames: Vec>, target: CargoTarget<'a> }, diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 7073b334c414d..4f42a46e56a25 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -23,7 +23,7 @@ use crate::core::backend::CodegenBackendKind; use crate::core::build_steps::compile::{ get_codegen_backend_file, libgccjit_path_relative_to_cg_dir, normalize_codegen_backend_name, }; -use crate::core::build_steps::doc::DocumentationFormat; +use crate::core::build_steps::doc::{CompilerWithTools, DocumentationFormat}; use crate::core::build_steps::gcc::GccTargetPair; use crate::core::build_steps::llvm::{ LLVM_CI_LINK_TYPE_PATH, LlvmBuildStatus, LlvmKind, get_llvm_build_status, @@ -185,7 +185,7 @@ impl CommandLineStep for JsonDocs { } } -/// Builds the `rustc-docs` installer component. +/// Builds the `rustc-docs` component. /// Apart from the documentation of the `rustc_*` crates, it also includes the documentation of /// various in-tree helper tools (bootstrap, build_helper, tidy), /// and also rustc_private tools like rustdoc, clippy, miri or rustfmt. @@ -214,11 +214,12 @@ impl CommandLineStep for RustcDocs { fn run(self, builder: &Builder<'_>) -> Self::Output { let target = self.target; - builder.run_default_doc_steps(); + let combined_docs = + builder.ensure(CompilerWithTools::for_stage(builder, builder.top_stage, self.target)); let mut tarball = Tarball::new(builder, "rustc-docs", &target.triple); tarball.set_product_name("Rustc Documentation"); - tarball.add_bulk_dir(builder.compiler_doc_out(target), "share/doc/rust/html/rustc-docs"); + tarball.add_bulk_dir(combined_docs, "share/doc/rust/html/rustc-docs"); tarball.generate() } } @@ -1400,8 +1401,10 @@ fn prepare_source_tarball<'a>( builder.require_and_update_all_submodules(); // Vendor packages that are required by opt-dist to collect PGO profiles. - let pkgs_for_pgo_training = - build_helper::LLVM_PGO_CRATES.iter().chain(build_helper::RUSTC_PGO_CRATES).map(|pkg| { + let pkgs_for_pgo_training = build_helper::BACKEND_PGO_CRATES + .iter() + .chain(build_helper::RUSTC_PGO_CRATES) + .map(|pkg| { let mut manifest_path = builder.src.join("./src/tools/rustc-perf/collector/compile-benchmarks"); manifest_path.push(pkg); @@ -1736,7 +1739,7 @@ impl CommandLineStep for GccCodegenBackend { fn is_default_step(builder: &Builder<'_>) -> bool { // We only want to build the gcc backend in `x dist` if the backend was enabled // in rust.codegen-backends. - // Sadly, we don't have access to the actual target for which we're disting clif here.. + // Sadly, we don't have access to the actual target for which we're disting gcc here.. // So we just use the host target. builder .config @@ -3141,6 +3144,7 @@ impl CommandLineStep for ReproducibleArtifacts { &builder.config.rustdoc_pgo.use_profile, &builder.config.cargo_pgo.use_profile, &builder.config.clippy_pgo.use_profile, + &builder.config.cranelift_pgo.use_profile, ]; for profile in pgo_profiles { if let Some(path) = profile.as_ref() { diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 2c64d7915c392..34e9b354bddb2 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -7,11 +7,13 @@ //! Everything here is basically just a shim around calling either `rustbook` or //! `rustdoc`. +use std::collections::HashSet; use std::io::{self, Write}; use std::path::{Path, PathBuf}; use std::{env, fs, mem}; use crate::core::build_steps::compile; +use crate::core::build_steps::compile::{CargoMessage, stream_cargo}; use crate::core::build_steps::tool::{ self, RustcPrivateCompilers, SourceType, Tool, prepare_tool_cargo, }; @@ -20,9 +22,9 @@ use crate::core::builder::{ crate_description, }; use crate::core::compiler::Compiler; -use crate::core::config::{Config, TargetSelection}; +use crate::core::config::TargetSelection; use crate::core::session::{FileType, Mode}; -use crate::utils::helpers::{submodule_path_of, symlink_dir, t, up_to_date}; +use crate::utils::helpers::{exit_process, submodule_path_of, symlink_dir, t, up_to_date}; macro_rules! book { ($($name:ident, $path:expr, $book_name:expr, $lang:expr ;)+) => { @@ -840,7 +842,6 @@ fn doc_std( .arg("--no-deps") .arg("--target-dir") .arg(&*target_dir.to_string_lossy()) - .arg("-Zskip-rustdoc-fingerprint") .arg("-Zrustdoc-map") .rustdocflag("--extern-html-root-url") .rustdocflag("std_detect=https://docs.rs/std_detect/latest/") @@ -871,7 +872,159 @@ pub fn prepare_doc_compiler( build_compiler } +/// Run rustdoc to merge cross-crate info metadata (like the search index) from individual +/// executions of rustdoc into `out_dir`. +/// The `json_files` parameter should contain paths to JSON file artifacts generated by previous +/// executions of `cargo doc`. +fn merge_rustdoc_cci( + builder: &Builder<'_>, + build_compiler: Compiler, + json_files: &[PathBuf], + out_dir: &Path, +) { + let mut cmd = builder.rustdoc_cmd(build_compiler); + + cmd.arg("--enable-index-page").arg("-Zunstable-options").arg("-o").arg(out_dir); + + if !builder.config.docs_minification { + cmd.arg("--disable-minification"); + } + + for json_file in json_files { + cmd.arg("--read-doc-meta-dir").arg(json_file.parent().unwrap()); + } + + cmd.run(builder); +} + +/// Generate the combined compiler + tools docs for a given toolchain. +/// This contains both the compiler docs, docs of rustc_private tools (miri, clippy, etc.), cargo +/// and also some bootstrap related tools (bootstrap itself, compiletest, tidy, etc.). +/// +/// It gets hosted at https://doc.rust-lang.org/nightly/nightly-rustc/index.html. +/// +/// Compiler documentation is distributed separately, so we make sure +/// we do not merge it with the other documentation from std, test and +/// proc_macros. This is largely just a wrapper around `cargo doc`. +/// +/// Returns a path to a directory with the generated documentation. +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct CompilerWithTools { + build_compiler: Compiler, + target: TargetSelection, + stage: u32, +} + +impl CompilerWithTools { + /// Document `stage` compiler for the given `target`. + pub(crate) fn for_stage(builder: &Builder<'_>, stage: u32, target: TargetSelection) -> Self { + let build_compiler = prepare_doc_compiler(builder, target, stage); + Self { build_compiler, target, stage } + } +} + +impl CommandLineStep for CompilerWithTools { + type Output = PathBuf; + const IS_HOST: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.alias("compiler-with-tools") + } + + fn is_default_step(builder: &Builder<'_>) -> bool { + builder.config.compiler_docs + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(CompilerWithTools::for_stage( + run.builder, + run.builder.top_stage, + run.target, + )); + } + + fn run(self, builder: &Builder<'_>) -> Self::Output { + let CompilerWithTools { target, build_compiler, stage } = self; + + // This is the intended out directory for combined compiler documentation. + let out = builder.compiler_doc_out(target); + let _ = fs::remove_dir_all(&out); + + let _guard = + builder.msg(Kind::Doc, "compiler-with-tools", Mode::Rustc, build_compiler, target); + + let combined_docs = vec![ + builder.ensure(Rustc::for_stage(builder, stage, target)), + builder.ensure(Rustdoc::new(builder, target)), + builder.ensure(Rustfmt::new(builder, target)), + builder.ensure(Clippy::new(builder, target)), + builder.ensure(Miri::new(builder, target)), + builder.ensure(Cargo::new(builder, target)), + builder.ensure(Tidy::new(builder, target)), + builder.ensure(Bootstrap::new(builder, target)), + builder.ensure(BuildHelper::new(builder, target)), + builder.ensure(Compiletest::new(builder, target)), + builder.ensure(RunMakeSupport::new(builder, target)), + ]; + + if !builder.config.dry_run() { + // Now copy all the individual docs into a single directory + let mut json_files = vec![]; + for docs in combined_docs { + json_files.extend(docs.artifacts.json_files); + + // Doc directories to link to the shared output directory + // We add the host doc dirs, which should already be symlinked in the target + // docs dir at this point (see `merge_host_and_target_docs`). + let dirs_to_copy: Vec<_> = docs + .artifacts + .target_dirs + .iter() + .chain(docs.artifacts.host_dirs.iter()) + .map(|d| d.file_name().unwrap().to_str().unwrap()) + .collect(); + for dir in dirs_to_copy { + // Link the docs dir + let docs_dir = docs.out_dir.join(dir); + assert!(docs_dir.exists(), "Docs directory {docs_dir:?} does not exist."); + let out_docs_dir = out.join(dir); + builder.create_dir(&out_docs_dir); + builder.cp_link_r(&docs_dir, &out_docs_dir); + + // And the src dir + let src_dir = docs.out_dir.join("src").join(dir); + assert!(src_dir.exists(), "Docs source directory {src_dir:?} does not exist."); + let out_src_dir = out.join("src").join(dir); + builder.create_dir(&out_src_dir); + builder.cp_link_r(&src_dir, &out_src_dir); + } + } + // And finally merge all the CCI metadata + merge_rustdoc_cci(builder, build_compiler, &json_files, &out); + } + + // Handle `--open`. + builder.open_in_browser(out.join("index.html")); + out + } + + fn metadata(&self) -> Option { + Some(StepMetadata::doc("CompilerWithTools", self.target).built_by(self.build_compiler)) + } +} + +/// Output of a Doc step. +#[derive(Clone)] +pub struct BuiltDocs { + /// Target doc directory with the generated documentation. + out_dir: PathBuf, + /// Doc artifacts gathered from Cargo during the doc build. + artifacts: DocArtifacts, +} + /// Document the compiler for the given `target` using rustdoc from `build_compiler`. +/// +/// Return the path to the generated rustc documentation directory. #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Rustc { build_compiler: Compiler, @@ -901,7 +1054,7 @@ impl Rustc { } impl CommandLineStep for Rustc { - type Output = (); + type Output = BuiltDocs; const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -922,13 +1075,9 @@ impl CommandLineStep for Rustc { /// Compiler documentation is distributed separately, so we make sure /// we do not merge it with the other documentation from std, test and /// proc_macros. This is largely just a wrapper around `cargo doc`. - fn run(self, builder: &Builder<'_>) { + fn run(self, builder: &Builder<'_>) -> Self::Output { let target = self.target; - // This is the intended out directory for compiler documentation. - let out = builder.compiler_doc_out(target); - t!(fs::create_dir_all(&out)); - // Build the standard library, so that proc-macros can use it. // (Normally, only the metadata would be necessary, but proc-macros are special since they run at compile-time.) let build_compiler = self.build_compiler; @@ -965,7 +1114,6 @@ impl CommandLineStep for Rustc { cargo.rustdocflag("--generate-macro-expansion"); compile::rustc_cargo(builder, &mut cargo, target, &build_compiler, &self.crates); - cargo.arg("-Zskip-rustdoc-fingerprint"); // Only include compiler crates, no dependencies of those, such as `libc`. // Do link to dependencies on `docs.rs` however using `rustdoc-map`. @@ -977,53 +1125,42 @@ impl CommandLineStep for Rustc { cargo.rustdocflag("--extern-html-root-url"); cargo.rustdocflag("ena=https://docs.rs/ena/latest/"); - let mut to_open = None; - - let out_dir = builder.stage_out(build_compiler, Mode::Rustc).join(target).join("doc"); + let cargo_target_dir = builder.stage_out(build_compiler, Mode::Rustc); + let target_doc_dir = cargo_target_dir.join(target).join("doc"); + let host_doc_dir = cargo_target_dir.join("doc"); for krate in &*self.crates { // Create all crate output directories first to make sure rustdoc uses // relative links. // FIXME: Cargo should probably do this itself. - let dir_name = krate.replace('-', "_"); - t!(fs::create_dir_all(out_dir.join(&*dir_name))); + let dir_name = normalize_doc_crate_name(krate); + t!(fs::create_dir_all(target_doc_dir.join(&*dir_name))); cargo.arg("-p").arg(krate); - if to_open.is_none() { - to_open = Some(dir_name); - } } - // This uses a shared directory so that librustdoc documentation gets - // correctly built and merged with the rustc documentation. - // - // This is needed because rustdoc is built in a different directory from - // rustc. rustdoc needs to be able to see everything, for example when - // merging the search index, or generating local (relative) links. - symlink_dir_force(&builder.config, &out, &out_dir); - // Cargo puts proc macros in `target/doc` even if you pass `--target` - // explicitly (https://github.com/rust-lang/cargo/issues/7677). - let proc_macro_out_dir = builder.stage_out(build_compiler, Mode::Rustc).join("doc"); - symlink_dir_force(&builder.config, &out, &proc_macro_out_dir); - - cargo.into_cmd().run(builder); + let artifacts = create_docs_and_gather_artifacts(builder, cargo); + artifacts.sanity_check_crates(builder, self.crates.iter()); if !builder.config.dry_run() { - // Sanity check on linked compiler crates - for krate in &*self.crates { - let dir_name = krate.replace('-', "_"); - // Making sure the directory exists and is not empty. - assert!(out.join(&*dir_name).read_dir().unwrap().next().is_some()); - } + merge_host_and_target_docs(builder, &artifacts, &host_doc_dir, &target_doc_dir); + merge_rustdoc_cci(builder, build_compiler, &artifacts.json_files, &target_doc_dir); } - if builder.paths.iter().any(|path| path.ends_with("compiler")) { - // For `x.py doc compiler --open`, open `rustc_middle` by default. - let index = out.join("rustc_middle").join("index.html"); - builder.open_in_browser(index); - } else if let Some(krate) = to_open { - // Let's open the first crate documentation page: - let index = out.join(krate).join("index.html"); + // We open rustc_middle as the default if invoked as `x.py doc --open RELEASES.md` + // with no particular explicit doc requested (e.g. library/core). + if builder.was_invoked_explicitly::(Kind::Doc) { + let index = if builder.paths.iter().any(|path| path.ends_with("compiler")) { + // For `x.py doc compiler --open`, open `rustc_middle` by default. + target_doc_dir.join("rustc_middle").join("index.html") + } else if let Some(krate) = self.crates.first() { + // Let's open the first crate documentation page: + target_doc_dir.join(normalize_doc_crate_name(krate)).join("index.html") + } else { + target_doc_dir.clone() + }; builder.open_in_browser(index); } + + BuiltDocs { out_dir: target_doc_dir, artifacts } } fn metadata(&self) -> Option { @@ -1031,6 +1168,145 @@ impl CommandLineStep for Rustc { } } +/// Stores generated documentation artifacts. +#[derive(Clone, Debug)] +struct DocArtifacts { + /// Directories with HTML docs for host (proc-macro) crates. + host_dirs: Vec, + /// Directories with HTML docs for target crates. + target_dirs: Vec, + /// JSON files used to create the final CCI index + json_files: Vec, +} + +impl DocArtifacts { + /// Ensure that all passed crates were documented. + fn sanity_check_crates(&self, builder: &Builder<'_>, crates: impl Iterator) + where + S: AsRef, + { + if builder.config.dry_run() { + return; + } + let crate_names: HashSet<&str> = self + .host_dirs + .iter() + .chain(self.target_dirs.iter()) + .filter_map(|d| d.file_name().and_then(|d| d.to_str())) + .collect(); + for krate in crates { + let krate = krate.as_ref(); + let krate = normalize_doc_crate_name(krate); + if !crate_names.contains(krate.as_str()) { + eprintln!("ERROR: crate {krate} was not documented!"); + exit_process(1); + } + } + } +} + +/// Run `cargo doc` and gather generated documentation artifacts. +fn create_docs_and_gather_artifacts(builder: &Builder<'_>, cargo: builder::Cargo) -> DocArtifacts { + let mut json_files = vec![]; + let mut host_dirs = vec![]; + let mut target_dirs = vec![]; + stream_cargo(builder, cargo, vec![], &mut |msg| { + let CargoMessage::CompilerArtifact { filenames, target } = msg else { + return; + }; + if !target.doc { + return; + } + // Note: An alternative way to check host docs would be to check whether the generated + // output is a child of the host doc directory (which we would have to pass to this + // function). + let is_host = target.crate_types.iter().any(|t| t == "proc-macro"); + for filename in filenames { + let path = Path::new(filename.as_ref()); + let Some(extension) = path.extension().and_then(|ext| ext.to_str()) else { + continue; + }; + let path = path.to_path_buf(); + if extension == "json" { + json_files.push(path.to_path_buf()); + } else if extension == "html" { + if is_host { + // doc//index.html -> doc/ + host_dirs.push(path.parent().unwrap().to_path_buf()); + } else { + target_dirs.push(path.parent().unwrap().to_path_buf()); + } + } + } + }); + DocArtifacts { host_dirs, target_dirs, json_files } +} + +/// Merge host and target documentation for a set of crates. +/// We pass `--target` when documenting, so Cargo will put the built documentation into two places: +/// - `target/doc` - contains documentation of host code, so proc macros +/// - `target//doc` - contains documentation of "normal" code +/// +/// See https://github.com/rust-lang/cargo/issues/7677. +/// +/// To produce a single unified documentation, we want to merge them together. +/// We do that by creating symlinks into the target doc dir that will point to the host doc +/// directories. +/// The target doc directory will then contain the combined docs. +fn merge_host_and_target_docs( + builder: &Builder<'_>, + docs: &DocArtifacts, + host_doc_dir: &Path, + target_doc_dir: &Path, +) { + // Sanity check that there is no host/target overlap + for dir in &docs.host_dirs { + let name = dir.file_name().and_then(|d| d.to_str()).unwrap(); + if let Some(target_dir) = docs.target_dirs.iter().find_map(|d| { + let dirname = d.file_name().and_then(|d| d.to_str())?; + if dirname == name { Some(d) } else { None } + }) { + eprintln!( + "ERROR: host docs directory `{name}` ({dir:?}) is also contained in target doc directory ({target_dir:?})" + ); + exit_process(1); + } + } + + let target_src_dir = target_doc_dir.join("src"); + let host_src_dir = host_doc_dir.join("src"); + + // Ideally, we would remove all previous symlinks here. + // However, some of the tools actually share the same build docs directory, so we shouldn't do + // that, otherwise they will invalidate one another. + + for host_docs_crate in &docs.host_dirs { + let dir_name = host_docs_crate.file_name().unwrap().to_str().unwrap(); + // Normalize crate name + let dir_name = normalize_doc_crate_name(dir_name); + + t!(symlink_dir(&builder.config, host_docs_crate, &target_doc_dir.join(&dir_name))); + + // Also symlink its source directory + let target_src_out = target_src_dir.join(&dir_name); + let host_src_out = host_src_dir.join(&dir_name); + t!(symlink_dir(&builder.config, &host_src_out, &target_src_out)); + } + + // Sanity check that all directories contain some documentation + for dir in docs.target_dirs.iter().chain(docs.host_dirs.iter()) { + // Making sure the directory exists and is not empty. + assert!(dir.exists(), "Doc directory {dir:?} does not exist"); + assert!(t!(dir.read_dir()).next().is_some(), "Doc directory {dir:?} is empty"); + } +} + +/// Normalizes crate name to get a name that is used to generate documentation on disk. +/// Turns `rustc-main` into `rustc_main`. +fn normalize_doc_crate_name(name: &str) -> String { + name.replace("-", "_") +} + macro_rules! tool_doc { ( $tool: ident, @@ -1048,46 +1324,53 @@ macro_rules! tool_doc { target: TargetSelection, } - impl CommandLineStep for $tool { - type Output = (); - const IS_HOST: bool = true; - - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.path($path) - } - - fn is_default_step(builder: &Builder<'_>) -> bool { - builder.config.compiler_docs - } - - fn make_run(run: RunConfig<'_>) { - let target = run.target; + impl $tool { + fn new(builder: &Builder<'_>, target: TargetSelection) -> $tool { let build_compiler = match $mode { Mode::ToolRustcPrivate => { // Rustdoc needs the rustc sysroot available to build. - let compilers = RustcPrivateCompilers::new(run.builder, run.builder.top_stage, target); + let compilers = RustcPrivateCompilers::new(builder, builder.top_stage, target); // Build rustc docs so that we generate relative links. - run.builder.ensure(Rustc::from_build_compiler(run.builder, compilers.build_compiler(), target)); + builder.ensure(Rustc::from_build_compiler(builder, compilers.build_compiler(), target)); compilers.build_compiler() } Mode::ToolTarget => { // when shipping multiple docs together in one folder, // they all need to use the same rustdoc version - prepare_doc_compiler(run.builder, run.builder.host_target, run.builder.top_stage) + prepare_doc_compiler(builder, builder.host_target, builder.top_stage) } _ => { panic!("Unexpected tool mode for documenting: {:?}", $mode); } }; + $tool { build_compiler, mode: $mode, target } + } + fn crates() -> &'static [&'static str] { + &$($crates)?[..] + } + } + + impl CommandLineStep for $tool { + type Output = BuiltDocs; + const IS_HOST: bool = true; - run.builder.ensure($tool { build_compiler, mode: $mode, target }); + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path($path) + } + + fn is_default_step(builder: &Builder<'_>) -> bool { + builder.config.compiler_docs + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure($tool::new(run.builder, run.target)); } /// Generates documentation for a tool. /// /// This is largely just a wrapper around `cargo doc`. - fn run(self, builder: &Builder<'_>) { + fn run(self, builder: &Builder<'_>) -> Self::Output { let mut source_type = SourceType::InTree; if let Some(submodule_path) = submodule_path_of(&builder, $path) { @@ -1097,10 +1380,6 @@ macro_rules! tool_doc { let $tool { build_compiler, mode, target } = self; - // This is the intended out directory for compiler documentation. - let out = builder.compiler_doc_out(target); - t!(fs::create_dir_all(&out)); - // Build cargo command. let mut cargo = prepare_tool_cargo( builder, @@ -1122,7 +1401,6 @@ macro_rules! tool_doc { cargo.allow_features(allow_features); } - cargo.arg("-Zskip-rustdoc-fingerprint"); // Only include compiler crates, no dependencies of those, such as `libc`. cargo.arg("--no-deps"); @@ -1130,9 +1408,9 @@ macro_rules! tool_doc { cargo.arg("--lib"); } - $(for krate in $crates { + for krate in $tool::crates() { cargo.arg("-p").arg(krate); - })? + } cargo.rustdocflag("--document-private-items"); // Since we always pass --document-private-items, there's no need to warn about linking to private items. @@ -1141,28 +1419,23 @@ macro_rules! tool_doc { cargo.rustdocflag("--show-type-layout"); cargo.rustdocflag("--generate-link-to-definition"); - let out_dir = builder.stage_out(build_compiler, mode).join(target).join("doc"); - $(for krate in $crates { - let dir_name = krate.replace("-", "_"); - t!(fs::create_dir_all(out_dir.join(&*dir_name))); - })? - - // Symlink compiler docs to the output directory of rustdoc documentation. - symlink_dir_force(&builder.config, &out, &out_dir); - let proc_macro_out_dir = builder.stage_out(build_compiler, mode).join("doc"); - symlink_dir_force(&builder.config, &out, &proc_macro_out_dir); + let cargo_target_dir = builder.stage_out(build_compiler, mode); + let target_doc_dir = cargo_target_dir.join(target).join("doc"); + let host_doc_dir = cargo_target_dir.join("doc"); + for krate in $tool::crates() { + let dir_name = normalize_doc_crate_name(krate); + t!(fs::create_dir_all(target_doc_dir.join(&*dir_name))); + } let _guard = builder.msg(Kind::Doc, stringify!($tool).to_lowercase(), None, build_compiler, target); - cargo.into_cmd().run(builder); + let artifacts = create_docs_and_gather_artifacts(builder, cargo); + artifacts.sanity_check_crates(builder, $tool::crates().iter()); if !builder.config.dry_run() { - // Sanity check on linked doc directories - $(for krate in $crates { - let dir_name = krate.replace("-", "_"); - // Making sure the directory exists and is not empty. - assert!(out.join(&*dir_name).read_dir().unwrap().next().is_some()); - })? + merge_host_and_target_docs(builder, &artifacts, &host_doc_dir, &target_doc_dir); + merge_rustdoc_cci(builder, build_compiler, &artifacts.json_files, &target_doc_dir); } + BuiltDocs { out_dir: target_doc_dir, artifacts } } fn metadata(&self) -> Option { @@ -1344,26 +1617,6 @@ impl CommandLineStep for UnstableBookGen { } } -fn symlink_dir_force(config: &Config, original: &Path, link: &Path) { - if config.dry_run() { - return; - } - if let Ok(m) = fs::symlink_metadata(link) { - if m.file_type().is_dir() { - t!(fs::remove_dir_all(link)); - } else { - // handle directory junctions on windows by falling back to - // `remove_dir`. - t!(fs::remove_file(link).or_else(|_| fs::remove_dir(link))); - } - } - - t!( - symlink_dir(config, original, link), - format!("failed to create link from {} -> {}", link.display(), original.display()) - ); -} - /// Builds the Rust compiler book. #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct RustcBook { diff --git a/src/bootstrap/src/core/build_steps/install.rs b/src/bootstrap/src/core/build_steps/install.rs index db0d18e8368e2..cad6117e686da 100644 --- a/src/bootstrap/src/core/build_steps/install.rs +++ b/src/bootstrap/src/core/build_steps/install.rs @@ -324,12 +324,12 @@ impl CommandLineStep for Src { const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.path("src") + run.alias("rust-src") } fn is_default_step(builder: &Builder<'_>) -> bool { let config = &builder.config; - config.extended && config.tools.as_ref().is_none_or(|t| t.contains("src")) + config.extended && config.tools.as_ref().is_none_or(|t| t.contains("rust-src")) } fn make_run(run: RunConfig<'_>) { diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 096184ee9e5b4..5dae391cc4bee 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -804,11 +804,11 @@ fn check_llvm_version(builder: &Builder<'_>, llvm_config: &Path) { let version = get_llvm_version(builder, llvm_config); let mut parts = version.split('.').take(2).filter_map(|s| s.parse::().ok()); if let (Some(major), Some(_minor)) = (parts.next(), parts.next()) - && major >= 21 + && major >= 22 { return; } - panic!("\n\nbad LLVM version: {version}, need >=21\n\n") + panic!("\n\nbad LLVM version: {version}, need >=22\n\n") } /// C/C++ debug info remap flags for LLVM build. @@ -832,6 +832,40 @@ fn debuginfo_map_cflags(builder: &Builder<'_>, target: TargetSelection) -> Vec, + target: TargetSelection, + llvm_output: &LlvmOutput, + cfg: &mut cmake::Config, + ldflags: &mut LdFlags, +) { + // Apple has it's own ld64 linker, so don't use LLD on Darwin. + if target.contains("apple") { + return; + } + + if builder.config.llvm_use_linker.is_some() || !builder.config.lld_enabled || target.is_msvc() { + // Logic derived from `configure_llvm` + // ThinLTO is only available when building with LLVM, enabling LLD is required. + if builder.config.llvm_thin_lto { + ldflags.push_all("-fuse-ld=lld"); + } + return; + } + + let lld_bin = builder.ensure(Lld { target }).join("bin"); + ldflags.push_all(format!("-B{} -fuse-ld=lld", lld_bin.display())); + + if llvm_output.link_shared() { + // LLD in this case needs the LLVM lib, so tell where to look for it. + let mut dylib_path = helpers::dylib_path(); + dylib_path.insert(0, llvm_output.root_dir().join("lib")); + cfg.env(helpers::dylib_path_var(), t!(env::join_paths(&dylib_path))); + } +} + fn configure_cmake( builder: &Builder<'_>, target: TargetSelection, @@ -1180,13 +1214,8 @@ impl CommandLineStep for RustOffload { let mut cfg = cmake::Config::new(builder.src.join("compiler/rustc_llvm/llvm-wrapper/offload/")); - // Logic copied from `configure_llvm` - // ThinLTO is only available when building with LLVM, enabling LLD is required. - // Apple's linker ld64 supports ThinLTO out of the box though, so don't use LLD on Darwin. let mut ldflags = LdFlags::default(); - if builder.config.llvm_thin_lto && !target.contains("apple") { - ldflags.push_all("-fuse-ld=lld"); - } + try_link_with_in_tree_lld(builder, target, &llvm_output, &mut cfg, &mut ldflags); configure_cmake(builder, target, &mut cfg, true, ldflags, CcFlags::default(), &[]); @@ -1397,13 +1426,8 @@ impl CommandLineStep for OmpOffload { cflags.push_all(format!(" -I {inc_dir}")); } - // Logic copied from `configure_llvm` - // ThinLTO is only available when building with LLVM, enabling LLD is required. - // Apple's linker ld64 supports ThinLTO out of the box though, so don't use LLD on Darwin. let mut ldflags = LdFlags::default(); - if builder.config.llvm_thin_lto && !target.contains("apple") { - ldflags.push_all("-fuse-ld=lld"); - } + try_link_with_in_tree_lld(builder, target, &llvm_output, &mut cfg, &mut ldflags); if let Some(dir) = &cxx_lib_dir { ldflags.push_all(format!("-L{}", dir.display())); @@ -1903,7 +1927,7 @@ fn supported_sanitizers( "aarch64-unknown-linux-gnu" => common_libs( "linux", "aarch64", - &["asan", "lsan", "msan", "tsan", "hwasan", "rtsan", "ubsan"], + &["asan", "lsan", "msan", "tsan", "hwasan", "rtsan", "ubsan", "ubsan_minimal"], ), "aarch64-unknown-linux-ohos" => { common_libs("linux", "aarch64", &["asan", "lsan", "msan", "tsan", "hwasan"]) @@ -1924,7 +1948,17 @@ fn supported_sanitizers( "x86_64-unknown-linux-gnu" => common_libs( "linux", "x86_64", - &["asan", "dfsan", "lsan", "msan", "safestack", "tsan", "rtsan", "ubsan"], + &[ + "asan", + "dfsan", + "lsan", + "msan", + "safestack", + "tsan", + "rtsan", + "ubsan", + "ubsan_minimal", + ], ), "x86_64-unknown-linux-gnuasan" => common_libs("linux", "x86_64", &["asan"]), "x86_64-unknown-linux-gnumsan" => common_libs("linux", "x86_64", &["msan"]), diff --git a/src/bootstrap/src/core/build_steps/perf.rs b/src/bootstrap/src/core/build_steps/perf.rs index 930f114f1a70e..2ee8f1e921c1e 100644 --- a/src/bootstrap/src/core/build_steps/perf.rs +++ b/src/bootstrap/src/core/build_steps/perf.rs @@ -1,5 +1,6 @@ use std::env::consts::EXE_EXTENSION; use std::fmt::{Display, Formatter}; +use std::path::PathBuf; use crate::core::build_steps::compile::Sysroot; use crate::core::build_steps::tool::{RustcPerf, Rustdoc}; @@ -50,6 +51,9 @@ enum PerfCommand { /// The name of the modified artifact to be compared. modified: String, + + #[clap(long, global = true)] + database_path: Option, }, } @@ -87,6 +91,14 @@ struct SharedOpts { /// Select the profiles that should be benchmarked. #[clap(long, global = true, value_delimiter = ',', default_value = "Check,Debug,Opt")] profiles: Vec, + + #[clap(long, global = true)] + database_path: Option, + + /// Comma-separated list of thread counts for the parallel frontend. Maps directly to the + /// `-Zthreads` rustc option. If unspecified, `rustc-perf` uses only 1. + #[clap(long, global = true, value_delimiter = ',')] + frontend_threads: Vec, } #[derive(Clone, Copy, Debug, PartialEq, clap::ValueEnum)] @@ -152,7 +164,17 @@ pub fn perf(builder: &Builder<'_>, args: &PerfArgs, trailing_args: &[String]) { // with compile-time benchmarks. cmd.current_dir(builder.src.join("src/tools/rustc-perf")); - let db_path = results_dir.join("results.db"); + let db_path = args + .cmd + .shared_opts() + .and_then(|i| i.database_path.as_ref()) + .or(if let PerfCommand::Compare { database_path: Some(path), .. } = &args.cmd { + Some(path) + } else { + None + }) + .map(|i| PathBuf::from(i.as_str())) + .unwrap_or_else(|| results_dir.join("results.db")); let is_profiling = match &args.cmd { PerfCommand::Eprintln { .. } @@ -214,7 +236,7 @@ Consider setting `rust.debuginfo-level = 1` in `bootstrap.toml`."#); cmd.args(trailing_args); cmd.run(builder); } - PerfCommand::Compare { base, modified } => { + PerfCommand::Compare { base, modified, database_path: _ } => { cmd.arg("bench_cmp"); cmd.arg("--db").arg(&db_path); cmd.arg(base).arg(modified); @@ -240,4 +262,8 @@ fn apply_shared_opts(cmd: &mut BootstrapCommand, opts: &SharedOpts) { cmd.arg("--scenarios") .arg(opts.scenarios.iter().map(|p| p.to_string()).collect::>().join(",")); } + if !opts.frontend_threads.is_empty() { + cmd.arg("--frontend-threads") + .arg(opts.frontend_threads.iter().map(|p| p.to_string()).collect::>().join(",")); + } } diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 47318d3c086f5..4ea824525e1fc 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -2040,6 +2040,12 @@ test!(BuildStd { default: false }); +test!(AssemblyGcc { + path: "tests/assembly-gcc", + mode: CompiletestMode::Assembly, + suite: "assembly-gcc", + default: true +}); test!(AssemblyLlvm { path: "tests/assembly-llvm", mode: CompiletestMode::Assembly, @@ -3634,7 +3640,13 @@ impl CommandLineStep for Crate { if crates.iter().any(|crate_| crate_ == "alloc") { crates.push("alloctests".to_owned()); }; - let description = crate_description(&self.crates); + let mut description = crate_description(&self.crates); + if builder.kind == Kind::Miri { + if !description.is_empty() { + description.push(' '); + } + description.push_str("in Miri"); + } run_cargo_test(cargo, &[], &crates, &*description, target, builder, record_failed_tests); } } @@ -4518,20 +4530,25 @@ impl CommandLineStep for CodegenGCC { let gcc = builder.ensure(Gcc { target_pair: GccTargetPair::for_native_build(target) }); + if builder.config.rustc_debug_assertions { + eprintln!( + "WARNING: cg_gcc tests will likely fail when debug assertions are enabled for rustc" + ); + } + + // We need to run the cg_gcc tests with the compiler which it links against, not with + // the build compiler. + let target_compiler = compilers.target_compiler(); + builder.ensure( - compile::Std::new(compilers.build_compiler(), target) + compile::Std::new(target_compiler, target) .extra_rust_args(&["-Csymbol-mangling-version=v0", "-Cpanic=abort"]), ); - let _guard = builder.msg_test( - "rustc_codegen_gcc", - compilers.target(), - compilers.target_compiler().stage, - ); - + let _guard = builder.msg_test("rustc_codegen_gcc", target, target_compiler.stage); let mut cargo = builder::Cargo::new( builder, - compilers.build_compiler(), + target_compiler, Mode::Codegen, // Must be codegen to ensure dlopen on compiled dylibs works SourceType::InTree, target, diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 754f4a547bd74..98cfd58198a6c 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -723,6 +723,12 @@ impl Builder<'_> { } if cmd_kind == Kind::Doc { + // Will be stabilized soon -> let's dogfood it. + // No effect on doc output but massive doc-generation time improvements. + cargo.arg("-Zrustdoc-mergeable-info"); + + // FIXME: remove this directory clearing here, and do it explicitly in individua doc + // steps, to reduce dependency on implicit doc output paths. let my_out = match mode { // This is the intended out directory for compiler documentation. Mode::Rustc | Mode::ToolRustcPrivate | Mode::ToolBootstrap | Mode::ToolTarget => { diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test.snap index 32ed54050dc3d..787686b117cd2 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test.snap @@ -23,6 +23,9 @@ expression: test [Test] test::CodegenUnits targets: [aarch64-unknown-linux-gnu] - Suite(tests/codegen-units) +[Test] test::AssemblyGcc + targets: [aarch64-unknown-linux-gnu] + - Suite(tests/assembly-gcc) [Test] test::AssemblyLlvm targets: [aarch64-unknown-linux-gnu] - Suite(tests/assembly-llvm) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage.snap index d1cdf81a14e08..879114ffc8170 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage.snap @@ -20,6 +20,9 @@ expression: test --skip=coverage [Test] test::CodegenUnits targets: [aarch64-unknown-linux-gnu] - Suite(tests/codegen-units) +[Test] test::AssemblyGcc + targets: [aarch64-unknown-linux-gnu] + - Suite(tests/assembly-gcc) [Test] test::AssemblyLlvm targets: [aarch64-unknown-linux-gnu] - Suite(tests/assembly-llvm) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage_map.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage_map.snap index df4b8a2ce1299..a30e3a2407937 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage_map.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage_map.snap @@ -23,6 +23,9 @@ expression: test --skip=coverage-map [Test] test::CodegenUnits targets: [aarch64-unknown-linux-gnu] - Suite(tests/codegen-units) +[Test] test::AssemblyGcc + targets: [aarch64-unknown-linux-gnu] + - Suite(tests/assembly-gcc) [Test] test::AssemblyLlvm targets: [aarch64-unknown-linux-gnu] - Suite(tests/assembly-llvm) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage_run.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage_run.snap index 75992df8aa616..12e0a3233f7ea 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage_run.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage_run.snap @@ -23,6 +23,9 @@ expression: test --skip=coverage-run [Test] test::CodegenUnits targets: [aarch64-unknown-linux-gnu] - Suite(tests/codegen-units) +[Test] test::AssemblyGcc + targets: [aarch64-unknown-linux-gnu] + - Suite(tests/assembly-gcc) [Test] test::AssemblyLlvm targets: [aarch64-unknown-linux-gnu] - Suite(tests/assembly-llvm) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests_coverage.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests_coverage.snap index a44052fac41f9..3b07718b8aecf 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests_coverage.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests_coverage.snap @@ -20,6 +20,9 @@ expression: test --skip=tests/coverage [Test] test::CodegenUnits targets: [aarch64-unknown-linux-gnu] - Suite(tests/codegen-units) +[Test] test::AssemblyGcc + targets: [aarch64-unknown-linux-gnu] + - Suite(tests/assembly-gcc) [Test] test::AssemblyLlvm targets: [aarch64-unknown-linux-gnu] - Suite(tests/assembly-llvm) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_tests.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_tests.snap index c0105510ae416..6db9443b8543c 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_tests.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_tests.snap @@ -20,6 +20,9 @@ expression: test tests [Test] test::CodegenUnits targets: [aarch64-unknown-linux-gnu] - Suite(tests/codegen-units) +[Test] test::AssemblyGcc + targets: [aarch64-unknown-linux-gnu] + - Suite(tests/assembly-gcc) [Test] test::AssemblyLlvm targets: [aarch64-unknown-linux-gnu] - Suite(tests/assembly-llvm) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_tests_skip_coverage.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_tests_skip_coverage.snap index d1a90ca426918..ab4e9e6f7d997 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_tests_skip_coverage.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_tests_skip_coverage.snap @@ -17,6 +17,9 @@ expression: test tests --skip=coverage [Test] test::CodegenUnits targets: [aarch64-unknown-linux-gnu] - Suite(tests/codegen-units) +[Test] test::AssemblyGcc + targets: [aarch64-unknown-linux-gnu] + - Suite(tests/assembly-gcc) [Test] test::AssemblyLlvm targets: [aarch64-unknown-linux-gnu] - Suite(tests/assembly-llvm) diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 0bd0781133684..f8b4ebb5841c7 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -344,6 +344,9 @@ pub fn crate_description(crates: &[impl AsRef]) -> String { if crates.is_empty() { return "".into(); } + if crates.len() == 1 { + return crates[0].as_ref().into(); + } let mut descr = String::from("{"); descr.push_str(crates[0].as_ref()); @@ -883,6 +886,7 @@ impl<'a> Builder<'a> { test::MirOpt, test::CodegenLlvm, test::CodegenUnits, + test::AssemblyGcc, test::AssemblyLlvm, test::Incremental, test::Debuginfo, @@ -961,6 +965,7 @@ impl<'a> Builder<'a> { doc::CargoBook, doc::Clippy, doc::ClippyBook, + doc::CompilerWithTools, doc::Miri, doc::EmbeddedBook, doc::EditionGuide, diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 9c613811c8ccc..893fc4f7ee017 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -989,58 +989,6 @@ mod snapshot { ); } - #[test] - fn dist_compiler_docs() { - let ctx = TestCtx::new(); - insta::assert_snapshot!( - ctx.config("dist") - .path("rustc-docs") - .args(&["--set", "build.compiler-docs=true"]) - .render_steps(), @r" - [build] llvm - [build] rustc 0 -> rustc 1 - [build] rustc 1 -> std 1 - [build] rustc 0 -> UnstableBookGen 1 - [build] rustc 0 -> Rustbook 1 - [doc] unstable-book (book) - [doc] book (book) - [doc] book/first-edition (book) - [doc] book/second-edition (book) - [doc] book/2018-edition (book) - [build] rustdoc 1 - [doc] rustc 1 -> standalone 2 - [doc] rustc 1 -> std 1 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] - [doc] rustc 1 -> rustc 2 - [build] rustc 1 -> rustc 2 - [doc] rustc 1 -> Rustdoc 2 - [doc] rustc 1 -> Rustfmt 2 - [build] rustc 1 -> error-index 2 - [doc] rustc 1 -> error-index 2 - [doc] nomicon (book) - [doc] rustc 1 -> reference (book) 2 - [doc] rustdoc (book) - [doc] rust-by-example (book) - [build] rustc 0 -> LintDocs 1 - [doc] rustc (book) - [doc] rustc 1 -> Cargo 2 - [doc] cargo (book) - [doc] rustc 1 -> Clippy 2 - [doc] clippy (book) - [doc] rustc 1 -> Miri 2 - [doc] embedded-book (book) - [doc] edition-guide (book) - [doc] style-guide (book) - [doc] rustc 1 -> Tidy 2 - [doc] rustc 1 -> Bootstrap 2 - [doc] rustc 1 -> releases 2 - [doc] rustc 1 -> RunMakeSupport 2 - [doc] rustc 1 -> BuildHelper 2 - [doc] rustc 1 -> Compiletest 2 - [build] rustc 0 -> RustInstaller 1 - " - ); - } - #[test] fn dist_extended() { let ctx = TestCtx::new(); @@ -1624,35 +1572,24 @@ mod snapshot { ctx .config("dist") .path("rustc-docs") - .render_steps(), @r" + .render_steps(), @" [build] llvm [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 - [build] rustc 0 -> UnstableBookGen 1 - [build] rustc 0 -> Rustbook 1 - [doc] unstable-book (book) - [doc] book (book) - [doc] book/first-edition (book) - [doc] book/second-edition (book) - [doc] book/2018-edition (book) [build] rustdoc 1 - [doc] rustc 1 -> standalone 2 - [doc] rustc 1 -> std 1 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] + [doc] rustc 1 -> rustc 2 [build] rustc 1 -> rustc 2 - [build] rustc 1 -> error-index 2 - [doc] rustc 1 -> error-index 2 - [doc] nomicon (book) - [doc] rustc 1 -> reference (book) 2 - [doc] rustdoc (book) - [doc] rust-by-example (book) - [build] rustc 0 -> LintDocs 1 - [doc] rustc (book) - [doc] cargo (book) - [doc] clippy (book) - [doc] embedded-book (book) - [doc] edition-guide (book) - [doc] style-guide (book) - [doc] rustc 1 -> releases 2 + [doc] rustc 1 -> Rustdoc 2 + [doc] rustc 1 -> Rustfmt 2 + [doc] rustc 1 -> Clippy 2 + [doc] rustc 1 -> Miri 2 + [doc] rustc 1 -> Cargo 2 + [doc] rustc 1 -> Tidy 2 + [doc] rustc 1 -> Bootstrap 2 + [doc] rustc 1 -> BuildHelper 2 + [doc] rustc 1 -> Compiletest 2 + [doc] rustc 1 -> RunMakeSupport 2 + [doc] rustc 1 -> CompilerWithTools 2 [build] rustc 0 -> RustInstaller 1 "); } @@ -1943,6 +1880,7 @@ mod snapshot { [test] compiletest-mir-opt 1 [test] compiletest-codegen-llvm 1 [test] compiletest-codegen-units 1 + [test] compiletest-assembly-gcc 1 [test] compiletest-assembly-llvm 1 [test] compiletest-incremental 1 [test] compiletest-debuginfo 1 @@ -2124,6 +2062,7 @@ mod snapshot { [test] compiletest-mir-opt 2 [test] compiletest-codegen-llvm 2 [test] compiletest-codegen-units 2 + [test] compiletest-assembly-gcc 2 [test] compiletest-assembly-llvm 2 [test] compiletest-incremental 2 [test] compiletest-debuginfo 2 @@ -2440,6 +2379,31 @@ mod snapshot { "); } + #[test] + fn doc_compiler_with_tools() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("doc") + .arg("compiler-with-tools") + .render_steps(), @" + [build] rustdoc 0 + [doc] rustc 0 -> rustc 1 + [build] llvm + [build] rustc 0 -> rustc 1 + [doc] rustc 0 -> Rustdoc 1 + [doc] rustc 0 -> Rustfmt 1 + [doc] rustc 0 -> Clippy 1 + [doc] rustc 0 -> Miri 1 + [doc] rustc 0 -> Cargo 1 + [doc] rustc 0 -> Tidy 1 + [doc] rustc 0 -> Bootstrap 1 + [doc] rustc 0 -> BuildHelper 1 + [doc] rustc 0 -> Compiletest 1 + [doc] rustc 0 -> RunMakeSupport 1 + [doc] rustc 0 -> CompilerWithTools 1 + "); + } + #[test] fn doc_cargo_stage_1() { let ctx = TestCtx::new(); @@ -2798,7 +2762,7 @@ mod snapshot { let ctx = TestCtx::new(); insta::assert_snapshot!( ctx.config("install") - .path("src") + .path("rust-src") .args(&[ // Using backslashes fails with `--set` "--set", &format!("install.prefix={}", ctx.normalized_dir()), @@ -2815,36 +2779,7 @@ mod snapshot { .render_with(RenderConfig { normalize_host: false }), @r" - [build] llvm - [build] rustc 0 -> rustc 1 - [build] rustc 1 -> std 1 - [build] rustc 0 -> UnstableBookGen 1 - [build] rustc 0 -> Rustbook 1 - [doc] unstable-book (book) - [doc] book (book) - [doc] book/first-edition (book) - [doc] book/second-edition (book) - [doc] book/2018-edition (book) - [build] rustdoc 1 - [doc] rustc 1 -> standalone 2 - [doc] rustc 1 -> std 1 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] - [build] rustc 1 -> rustc 2 - [build] rustc 1 -> error-index 2 - [doc] rustc 1 -> error-index 2 - [doc] nomicon (book) - [doc] rustc 1 -> reference (book) 2 - [doc] rustdoc (book) - [doc] rust-by-example (book) - [build] rustc 0 -> LintDocs 1 - [doc] rustc (book) - [doc] cargo (book) - [doc] clippy (book) - [doc] embedded-book (book) - [doc] edition-guide (book) - [doc] style-guide (book) - [doc] rustc 1 -> releases 2 [build] rustc 0 -> RustInstaller 1 - [dist] docs [dist] src <> "); } @@ -2854,7 +2789,7 @@ mod snapshot { let ctx = TestCtx::new(); insta::assert_snapshot!( ctx.config("install") - .path("src") + .path("rust-src") .args(&[ // Using backslashes fails with `--set` "--set", &format!("install.prefix={}", ctx.normalized_dir()), @@ -2872,10 +2807,7 @@ mod snapshot { .render_with(RenderConfig { normalize_host: false }), @r" - [build] llvm - [build] rustc 0 -> rustc 1 [build] rustc 0 -> RustInstaller 1 - [dist] docs [dist] src <> "); } diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index f8b00094a0f4b..1e58d86024874 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -229,10 +229,13 @@ pub(crate) struct Config { pub rust_break_on_ice: bool, pub rust_parallel_frontend_threads: Option, pub rust_rustflags: Vec, + + // Note: modify the ReproducibleArtifacts step when adding new PGO configs here! pub rust_pgo: PgoConfig, pub rustdoc_pgo: PgoConfig, pub cargo_pgo: PgoConfig, pub clippy_pgo: PgoConfig, + pub cranelift_pgo: PgoConfig, pub stdlib_semver_baseline: Option, @@ -675,6 +678,7 @@ impl Config { rustdoc: pgo_rustdoc, cargo: pgo_cargo, clippy: pgo_clippy, + cranelift: pgo_cranelift, llvm: pgo_llvm, } = toml_pgo.unwrap_or_default(); @@ -732,6 +736,7 @@ impl Config { let pgo_rustdoc = init_pgo(pgo_rustdoc, "rustdoc"); let pgo_cargo = init_pgo(pgo_cargo, "cargo"); let pgo_clippy = init_pgo(pgo_clippy, "clippy"); + let pgo_cranelift = init_pgo(pgo_cranelift, "cranelift"); let bootstrap_override_lld = rust_bootstrap_override_lld.unwrap_or_default(); @@ -1426,6 +1431,7 @@ NOTE: Please add `--stage 2` to your command line, or if you're sure you want to compiletest_diff_tool: build_compiletest_diff_tool, config: toml_path, control_flow_guard: rust_control_flow_guard.unwrap_or(false), + cranelift_pgo: pgo_cranelift, datadir: install_datadir.map(PathBuf::from), deny_warnings, description: build_description, diff --git a/src/bootstrap/src/core/config/toml/pgo.rs b/src/bootstrap/src/core/config/toml/pgo.rs index befe589f192d3..0d69afdd13077 100644 --- a/src/bootstrap/src/core/config/toml/pgo.rs +++ b/src/bootstrap/src/core/config/toml/pgo.rs @@ -28,5 +28,6 @@ define_config! { cargo: Option = "cargo", clippy: Option = "clippy", llvm: Option = "llvm", + cranelift: Option = "cranelift", } } diff --git a/src/bootstrap/src/core/session.rs b/src/bootstrap/src/core/session.rs index 4ad04df753d00..1808f1b8855bc 100644 --- a/src/bootstrap/src/core/session.rs +++ b/src/bootstrap/src/core/session.rs @@ -810,7 +810,7 @@ impl Session { self.out.join(target).join("json-doc") } - /// Output directory for all documentation for a target + /// Output directory for combined compiler + tools docs. pub(crate) fn compiler_doc_out(&self, target: TargetSelection) -> PathBuf { self.out.join(target).join("compiler-doc") } diff --git a/src/bootstrap/src/utils/cc_detect/tests.rs b/src/bootstrap/src/utils/cc_detect/tests.rs index b31850759687a..34c7901d077e1 100644 --- a/src/bootstrap/src/utils/cc_detect/tests.rs +++ b/src/bootstrap/src/utils/cc_detect/tests.rs @@ -7,11 +7,8 @@ use crate::core::session::Session; use crate::utils::tests::TestCtx; #[test] -fn test_ndk_compiler_c() { +fn test_ndk_compiler() { let ndk_path = PathBuf::from("/ndk"); - let target_triple = "arm-unknown-linux-android"; - let expected_triple_translated = "armv7a-unknown-linux-android"; - let expected_compiler = format!("{}21-{}", expected_triple_translated, Language::C.clang()); let host_tag = if cfg!(target_os = "macos") { "darwin-x86_64" } else if cfg!(target_os = "windows") { @@ -19,50 +16,23 @@ fn test_ndk_compiler_c() { } else { "linux-x86_64" }; - let expected_path = ndk_path - .join("toolchains") - .join("llvm") - .join("prebuilt") - .join(host_tag) - .join("bin") - .join(&expected_compiler); - let result = ndk_compiler(Language::C, target_triple, &ndk_path); - assert_eq!(result, expected_path); -} -#[test] -fn test_ndk_compiler_cpp() { - let ndk_path = PathBuf::from("/ndk"); - let target_triple = "arm-unknown-linux-android"; - let expected_triple_translated = "armv7a-unknown-linux-android"; - let expected_compiler = - format!("{}21-{}", expected_triple_translated, Language::CPlusPlus.clang()); - let host_tag = if cfg!(target_os = "macos") { - "darwin-x86_64" - } else if cfg!(target_os = "windows") { - "windows-x86_64" - } else { - "linux-x86_64" - }; - let expected_path = ndk_path - .join("toolchains") - .join("llvm") - .join("prebuilt") - .join(host_tag) - .join("bin") - .join(&expected_compiler); - let result = ndk_compiler(Language::CPlusPlus, target_triple, &ndk_path); - assert_eq!(result, expected_path); + for (language, compiler) in [ + (Language::C, "armv7a-unknown-linux-android21-clang"), + (Language::CPlusPlus, "armv7a-unknown-linux-android21-clang++"), + ] { + let expected = + ndk_path.join("toolchains/llvm/prebuilt").join(host_tag).join("bin").join(compiler); + + assert_eq!(ndk_compiler(language, "arm-unknown-linux-android", &ndk_path), expected); + } } #[test] -fn test_language_gcc() { +fn test_language_compiler_names() { assert_eq!(Language::C.gcc(), "gcc"); assert_eq!(Language::CPlusPlus.gcc(), "g++"); -} -#[test] -fn test_language_clang() { assert_eq!(Language::C.clang(), "clang"); assert_eq!(Language::CPlusPlus.clang(), "clang++"); } diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 25c0963aa4191..922d969ec1f35 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -671,4 +671,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Warning, summary: "The `override-allocator` option has been renamed: The global setting is now `build.allocator` and the per-target setting is `target..allocator`. It can now be set to 'system' to explicitly request the system allocator.", }, + ChangeInfo { + change_id: 162423, + severity: ChangeSeverity::Warning, + summary: "You should now use `x install rust-src` instead of `x install src` to install the standard library source component. If you want to install it as part of a custom `build.tools` set, include `rust-src` in `build.tools.", + }, ]; diff --git a/src/bootstrap/stdlib-semver-check-stamp b/src/bootstrap/stdlib-semver-check-stamp index a2f33f00e3ec3..fad994b27f122 100644 --- a/src/bootstrap/stdlib-semver-check-stamp +++ b/src/bootstrap/stdlib-semver-check-stamp @@ -2,4 +2,4 @@ Change this file to explicitly acknowledge making a breaking change to the Rust If this file is modified in the same PR as the breaking change, then CI will not fail due to the breaking change being detected by cargo-semver-checks. -Last change is for: https://github.com/rust-lang/rust/pull/162909 +Last change is for: https://github.com/rust-lang/rust/pull/158186 diff --git a/src/build_helper/src/lib.rs b/src/build_helper/src/lib.rs index e23a158ac0598..493ff92fbdbc3 100644 --- a/src/build_helper/src/lib.rs +++ b/src/build_helper/src/lib.rs @@ -11,8 +11,8 @@ pub mod stage0_parser; pub mod targets; pub mod util; -/// The default set of crates for opt-dist to collect LLVM profiles. -pub const LLVM_PGO_CRATES: &[&str] = &[ +/// The default set of crates for opt-dist to collect backend (LLVM, Cranelift) profiles. +pub const BACKEND_PGO_CRATES: &[&str] = &[ "syn-2.0.101", "cargo-0.87.1", "serde-1.0.219", diff --git a/src/ci/citool/src/main.rs b/src/ci/citool/src/main.rs index ff55b4df8f3d1..1bb0c2a34b8d1 100644 --- a/src/ci/citool/src/main.rs +++ b/src/ci/citool/src/main.rs @@ -72,10 +72,12 @@ impl GitHubContext { /// The first is a set of patterns of CI jobs that should be executed. /// /// They can be specified in the form of - /// try-job: + /// ``` + /// try-job[s]: /// or - /// try-job: `` + /// try-job[s]: `` /// (to avoid GitHub rendering the glob patterns as Markdown) + /// ``` /// /// # No limit /// The second is a marker that specifies that the limit on the maximum number of allowed try @@ -96,7 +98,9 @@ impl GitHubContext { nolimit = true; continue; } - let Some(pattern) = line.strip_prefix("try-job: ") else { + let Some(pattern) = + line.strip_prefix("try-job: ").or_else(|| line.strip_prefix("try-jobs: ")) + else { continue; }; // Strip backticks if present diff --git a/src/ci/citool/tests/jobs.rs b/src/ci/citool/tests/jobs.rs index e45510d62d9af..1b19d97534982 100644 --- a/src/ci/citool/tests/jobs.rs +++ b/src/ci/citool/tests/jobs.rs @@ -27,11 +27,12 @@ fn try_custom_jobs() { r#"This is a test PR try-job: test-aarch64-gnu +try-jobs: test-aarch64-apple try-job: dist-i686-msvc"#, "refs/heads/automation/bors/try", ); insta::assert_snapshot!(stdout, @r#" - jobs=[{"name":"dist-i686-msvc","full_name":"try - dist-i686-msvc","os":"windows-2022","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","CODEGEN_BACKENDS":"llvm,cranelift","DEPLOY_BUCKET":"rust-lang-ci2","DIST_REQUIRE_ALL_TOOLS":1,"RUST_CONFIGURE_ARGS":"--build=i686-pc-windows-msvc --host=i686-pc-windows-msvc --target=i686-pc-windows-msvc,i586-pc-windows-msvc --enable-full-tools --enable-profiler","SCRIPT":"python x.py dist bootstrap --include-default-paths","TOOLSTATE_PUBLISH":1}},{"name":"test-aarch64-gnu","full_name":"try - test-aarch64-gnu","os":"ubuntu-22.04-arm","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","TOOLSTATE_PUBLISH":1},"free_disk":true}] + jobs=[{"name":"dist-i686-msvc","full_name":"try - dist-i686-msvc","os":"windows-2022","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","CODEGEN_BACKENDS":"llvm,cranelift","DEPLOY_BUCKET":"rust-lang-ci2","DIST_REQUIRE_ALL_TOOLS":1,"RUST_CONFIGURE_ARGS":"--build=i686-pc-windows-msvc --host=i686-pc-windows-msvc --target=i686-pc-windows-msvc,i586-pc-windows-msvc --enable-full-tools --enable-profiler","SCRIPT":"python x.py dist bootstrap --include-default-paths","TOOLSTATE_PUBLISH":1}},{"name":"test-aarch64-apple","full_name":"try - test-aarch64-apple","os":"macos-15","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","DEVELOPER_DIR":"/Applications/Xcode_26.2.app/Contents/Developer","MACOSX_DEPLOYMENT_TARGET":11.0,"MACOSX_STD_DEPLOYMENT_TARGET":11.0,"NO_DEBUG_ASSERTIONS":1,"NO_LLVM_ASSERTIONS":1,"NO_OVERFLOW_CHECKS":1,"RUSTC_RETRY_LINKER_ON_SEGFAULT":1,"RUST_CONFIGURE_ARGS":"--enable-sanitizers --enable-profiler --set build.allocator=jemalloc","SCRIPT":"./x.py --stage 2 test --host=aarch64-apple-darwin --target=aarch64-apple-darwin","TOOLSTATE_PUBLISH":1}},{"name":"test-aarch64-gnu","full_name":"try - test-aarch64-gnu","os":"ubuntu-22.04-arm","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","TOOLSTATE_PUBLISH":1},"free_disk":true}] run_type=try "#); } diff --git a/src/ci/docker/README.md b/src/ci/docker/README.md index 8360c8e9d8b48..167121a5a000c 100644 --- a/src/ci/docker/README.md +++ b/src/ci/docker/README.md @@ -14,9 +14,9 @@ To run a specific CI job locally, you can use the `citool` Rust crate: cargo run --manifest-path src/ci/citool/Cargo.toml run-local ``` -For example, to run the `x86_64-gnu-llvm-21-1` job: +For example, to run the `x86_64-gnu-llvm-22-1` job: ``` -cargo run --manifest-path src/ci/citool/Cargo.toml run-local x86_64-gnu-llvm-21-1 +cargo run --manifest-path src/ci/citool/Cargo.toml run-local x86_64-gnu-llvm-22-1 ``` The job will output artifacts in an `obj/` dir at the root of a repository. Note @@ -27,10 +27,10 @@ Docker image executed in the given CI job. while locally, to the `obj/` directory. This is primarily to prevent strange linker errors when using multiple Docker images. -For some Linux workflows (for example `x86_64-gnu-llvm-21-N`), the process is more involved. You will need to see which script is executed for the given workflow inside the [`jobs.yml`](../github-actions/jobs.yml) file and pass it through the `DOCKER_SCRIPT` environment variable. For example, to reproduce the `x86_64-gnu-llvm-21-3` workflow, you can run the following script: +For some Linux workflows (for example `x86_64-gnu-llvm-22-N`), the process is more involved. You will need to see which script is executed for the given workflow inside the [`jobs.yml`](../github-actions/jobs.yml) file and pass it through the `DOCKER_SCRIPT` environment variable. For example, to reproduce the `x86_64-gnu-llvm-22-3` workflow, you can run the following script: ``` -DOCKER_SCRIPT=x86_64-gnu-llvm3.sh ./src/ci/docker/run.sh x86_64-gnu-llvm-21 +DOCKER_SCRIPT=x86_64-gnu-llvm3.sh ./src/ci/docker/run.sh x86_64-gnu-llvm-22 ``` ## Local Development diff --git a/src/ci/docker/host-aarch64/test-aarch64-gnu-llvm-21/Dockerfile b/src/ci/docker/host-aarch64/test-aarch64-gnu-llvm-22/Dockerfile similarity index 85% rename from src/ci/docker/host-aarch64/test-aarch64-gnu-llvm-21/Dockerfile rename to src/ci/docker/host-aarch64/test-aarch64-gnu-llvm-22/Dockerfile index c364ac27aaa52..53030a098841d 100644 --- a/src/ci/docker/host-aarch64/test-aarch64-gnu-llvm-21/Dockerfile +++ b/src/ci/docker/host-aarch64/test-aarch64-gnu-llvm-22/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:25.10 +FROM ubuntu:26.04 ARG DEBIAN_FRONTEND=noninteractive @@ -15,8 +15,10 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ cmake \ sudo \ gdb \ - llvm-21-tools \ - llvm-21-dev \ + llvm-22-tools \ + llvm-22-dev \ + lldb-22 \ + python3-lldb-22 \ libedit-dev \ libssl-dev \ pkg-config \ @@ -40,9 +42,12 @@ RUN sh /scripts/sccache.sh ENV NO_DOWNLOAD_CI_LLVM="1" ENV EXTERNAL_LLVM="1" +# Override the default debugger `discover` behavior +ENV LLDB_PATH="lldb-22" + # Using llvm-link-shared due to libffi issues -- see #34486 ENV RUST_CONFIGURE_ARGS="--build=aarch64-unknown-linux-gnu \ - --llvm-root=/usr/lib/llvm-21 \ + --llvm-root=/usr/lib/llvm-22 \ --enable-llvm-link-shared \ --set rust.randomize-layout=true \ --set rust.thin-lto-import-instr-limit=10" diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile index 6a5bdf05157bb..911bb168d2489 100644 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile @@ -52,6 +52,10 @@ WORKDIR /tmp RUN mkdir /home/user COPY scripts/shared.sh /tmp/ +# GCC needs make >= 4 +COPY scripts/build-make.sh /tmp/ +RUN ./build-make.sh + # Need at least GCC 5.1 to compile LLVM nowadays COPY scripts/build-gcc.sh /tmp/ ENV GCC_VERSION=9.5.0 diff --git a/src/ci/docker/host-x86_64/test-x86_64-fuchsia/install-rust-for-fuchsia.sh b/src/ci/docker/host-x86_64/test-x86_64-fuchsia/install-rust-for-fuchsia.sh index a7b0f295090d6..edfbc7fdf5c66 100755 --- a/src/ci/docker/host-x86_64/test-x86_64-fuchsia/install-rust-for-fuchsia.sh +++ b/src/ci/docker/host-x86_64/test-x86_64-fuchsia/install-rust-for-fuchsia.sh @@ -5,7 +5,7 @@ set -ex # Build the components of Rust that are needed by the Fuchsia build system. python3 ../x.py install \ --target=$TARGETS \ - compiler/rustc library/std clippy rustfmt src + compiler/rustc library/std clippy rustfmt rust-src # The Fuchsia build system requires that shared libraries that are used inside Fuchsia are stripped, # so use `llvm-objcopy` to remove the debug symbols and strip the libraries, but keeping the diff --git a/src/ci/docker/host-x86_64/test-x86_64-gnu-llvm-21/Dockerfile b/src/ci/docker/host-x86_64/test-x86_64-gnu-llvm-21/Dockerfile deleted file mode 100644 index fc96735521077..0000000000000 --- a/src/ci/docker/host-x86_64/test-x86_64-gnu-llvm-21/Dockerfile +++ /dev/null @@ -1,65 +0,0 @@ -FROM ubuntu:25.10 - -ARG DEBIAN_FRONTEND=noninteractive - -RUN apt-get update && apt-get install -y --no-install-recommends \ - bzip2 \ - g++ \ - gcc-multilib \ - make \ - ninja-build \ - file \ - curl \ - ca-certificates \ - python3 \ - git \ - cmake \ - sudo \ - gdb \ - llvm-21-tools \ - llvm-21-dev \ - libedit-dev \ - libssl-dev \ - pkg-config \ - zlib1g-dev \ - xz-utils \ - nodejs \ - mingw-w64 \ - # libgccjit dependencies - flex \ - libmpfr-dev \ - libgmp-dev \ - libmpc3 \ - libmpc-dev \ - && rm -rf /var/lib/apt/lists/* - -# Install powershell (universal package) so we can test x.ps1 on Linux -# FIXME: need a "universal" version that supports libicu74, but for now it still works to ignore that dep. -RUN curl -sL "https://github.com/PowerShell/PowerShell/releases/download/v7.3.1/powershell_7.3.1-1.deb_amd64.deb" > powershell.deb && \ - dpkg --ignore-depends=libicu72 -i powershell.deb && \ - rm -f powershell.deb - -COPY scripts/sccache.sh /scripts/ -RUN sh /scripts/sccache.sh - -# We are disabling CI LLVM since this builder is intentionally using a host -# LLVM, rather than the typical src/llvm-project LLVM. -ENV NO_DOWNLOAD_CI_LLVM="1" -ENV EXTERNAL_LLVM="1" - -# Using llvm-link-shared due to libffi issues -- see #34486 -ENV RUST_CONFIGURE_ARGS="--build=x86_64-unknown-linux-gnu \ - --llvm-root=/usr/lib/llvm-21 \ - --enable-llvm-link-shared \ - --set rust.randomize-layout=true \ - --set rust.thin-lto-import-instr-limit=10" - -COPY scripts/shared.sh /scripts/ - -COPY scripts/x86_64-gnu-llvm.sh /scripts/ -COPY scripts/x86_64-gnu-llvm2.sh /scripts/ -COPY scripts/x86_64-gnu-llvm3.sh /scripts/ -COPY scripts/stage_2_test_set1.sh /scripts/ -COPY scripts/stage_2_test_set2.sh /scripts/ - -ENV SCRIPT="Must specify DOCKER_SCRIPT for this image" diff --git a/src/ci/docker/host-x86_64/test-x86_64-gnu-llvm-22/Dockerfile b/src/ci/docker/host-x86_64/test-x86_64-gnu-llvm-22/Dockerfile index a22e8de90804f..e7191aa31c47b 100644 --- a/src/ci/docker/host-x86_64/test-x86_64-gnu-llvm-22/Dockerfile +++ b/src/ci/docker/host-x86_64/test-x86_64-gnu-llvm-22/Dockerfile @@ -44,16 +44,15 @@ RUN sh /scripts/sccache.sh # We are disabling CI LLVM since this builder is intentionally using a host # LLVM, rather than the typical src/llvm-project LLVM. -ENV NO_DOWNLOAD_CI_LLVM 1 -ENV EXTERNAL_LLVM 1 +ENV NO_DOWNLOAD_CI_LLVM="1" +ENV EXTERNAL_LLVM="1" # Using llvm-link-shared due to libffi issues -- see #34486 -ENV RUST_CONFIGURE_ARGS \ - --build=x86_64-unknown-linux-gnu \ +ENV RUST_CONFIGURE_ARGS="--build=x86_64-unknown-linux-gnu \ --llvm-root=/usr/lib/llvm-22 \ --enable-llvm-link-shared \ --set rust.randomize-layout=true \ - --set rust.thin-lto-import-instr-limit=10 + --set rust.thin-lto-import-instr-limit=10" COPY scripts/shared.sh /scripts/ @@ -63,4 +62,4 @@ COPY scripts/x86_64-gnu-llvm3.sh /scripts/ COPY scripts/stage_2_test_set1.sh /scripts/ COPY scripts/stage_2_test_set2.sh /scripts/ -ENV SCRIPT "Must specify DOCKER_SCRIPT for this image" +ENV SCRIPT="Must specify DOCKER_SCRIPT for this image" diff --git a/src/ci/docker/scripts/build-make.sh b/src/ci/docker/scripts/build-make.sh new file mode 100755 index 0000000000000..eb2bcfcf9a377 --- /dev/null +++ b/src/ci/docker/scripts/build-make.sh @@ -0,0 +1,11 @@ +#!/bin/sh +set -ex + +MAKE_VERSION="4.4.1" +curl -f "https://ci-mirrors.rust-lang.org/rustc/gcc/make-${MAKE_VERSION}.tar.gz" | tar xzf - +cd "make-${MAKE_VERSION}" +./configure --prefix=/rustroot +make +make install +cd .. +rm -rf "make-${MAKE_VERSION}" diff --git a/src/ci/docker/scripts/make3.sh b/src/ci/docker/scripts/make3.sh deleted file mode 100644 index 283700d06f6cc..0000000000000 --- a/src/ci/docker/scripts/make3.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh -set -ex - -curl -f https://ftp.gnu.org/gnu/make/make-3.81.tar.gz | tar xzf - -cd make-3.81 -./configure --prefix=/usr -make -make install -cd .. -rm -rf make-3.81 diff --git a/src/ci/docker/scripts/musl.sh b/src/ci/docker/scripts/musl.sh index 370b243dd4551..2402ec6ae34bf 100644 --- a/src/ci/docker/scripts/musl.sh +++ b/src/ci/docker/scripts/musl.sh @@ -78,12 +78,14 @@ EOF # project, so we just apply them directly on top of the version we were distributing already. The # patches should be removed once we upgrade to musl >= 1.2.7. # + SCRIPT_DIR=$(cd -- "$(dirname -- "$0")" && pwd) + MUSL_PATCH_DIR="$SCRIPT_DIR" # Advisory: https://www.openwall.com/lists/oss-security/2026/04/09/19 # Patches: https://www.openwall.com/lists/musl/2026/04/03/2/1 - patch -p1 -d $MUSL = 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\.|\-|\*|\d\))\s+").unwrap()); @@ -45,11 +48,14 @@ fn main() -> Result<()> { continue; } let old = fs::read_to_string(&path)?; - let new = comply(&old); + let mut new = comply(&old); + if cli.reflow_harder { + new = reformat(&new, cli.line_length_limit) + } if new == old { compliant.push(path.clone()); } else if cli.overwrite { - fs::write(&path, lengthen_lines(&new, cli.line_length_limit))?; + fs::write(&path, reformat(&new, cli.line_length_limit))?; made_compliant.push(path.clone()); } else { not_compliant.push(path.clone()); @@ -127,7 +133,8 @@ fn comply(content: &str) -> String { new_content.join("\n") + "\n" } -fn lengthen_lines(content: &str, limit: usize) -> String { +// This reformats lines, so that changes of "fn comply" look more pretty +fn reformat(content: &str, limit: usize) -> String { let content: Vec<_> = content.lines().map(std::borrow::ToOwned::to_owned).collect(); let mut new_content = content.clone(); let mut new_n = 0; @@ -182,13 +189,26 @@ fn lengthen_lines(content: &str, limit: usize) -> String { new_content[new_n] = format!("{line} {}", next_line.trim_start()); new_content.remove(new_n + 1); skip_next = true; - } else { - const SEP: &str = ", "; - let Some((before_comma, after_comma)) = next_line.split_once(SEP) else { continue }; - if line.len() + before_comma.len() < limit - SEP.len() { - new_content[new_n] = format!("{line} {before_comma}{}", SEP.trim_end()); + continue; + } + const SEP: &str = ", "; + let indent = next_line.find(|ch: char| !ch.is_whitespace()).unwrap(); + if next_line.contains(SEP) { + let (before_sep, after_sep) = next_line.split_once(SEP).unwrap(); + if line.len() + before_sep.len() < limit - SEP.len() { + new_content[new_n] = + format!("{line} {}{}", before_sep.trim_start(), SEP.trim_end()); new_n += 1; - new_content[new_n] = after_comma.to_owned(); + new_content[new_n] = format!("{:indent$}{after_sep}", ""); + skip_next = true; + } + } else if line.contains(SEP) { + let (before_sep, after_sep) = line.rsplit_once(SEP).unwrap(); + if after_sep.len() + next_line.len() < limit { + new_content[new_n] = format!("{before_sep}{}", SEP.trim_end()); + new_n += 1; + new_content[new_n] = + format!("{:indent$}{after_sep} {}", "", next_line.trim_start()); skip_next = true; } } @@ -220,6 +240,8 @@ o? whatever r? @reviewer r? @reviewer ~? diagnostic + +the queries that we do, as well as the **query DAG**. The "; let expected = " # some. heading @@ -253,12 +275,15 @@ whatever r? @reviewer r? @reviewer ~? diagnostic + +the queries that we do, as well as the **query DAG**. +The "; assert_eq!(expected, comply(original)); } #[test] -fn test_lengthen_lines() { +fn test_reformat_lines() { let original = "\ do not split short sentences @@ -289,6 +314,18 @@ html comment closing handle the indented well +split on comma (filler), of +current line + + split on comma (filler), of + current line + +split on +comma (filler), of next line + + split on + comma (filler), of next line + [a target]: https://example.com [another target]: https://example.com "; @@ -320,10 +357,22 @@ html comment closing handle the indented well +split on comma (filler), +of current line + + split on comma (filler), + of current line + +split on comma (filler), +of next line + + split on comma (filler), + of next line + [a target]: https://example.com [another target]: https://example.com "; - assert_eq!(expected, lengthen_lines(original, 50)); + assert_eq!(expected, reformat(original, 30)); } #[test] @@ -332,38 +381,3 @@ fn should_pass() { let original = "if you see `input isn't interesting! verify interesting-ness test`."; assert_eq!(original, comply(original)); } - -#[test] -#[ignore] -fn split_on_comma_of_current_line() { - let original = " -Each derived value has a dependency on other values, which could themselves be either base or -derived. -"; - let expected = " -Each derived value has a dependency on other values, -which could themselves be either base or derived. -"; - assert_eq!(expected, lengthen_lines(original, 100)) -} - -#[test] -fn split_on_comma_of_next_line() { - let original = " -Because of canonicalization of regions and -inference variables, encountering a cycle doesn't mean that we would get an infinite proof tree. -"; - let expected = " -Because of canonicalization of regions and inference variables, -encountering a cycle doesn't mean that we would get an infinite proof tree. -"; - assert_eq!(expected, lengthen_lines(original, 100)) -} - -#[test] -#[ignore] -fn should_split() { - let original = "the queries that we do, as well as the **query DAG**. The"; - let expected = "the queries that we do, as well as the **query DAG**.\nThe\n"; - assert_eq!(expected, comply(original)); -} diff --git a/src/doc/rustc-dev-guide/ci/tests/fakes/gh b/src/doc/rustc-dev-guide/ci/tests/fakes/gh new file mode 100755 index 0000000000000..197b83af250ca --- /dev/null +++ b/src/doc/rustc-dev-guide/ci/tests/fakes/gh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +set -euo pipefail + +command=$1 +{ + printf '%s' "$command" + shift + printf ' <%s>' "$@" + printf '\n' +} >> "$GH_MOCK_LOG" + +case "$command" in + api) + if [[ ${GH_MOCK_API_FAIL-} == 1 ]]; then + exit 1 + fi + echo "https://github.example/jobs/123" + ;; + issue) + if [[ ${1-} == list && -n ${GH_MOCK_ISSUE_NUMBER-} ]]; then + echo "$GH_MOCK_ISSUE_NUMBER" + fi + ;; +esac diff --git a/src/doc/rustc-dev-guide/ci/tests/report-scheduled-linkcheck-failure.sh b/src/doc/rustc-dev-guide/ci/tests/report-scheduled-linkcheck-failure.sh new file mode 100755 index 0000000000000..96526ef0ea350 --- /dev/null +++ b/src/doc/rustc-dev-guide/ci/tests/report-scheduled-linkcheck-failure.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash + +set -euo pipefail + +repo_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd) +script="$repo_root/ci/report-scheduled-linkcheck-failure.sh" +tmp=$(mktemp -d) +trap 'rm -rf "$tmp"' EXIT + +export PATH="$repo_root/ci/tests/fakes:$PATH" +export GH_MOCK_LOG="$tmp/gh.log" +export GITHUB_REPOSITORY="rust-lang/rustc-dev-guide" +export GITHUB_RUN_ID=123 +export GITHUB_RUN_NUMBER=456 +export GITHUB_SERVER_URL="https://github.com" +export GITHUB_SHA=0123456789abcdef +export GH_TOKEN=test-token + +fail() { + echo "error: $*" >&2 + exit 1 +} + +assert_log_contains() { + grep -F -- "$1" "$GH_MOCK_LOG" >/dev/null || fail "gh log does not contain: $1" +} + +assert_log_excludes() { + if grep -F -- "$1" "$GH_MOCK_LOG" >/dev/null; then + fail "gh log unexpectedly contains: $1" + fi +} + +# A first failure creates the issue with all labels and links to the failed job. +: > "$GH_MOCK_LOG" +unset GH_MOCK_ISSUE_NUMBER GH_MOCK_API_FAIL +"$script" >/dev/null +assert_log_contains 'issue <--repo> <--state> ' +assert_log_contains '<--label> <--label> <--label> ' +assert_log_contains '<--json> <--jq> <[.[] | select(.title == "[automation] Dead links found")][0].number // empty>' +assert_log_contains 'issue ' +assert_log_contains '<--title> <[automation] Dead links found>' +assert_log_contains '<--label> <--label> <--label> ' +assert_log_contains '[CI run #456](https://github.example/jobs/123)' +assert_log_excludes 'issue ' + +# A later failure comments on the existing issue instead of creating another. +: > "$GH_MOCK_LOG" +export GH_MOCK_ISSUE_NUMBER=42 +"$script" >/dev/null +assert_log_contains 'issue <42>' +assert_log_contains 'failed again in [CI run #456](https://github.example/jobs/123)' +assert_log_excludes 'issue ' + +# GitHub API errors and missing required environment variables remain fatal. +: > "$GH_MOCK_LOG" +export GH_MOCK_API_FAIL=1 +if "$script" >/dev/null 2>&1; then + fail "script succeeded after gh api failed" +fi +unset GH_MOCK_API_FAIL + +for variable in GITHUB_REPOSITORY GITHUB_RUN_ID GITHUB_RUN_NUMBER GITHUB_SERVER_URL GITHUB_SHA GH_TOKEN; do + if env -u "$variable" "$script" >/dev/null 2>&1; then + fail "script succeeded without $variable" + fi +done + +echo "report-scheduled-linkcheck-failure tests passed" diff --git a/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs b/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs index db6ac18578542..55665220c5f89 100644 --- a/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs +++ b/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs @@ -20,7 +20,7 @@ use std::path::Path; use std::sync::Arc; use rustc_ast_pretty::pprust::item_to_string; -use rustc_driver::{Compilation, run_compiler}; +use rustc_driver::{Compilation, compiler_entrypoint}; use rustc_interface::interface::{Compiler, Config}; use rustc_middle::ty::TyCtxt; @@ -87,7 +87,7 @@ impl rustc_driver::Callbacks for MyCallbacks { } fn main() { - run_compiler( + compile_entrypoint( &[ // The first argument, which in practice contains the name of the binary being executed // (i.e. "rustc") is ignored by rustc. diff --git a/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs b/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs index c0d7f977d3500..f3449e830c9b7 100644 --- a/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs +++ b/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs @@ -20,7 +20,7 @@ use std::path::Path; use std::sync::Arc; use rustc_ast_pretty::pprust::item_to_string; -use rustc_driver::{Compilation, run_compiler}; +use rustc_driver::{Compilation, compiler_entrypoint}; use rustc_interface::interface::{Compiler, Config}; use rustc_middle::ty::TyCtxt; @@ -94,7 +94,7 @@ impl rustc_driver::Callbacks for MyCallbacks { } fn main() { - run_compiler( + compiler_entrypoint( &[ // The first argument, which in practice contains the name of the binary being executed // (i.e. "rustc") is ignored by rustc. diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index 18fea436747c7..af092f4d3417d 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -32d94cc9be3f6e6c3fa1deaea9e0ab93c4980dba +7d2cd0fbc092625ea371da704f15f80216d2220e 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 1808a8e000c32..351be5e39f921 100644 --- a/src/doc/rustc-dev-guide/src/appendix/code-index.md +++ b/src/doc/rustc-dev-guide/src/appendix/code-index.md @@ -23,7 +23,7 @@ Item | Kind | Short description | Chapter | `SourceFile` | struct | Part of the `SourceMap`. Maps AST nodes to their source code for a single source file. Was previously called FileMap | [The parser] | [compiler/rustc_span/src/lib.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.SourceFile.html) `SourceMap` | struct | Maps AST nodes to their source code. It is composed of `SourceFile`s. Was previously called CodeMap | [The parser] | [compiler/rustc_span/src/source_map.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/source_map/struct.SourceMap.html) `Span` | struct | A location in the user's source code, used for error reporting primarily | [Emitting Diagnostics] | [compiler/rustc_span/src/span_encoding.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html) -`rustc_ast::token_stream::TokenStream` | struct | An abstract sequence of tokens, organized into `TokenTree`s | [The parser], [Macro expansion] | [compiler/rustc_ast/src/tokenstream.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/tokenstream/struct.TokenStream.html) +`TokenStream` | struct | An abstract sequence of tokens, organized into `TokenTree`s | [The parser], [Macro expansion] | [compiler/rustc_ast/src/tokenstream.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/tokenstream/struct.TokenStream.html) `TraitDef` | struct | This struct contains a trait's definition with type information | [The `ty` modules] | [compiler/rustc_middle/src/ty/trait_def.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/trait_def/struct.TraitDef.html) `TraitRef` | struct | The combination of a trait and its input types (e.g. `P0: Trait`) | [Trait Solving: Goals and Clauses] | [compiler/rustc_middle/src/ty/sty.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.TraitRef.html) `Ty<'tcx>` | struct | This is the internal representation of a type used for type checking | [Type checking] | [compiler/rustc_middle/src/ty/mod.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html) diff --git a/src/doc/rustc-dev-guide/src/backend/monomorph.md b/src/doc/rustc-dev-guide/src/backend/monomorph.md index 670614fe51379..f25211b23e602 100644 --- a/src/doc/rustc-dev-guide/src/backend/monomorph.md +++ b/src/doc/rustc-dev-guide/src/backend/monomorph.md @@ -33,7 +33,7 @@ Take this example: ```rust fn banana() { - peach::(); + peach::(); } fn main() { @@ -67,9 +67,9 @@ or more modules in Crate B. | Crate A function | Behavior | | - | - | -| Non-generic function | Crate A function doesn't appear in any codegen units of Crate B | -| Non-generic `#[inline]` function | Crate A function appears within a single CGU of Crate B, and exists even after post-inlining stage| -| Generic function | Regardless of inlining, all monomorphized (specialized) functions
    from Crate A appear within a single codegen unit for Crate B.
    The codegen unit exists even after the post inlining stage.| +| Non-generic function | Crate A function doesn't appear in any codegen units of Crate B. | +| Non-generic `#[inline]` function | Crate A function appears within a single CGU of Crate B.
    The codegen unit exists even after the post inlining stage. | +| Generic function | Regardless of inlining, all monomorphized (specialized) functions
    from Crate A appear within a single codegen unit for Crate B.
    The codegen unit exists even after the post inlining stage. | | Generic `#[inline]` function | - same - | For more details about the partitioner read the module level [documentation]. diff --git a/src/doc/rustc-dev-guide/src/bug-fix-procedure.md b/src/doc/rustc-dev-guide/src/bug-fix-procedure.md index 0db0e07d4e7be..5416e361e5d24 100644 --- a/src/doc/rustc-dev-guide/src/bug-fix-procedure.md +++ b/src/doc/rustc-dev-guide/src/bug-fix-procedure.md @@ -101,7 +101,7 @@ declare_lint! { }, } -// 2. Add a decidacted lint pass for it. +// 2. Add a dedicated lint pass for it. // This step can be skipped if you emit the lint as part of an existing pass. #[derive(Default)] diff --git a/src/doc/rustc-dev-guide/src/building/bootstrapping/what-bootstrapping-does.md b/src/doc/rustc-dev-guide/src/building/bootstrapping/what-bootstrapping-does.md index b986895c3b100..4fe42ebffd574 100644 --- a/src/doc/rustc-dev-guide/src/building/bootstrapping/what-bootstrapping-does.md +++ b/src/doc/rustc-dev-guide/src/building/bootstrapping/what-bootstrapping-does.md @@ -15,13 +15,13 @@ See [bootstrap/README.md][bootstrap-internals] to read about bootstrap internals - Stage 3: the same-result test Compiling `rustc` is done in stages. -Here's a diagram, adapted from Jynn -Nelson's [talk on bootstrapping][rustconf22-talk] at RustConf 2022, with -detailed explanations below. +Here's a diagram, +adapted from Jynn Nelson's [talk on bootstrapping][rustconf22-talk] at RustConf 2022, +with detailed explanations below. The `A`, `B`, `C`, and `D` show the ordering of the stages of bootstrapping. -Blue nodes are -downloaded, yellow +Blue nodes are downloaded, +yellow nodes are built with the `stage0` compiler, and green nodes are built with the `stage1` compiler. @@ -54,8 +54,8 @@ The precompiled stage0 compiler is then used only to compile [`src/bootstrap`] a with precompiled stage0 std. Note that to build the stage1 compiler we use the precompiled stage0 compiler and std. -Therefore, to use a compiler with a std that is freshly built from the tree, you need to -build the stage2 compiler. +Therefore, to use a compiler with a std that is freshly built from the tree, +you need to build the stage2 compiler. There are two concepts at play here: a compiler (with its set of dependencies) and its 'target' or 'object' libraries (`std` and `rustc`). @@ -77,8 +77,8 @@ The `stage1` compiler itself was built by precompiled `stage0` compiler and std and hence not by the source in your working directory. This means that the ABI generated by the `stage0` compiler may not match the ABI that would have been made -by the `stage1` compiler, which can cause problems for dynamic libraries, tests -and tools using `rustc_private`. +by the `stage1` compiler, which can cause problems for dynamic libraries, +tests, and tools using `rustc_private`. Note that the `proc_macro` crate avoids this issue with a `C` FFI layer called `proc_macro::bridge`, allowing it to be used with `stage1`. @@ -119,20 +119,20 @@ For more information about stages, [see below](#understanding-stages-of-bootstra ## Complications of bootstrapping -Since the build system uses the current beta compiler to build a `stage1` -bootstrapping compiler, the compiler source code can't use some features until +Since the build system uses the current beta compiler to build a `stage1` bootstrapping compiler, +the compiler source code can't use some features until they reach beta (because otherwise the beta compiler doesn't support them). -On the other hand, for [compiler intrinsics][intrinsics] and internal features, the -features _have_ to be used. +On the other hand, for [compiler intrinsics] and internal features, +the features _have_ to be used. Additionally, the compiler makes heavy use of `nightly` features (`#![feature(...)]`). How can we resolve this problem? There are two methods used: -1. The build system sets `--cfg bootstrap` when building with `stage0`, so we - can use `cfg(not(bootstrap))` to only use features when built with `stage1`. - Setting `--cfg bootstrap` in this way is used for features that were just - stabilized, which require `#![feature(...)]` when built with `stage0`, but not for `stage1`. +1. The build system sets `--cfg bootstrap` when building with `stage0`, + so we can use `cfg(not(bootstrap))` to only use features when built with `stage1`. + Setting `--cfg bootstrap` in this way is used for features that were just stabilized, + which require `#![feature(...)]` when built with `stage0`, but not for `stage1`. 2. The build system sets `RUSTC_BOOTSTRAP=1`. This special variable means to _break the stability guarantees_ of Rust: allowing use of `#![feature(...)]` @@ -140,7 +140,7 @@ There are two methods used: _Setting `RUSTC_BOOTSTRAP=1` should never be used except when bootstrapping the compiler._ [boot]: https://en.wikipedia.org/wiki/Bootstrapping_(compilers) -[intrinsics]: ../../appendix/glossary.md#intrinsic +[compiler intrinsics]: ../../appendix/glossary.md#intrinsic ## Understanding stages of bootstrap @@ -152,8 +152,8 @@ The convention `./x` uses is that: - A `--stage N` flag means to run the stage N compiler (`stageN/rustc`). - A "stage N artifact" is a build artifact that is _produced_ by the stage N compiler. -- The stage N+1 compiler is assembled from stage N *artifacts*. This process is - called _uplifting_. +- The stage N+1 compiler is assembled from stage N *artifacts*. + This process is called _uplifting_. #### Build artifacts @@ -186,10 +186,10 @@ Build artifacts include, but are not limited to: - `./x test --stage 0 compiler/rustc` builds the compiler but runs no tests: it's running `cargo test -p rustc`, but `cargo` doesn't understand Rust's tests. You shouldn't need to use this; use `test` instead (without arguments). -- `./x build --stage 0 compiler/rustc` builds the compiler, but does not build - `libstd` or even `libcore`. - Most of the time, you'll want `./x build library` - instead, which allows compiling programs without needing to define lang items. +- `./x build --stage 0 compiler/rustc` builds the compiler, + but does not build `libstd` or even `libcore`. + Most of the time, you'll want `./x build library` instead, + which allows compiling programs without needing to define lang items. ### Building vs. running @@ -199,8 +199,8 @@ will later be uplifted to be the stage1 compiler_. In each stage besides 0, two major steps are performed: 1. `std` is compiled by the stage N compiler. -2. That `std` is linked to programs built by the stage N compiler, including the - stage N artifacts (stage N+1 compiler). +2. That `std` is linked to programs built by the stage N compiler, + including the stage N artifacts (stage N+1 compiler). This is somewhat intuitive if one thinks of the stage N artifacts as "just" another program we are building with the stage N compiler: `build --stage N @@ -211,23 +211,23 @@ compiler/rustc` is linking the stage N artifacts to the `std` built by the stage Note that there are two `std` libraries in play here: 1. The library _linked_ to `stageN/rustc`, which was built by stage N-1 (stage N-1 `std`) -2. The library _used to compile programs_ with `stageN/rustc`, which was built - by stage N (stage N `std`). +2. The library _used to compile programs_ with `stageN/rustc`, + which was built by stage N (stage N `std`). Stage N `std` is pretty much necessary for any useful work with the stage N compiler. Without it, you can only compile programs with `#![no_core]` -- not terribly useful! The reason these need to be different is because they aren't necessarily -ABI-compatible: there could be new layout optimizations, changes to `MIR`, or -other changes to Rust metadata on `nightly` that aren't present in beta. +ABI-compatible: there could be new layout optimizations, changes to `MIR`, +or other changes to Rust metadata on `nightly` that aren't present in beta. This is also where `--keep-stage 1 library/std` comes into play. Since most changes to the compiler don't actually change the ABI, once you've produced a `std` in `stage1`, you can probably just reuse it with a different compiler. If the ABI hasn't changed, you're good to go; no need to spend time recompiling that `std`. The flag `--keep-stage` simply instructs the build script to assume -the previous compile is fine and copies those artifacts into the appropriate -place, skipping the `cargo` invocation. +the previous compile is fine and copies those artifacts into the appropriate place, +skipping the `cargo` invocation. ### Cross-compiling rustc @@ -235,8 +235,8 @@ place, skipping the `cargo` invocation. For instance, you might want to build an ARM version of rustc using an x86 machine. Building `stage2` `std` is different when you are cross-compiling. -This is because `./x` uses the following logic: if `HOST` and `TARGET` are the -same, it will reuse `stage1` `std` for `stage2`! +This is because `./x` uses the following logic: if `HOST` and `TARGET` are the same, +it will reuse `stage1` `std` for `stage2`! This is sound because `stage1` `std` was compiled with the `stage1` compiler, i.e. a compiler using the source code you currently have checked out. @@ -250,23 +250,23 @@ So, the `stage2` compiler has to recompile `std` for the target. ### What is a 'sysroot'? -When you build a project with `cargo`, the build artifacts for dependencies are -normally stored in `target/debug/deps`. -This only contains dependencies `cargo` -knows about; in particular, it doesn't have the standard library. +When you build a project with `cargo`, +the build artifacts for dependencies are normally stored in `target/debug/deps`. +This only contains dependencies `cargo` knows about; in particular, +it doesn't have the standard library. Where do `std` or `proc_macro` come from? -They come from the **sysroot**, the root of a number -of directories where the compiler loads build artifacts at runtime. -The `sysroot` doesn't just store the standard library, though - it includes anything -that needs to be loaded at runtime. +They come from the **sysroot**, +the root of a number of directories where the compiler loads build artifacts at runtime. +The `sysroot` doesn't just store the standard library though; +it includes anything that needs to be loaded at runtime. That includes (but is not limited to): - Libraries `libstd`/`libtest`/`libproc_macro`. - Compiler crates themselves, when using `rustc_private`. In-tree, these are always present; out-of-tree, you need to install `rustc-dev` with `rustup`. - Shared object file `libLLVM.so` for the LLVM project. - In-tree, this is either built from source or downloaded from CI; out-of-tree, you need to install - `llvm-tools-preview` with `rustup`. + In-tree, this is either built from source or downloaded from CI; out-of-tree, + you need to install `llvm-tools-preview` with `rustup`. All the artifacts listed so far are *compiler* runtime dependencies. You can see them with `rustc --print sysroot`: @@ -293,17 +293,17 @@ libcompiler_builtins-ef2408da76957905.rlib Directory `lib/rustlib/` includes libraries like `hashbrown` and `cfg_if`, which are not part of the public API of the standard library, but are used to implement it. -Also,`lib/rustlib/` is part of the search path for linkers, but -`lib` will never be part of the search path. +Also,`lib/rustlib/` is part of the search path for linkers, +but `lib` will never be part of the search path. #### `-Z force-unstable-if-unmarked` -Since `lib/rustlib/` is part of the search path we have to be careful about -which crates are included in it. -In particular, all crates except for the -standard library are built with the flag `-Z force-unstable-if-unmarked`, which -means that you have to use `#![feature(rustc_private)]` in order to load it (as -opposed to the standard library, which is always available). +Since `lib/rustlib/` is part of the search path, +we have to be careful about which crates are included in it. +In particular, all crates, except for the standard library, +are built with the flag `-Z force-unstable-if-unmarked`, +meaning that you have to use `#![feature(rustc_private)]` in order to load it +(as opposed to the standard library, which is always available). The `-Z force-unstable-if-unmarked` flag has a variety of purposes to help enforce that the correct crates are marked as `unstable`. @@ -323,12 +323,12 @@ This flag has the following effects: - Marks the crate as "`unstable`" with the `rustc_private` feature if it is not itself marked as `stable` or `unstable`. - Allows these crates to access other forced-unstable crates without any need for attributes. - Normally, a crate would need a `#![feature(rustc_private)]` - attribute to use other `unstable` crates. - However, that would make it - impossible for a crate from crates.io to access its own dependencies since - that crate won't have a `feature(rustc_private)` attribute, but *everything* - is compiled with `-Z force-unstable-if-unmarked`. + Normally, + a crate would need a `#![feature(rustc_private)]` attribute to use other `unstable` crates. + However, + that would make it impossible for a crate from crates.io to access its own dependencies since + that crate won't have a `feature(rustc_private)` attribute, + but *everything* is compiled with `-Z force-unstable-if-unmarked`. Code which does not use `-Z force-unstable-if-unmarked` should include the `#![feature(rustc_private)]` crate attribute to access these forced-unstable crates. @@ -348,10 +348,10 @@ You can find more discussion about sysroots in: Conveniently `./x` allows you to pass stage-specific flags to `rustc` and `cargo` when bootstrapping. The `RUSTFLAGS_BOOTSTRAP` environment variable is -passed as `RUSTFLAGS` to the bootstrap stage (`stage0`), and -`RUSTFLAGS_NOT_BOOTSTRAP` is passed when building artifacts for later stages. -`RUSTFLAGS` will work, but also affects the build of `bootstrap` itself, so it -will be rare to want to use it. +passed as `RUSTFLAGS` to the bootstrap stage (`stage0`), +and `RUSTFLAGS_NOT_BOOTSTRAP` is passed when building artifacts for later stages. +`RUSTFLAGS` will work, but also affects the build of `bootstrap` itself, +so it will be rare to want to use it. Finally, `MAGIC_EXTRA_RUSTFLAGS` bypasses the `cargo` cache to pass flags to rustc without recompiling all dependencies. @@ -370,19 +370,19 @@ which you can use to find out the options accepted by the runner. ## Environment Variables During bootstrapping, there are a bunch of compiler-internal environment variables that are used. -If you are trying to run an intermediate version of -`rustc`, sometimes you may need to set some of these environment variables manually. +If you are trying to run an intermediate version of `rustc`, +sometimes you may need to set some of these environment variables manually. Otherwise, you get an error like the following: ```text thread 'main' panicked at 'RUSTC_STAGE was not set: NotPresent', library/core/src/result.rs:1165:5 ``` -If `./stageN/bin/rustc` gives an error about environment variables, that usually -means something is quite wrong -- such as you're trying to compile `rustc` or +If `./stageN/bin/rustc` gives an error about environment variables, +that usually means something is quite wrong -- such as you're trying to compile `rustc` or `std` or something which depends on environment variables. -In the unlikely case that you actually need to invoke `rustc` in such a situation, you can tell the -bootstrap shim to print all `env` variables by adding `-vvv` to your `x` command. +In the unlikely case that you actually need to invoke `rustc` in such a situation, +you can tell the bootstrap shim to print all `env` variables by adding `-vvv` to your `x` command. Finally, bootstrap makes use of the [cc-rs crate] which has [its own method][env-vars] of configuring `C` compilers and `C` flags via environment variables. @@ -392,8 +392,8 @@ method][env-vars] of configuring `C` compilers and `C` flags via environment var ## Clarification of build command's `stdout` -In this part, we will investigate the build command's `stdout` in an action -(similar, but more detailed and complete documentation compare to topic above). +In this part, we will investigate the build command's `stdout` in an action (similar, +but more detailed and complete documentation compared to topic above). When you execute `x build --dry-run` command, the build output will be something like the following: ```text @@ -410,8 +410,8 @@ Building rustdoc for stage1 (x86_64-unknown-linux-gnu) ### Building stage0 {std,compiler} artifacts -These steps use the provided (downloaded, usually) compiler to compile the local -Rust source into libraries we can use. +These steps use the provided (downloaded, +usually) compiler to compile the local Rust source into libraries we can use. ### Copying stage0 \{std,rustc\} diff --git a/src/doc/rustc-dev-guide/src/building/suggested.md b/src/doc/rustc-dev-guide/src/building/suggested.md index 01869dfdc3f75..ca8dd74ee4ff0 100644 --- a/src/doc/rustc-dev-guide/src/building/suggested.md +++ b/src/doc/rustc-dev-guide/src/building/suggested.md @@ -5,14 +5,14 @@ Here are some suggestions to make your life easier. ## Installing a pre-push hook -CI will automatically fail your build if it doesn't pass `tidy`, our internal -tool for ensuring code quality. -If you'd like, you can install a [Git -hook](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) that will +CI will automatically fail your build if it doesn't pass `tidy`, +our internal tool for ensuring code quality. +If you'd like, +you can install a [Git hook](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) that will automatically run `./x test tidy` on each push, to ensure your code is up to par. If the hook fails then run `./x test tidy --bless` and commit the changes. -If you decide later that the pre-push behavior is undesirable, you can delete -the `pre-push` file in `.git/hooks`. +If you decide later that the pre-push behavior is undesirable, +you can delete the `pre-push` file in `.git/hooks`. A prebuilt git hook lives at [`src/etc/pre-push.sh`]. It can be copied into your `.git/hooks` folder as `pre-push` (without the `.sh` extension!). @@ -24,8 +24,8 @@ You can also install the hook as a step of running `./x setup`! When working on different tasks, you might need to switch between different bootstrap configurations. Sometimes you may want to keep an old configuration for future use. But saving raw config values in -random files and manually copying and pasting them can quickly become messy, especially if you have a -long history of different configurations. +random files and manually copying and pasting them can quickly become messy, +especially if you have a long history of different configurations. To simplify managing multiple configurations, you can create config extensions. @@ -65,8 +65,8 @@ Also, parent extensions always override the inner ones. Checking the "library" tree requires a stage1 compiler, which can be a heavy process on some computers. For this reason, bootstrap has a flag called `--skip-std-check-if-no-download-rustc` that skips checking the "library" tree if `rust.download-rustc` isn't available. -If you want to avoid putting a heavy load on your computer -with `rust-analyzer`, you can add the `--skip-std-check-if-no-download-rustc` flag to your `./x check` command in +If you want to avoid putting a heavy load on your computer with `rust-analyzer`, +you can add the `--skip-std-check-if-no-download-rustc` flag to your `./x check` command in the `rust-analyzer` configuration. ### Project-local rust-analyzer setup @@ -75,15 +75,15 @@ the `rust-analyzer` configuration. By default, `rust-analyzer` runs the `cargo check` and `rustfmt` commands, but you can override these commands to use more adapted versions of these tools when hacking on `rustc`. -With custom setup, `rust-analyzer` can use `./x check` -to check the sources, and the stage 0 rustfmt to format them. +With custom setup, `rust-analyzer` can use `./x check` to check the sources, +and the stage 0 rustfmt to format them. The default `rust-analyzer.check.overrideCommand` command line will check all the crates and tools in the repository. If you are working on a specific part, you can override the command to only check the part you are working on to save checking time. -For example, if you are working on the compiler, you can override -the command to `x check compiler --json-output` to only check the compiler part. +For example, if you are working on the compiler, +you can override the command to `x check compiler --json-output` to only check the compiler part. You can run `x check --help --verbose` to see the available parts. Running `./x setup editor` will prompt you to create a project-local LSP config @@ -97,12 +97,12 @@ it will use a separate build directory from manual command-line builds. You can override this your generated LSP config file if you want to save disk space. However, this is not recommended: -- Each build will lock the build directory and force the other to wait, so it - becomes impossible to run command-line builds while rust-analyzer is running +- Each build will lock the build directory and force the other to wait, + so it becomes impossible to run command-line builds while rust-analyzer is running commands in the background. - There is an increased risk of one of the builds deleting previously-built - artifacts due to conflicting compiler flags or other settings, forcing - additional rebuilds in some cases. + artifacts due to conflicting compiler flags or other settings, + forcing additional rebuilds in some cases. ### Visual Studio Code @@ -153,13 +153,13 @@ Steps for this can be [found here][r-a nvim lsp]. 2. Run `./x setup editor`, and select `vscode` to create a `.vscode/settings.json` file. `neoconf` is able to read and update rust-analyzer settings automatically when the project is opened when this file is detected. - Neovim does not expand VS Code's `${workspaceFolder}` variable, so replace each occurrence - in the generated file with the absolute path to your rust repository. + Neovim does not expand VS Code's `${workspaceFolder}` variable, + so replace each occurrence in the generated file with the absolute path to your rust repository. #### coc.nvim -If you're using `coc.nvim`, you can run `./x setup editor` and select `vim` to -create a `.vim/coc-settings.json`. +If you're using `coc.nvim`, +you can run `./x setup editor` and select `vim` to create a `.vim/coc-settings.json`. The settings can be edited with `:CocLocalConfig`. The recommended settings live at [`src/etc/rust_analyzer_settings.json`]. @@ -229,9 +229,9 @@ Emacs provides support for rust-analyzer with project-local configuration through [Eglot](https://www.gnu.org/software/emacs/manual/html_node/eglot/). Steps for setting up Eglot with rust-analyzer can be [found here](https://rust-analyzer.github.io/manual.html#eglot). -Having set up Emacs & Eglot for Rust development in general, you can run -`./x setup editor` and select `emacs`, which will prompt you to create -`.dir-locals.el` with the recommended configuration for Eglot. +Having set up Emacs & Eglot for Rust development in general, +you can run `./x setup editor` and select `emacs`, +which will prompt you to create `.dir-locals.el` with the recommended configuration for Eglot. The recommended settings live at [`src/etc/rust_analyzer_eglot.el`]. For more information on project-specific Eglot configuration, consult [the manual](https://www.gnu.org/software/emacs/manual/html_node/eglot/Project_002dspecific-configuration.html). @@ -239,17 +239,17 @@ manual](https://www.gnu.org/software/emacs/manual/html_node/eglot/Project_002dsp ### Helix Helix comes with built-in LSP and rust-analyzer support. -It can be configured through `languages.toml`, as described -[here](https://docs.helix-editor.com/languages.html). -You can run `./x setup editor` and select `helix`, which will prompt you to -create `languages.toml` with the recommended configuration for Helix. +It can be configured through `languages.toml`, +as described [here](https://docs.helix-editor.com/languages.html). +You can run `./x setup editor` and select `helix`, +which will prompt you to create `languages.toml` with the recommended configuration for Helix. The recommended settings live at [`src/etc/rust_analyzer_helix.toml`]. ### Zed Zed comes with built-in LSP and rust-analyzer support. -It can be configured through `.zed/settings.json`, as described -[here](https://zed.dev/docs/configuring-languages). +It can be configured through `.zed/settings.json`, +as described [here](https://zed.dev/docs/configuring-languages). Selecting `zed` in `./x setup editor` will prompt you to create a `.zed/settings.json` file which will configure Zed with the recommended configuration. The recommended `rust-analyzer` settings live at [`src/etc/rust_analyzer_zed.json`]. @@ -257,8 +257,8 @@ The recommended `rust-analyzer` settings live at [`src/etc/rust_analyzer_zed.jso ## Check, check, and check again When doing simple refactoring, it can be useful to run `./x check` continuously. -If you set up `rust-analyzer` as described above, this will be -done for you every time you save a file. +If you set up `rust-analyzer` as described above, +this will be done for you every time you save a file. Here you are just checking that the compiler can **build**, but often that is all you need (e.g., when renaming a method). @@ -268,8 +268,8 @@ In fact, it is sometimes useful to put off tests even when you are not 100% sure You can then keep building up refactoring commits and only run the tests at some later time. You can then use `git bisect` to track down **precisely** which commit caused the problem. A nice side-effect of this style -is that you are left with a fairly fine-grained set of commits at the end, all -of which build and pass tests. +is that you are left with a fairly fine-grained set of commits at the end, +all of which build and pass tests. This often helps reviewing. ## Configuring `rustup` to use nightly @@ -284,16 +284,15 @@ rustup override set nightly ``` Don't forget to do this for all directories you have [setup a worktree for]. -You may need to use the -pinned nightly version from `src/stage0`, but often the normal `nightly` channel will work. +You may need to use the pinned nightly version from `src/stage0`, +but often the normal `nightly` channel will work. -**Note** see [the section on vscode] for how to configure it with this real -rustfmt `x` uses, and [the section on rustup] for how to setup `rustup` -toolchain for your bootstrapped compiler +**Note** see [the section on vscode] for how to configure it with this real rustfmt `x` uses, +and [the section on rustup] for how to setup `rustup` toolchain for your bootstrapped compiler **Note** This does _not_ allow you to build `rustc` with cargo directly. -You still have to use `x` to work on the compiler or standard library, this just -lets you use `cargo fmt`. +You still have to use `x` to work on the compiler or standard library, +this just lets you use `cargo fmt`. [install a nightly toolchain]: https://rust-lang.github.io/rustup/concepts/channels.html?highlight=nightl#working-with-nightly-rust [setup a worktree for]: ./suggested.md#working-on-multiple-branches-at-the-same-time @@ -303,12 +302,12 @@ lets you use `cargo fmt`. ## Faster Builds with CI-rustc If you are not working on the compiler, you often don't need to build the compiler tree. -For example, you can skip building the compiler and only build the `library` tree or the -tools under `src/tools`. -To achieve that, you have to enable this by setting the `download-rustc` -option in your configuration. -This tells bootstrap to use the latest nightly compiler for `stage > 0` -steps, meaning it will have two precompiled compilers: stage0 compiler and `download-rustc` compiler +For example, +you can skip building the compiler and only build the `library` tree or the tools under `src/tools`. +To achieve that, +you have to enable this by setting the `download-rustc` option in your configuration. +This tells bootstrap to use the latest nightly compiler for `stage > 0` steps, +meaning it will have two precompiled compilers: stage0 compiler and `download-rustc` compiler for `stage > 0` steps. This way, it will never need to build the in-tree compiler. As a result, your build time will be significantly reduced by not building the in-tree compiler. @@ -330,15 +329,14 @@ The sequence of commands you want is as follows: As mentioned, the effect of `--keep-stage-std=1` is that we just _assume_ that the old standard library can be re-used. -If you are editing the compiler, this is -often true: you haven't changed the standard library, after all. -But sometimes, it's not true: for example, if you are editing the "metadata" part of -the compiler, which controls how the compiler encodes types and other states -into the `rlib` files, or if you are editing things that wind up in the metadata -(such as the definition of the MIR). - -That is, you might get weird behavior from a compile when using -`--keep-stage-std=1`, for example, strange [ICEs](../appendix/glossary.md#ice) or other panics. +If you are editing the compiler, this is often true: you haven't changed the standard library, +after all. +But sometimes, it's not true: for example, if you are editing the "metadata" part of the compiler, +which controls how the compiler encodes types and other states into the `rlib` files, +or if you are editing things that wind up in the metadata (such as the definition of the MIR). + +That is, you might get weird behavior from a compile when using `--keep-stage-std=1`, +for example, strange [ICEs](../appendix/glossary.md#ice) or other panics. In that case, you should simply remove the `--keep-stage-std=1` from the command and rebuild. You can also use `--keep-stage-std=1` when running tests. @@ -363,17 +361,17 @@ incremental = true ``` Note that incremental compilation will use more disk space than usual. -If disk space is a concern for you, you might want to check the size of the `build` -directory from time to time. +If disk space is a concern for you, +you might want to check the size of the `build` directory from time to time. ## Fine-tuning optimizations Setting `optimize = false` makes the compiler too slow for tests. -However, to improve the test cycle, you can disable optimizations selectively only for the -crates you'll have to rebuild +However, to improve the test cycle, +you can disable optimizations selectively only for the crates you'll have to rebuild ([source](https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/incremental.20compilation.20question/near/202712165)). -For example, when working on `rustc_mir_build`, the `rustc_mir_build` and -`rustc_driver` crates take the most time to incrementally rebuild. +For example, when working on `rustc_mir_build`, +the `rustc_mir_build` and `rustc_driver` crates take the most time to incrementally rebuild. You could therefore set the following in the root `Cargo.toml`: ```toml @@ -385,12 +383,12 @@ opt-level = 0 ## Working on multiple branches at the same time -Working on multiple branches in parallel can be a little annoying, since -building the compiler on one branch will cause the old build and the incremental +Working on multiple branches in parallel can be a little annoying, +since building the compiler on one branch will cause the old build and the incremental compilation cache to be overwritten. -One solution would be to have multiple -clones of the repository, but that would mean storing the Git metadata multiple -times, and having to update each clone individually. +One solution would be to have multiple clones of the repository, +but that would mean storing the Git metadata multiple times, +and having to update each clone individually. Fortunately, Git has a better solution called [worktrees]. This lets you create multiple "working trees", which all share the same Git database. @@ -403,8 +401,8 @@ They will still be cloned multiple times. [worktrees]: https://git-scm.com/docs/git-worktree -Given you are inside the root directory for your Rust repository, you can create -a "linked working tree" in a new "rust2" directory by running the following command: +Given you are inside the root directory for your Rust repository, +you can create a "linked working tree" in a new "rust2" directory by running the following command: ```bash git worktree add ../rust2 @@ -443,10 +441,10 @@ set up correctly. ### Note -Note that when using nix on a not-NixOS distribution, it may be necessary to set -**`build.patch-binaries-for-nix = true` in `bootstrap.toml`**. Bootstrap tries to detect -whether it's running in nix and enable patching automatically, but this -detection can have false negatives. +Note that when using nix on a not-NixOS distribution, +it may be necessary to set **`build.patch-binaries-for-nix = true` in `bootstrap.toml`**. +Bootstrap tries to detect whether it's running in nix and enable patching automatically, +but this detection can have false negatives. You can also use your nix shell to manage `bootstrap.toml`: @@ -464,8 +462,8 @@ pkgs.mkShell { ## Shell Completions -If you use Bash, Zsh, Fish or PowerShell, you can find automatically-generated shell -completion scripts for `./x` in +If you use Bash, Zsh, Fish or PowerShell, +you can find automatically-generated shell completion scripts for `./x` in [`src/etc/completions`](https://github.com/rust-lang/rust/tree/HEAD/src/etc/completions). You can use `source ./src/etc/completions/x.` to load completions diff --git a/src/doc/rustc-dev-guide/src/closure.md b/src/doc/rustc-dev-guide/src/closure.md index 427919cd57995..81bddd73caba7 100644 --- a/src/doc/rustc-dev-guide/src/closure.md +++ b/src/doc/rustc-dev-guide/src/closure.md @@ -1,13 +1,12 @@ # Closure Capture Inference -This section describes how rustc handles closures. Closures in Rust are -effectively "desugared" into structs that contain the values they use (or -references to the values they use) from their creator's stack frame. rustc has -the job of figuring out which values a closure uses and how, so it can decide -whether to capture a given variable by shared reference, mutable reference, or -by move. rustc also has to figure out which of the closure traits ([`Fn`][fn], -[`FnMut`][fn_mut], or [`FnOnce`][fn_once]) a closure is capable of -implementing. +This section describes how rustc handles closures. +Closures in Rust are effectively "desugared" into structs that contain the values they use (or +references to the values they use) from their creator's stack frame. +rustc has the job of figuring out which values a closure uses and how, so it can decide +whether to capture a given variable by shared reference, mutable reference, or by move. +rustc also has to figure out which of the closure traits ([`Fn`][fn], +[`FnMut`][fn_mut], or [`FnOnce`][fn_once]) a closure is capable of implementing. [fn]: https://doc.rust-lang.org/std/ops/trait.Fn.html [fn_mut]:https://doc.rust-lang.org/std/ops/trait.FnMut.html @@ -31,9 +30,11 @@ fn main() { } ``` -Let's say the above is the content of a file called `immut.rs`. If we compile -`immut.rs` using the following command. The [`-Z dump-mir=all`][dump-mir] flag will cause -`rustc` to generate and dump the [MIR][mir] to a directory called `mir_dump`. +Let's say the above is the content of a file called `immut.rs`. +If we compile `immut.rs` using the following command, +the [`-Z dump-mir=all`][dump-mir] flag will cause +`rustc` to generate and dump the [MIR] to a directory called `mir_dump`. + ```console > rustc +stage1 immut.rs -Z dump-mir=all ``` @@ -43,8 +44,8 @@ Let's say the above is the content of a file called `immut.rs`. If we compile After we run this command, we will see a newly generated directory in our current working directory called `mir_dump`, which will contain several files. -If we look at file `rustc.main.-------.mir_map.0.mir`, we will find, among -other things, it also contains this line: +If we look at file `rustc.main.-------.mir_map.0.mir`, we will find, among other things, +it also contains this line: ```rust,ignore _4 = &_1; @@ -53,9 +54,9 @@ _3 = [closure@immut.rs:7:13: 7:36] { x: move _4 }; Note that in the MIR examples in this chapter, `_1` is `x`. -Here in first line `_4 = &_1;`, the `mir_dump` tells us that `x` was borrowed -as an immutable reference. This is what we would hope as our closure just -reads `x`. +Here in first line `_4 = &_1;`, +the `mir_dump` tells us that `x` was borrowed as an immutable reference. +This is what we would hope as our closure just reads `x`. ### Example 2 @@ -81,7 +82,8 @@ _4 = &mut _1; _3 = [closure@mut.rs:7:13: 10:6] { x: move _4 }; ``` This time along, in the line `_4 = &mut _1;`, we see that the borrow is changed to mutable borrow. -Fair enough! The closure increments `x` by 10. +Fair enough! +The closure increments `x` by 10. ### Example 3 @@ -104,33 +106,39 @@ fn main() { ```rust,ignore _6 = [closure@move.rs:7:13: 9:6] { x: move _1 }; // bb16[3]: scope 1 at move.rs:7:13: 9:6 ``` -Here, `x` is directly moved into the closure and the access to it will not be permitted after the -closure. +Here, `x` is directly moved into the closure, +and the access to it will not be permitted after the closure. ## Inferences in the compiler Now let's dive into rustc code and see how all these inferences are done by the compiler. Let's start with defining a term that we will be using quite a bit in the rest of the discussion - -*upvar*. An **upvar** is a variable that is local to the function where the closure is defined. So, -in the above examples, **x** will be an upvar to the closure. They are also sometimes referred to as -the *free variables* meaning they are not bound to the context of the closure. +*upvar*. +An **upvar** is a variable that is local to the function where the closure is defined. +So, in the above examples, **x** will be an upvar to the closure. +They are also sometimes referred to as the *free variables*, +meaning they are not bound to the context of the closure. [`compiler/rustc_passes/src/upvars.rs`][upvars] defines a query called *upvars_mentioned* for this purpose. [upvars]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_passes/upvars/index.html -Other than lazy invocation, one other thing that distinguishes a closure from a -normal function is that it can use the upvars. It borrows these upvars from its surrounding -context; therefore the compiler has to determine the upvar's borrow type. The compiler starts with -assigning an immutable borrow type and lowers the restriction (that is, changes it from -**immutable** to **mutable** to **move**) as needed, based on the usage. In the Example 1 above, the -closure only uses the variable for printing but does not modify it in any way and therefore, in the -`mir_dump`, we find the borrow type for the upvar `x` to be immutable. In example 2, however, the -closure modifies `x` and increments it by some value. Because of this mutation, the compiler, which +Other than lazy invocation, +one other thing that distinguishes a closure from a normal function is that it can use the upvars. +It borrows these upvars from its surrounding +context; therefore the compiler has to determine the upvar's borrow type. +The compiler starts with assigning an immutable borrow type and lowers the restriction +(that is, changes it from **immutable** to **mutable** to **move**) as needed, based on the usage. +In the Example 1 above, +the closure only uses the variable for printing but does not modify it in any way and therefore, +in the `mir_dump`, we find the borrow type for the upvar `x` to be immutable. +In example 2, however, the closure modifies `x` and increments it by some value. +Because of this mutation, the compiler, which started off assigning `x` as an immutable reference type, has to adjust it as a mutable reference. Likewise in the third example, the closure drops the vector and therefore this requires the variable -`x` to be moved into the closure. Depending on the borrow kind, the closure has to implement the +`x` to be moved into the closure. +Depending on the borrow kind, the closure has to implement the appropriate trait: `Fn` trait for immutable borrow, `FnMut` for mutable borrow, and `FnOnce` for move semantics. @@ -141,17 +149,17 @@ declared in the file [`compiler/rustc_middle/src/ty/mod.rs`][ty]. [upvar]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/upvar/index.html [ty]:https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/index.html -Before we go any further, let's discuss how we can examine the flow of control through the rustc -codebase. For closures specifically, set the `RUSTC_LOG` env variable as below and collect the -output in a file: +Before we go any further, +let's discuss how we can examine the flow of control through the rustc codebase. +For closures specifically, +set the `RUSTC_LOG` env variable as below and collect the output in a file: ```console > RUSTC_LOG=rustc_hir_typeck::upvar rustc +stage1 -Z dump-mir=all \ <.rs file to compile> 2> ``` -This uses the stage1 compiler and enables `debug!` logging for the -`rustc_hir_typeck::upvar` module. +This uses the stage1 compiler and enables `debug!` logging for the `rustc_hir_typeck::upvar` module. The other option is to step through the code using lldb or gdb. @@ -160,8 +168,8 @@ The other option is to step through the code using lldb or gdb. 1. `b upvar.rs:134` // Setting the breakpoint on a certain line in the upvar.rs file 2. `r` // Run the program until it hits the breakpoint -Let's start with [`upvar.rs`][upvar]. This file has something called -the [`euv::ExprUseVisitor`] which walks the source of the closure and +Let's start with [`upvar.rs`][upvar]. +This file has something called the [`euv::ExprUseVisitor`] which walks the source of the closure and invokes a callback for each upvar that is borrowed, mutated, or moved. [`euv::ExprUseVisitor`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/expr_use_visitor/struct.ExprUseVisitor.html @@ -176,32 +184,33 @@ fn main() { } ``` -In the above example, our visitor will be called twice, for the lines marked 1 and 2, once for a -shared borrow and another one for a mutable borrow. It will also tell us what was borrowed. +In the above example, our visitor will be called twice, for the lines marked 1 and 2, +once for a shared borrow and another one for a mutable borrow. +It will also tell us what was borrowed. -The callbacks are defined by implementing the [`Delegate`] trait. The -[`InferBorrowKind`][ibk] type implements `Delegate` and keeps a map that -records for each upvar which mode of capture was required. The modes of capture -can be `ByValue` (moved) or `ByRef` (borrowed). For `ByRef` borrows, the possible -[`BorrowKind`]s are `ImmBorrow`, `UniqueImmBorrow`, `MutBorrow` as defined in the +The callbacks are defined by implementing the [`Delegate`] trait. +The [`InferBorrowKind`][ibk] type implements `Delegate` and keeps a map that +records for each upvar which mode of capture was required. +The modes of capture can be `ByValue` (moved) or `ByRef` (borrowed). +For `ByRef` borrows, the possible [`BorrowKind`]s are `ImmBorrow`, +`UniqueImmBorrow`, `MutBorrow` as defined in the [`compiler/rustc_middle/src/ty/mod.rs`][middle_ty]. [`BorrowKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.BorrowKind.html [middle_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/index.html `Delegate` defines a few different methods (the different callbacks): -**consume** for *move* of a variable, **borrow** for a *borrow* of some kind -(shared or mutable), and **mutate** when we see an *assignment* of something. +**consume** for *move* of a variable, **borrow** for a *borrow* of some kind (shared or mutable), +and **mutate** when we see an *assignment* of something. All of these callbacks have a common argument *cmt* which stands for Category, -Mutability and Type and is defined in -[`compiler/rustc_hir_typeck/src/expr_use_visitor.rs`][cmt]. Borrowing from the code -comments, "`cmt` is a complete categorization of a value indicating where it +Mutability and Type and is defined in [`compiler/rustc_hir_typeck/src/expr_use_visitor.rs`][cmt]. +Borrowing from the code comments, "`cmt` is a complete categorization of a value indicating where it originated and how it is located, as well as the mutability of the memory in which the value is stored". Based on the callback (consume, borrow etc.), we -will call the relevant `adjust_upvar_borrow_kind_for_` and pass the -`cmt` along. Once the borrow type is adjusted, we store it in the table, which -basically says what borrows were made for each closure. +will call the relevant `adjust_upvar_borrow_kind_for_` and pass the `cmt` along. +Once the borrow type is adjusted, we store it in the table, +which basically says what borrows were made for each closure. ```rust,ignore self.tables diff --git a/src/doc/rustc-dev-guide/src/compiler-debugging.md b/src/doc/rustc-dev-guide/src/compiler-debugging.md index 79f2145afce1b..3c355c0d3b972 100644 --- a/src/doc/rustc-dev-guide/src/compiler-debugging.md +++ b/src/doc/rustc-dev-guide/src/compiler-debugging.md @@ -75,7 +75,7 @@ stack backtrace: 34: >::with 35: rustc::ty::context::TyCtxt::create_and_enter 36: rustc_driver::driver::compile_input - 37: rustc_driver::run_compiler + 37: rustc_driver::compiler_entrypoint ``` If you set `debug = true`, you will get line numbers for the stack trace. @@ -93,7 +93,7 @@ stack backtrace: 33: rustc_driver::driver::compile_input at /home/user/rust/compiler/rustc_driver/src/driver.rs:1010 at /home/user/rust/compiler/rustc_driver/src/driver.rs:212 - 34: rustc_driver::run_compiler + 34: rustc_driver::compiler_entrypoint at /home/user/rust/compiler/rustc_driver/src/lib.rs:253 ``` @@ -181,7 +181,7 @@ stack backtrace: 9: rustc_typeck::check::FnCtxt::select_obligations_where_possible at /home/user/rust/compiler/rustc_typeck/src/check/mod.rs:2192 (~~~ IRRELEVANT PART OF BACKTRACE REMOVED BY ME ~~~) - 36: rustc_driver::run_compiler + 36: rustc_driver::compiler_entrypoint at /home/user/rust/compiler/rustc_driver/src/lib.rs:253 ``` diff --git a/src/doc/rustc-dev-guide/src/const-generics.md b/src/doc/rustc-dev-guide/src/const-generics.md index 9e26a174d8cf6..2738782db9e48 100644 --- a/src/doc/rustc-dev-guide/src/const-generics.md +++ b/src/doc/rustc-dev-guide/src/const-generics.md @@ -5,7 +5,7 @@ Most of the kinds of `ty::Const` that exist have direct parallels to kinds of types that exist, for example `ConstKind::Param` is equivalent to `TyKind::Param`. The main interesting points here are: -- [`ConstKind::Unevaluated`], which is equivalent to `TyKind::Alias` and in the long term should be renamed (as well as introducing an `AliasConstKind` to parallel `ty::AliasKind`). +- [`ConstKind::Alias`], which is equivalent to `TyKind::Alias`. - [`ConstKind::Value`], which is the final value of a `ty::Const` after monomorphization. This is somewhat similar to fully concrete things like `TyKind::Str` or `TyKind::ADT`. @@ -27,7 +27,8 @@ struct Foo; type Alias = [u8; 1 + 1]; ``` -In this example we have a const argument of `1 + 1` (the array length) which is represented as an *anon const*. The desugaring would look something like: +In this example we have a const argument of `1 + 1` (the array length) which is represented as an *anon const*. +The desugaring would look something like: ```rust struct Foo; @@ -35,7 +36,7 @@ const ANON: usize = 1 + 1; type Alias = [u8; ANON]; ``` -Where the array length in `[u8; ANON]` isn't itself an anon const containing a usage of `ANON`, but a kind of "direct" usage of the `ANON` const item ([`ConstKind::Unevaluated`]). +Where the array length in `[u8; ANON]` isn't itself an anon const containing a usage of `ANON`, but a kind of "direct" usage of the `ANON` const item ([`ConstKind::Alias`]). Anon consts do not inherit any generic parameters of the item they are inside of: ```rust @@ -81,9 +82,11 @@ type Alias = [u8; ANON]; When we go through HIR ty lowering for the array type in `Alias`, we will lower the array length too, and feed `type_of(ANON) -> usize`. This will effectively set the type of the `ANON` const item during some later part of the compiler rather than when constructing the HIR. -After all of this desugaring has taken place the final representation in the type system (ie as a `ty::Const`) is a `ConstKind::Unevaluated` with the `DefId` of the `AnonConst`. This is equivalent to how we would representa a usage of an actual const item if we were to represent them without going through an anon const (e.g. when `min_generic_const_args` is enabled). +After all of this desugaring has taken place the final representation in the type system (ie as a `ty::Const`) is a `ConstKind::Alias` with the `DefId` of the `AnonConst`. +This is equivalent to how we would represent a usage of an actual const item if we were to represent them without going through an anon const (e.g. when `gca_generic_const_args` is enabled). -This allows the representation for const "aliases" to be the same as the representation of `TyKind::Alias`. Having a proper HIR body also allows for a *lot* of code re-use, e.g. we can reuse HIR typechecking and all of the lowering steps to MIR where we can then reuse const eval. +This allows the representation for const "aliases" to be the same as the representation of `TyKind::Alias`. +Having a proper HIR body also allows for a *lot* of code re-use, e.g. we can reuse HIR typechecking and all of the lowering steps to MIR where we can then reuse const eval. ### Enforcing lack of generic parameters @@ -141,7 +144,7 @@ In some sense we only allow generic parameters here when they are semantically u In the previous example the anon const can be evaluated for any type parameter `T` because raw pointers to sized types always have the same size (e.g. `8` on 64bit platforms). When detecting that we evaluated an anon const that syntactically contained generic parameters, but did not actually depend on them for evaluation to succeed, we emit the [`const_evaluatable_unchecked` FCW][cec_fcw]. -This is intended to become a hard error once we stabilize more ways of using generic parameters in const arguments, for example `min_generic_const_args` or (the now dead) `generic_const_exprs`. +This is intended to become a hard error once we stabilize more ways of using generic parameters in const arguments, for example `gca_min_const_items` or (the now dead) `generic_const_exprs`. The implementation for this FCW can be found here: [`const_eval_resolve_for_typeck`] @@ -182,7 +185,7 @@ It is currently unclear what the right way to make `generic_const_parameter_type `generic_const_exprs` would have allowed for anon consts with types referencing generic parameters, but that design wound up unworkable. -`min_generic_const_args` will allow for some expressions (for example array construction) to be representable without an anon const and therefore without running into these issues, though whether this is *enough* has yet to be determined. +`gca_min_const_items` will allow for some expressions (for example array construction) to be representable without an anon const and therefore without running into these issues, though whether this is *enough* has yet to be determined. ## Checking types of const arguments @@ -215,7 +218,7 @@ Proving `ConstArgHasType` goals is implemented by first computing the type of th A rough outline of how the type of a Const Argument may be computed: - [`ConstKind::Param(N)`][`ConstKind::Param`] can be looked up in the [`ParamEnv`] to find a `ConstArgHasType(N, ty)` clause - [`ConstKind::Value`] stores the type of the value inside itself so can trivially be accessed -- [`ConstKind::Unevaluated`] can have its type computed by calling the `type_of` query +- [`ConstKind::Alias`] can have its type computed by calling the `type_of` query - See the implementation of proving `ConstArgHasType` goals for more detailed information `ConstArgHasType` is *the* soundness critical way that we check Const Arguments have the correct type. @@ -238,11 +241,11 @@ what actually happens is a *type checking* error when type checking the anon con Looking at the above example, this corresponds to `[u8; ANON]` being a well formed type because `ANON` has type `usize`, but the *body* of `ANON` being illformed and resulting in a type checking error because `true` can't be returned from a const item of type `usize`. [ambig-unambig-ty-and-consts]: ./ambig-unambig-ty-and-consts.md -[`ConstKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.ConstKind.html -[`ConstKind::Infer`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.ConstKind.html#variant.Infer -[`ConstKind::Param`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.ConstKind.html#variant.Param -[`ConstKind::Unevaluated`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.ConstKind.html#variant.Unevaluated -[`ConstKind::Value`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.ConstKind.html#variant.Value +[`ConstKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/consts/type.ConstKind.html +[`ConstKind::Infer`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/consts/type.ConstKind.html#variant.Infer +[`ConstKind::Param`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/consts/type.ConstKind.html#variant.Param +[`ConstKind::Alias`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/consts/type.ConstKind.html#variant.Alias +[`ConstKind::Value`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/consts/type.ConstKind.html#variant.Value [const_arg_has_type]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.ClauseKind.html#variant.ConstArgHasType [`ParamEnv`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html [`generics_of`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#impl-TyCtxt%3C'tcx%3E/method.generics_of diff --git a/src/doc/rustc-dev-guide/src/debuginfo/llvm-codegen.md b/src/doc/rustc-dev-guide/src/debuginfo/llvm-codegen.md index 4f5bdaa1e6795..1cdc7c0929142 100644 --- a/src/doc/rustc-dev-guide/src/debuginfo/llvm-codegen.md +++ b/src/doc/rustc-dev-guide/src/debuginfo/llvm-codegen.md @@ -1,13 +1,13 @@ # LLVM codegen -When Rust calls an LLVM `DIBuilder` function, LLVM translates the given information to a -["debug record"][dbg_record] that is format-agnostic. +When Rust calls an LLVM `DIBuilder` function, +LLVM translates the given information to a ["debug record"][dbg_record] that is format-agnostic. These records can be inspected in the LLVM-IR. [dbg_record]: https://llvm.org/docs/SourceLevelDebugging.html#debug-records -It is important to note that tags within the debug records are **always stored as DWARF tags**. If -the target calls for PDB debug info, during codegen the debug records will then be passed through +It is important to note that tags within the debug records are **always stored as DWARF tags**. +If the target calls for PDB debug info, during codegen, the debug records will then be passed through [a module that translates the DWARF tags to their CodeView counterparts][cv]. [cv]:https://github.com/llvm/llvm-project/blob/main/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp diff --git a/src/doc/rustc-dev-guide/src/debuginfo/testing.md b/src/doc/rustc-dev-guide/src/debuginfo/testing.md index bcbe960034b02..5b80c42afc3de 100644 --- a/src/doc/rustc-dev-guide/src/debuginfo/testing.md +++ b/src/doc/rustc-dev-guide/src/debuginfo/testing.md @@ -36,9 +36,8 @@ To help remedy this: # The `repr` directive > [!IMPORTANT] -> As of July 2026, this command is only supported by LLDB. GDB support is planned, but -> has not been implemented. It is unclear whether this directive will ever be suited for use with -> CDB. +> As of September 2026, this command is only supported for LLDB and GDB. +> It is unclear whether this directive will ever be suited for use with CDB. In short, `$DEBUGGER-repr` commands are desugared to: @@ -53,7 +52,7 @@ and runs special logic on them, testing against data stored in "Target groups" cover the set of targets where we cannot guarantee identical output. Those targets are defined by the - [`Target` enum in `common.py`](https://github.com/rust-lang/rust/blob/bf9944f0b8006b152ef4d5f408ae75a0dde3d044/src/etc/lldb_batchmode/common.py#L54). + [`Target` enum in `common.py`](https://github.com/rust-lang/rust/blob/c26ce708de5d14682647895d2f3caf38f70b5aa6/src/etc/debugger_tester/common.py#L54). As of Jul 2026, this list includes `non_windows`, `windows_gnu`, and `windows_msvc`. It is intentionally kept as short as possible, since each target is a new set of test data that must be updated when changes are made. @@ -66,7 +65,7 @@ invocation (e.g. `./x test tests/debuginfo/basic-types/main.rs --bless`). and if no errors occur, saves the data back to the target file (or creates a new file if necessary). The schema of the input data is defined by the classes in -[`common.py`](https://github.com/rust-lang/rust/blob/be3d26db984c6f96335faca1f254dc04873cb1c1/src/etc/lldb_batchmode/common.py). +[`common.py`](https://github.com/rust-lang/rust/blob/c26ce708de5d14682647895d2f3caf38f70b5aa6/src/etc/debugger_tester/common.py). The top-level container is `TargetData`. This schema is identical for all debuggers. @@ -98,6 +97,13 @@ as well (e.g. if you are on a Windows machine, bless once for `x86_64-pc-windows ## Implementation +Many of the details of the implementation are shared between LLDB and GDB, +and live in `src/etc/debugger_tester/common.py`. +Notably, LLDB's batch execution features execute commands without waiting for breakpoints to be reached, +making it generally unsuited for our purposes. +As such, `debugger_tester/lldb/lldb_batchmode.py` simulates GDB's batchmode behavior by manually +invoking the test script's commands via `lldb.SBDebugger` and `lldb.SBCommandInterpreter` objects. + ### Ser/De `TargetData` is converted to a dictionary with `dataclasses.asdict`, and is serialized with Python's @@ -108,7 +114,7 @@ The current deserialization logic should be resilient to changes in the schema, but requires that all fields contain ONLY types that can be directly serialized/deserialized by `json.dumps`. The acceptable types are those that make up -[`common.JsonType`](https://github.com/rust-lang/rust/blob/bf9944f0b8006b152ef4d5f408ae75a0dde3d044/src/etc/lldb_batchmode/common.py#L17) +[`common.JsonType`](https://github.com/rust-lang/rust/blob/c26ce708de5d14682647895d2f3caf38f70b5aa6/src/etc/debugger_tester/common.py#L17) Since the serialization/deserialization is decoupled from the debugger logic, we can easily switch to an alternative format if we find a better alternative to json. @@ -117,11 +123,11 @@ The conversion logic from the debugger's internal representation to our schema c `from_$DEBUGGER.py`. Once imported, `common` automatically deserializes any existing input data and [stores it in the -global variable `INPUT_DATA`](https://github.com/rust-lang/rust/blob/bf9944f0b8006b152ef4d5f408ae75a0dde3d044/src/etc/lldb_batchmode/common.py#L523). +global variable `INPUT_DATA`](https://github.com/rust-lang/rust/blob/c26ce708de5d14682647895d2f3caf38f70b5aa6/src/etc/debugger_tester/common.py#L601). This data is what we test against. > [!NOTE] -> Special care was taken to prevent `lldb_batchmode` from importing `common` unless a `repr` command +> Special care was taken to prevent `debugger_tester` from importing `common` unless a `repr` command > was actually processed. This saves us from reading/writing input data for tests that don't need > it. @@ -140,18 +146,27 @@ to help in diagnosing issues that may occur due to Python or the debugger changi ### Entry point and `--bless` -Upon encountering a `repr` pseudo-command, `lldb_batchmode.main` dispatches to -`check_$DEBUGGER.check()`. +* GDB: Upon import, the `repr` command is registered via the Python API in +[`debugger_tester/gdb/gdb_commands.py](https://github.com/rust-lang/rust/blob/c26ce708de5d14682647895d2f3caf38f70b5aa6/src/etc/debugger_tester/gdb/gdb_commands.py). +`ReprCommand.invoke` initiates the check logic with the given variable, and handles top-level error reporting. + +* LLDB: Upon encountering a `repr` pseudo-command, `lldb_batchmode.main` dispatches to `check_lldb.check()`. + +> [!Note] +> LLDB also supports registering +> [custom CLI commands via the Python API](https://lldb.llvm.org/use/tutorials/writing-custom-commands.html). +> In the future, LLDB's `repr` logic may be refactored to behave more similarly to GDB's. + If the `--bless` option was specified, the variable is converted from the in-memory representation to our equivalent schema class. This includes the variable's type, visualizers, children, the children's types, etc. Once inserted into `TargetData`, -the variable is tested against the data that was just saved to `TargetData`. +the debugger's variable object is sanity-tested against the data that was just added to `TargetData`. If no exception or errors occurred and the `--bless` option was specified, `INPUT_DATA` is written -to the appropriate file just before `lldb_batchmode` exits. -If errors occur, `INPUT_DATA` is simply discarded. +to the appropriate file just before `debugger_tester` exits. +If errors occur, the blessed data is simply discarded. Currently, the `repr` pseudo-command is checked for directly. GDB and LLDB both support creating custom CLI commands via Python code. @@ -170,7 +185,7 @@ to provide more useful error messages. For example, LLDB can be a bit coy when it comes to reporting errors that occur within synthetic/summary provider calls. This is especially true when running the command within another -command, as the tests do by calling `script import lldb_batchmode; lldb_batchmode.main()` and +command, as the tests do by calling `script import debugger_tester; debugger_tester.main()` and executing commands in that context. When we encounter an error, we can import the appropriate summary provider, pass the variable object @@ -186,7 +201,7 @@ We absolutely do not want people accidentally blessing bad data purely because the first error happened to be an expected change. Errors are printed directly to `stdout` to appear as visible output from the `repr` pseudo command. -There are [several error helper functions](https://github.com/rust-lang/rust/blob/e7b595554e664e6bd281c8cf881093d6c71bc0e1/src/etc/lldb_batchmode/common.py#L35-L51) +There are [several error helper functions](https://github.com/rust-lang/rust/blob/c26ce708de5d14682647895d2f3caf38f70b5aa6/src/etc/debugger_tester/common.py#L35-L51) to keep formatting consistent. > [!NOTE] @@ -198,7 +213,7 @@ to keep formatting consistent. If no errors occurred for a given variable, `$VAR_NAME ok` is printed to `stdout` for `compiletest` to match against. -Before `lldb_batchmode` exits, one last check is done to ensure that all the types and variables +Before `debugger_tester` exits, one last check is done to ensure that all the types and variables that were present in `INPUT_DATA` have been checked against. If this check fails, the script reports the untested types/variables and exits with an error code. diff --git a/src/doc/rustc-dev-guide/src/diagnostics.md b/src/doc/rustc-dev-guide/src/diagnostics.md index 0928f27940885..661e07af7189c 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics.md +++ b/src/doc/rustc-dev-guide/src/diagnostics.md @@ -41,15 +41,15 @@ LL | more code - Primary and secondary spans underlying the users' code. These spans can optionally contain one or more labels. - Primary spans should have enough text to describe the problem in such a - way that if it were the only thing being displayed (for example, in an - IDE) it would still make sense. - Because it is "spatially aware" (it - points at the code), it can generally be more succinct than the error message. - - If cluttered output can be foreseen in cases when multiple span labels - overlap, it is a good idea to tweak the output appropriately. + way that if it were the only thing being displayed (for example, + in an IDE) it would still make sense. + Because it is "spatially aware" (it points at the code), + it can generally be more succinct than the error message. + - If cluttered output can be foreseen in cases when multiple span labels overlap, + it is a good idea to tweak the output appropriately. For example, the `if/else arms have incompatible types` error uses different - spans depending on whether the arms are all in the same line, if one of - the arms is empty and if none of those cases applies. + spans depending on whether the arms are all in the same line, + if one of the arms is empty, and if none of those cases applies. - Sub-diagnostics. Any error can have multiple sub-diagnostics that look similar to the main part of the error. These are used for cases where the @@ -57,15 +57,15 @@ LL | more code If the order of the explanation can be "order free", leveraging secondary labels in the main diagnostic is preferred, as it is typically less verbose. -The text should be matter of fact and avoid capitalization and periods, unless -multiple sentences are _needed_: +The text should be matter of fact and avoid capitalization and periods, +unless multiple sentences are _needed_: ```txt error: the fobrulator needs to be krontrificated ``` -When code or an identifier must appear in a message or label, it should be -surrounded with backticks: +When code or an identifier must appear in a message or label, +it should be surrounded with backticks: ```txt error: the identifier `foo.bar` is invalid @@ -84,12 +84,12 @@ explanation would give more information than the error itself. A lot of the time it's better to put all the information in the emitted error itself. However, sometimes that would make the error verbose or there are too many possible -triggers to include useful information for all cases in the error, in which case -it's a good idea to add an explanation.[^estebank] +triggers to include useful information for all cases in the error, +in which case it's a good idea to add an explanation.[^estebank] As always, if you are not sure, just ask your reviewer! -If you decide to add a new error with an associated error code, please read -[this section][error-codes] for a guide and important details about the process. +If you decide to add a new error with an associated error code, +please read [this section][error-codes] for a guide and important details about the process. [^estebank]: This rule of thumb was suggested by **@estebank** [here][estebank-comment]. @@ -102,23 +102,23 @@ If you decide to add a new error with an associated error code, please read Some messages are emitted via [lints](#lints), where the user can control the level. Most diagnostics are hard-coded such that the user cannot control the level. -Usually it is obvious whether a diagnostic should be "fixed" or a lint, but -there are some grey areas. +Usually it is obvious whether a diagnostic should be "fixed" or a lint, +but there are some grey areas. Here are a few examples: - Borrow checker errors: these are fixed errors. The user cannot adjust the level of these diagnostics to silence the borrow checker. - Dead code: this is a lint. - While the user probably doesn't want dead code in - their crate, making this a hard error would make refactoring and development very painful. + While the user probably doesn't want dead code in their crate, + making this a hard error would make refactoring and development very painful. - [future-incompatible lints]: these are silenceable lints. It was decided that making them fixed errors would cause too much breakage, so warnings are instead emitted, and will eventually be turned into fixed (hard) errors. -Hard-coded warnings (those using methods like `span_warn`) should be avoided -for normal code, preferring to use lints instead. +Hard-coded warnings (those using methods like `span_warn`) should be avoided for normal code, +preferring to use lints instead. Some cases, such as warnings with CLI flags, will require the use of hard-coded warnings. See the `deny` [lint level](#diagnostic-levels) below for guidelines when to @@ -133,11 +133,11 @@ use an error-level lint instead of a fixed error. small – screen (which hasn't been cleaned for a while), cannot be understood by a normal programmer, who just came out of bed after a night partying, it's too complex. -- `Error`, `Warning`, `Note`, and `Help` messages start with a lowercase - letter and do not end with punctuation. +- `Error`, `Warning`, `Note`, + and `Help` messages start with a lowercase letter and do not end with punctuation. - Error messages should be succinct. - Users will see these error messages many - times, and more verbose descriptions can be viewed with the `--explain` flag. + Users will see these error messages many times, + and more verbose descriptions can be viewed with the `--explain` flag. That said, don't make it so terse that it's hard to understand. - The word "illegal" is illegal. Prefer "invalid" or a more specific word instead. @@ -145,8 +145,8 @@ use an error-level lint instead of a fixed error. [`rustc_errors::DiagCtxt`][DiagCtxt]'s `span_*` methods or a diagnostic struct's `#[primary_span]` to easily do this). Also `note` other spans that have contributed to the error if the span isn't too large. -- When emitting a message with span, try to reduce the span to the smallest - amount possible that still signifies the issue +- When emitting a message with span, + try to reduce the span to the smallest amount possible that still signifies the issue - Try not to emit multiple error messages for the same error. This may require detecting duplicates. - When the compiler has too little information for a specific error message, @@ -154,8 +154,8 @@ use an error-level lint instead of a fixed error. allow adding more information. For example, see [`#[rustc_on_unimplemented]`](#rustc_on_unimplemented). Use these annotations when available! -- Keep in mind that Rust's learning curve is rather steep, and that the - compiler messages are an important learning tool. +- Keep in mind that Rust's learning curve is rather steep, + and that the compiler messages are an important learning tool. - When talking about the compiler, call it `the compiler`, not `Rust` or `rustc`. - Use the [Oxford comma](https://en.wikipedia.org/wiki/Serial_comma) when writing lists of items. - When mentioning attributes, use this form whenever possible: "the `inline` attribute". @@ -168,8 +168,8 @@ From [RFC 0344], lint names should be consistent, with the following guidelines: The basic rule is: the lint name should make sense when read as "allow *lint-name*" or "allow *lint-name* items". -For example, "allow `deprecated` items" and "allow `dead_code`" makes sense, while "allow -`unsafe_block`" is ungrammatical (should be plural). +For example, "allow `deprecated` items" and "allow `dead_code`" makes sense, +while "allow `unsafe_block`" is ungrammatical (should be plural). - Lint names should state the bad thing being checked for, e.g. `deprecated`, so that `#[allow(deprecated)]` (items) reads correctly. @@ -180,8 +180,8 @@ For example, "allow `deprecated` items" and "allow `dead_code`" makes sense, whi This keeps lint names short. (Again, think "allow *lint-name* items".) -- If a lint applies to a specific grammatical class, mention that class and - use the plural form: use `unused_variables` rather than `unused_variable`. +- If a lint applies to a specific grammatical class, + mention that class and use the plural form: use `unused_variables` rather than `unused_variable`. This makes `#[allow(unused_variables)]` read correctly. - Lints that catch unnecessary, unused, or useless aspects of code should use @@ -200,12 +200,12 @@ Guidelines for different diagnostic levels: has decided to make a specific `warning` into an error. - `warning`: emitted when the compiler detects something odd about a program. - Care should be taken when adding warnings to avoid warning fatigue, and - avoid false-positives where there really isn't a problem with the code. + Care should be taken when adding warnings to avoid warning fatigue, + and avoid false-positives where there really isn't a problem with the code. Some examples of when it is appropriate to issue a warning: - - A situation where the user *should* take action, such as swap out a - deprecated item, or use a `Result`, but otherwise doesn't prevent compilation. + - A situation where the user *should* take action, such as swap out a deprecated item, + or use a `Result`, but otherwise doesn't prevent compilation. - Unnecessary syntax that can be removed without affecting the semantics of the code. For example, unused code, or unnecessary `unsafe`. - Code that is very likely to be incorrect, dangerous, or confusing, but the @@ -214,13 +214,13 @@ Guidelines for different diagnostic levels: `bindings_with_variant_name` (the user likely did not intend to create a binding in a pattern). - [Future-incompatible lints](#future-incompatible), where something was - accidentally or erroneously accepted in the past, but rejecting would - cause excessive breakage in the ecosystem. + accidentally or erroneously accepted in the past, + but rejecting would cause excessive breakage in the ecosystem. - Stylistic choices. For example, camel or snake case, or the `dyn` trait warning in the 2018 edition. These have a high bar to be added, and should only be used in exceptional circumstances. - Other stylistic choices should - either be allow-by-default lints, or part of other tools like Clippy or rustfmt. + Other stylistic choices should either be allow-by-default lints, + or part of other tools like Clippy or rustfmt. - `help`: emitted following an `error` or `warning` to give additional information to the user about how to solve their problem. @@ -247,8 +247,8 @@ Not to be confused with *lint levels*, whose guidelines are: Some examples: - A future-incompatible or edition-based lint that has graduated from the warning level. - - Something that has an extremely high confidence that is incorrect, but - still want an escape hatch to allow it to pass. + - Something that has an extremely high confidence that is incorrect, + but still want an escape hatch to allow it to pass. - `warn`: Equivalent to the `warning` diagnostic level. See `warning` above for guidelines. @@ -279,8 +279,8 @@ There are three main ways to find where a given error is emitted: constructed behind a relatively deep call-stack. Even then, it is a good way to get your bearings. - Invoking `rustc` with the nightly-only flag `-Z treat-err-as-bug=1` - will treat the first error being emitted as an Internal Compiler Error, which - allows you to get a stack trace at the point the error has been emitted. + will treat the first error being emitted as an Internal Compiler Error, + which allows you to get a stack trace at the point the error has been emitted. Change the `1` to something else if you wish to trigger on a later error. There are limitations with this approach: @@ -299,8 +299,8 @@ order things are happening. [`Span`][span] is the primary data structure in `rustc` used to represent a location in the code being compiled. -`Span`s are attached to most constructs in -HIR and MIR, allowing for more informative error reporting. +`Span`s are attached to most constructs in HIR and MIR, +allowing for more informative error reporting. [span]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html @@ -320,8 +320,8 @@ The [`rustc_errors`][errors] crate defines most of the utilities used for report Diagnostics can be implemented as types which implement the `Diagnostic` trait. This is preferred for new diagnostics as it enforces a separation between diagnostic emitting logic and the main code paths. -For less-complex diagnostics, the `Diagnostic` trait can be derived -- see [Diagnostic -structs][diagnostic-structs]. +For less-complex diagnostics, +the `Diagnostic` trait can be derived -- see [Diagnostic structs][diagnostic-structs]. Within the trait implementation, the APIs described below can be used as normal. [diagnostic-structs]: ./diagnostics/diagnostic-structs.md @@ -337,12 +337,11 @@ warnings, errors, fatal errors, suggestions, etc. In general, there are two classes of such methods: ones that emit an error directly and ones that allow finer control over what to emit. For example, -[`span_err`][spanerr] emits the given error message at the given `Span`, but -[`struct_span_err`][strspanerr] instead returns a [`Diag`][diag]. +[`span_err`][spanerr] emits the given error message at the given `Span`, +but [`struct_span_err`][strspanerr] instead returns a [`Diag`][diag]. Most of these methods will accept strings, but it is recommended that typed -identifiers for translatable diagnostics be used for new diagnostics (see -[Translation]). +identifiers for translatable diagnostics be used for new diagnostics (see [Translation]). [translation]: ./diagnostics/translation.md @@ -385,8 +384,8 @@ example-example-error = oh no! this is an error! ## Suggestions -In addition to telling the user exactly _why_ their code is wrong, it's -oftentimes furthermore possible to tell them how to fix it. +In addition to telling the user exactly _why_ their code is wrong, +it's oftentimes furthermore possible to tell them how to fix it. To this end, [`Diag`][diag] offers a structured suggestions API, which formats code suggestions pleasingly in the terminal, or (when the `--error-format json` flag @@ -398,8 +397,7 @@ Not all suggestions should be applied mechanically; they have a degree of confidence in the suggested code, from high (`Applicability::MachineApplicable`) to low (`Applicability::MaybeIncorrect`). Be conservative when choosing the level. -Use the [`span_suggestion`][span_suggestion] method of `Diag` to -make a suggestion. +Use the [`span_suggestion`][span_suggestion] method of `Diag` to make a suggestion. The last argument provides a hint to tools whether the suggestion is mechanically applicable or not. Suggestions point to one or more spans with corresponding code that will @@ -515,8 +513,8 @@ Some of the passes are: - Example: [`keyword_idents`] checks for identifiers that will become keywords in future editions, but is sensitive to identifiers used in macros. -- Early lint pass: Works on [AST nodes] after [macro expansion] and name - resolution, just before [AST lowering]. +- Early lint pass: Works on [AST nodes] after [macro expansion] and name resolution, + just before [AST lowering]. These lints are for purely syntactical lints. - Example: The [`unused_parens`] lint checks for parenthesized-expressions in situations where they are not needed, like an `if` condition. @@ -534,11 +532,11 @@ Some of the passes are: - Example: The [`arithmetic_overflow`] lint is emitted when it detects a constant value that may overflow. -Most lints work well via the pass systems, and they have a fairly -straightforward interface and easy way to integrate (mostly just implementing +Most lints work well via the pass systems, +and they have a fairly straightforward interface and easy way to integrate (mostly just implementing a specific `check` function). -However, some lints are easier to write when -they live on a specific code path anywhere in the compiler. +However, +some lints are easier to write when they live on a specific code path anywhere in the compiler. For example, the [`unused_mut`] lint is implemented in the borrow checker as it requires some information and state in the borrow checker. @@ -589,8 +587,8 @@ One benefit is that it is close to the dependency root, so it can be much faster [`rustc_lint_defs`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint_defs/index.html Every lint is implemented via a `struct` that implements the `LintPass` `trait` -(you can also implement one of the more specific lint pass traits, either -`EarlyLintPass` or `LateLintPass` depending on when is best for your lint to run). +(you can also implement one of the more specific lint pass traits, +either `EarlyLintPass` or `LateLintPass`, depending on when is best for your lint to run). The trait implementation allows you to check certain syntactic constructs as the linter walks the AST. You can then choose to emit lints in a very similar way to compile errors. @@ -700,24 +698,25 @@ declare_lint! { } ``` -### Future-incompatible lints +### future-incompatible lints The use of the term `future-incompatible` within the compiler has a slightly broader meaning than what rustc exposes to users of the compiler. -Inside rustc, future-incompatible lints are for signalling to the user that code they have -written may not compile in the future. +Inside rustc, +future-incompatible lints are for signalling to the user their code may not compile in the future. In general, future-incompatible code exists for two reasons: -* The user has written unsound code that the compiler mistakenly accepted. +* The code is unsound, and the compiler mistakenly accepted it. While it is within Rust's backwards compatibility guarantees to fix the soundness hole -(breaking the user's code), the lint is there to warn the user that this will happen -in some upcoming version of rustc *regardless of which edition the code uses*. This is the -meaning that rustc exclusively exposes to users as "future incompatible". -* The user has written code that will either no longer compiler *or* will change -meaning in an upcoming *edition*. These are often called "edition lints" and can be -typically seen in the various "edition compatibility" lint groups (e.g., `rust_2021_compatibility`) -that are used to lint against code that will break if the user updates the crate's edition. -See [migration lints](guides/editions.md#migration-lints) for more details. + (breaking the user's code), the lint is there to warn the user that this will happen + in some upcoming version of rustc *regardless of which edition the code uses*. + This is the meaning that rustc exclusively exposes to users as "future incompatible". +* The code will either no longer compile *or* will change + meaning in an upcoming *edition*. + These are often called "edition lints" and can be + typically seen in the various "edition compatibility" lint groups (e.g., `rust_2021_compatibility`) + that are used to lint against code that will break if the user updates the crate's edition. + See [migration lints](guides/editions.md#migration-lints) for more details. A future-incompatible lint should be declared with the `@future_incompatible` additional "field": @@ -735,8 +734,8 @@ declare_lint! { Notice the `reason` field which describes why the future incompatible change is happening. This will change the diagnostic message the user receives as well as determine which lint groups the lint is added to. -In the example above, the lint is an "edition lint" -(since its "reason" is `EditionError`), signifying to the user that the use of anonymous +In the example above, the lint is an "edition lint" (since its "reason" is `EditionError`), +signifying to the user that the use of anonymous parameters will no longer compile in Rust 2018 and beyond. Inside [LintStore::register_lints][fi-lint-groupings], lints with `future_incompatible` @@ -745,16 +744,16 @@ an edition) or into the `future_incompatibility` lint group. [fi-lint-groupings]: https://github.com/rust-lang/rust/blob/51fd129ac12d5bfeca7d216c47b0e337bf13e0c2/compiler/rustc_lint/src/context.rs#L212-L237 -If you need a combination of options that's not supported by the -`declare_lint!` macro, you can always change the `declare_lint!` macro to support this. +If you need a combination of options that's not supported by the `declare_lint!` macro, +you can always change the `declare_lint!` macro to support this. ### Renaming or removing a lint If it is determined that a lint is either improperly named or no longer needed, -the lint must be registered for renaming or removal, which will trigger a warning if a user tries -to use the old lint name. -To declare a rename/remove, add a line with -[`store.register_renamed`] or [`store.register_removed`] to the code of the +the lint must be registered for renaming or removal, +which will trigger a warning if a user tries to use the old lint name. +To declare a rename/remove, +add a line with [`store.register_renamed`] or [`store.register_removed`] to the code of the [`rustc_lint::register_builtins`] function. ```rust,ignore @@ -785,15 +784,15 @@ add_lint_group!(sess, ``` This defines the `nonstandard_style` group which turns on the listed lints. -A user can turn on these lints with a `#![warn(nonstandard_style)]` attribute in -the source code, or by passing `-W nonstandard-style` on the command line. +A user can turn on these lints with a `#![warn(nonstandard_style)]` attribute in the source code, +or by passing `-W nonstandard-style` on the command line. Some lint groups are created automatically in `LintStore::register_lints`. For instance, any lint declared with `FutureIncompatibleInfo` where the reason is `FutureIncompatibilityReason::FutureReleaseError` (the default when -`@future_incompatible` is used in `declare_lint!`), will be added to -the `future_incompatible` lint group. +`@future_incompatible` is used in `declare_lint!`), +will be added to the `future_incompatible` lint group. Editions also have their own lint groups (e.g., `rust_2021_compatibility`) automatically generated for any lints signaling future-incompatible code that will break in the specified edition. @@ -813,15 +812,15 @@ The linting system automatically takes care of handling buffered lints later. [sessbl]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/struct.Session.html#method.buffer_lint [parsebl]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/parse/struct.ParseSess.html#method.buffer_lint -Thus, to define a lint that runs early in the compilation, one defines a lint -like normal but invokes the lint with `buffer_lint`. +Thus, to define a lint that runs early in the compilation, +one defines a lint like normal but invokes the lint with `buffer_lint`. #### Linting even earlier in the compiler The parser (`rustc_ast`) is interesting in that it cannot have dependencies on any of the other `rustc*` crates. -In particular, it cannot depend on -`rustc_middle::lint` or `rustc_lint`, where all of the compiler linting infrastructure is defined. +In particular, it cannot depend on `rustc_middle::lint` or `rustc_lint`, +where all of the compiler linting infrastructure is defined. That's troublesome! To solve this, `rustc_ast` defines its own buffered lint type, which `ParseSess::buffer_lint` uses. @@ -841,20 +840,20 @@ $ rustc json_error_demo.rs --error-format json {"message":"For more information about this error, try `rustc --explain E0277`.","code":null,"level":"","spans":[],"children":[],"rendered":"For more information about this error, try `rustc --explain E0277`.\n"} ``` -Note that the output is a series of lines, each of which is a JSON -object, but the series of lines taken together is, unfortunately, not +Note that the output is a series of lines, each of which is a JSON object, +but the series of lines taken together is, unfortunately, not valid JSON, thwarting tools and tricks (such as [piping to `python3 -m json.tool`](https://docs.python.org/3/library/json.html#module-json.tool)) that require such. -(One speculates that this was intentional for LSP -performance purposes, so that each line/object can be sent as it is flushed?) +(One speculates that this was intentional for LSP performance purposes, +so that each line/object can be sent as it is flushed?) Also note the "rendered" field, which contains the "human" output as a string; this was introduced so that UI tests could both make use of -the structured JSON and see the "human" output (well, _sans_ colors) -without having to compile everything twice. +the structured JSON and see the "human" output (well, +_sans_ colors) without having to compile everything twice. -The "human" readable and the json format emitter can be found under -`rustc_errors`, both were moved from the `rustc_ast` crate to the +The "human" readable and the json format emitter can be found under `rustc_errors`, +both were moved from the `rustc_ast` crate to the [rustc_errors crate](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/index.html). The JSON emitter defines [its own `Diagnostic` @@ -937,16 +936,16 @@ If you can, you should use that instead. ### Filtering -To allow more targeted error messages, it is possible to filter the -application of these fields with `on`. +To allow more targeted error messages, +it is possible to filter the application of these fields with `on`. You can filter on the following boolean flags: - `crate_local`: whether the code causing the trait bound to not be fulfilled is part of the user's crate. This is used to avoid suggesting code changes that would require modifying a dependency. - `direct`: whether this is a user-specified rather than derived obligation. - - `from_desugaring`: whether we are in some kind of desugaring, like `?` - or a `try` block for example. + - `from_desugaring`: whether we are in some kind of desugaring, + like `?` or a `try` block for example. This flag can also be matched on, see below. You can match on the following names and values, using `name = "value"`: @@ -955,8 +954,8 @@ You can match on the following names and values, using `name = "value"`: - `from_desugaring`: Match against a particular variant of the `DesugaringKind` enum. The desugaring is identified by its variant name, for example `"QuestionMark"` for `?` desugaring, or `"TryBlock"` for `try` blocks. - - `Self` and any generic arguments of the trait, like `Self = "alloc::string::String"` - or `Rhs="i32"`. + - `Self` and any generic arguments of the trait, + like `Self = "alloc::string::String"` or `Rhs="i32"`. The compiler can provide several values to match on, for example: - the self_ty, pretty printed with and without type arguments resolved. diff --git a/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-structs.md b/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-structs.md index 7ca97b7b059e4..f8df01ba5e1cb 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-structs.md +++ b/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-structs.md @@ -93,8 +93,8 @@ In the end, the `Diagnostic` derive will generate an implementation of `Diagnostic` that looks like the following: ```rust,ignore -impl<'a, G> Diagnostic<'a> for FieldAlreadyDeclared { - fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G> { +impl<'a> Diagnostic<'a> for FieldAlreadyDeclared { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let mut diag = Diag::new(dcx, level, "field `{$field_name}` is already declared"); diag.set_span(self.span); diag.span_label( diff --git a/src/doc/rustc-dev-guide/src/effects.md b/src/doc/rustc-dev-guide/src/effects.md index 9c54705e000fd..f6a11b435ad32 100644 --- a/src/doc/rustc-dev-guide/src/effects.md +++ b/src/doc/rustc-dev-guide/src/effects.md @@ -3,20 +3,20 @@ ## The `HostEffect` predicate [`HostEffectPredicate`]s are a kind of predicate from `[const] Tr` or `const Tr` bounds. -It has a trait reference, and a `constness` which could be `Maybe` or -`Const` depending on the bound. +It has a trait reference, +and a `constness` which could be `Maybe` or `Const`, depending on the bound. Because `[const] Tr`, or rather `Maybe` bounds -apply differently based on whichever contexts they are in, they have different -behavior than normal bounds. +apply differently based on whichever contexts they are in, +they have different behavior than normal bounds. Where normal trait bounds on a function such as `T: Tr` are collected within the [`clauses_of`] query to be proven when a -function is called and to be assumed within the function, bounds such as -`T: [const] Tr` will behave as a normal trait bound and add `T: Tr` to the result +function is called and to be assumed within the function, +bounds such as `T: [const] Tr` will behave as a normal trait bound and add `T: Tr` to the result from `clauses_of`, but also adds a `HostEffectPredicate` to the [`const_conditions`] query. On the other hand, `T: const Tr` bounds do not change meaning across contexts, -therefore they will result in `HostEffect(T: Tr, const)` being added to -`clauses_of`, and not `const_conditions`. +therefore they will result in `HostEffect(T: Tr, const)` being added to `clauses_of`, +and not `const_conditions`. [`HostEffectPredicate`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/predicate/struct.HostEffectPredicate.html [`clauses_of`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#method.clauses_of @@ -34,14 +34,15 @@ fn foo() where T: Default {} We must be able to prove that `T` implements `Default`. In a similar vein, `const_conditions` represents a set of predicates that need to be proven to use -an item *in const contexts*. If we adjust the example above to use `const` trait bounds: +an item *in const contexts*. +If we adjust the example above to use `const` trait bounds: ```rust const fn foo() where T: [const] Default {} ``` -Then `foo` would get a `HostEffect(T: Default, maybe)` in the `const_conditions` -query, suggesting that in order to call `foo` from const contexts, one must +Then `foo` would get a `HostEffect(T: Default, maybe)` in the `const_conditions` query, +suggesting that in order to call `foo` from const contexts, one must prove that `T` has a const implementation of `Default`. ## Enforcement of `const_conditions` @@ -51,8 +52,8 @@ prove that `T` has a const implementation of `Default`. Every call in HIR from a const context (which includes `const fn` and `const` items) will check that `const_conditions` of the function we are calling hold. This is done in [`FnCtxt::enforce_context_effects`]. -Note that we don't check -if the function is only referred to but not called, as the following code needs to compile: +Note that we don't check if the function is only referred to but not called, +as the following code needs to compile: ```rust const fn hi() -> T { @@ -61,8 +62,8 @@ const fn hi() -> T { const X: fn() -> u32 = hi::; ``` -For a trait `impl` to be well-formed, we must be able to prove the -`const_conditions` of the trait from the `impl`'s environment. +For a trait `impl` to be well-formed, +we must be able to prove the `const_conditions` of the trait from the `impl`'s environment. This is checked in [`wfcheck::check_impl`]. Here's an example: @@ -79,8 +80,8 @@ impl const Foo for () {} Methods of trait impls must not have stricter bounds than the method of the trait that they are implementing. -To check that the methods are compatible, a -hybrid environment is constructed with the predicates of the `impl` plus the +To check that the methods are compatible, +a hybrid environment is constructed with the predicates of the `impl` plus the predicates of the trait method, and we attempt to prove the predicates of the impl method. We do the same for `const_conditions`: @@ -113,8 +114,8 @@ are revalidated again in [`Checker::revalidate_conditional_constness`]. ## `explicit_implied_const_bounds` on associated types and traits -Bounds on associated types, opaque types, and supertraits such as the following -have their bounds represented differently: +Bounds on associated types, opaque types, +and supertraits such as the following have their bounds represented differently: ```rust trait Foo: [const] PartialEq { @@ -132,8 +133,8 @@ bounds on functions), these bounds need to be proved at definition (at the impl, or when returning the opaque) but can be assumed for callers. The non-const equivalent of these bounds are called [`explicit_item_bounds`]. -These bounds are checked in [`compare_impl_item::check_type_bounds`] for HIR -typeck, [`evaluate_host_effect_from_item_bounds`] in the old solver and +These bounds are checked in [`compare_impl_item::check_type_bounds`] for HIR typeck, +[`evaluate_host_effect_from_item_bounds`] in the old solver and [`consider_additional_alias_assumptions`] in the new solver. [`explicit_item_bounds`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#method.explicit_item_bounds @@ -147,11 +148,11 @@ typeck, [`evaluate_host_effect_from_item_bounds`] in the old solver and In general, we can prove a `HostEffect` predicate when either of these conditions are met: * The predicate can be assumed from caller bounds; -* The type has a `const` `impl` for the trait, *and* that const conditions on - the impl holds, *and* that the `explicit_implied_const_bounds` on the trait holds; or +* The type has a `const` `impl` for the trait, *and* that const conditions on the impl holds, + *and* that the `explicit_implied_const_bounds` on the trait holds; or * The type has a built-in implementation for the trait in const contexts. - For example, `Fn` may be implemented by function items if their const conditions - are satisfied, or `Destruct` is implemented in const contexts if the type can + For example, `Fn` may be implemented by function items if their const conditions are satisfied, + or `Destruct` is implemented in const contexts if the type can be dropped at compile time. [old solver]: https://doc.rust-lang.org/nightly/nightly-rustc/src/rustc_trait_selection/traits/effects.rs.html @@ -164,8 +165,8 @@ To be expanded later. ### The `#[rustc_non_const_trait_method]` attribute This is intended for internal (standard library) usage only. -With this attribute applied to a trait method, the compiler will not check the default body of this -method for ability to run in compile time. +With this attribute applied to a trait method, +the compiler will not check the default body of this method for ability to run in compile time. Users of the trait will also not be allowed to use this trait method in const contexts. This attribute is primarily used for constifying large traits such as `Iterator` without having to make all diff --git a/src/doc/rustc-dev-guide/src/external-repos.md b/src/doc/rustc-dev-guide/src/external-repos.md index 1aca1b2eab1c5..0e23be130c403 100644 --- a/src/doc/rustc-dev-guide/src/external-repos.md +++ b/src/doc/rustc-dev-guide/src/external-repos.md @@ -27,12 +27,12 @@ The following external projects are managed using some form of a `subtree`: * [compiler-builtins](https://github.com/rust-lang/compiler-builtins) * [stdarch](https://github.com/rust-lang/stdarch) -In contrast to `submodule` dependencies -(see below for those), the `subtree` dependencies are just regular files and directories which can -be updated in-tree. However, if possible, enhancements, bug fixes, etc. specific +In contrast to `submodule` dependencies (see below for those), +the `subtree` dependencies are just regular files and directories which can be updated in-tree. +However, if possible, enhancements, bug fixes, etc. specific to these tools should be filed against the tools directly in their respective upstream repositories. -The exception is that when rustc changes are required to -implement a new tool feature or test, that should happen in one collective rustc PR. +The exception is that when rustc changes are required to implement a new tool feature or test, +that should happen in one collective rustc PR. Similarly, if your rustc changes break the build of some subtree dependency, you should include fixes for those in the rustc PR as well. @@ -42,13 +42,13 @@ you should include fixes for those in the rustc PR as well. * Using `git subtree` * `clippy` ([sync guide](https://doc.rust-lang.org/nightly/clippy/development/infrastructure/sync.html#performing-the-sync-from-rust-langrust-to-clippy)) * `portable-simd` ([sync script](https://github.com/rust-lang/portable-simd/blob/master/subtree-sync.sh)) - * `rustfmt` - * `rustc_codegen_cranelift` ([sync script](https://github.com/rust-lang/rustc_codegen_cranelift/blob/113af154d459e41b3dc2c5d7d878e3d3a8f33c69/scripts/rustup.sh#L7)) * `rustc_codegen_gcc` ([sync guide](https://github.com/rust-lang/rustc_codegen_gcc/blob/master/doc/subtree.md)) * Using the [josh](#synchronizing-a-josh-subtree) tool * `miri` * `rust-analyzer` + * `rustc_codegen_cranelift` * `rustc-dev-guide` + * `rustfmt` * `compiler-builtins` * `stdarch` @@ -56,7 +56,7 @@ you should include fixes for those in the rustc PR as well. The [josh] tool is an alternative to git subtrees, which manages git history in a different way and scales better to larger repositories. Specific tooling is required to work with josh. -We provide a helper [`rustc-josh-sync`][josh-sync] tool to help with the synchronization, described [below](#synchronizing-a-josh-subtree). +We provide a helper tool, [`rustc-josh-sync`][josh-sync], to help with the synchronization, described [below](#synchronizing-a-josh-subtree). ### Synchronizing a Josh subtree @@ -103,20 +103,20 @@ Periodically the changes made to subtree based dependencies need to be synchroni repository and the upstream tool repositories. Subtree synchronizations are typically handled by the respective tool maintainers. -Other users -are welcome to submit synchronization PRs, however, in order to do so you will need to modify -your local git installation and follow a very precise set of instructions. -These instructions are documented, along with several useful tips and tricks, in the -[syncing subtree changes][clippy-sync-docs] section in Clippy's Contributing guide. -The instructions are applicable for use with any subtree based tool, just be sure to -use the correct corresponding subtree directory and remote repository. +Other users are welcome to submit synchronization PRs. +However, in order to do so, +you will need to modify your local git installation and follow a very precise set of instructions. +These instructions are documented, along with several useful tips and tricks, +in the [syncing subtree changes][clippy-sync-docs] section in Clippy's Contributing guide. +The instructions are applicable for use with any subtree-based tool; +just be sure to use the correct corresponding subtree directory and remote repository. The synchronization process goes in two directions: `subtree push` and `subtree pull`. A `subtree push` takes all the changes that happened to the copy in this repo and creates commits on the remote repo that match the local changes. -Every local commit that touched the subtree causes a commit on the remote repo, but -is modified to move the files from the specified directory to the tool repo root. +Every local commit that touched the subtree causes a commit on the remote repo, +but is modified to move the files from the specified directory to the tool repo root. A `subtree pull` takes all changes since the last `subtree pull` from the tool repo and adds these commits to the rustc repo along with a merge commit that moves @@ -142,8 +142,8 @@ that this is happening because you suddenly get thousands of commits that want t ### Creating a new subtree dependency -If you want to create a new subtree dependency from an existing repository, call (from this -repository's root directory!) +If you want to create a new subtree dependency from an existing repository, +call (from this repository's root directory!) ``` git subtree add -P src/tools/clippy https://github.com/rust-lang/rust-clippy.git master @@ -152,9 +152,9 @@ git subtree add -P src/tools/clippy https://github.com/rust-lang/rust-clippy.git This will create a new commit, which you may not rebase under any circumstances! Delete the commit and redo the operation if you need to rebase. -Now you're done, the `src/tools/clippy` directory behaves as if Clippy were -part of the rustc monorepo, so no one but you (or others that synchronize -subtrees) actually needs to use `git subtree`. +Now you're done; +the `src/tools/clippy` directory behaves as if Clippy were part of the rustc monorepo, +so no one but you (or others that synchronize subtrees) actually needs to use `git subtree`. ## External dependencies (submodules) @@ -168,14 +168,14 @@ Usage of submodules is discussed more in the [Using Git chapter](git.md#git-subm Some of the submodules are allowed to be in a "broken" state where they either don't build or their tests don't pass, e.g. the documentation books like [The Rust Reference]. -Maintainers of these projects will be notified -when the project is in a broken state, and they should fix them as soon as possible. +Maintainers of these projects will be notified when the project is in a broken state, +and they should fix them as soon as possible. The current status is tracked on the [toolstate website]. More information may be found on the Forge [Toolstate chapter]. In practice, it is very rare for documentation to have broken toolstate. -Breakage is not allowed in the beta and stable channels, and must be addressed -before the PR is merged. +Breakage is not allowed in the beta and stable channels, +and must be addressed before the PR is merged. They are also not allowed to be broken on `main` in the week leading up to the beta cut. [git submodules]: https://git-scm.com/book/en/v2/Git-Tools-Submodules diff --git a/src/doc/rustc-dev-guide/src/mir/index.md b/src/doc/rustc-dev-guide/src/mir/index.md index b5b5b20500f39..005dc0cac21d9 100644 --- a/src/doc/rustc-dev-guide/src/mir/index.md +++ b/src/doc/rustc-dev-guide/src/mir/index.md @@ -7,18 +7,18 @@ It is a radically simplified form of Rust that is used for certain flow-sensitive safety checks – notably the borrow checker! – and also for optimization and code generation. -If you'd like a very high-level introduction to MIR, as well as some -of the compiler concepts that it relies on (such as control-flow +If you'd like a very high-level introduction to MIR, +as well as some of the compiler concepts that it relies on (such as control-flow graphs and desugaring), you may enjoy the [rust-lang blog post that introduced MIR][blog]. [blog]: https://blog.rust-lang.org/2016/04/19/MIR.html ## Introduction to MIR -MIR is defined in the [`compiler/rustc_middle/src/mir/`][mir] module, but much of the code -that manipulates it is found in [`compiler/rustc_mir_build`][mirmanip_build], -[`compiler/rustc_mir_transform`][mirmanip_transform], and -[`compiler/rustc_mir_dataflow`][mirmanip_dataflow]. +MIR is defined in the [`compiler/rustc_middle/src/mir/`][mir] module, +but much of the code that manipulates it is found in [`compiler/rustc_mir_build`][mirmanip_build], +[`compiler/rustc_mir_transform`][mirmanip_transform], +and [`compiler/rustc_mir_dataflow`][mirmanip_dataflow]. [RFC 1211]: https://rust-lang.github.io/rfcs/1211-mir.html @@ -38,22 +38,22 @@ This section introduces the key concepts of MIR, summarized here: - **statements:** actions with one successor - **terminators:** actions with potentially multiple successors; always at the end of a block - (if you're not familiar with the term *basic block*, see the [background chapter][cfg]) -- **Locals:** Memory locations allocated on the stack (conceptually, at - least), such as function arguments, local variables, and temporaries. +- **Locals:** Memory locations allocated on the stack (conceptually, at least), + such as function arguments, local variables, and temporaries. These are identified by an index, written with a leading underscore, like `_1`. There is also a special "local" (`_0`) allocated to store the return value. - **Places:** expressions that identify a location in memory, like `_1` or `_1.f`. - **Rvalues:** expressions that produce a value. The "R" stands for the fact that these are the "right-hand side" of an assignment. - - **Operands:** the arguments to an rvalue, which can either be a - constant (like `22`) or a place (like `_1`). + - **Operands:** the arguments to an rvalue, + which can either be a constant (like `22`) or a place (like `_1`). You can get a feeling for how MIR is constructed by translating simple programs into MIR and reading the pretty printed output. -In fact, the playground makes this easy, since it supplies a MIR button that will -show you the MIR for your program. -Try putting this program into play -(or [clicking on this link][sample-play]), and then clicking the "MIR" button on the top: +In fact, the playground makes this easy, +since it supplies a MIR button that will show you the MIR for your program. +Try putting this program into play (or [clicking on this link][sample-play]), +and then clicking the "MIR" button on the top: [sample-play]: https://play.rust-lang.org/?gist=30074856e62e74e91f06abd19bd72ece&version=stable&edition=2021 @@ -83,8 +83,8 @@ We can use `rustc [filename].rs -Z mir-opt-level=0 --emit mir` to view unoptimiz This requires the nightly toolchain. -**Variable declarations.** If we drill in a bit, we'll see it begins -with a bunch of variable declarations. +**Variable declarations.** If we drill in a bit, +we'll see it begins with a bunch of variable declarations. They look like this: ```mir @@ -101,8 +101,8 @@ like `_0` or `_1`. We also intermingle the user's variables (e.g., `_1`) with temporary values (e.g., `_2` or `_3`). You can tell apart user-defined variables because they have debuginfo associated to them (see below). -**User variable debuginfo.** Below the variable declarations, we find the only -hint that `_1` represents a user variable: +**User variable debuginfo.** Below the variable declarations, +we find the only hint that `_1` represents a user variable: ```mir scope 1 { debug vec => _1; // in scope 1 at src/main.rs:2:9: 2:16 @@ -130,15 +130,15 @@ bb0: { } ``` -A basic block is defined by a series of **statements** and a final -**terminator**. In this case, there is one statement: +A basic block is defined by a series of **statements** and a final **terminator**. +In this case, there is one statement: ```mir StorageLive(_1); ``` -This statement indicates that the variable `_1` is "live", meaning -that it may be used later – this will persist until we encounter a +This statement indicates that the variable `_1` is "live", +meaning that it may be used later – this will persist until we encounter a `StorageDead(_1)` statement, which indicates that the variable `_1` is done being used. These "storage statements" are used by LLVM to allocate stack space. @@ -151,9 +151,9 @@ _1 = const >::new() -> bb2; Terminators are different from statements because they can have more than one successor – that is, control may flow to different places. Function calls like the call to `Vec::new` are always -terminators because of the possibility of unwinding, although in the -case of `Vec::new` we are able to see that indeed unwinding is not -possible, and hence we list only one successor block, `bb2`. +terminators because of the possibility of unwinding, +although in the case of `Vec::new` we are able to see that indeed unwinding is not possible, +and hence we list only one successor block, `bb2`. If we look ahead to `bb2`, we will see it looks like this: @@ -165,8 +165,8 @@ bb2: { } ``` -Here there are two statements: another `StorageLive`, introducing the `_3` -temporary, and then an assignment: +Here there are two statements: another `StorageLive`, introducing the `_3` temporary, +and then an assignment: ```mir _3 = &mut _1; @@ -179,8 +179,8 @@ Assignments in general have the form: ``` A place is an expression like `_3`, `_3.f` or `*_3` – it denotes a location in memory. -An **Rvalue** is an expression that creates a -value: in this case, the rvalue is a mutable borrow expression, which looks like `&mut `. +An **Rvalue** is an expression that creates a value: in this case, +the rvalue is a mutable borrow expression, which looks like `&mut `. So we can kind of define a grammar for rvalues like so: ```text @@ -194,21 +194,21 @@ So we can kind of define a grammar for rvalues like so: | move Place ``` -As you can see from this grammar, rvalues cannot be nested – they can -only reference places and constants. +As you can see from this grammar, +rvalues cannot be nested – they can only reference places and constants. Moreover, when you use a place, we indicate whether we are **copying it** (which requires that the place have a type `T` where `T: Copy`) or **moving it** (which works for a place of any type). -So, for example, if we had the expression `x -= a + b + c` in Rust, that would get compiled to two statements and a temporary: +So, for example, if we had the expression `x = a + b + c` in Rust, +that would get compiled to two statements and a temporary: ```mir TMP1 = a + b x = TMP1 + c ``` -([Try it and see][play-abc], though you may want to do release mode to skip -over the overflow checks.) +([Try it and see][play-abc], +though you may want to do release mode to skip over the overflow checks.) [play-abc]: https://play.rust-lang.org/?gist=1751196d63b2a71f8208119e59d8a5b6&version=stable @@ -225,8 +225,8 @@ but [you can read about those below](#promoted-constants)). - **Basic blocks**: The basic blocks are stored in the field [`Body::basic_blocks`][basicblocks]; this is a vector of [`BasicBlockData`] structures. - Nobody ever references a basic block directly: instead, we pass around [`BasicBlock`] - values, which are [newtype'd] indices into this vector. + Nobody ever references a basic block directly: instead, we pass around [`BasicBlock`] values, + which are [newtype'd] indices into this vector. - **Statements** are represented by the type [`Statement`]. - **Terminators** are represented by the [`Terminator`]. - **Locals** are represented by a [newtype'd] index type [`Local`]. @@ -240,8 +240,8 @@ but [you can read about those below](#promoted-constants)). These are represented by the [newtype'd] type [`ProjectionElem`]. So e.g. the place `_1.f` is a projection, with `f` being the "projection element" and `_1` being the base path. - `*_1` is also a projection, with the `*` being represented - by the [`ProjectionElem::Deref`] element. + `*_1` is also a projection, + with the `*` being represented by the [`ProjectionElem::Deref`] element. - **Rvalues** are represented by the enum [`Rvalue`]. - **Operands** are represented by the enum [`Operand`]. @@ -251,8 +251,8 @@ When code has reached the MIR stage, constants can generally come in two forms: *MIR constants* ([`mir::Constant`]) and *type system constants* ([`ty::Const`]). MIR constants are used as operands: in `x + CONST`, `CONST` is a MIR constant; similarly, in `x + 2`, `2` is a MIR constant. -Type system constants are used in -the type system, in particular for array lengths but also for const generics. +Type system constants are used in the type system, +in particular for array lengths, but also for const generics. Generally, both kinds of constants can be "unevaluated" or "already evaluated". An unevaluated constant simply stores the `DefId` of what needs to be evaluated @@ -270,8 +270,8 @@ parameter is used as an operand. ### MIR constant values -In general, a MIR constant value (`mir::ConstValue`) was computed by evaluating -some constant the user wrote. +In general, +a MIR constant value (`mir::ConstValue`) was computed by evaluating some constant the user wrote. This [const evaluation](../const-eval.md) produces a very low-level representation of the result in terms of individual bytes. We call this an "indirect" constant (`mir::ConstValue::Indirect`) since the value @@ -280,9 +280,9 @@ is stored in-memory. However, storing everything in-memory would be awfully inefficient. Hence there are some other variants in `mir::ConstValue` that can represent certain simple and common values more efficiently. -In particular, everything that can be -directly written as a literal in Rust (integers, floats, chars, bools, but also -`"string literals"` and `b"byte string literals"`) has an optimized variant that +In particular, everything that can be directly written as a literal in Rust (integers, +floats, chars, bools, +but also `"string literals"` and `b"byte string literals"`) has an optimized variant that avoids the full overhead of the in-memory representation. ### ValTrees @@ -301,8 +301,8 @@ In other words, a specific value must only be representable in one specific way. For example, there is only one way to represent an array of two integers as a `ValTree`: `Branch([Leaf(first_int), Leaf(second_int)])`. Even though theoretically a `[u32; 2]` could be encoded in a `u64` and thus just be a -`Leaf(bits_of_two_u32)`, that is not a legal construction of `ValTree` -(and is very complex to do, so it is unlikely anyone is tempted to do so). +`Leaf(bits_of_two_u32)`, that is not a legal construction of `ValTree` (and is very complex to do, +so it is unlikely anyone is tempted to do so). These rules also mean that some values are not representable. There can be no `union`s in type level constants, diff --git a/src/doc/rustc-dev-guide/src/normalization.md b/src/doc/rustc-dev-guide/src/normalization.md index 05484a672aaf3..cefd92999d9e5 100644 --- a/src/doc/rustc-dev-guide/src/normalization.md +++ b/src/doc/rustc-dev-guide/src/normalization.md @@ -155,7 +155,7 @@ fn bar() { ``` This is likely to change as const generics functionality is improved. -For example, `feature(associated_const_equality)` and `feature(min_generic_const_args)` both require handling const aliases similarly to types (without an anonymous constant wrapping all const args). +For example, `feature(associated_const_equality)` and `feature(gca_min_const_items)` both require handling const aliases similarly to types (without an anonymous constant wrapping all const args). ## What is normalization diff --git a/src/doc/rustc-dev-guide/src/offload/installation.md b/src/doc/rustc-dev-guide/src/offload/installation.md index 8422d072bca3b..2bb7ea98f6d5b 100644 --- a/src/doc/rustc-dev-guide/src/offload/installation.md +++ b/src/doc/rustc-dev-guide/src/offload/installation.md @@ -3,9 +3,16 @@ `std::offload` is partly available in nightly builds for users. For now, everyone however still needs to build rustc from source to use all features of it. +## Rustup installation. + +If you are on `x86_64` Linux, you can install the nightly toolchain with: +```console +rustup +nightly component add offload +``` + ## Build instructions -First you need to clone and configure the Rust repository: +Otherwise you need to clone and configure the Rust repository: ```console git clone git@github.com:rust-lang/rust cd rust @@ -14,7 +21,8 @@ cd rust If you would rather reuse an existing clang than build one, drop `--enable-clang` and pass `--enable-llvm-offload-clang-dir=` -instead. It should match the (major version of the) LLVM in `src/llvm-project`. +instead. +It should match the (major version of the) LLVM in `src/llvm-project`. Afterwards you can build rustc using: ```console @@ -27,9 +35,8 @@ rustup toolchain link offload build/host/stage1 rustup toolchain install nightly # enables -Z unstable-options ``` - - ## Build instruction for LLVM itself + ```console git clone git@github.com:llvm/llvm-project cd llvm-project @@ -41,8 +48,8 @@ ninja install ``` This gives you a working LLVM build. - ## Testing + Run this test script for offload-specific tests: ```console ./x test --stage 1 tests/codegen-llvm/gpu_offload diff --git a/src/doc/rustc-dev-guide/src/offload/usage.md b/src/doc/rustc-dev-guide/src/offload/usage.md index 77b8935fab837..2570d60b2663d 100644 --- a/src/doc/rustc-dev-guide/src/offload/usage.md +++ b/src/doc/rustc-dev-guide/src/offload/usage.md @@ -49,7 +49,7 @@ fn kernel(x: *mut [T; 256], value: T) { fn main() { let mut x = [0.0f64; 256]; core::offload::offload! { - kernel = kernel, + kernel = kernel::, workgroup_dim = [256, 1, 1], args = (&mut x as *mut [f64; 256], 2.5), } @@ -89,38 +89,21 @@ Now we generate the device (GPU) code, passing the manifest: ``` RUSTFLAGS="-Ctarget-cpu=gfx90a --emit=llvm-bc,llvm-ir -Zoffload=Device=/absolute/path/to/offload.manifest -Csave-temps -Zunstable-options" cargo +offload build -Zunstable-options -r -v --target amdgcn-amd-amdhsa -Zbuild-std=core ``` -You might afterwards need to copy your target/release/deps/.bc to lib.bc for now, before the next step. -Now we generate the host (CPU) code. +Next we generate the host (CPU) code. ``` -RUSTFLAGS="--emit=llvm-bc,llvm-ir -Csave-temps -Zoffload=Host=/p/lustre1/drehwald1/prog/offload/r/target/amdgcn-amd-amdhsa/release/deps/device.bin -Zunstable-options" cargo +offload build -r -``` -This call also does a lot of work and generates multiple intermediate files for LLVM offload. -While we integrated most offload steps into rustc by now, one binary invocation still remains for now: - -``` -"clang-linker-wrapper" "--should-extract=gfx90a" "--device-compiler=amdgcn-amd-amdhsa=-g" "--device-compiler=amdgcn-amd-amdhsa=-save-temps=cwd" "--device-linker=amdgcn-amd-amdhsa=-lompdevice" "--host-triple=x86_64-unknown-linux-gnu" "--save-temps" "--linker-path=/ABSOlUTE_PATH_TO/rust/build/x86_64-unknown-linux-gnu/lld/bin/ld.lld" "--hash-style=gnu" "--eh-frame-hdr" "-m" "elf_x86_64" "-pie" "-dynamic-linker" "/lib64/ld-linux-x86-64.so.2" "-o" "main" "/lib/../lib64/Scrt1.o" "/lib/../lib64/crti.o" "/ABSOLUTE_PATH_TO/crtbeginS.o" "-L/ABSOLUTE_PATH_TO/rust/build/x86_64-unknown-linux-gnu/llvm/bin/../lib/x86_64-unknown-linux-gnu" "-L/ABSOLUTE_PATH_TO/rust/build/x86_64-unknown-linux-gnu/llvm/lib/clang/21/lib/x86_64-unknown-linux-gnu" "-L/lib/../lib64" "-L/usr/lib64" "-L/lib" "-L/usr/lib" "target//release/host.o" "-lstdc++" "-lm" "-lomp" "-lomptarget" "-L/ABSOLUTE_PATH_TO/rust/build/x86_64-unknown-linux-gnu/llvm/lib" "-lgcc_s" "-lgcc" "-lpthread" "-lc" "-lgcc_s" "-lgcc" "/ABSOLUTE_PATH_TO/crtendS.o" "/lib/../lib64/crtn.o" -``` - -You can try to find the paths to those files on your system. -However, I recommend to not fix the paths, but rather just re-generate them by copying a bare-mode OpenMP example and compiling it with your clang. -By adding `-###` to your clang invocation, you can see the invidual steps. -It will show multiple steps, just look for the clang-linker-wrapper example. -Make sure to still include the path to the `host.o` file, and not whatever tmp file you got when compiling your c++ example with the following call. -``` -myclang++ -fuse-ld=lld -O3 -fopenmp -fopenmp-offload-mandatory --offload-arch=gfx90a omp_bare.cpp -o main -### +RUSTFLAGS="--emit=llvm-bc,llvm-ir -Csave-temps -Zoffload=Host=$PWD/target/amdgcn-amd-amdhsa/release/deps/device.bin -Zunstable-options" cargo +offload build -r ``` In the final step, you can now run your binary ``` -./main +./target/x86_64-unknown-linux-gnu/release/binary-name all checks passed! ``` -To receive more information about the memory transfer, you can enable info printing with -``` -LIBOMPTARGET_INFO=-1 ./main -``` +These three steps will soon be wrapped into a single command, once we had more time to test all steps. + +To receive more information about the memory transfer, you can enable info printing by adding `LIBOMPTARGET_INFO=-1` ahead of your binary call. [^list]: https://rocm.docs.amd.com/en/latest/reference/gpu-arch-specs.html or https://developer.nvidia.com/cuda/gpus. Alternatively, check `rustc --print target-cpus`. diff --git a/src/doc/rustc-dev-guide/src/profiling/with-perf.md b/src/doc/rustc-dev-guide/src/profiling/with-perf.md index b55afed47fbdc..1a3b986d8213b 100644 --- a/src/doc/rustc-dev-guide/src/profiling/with-perf.md +++ b/src/doc/rustc-dev-guide/src/profiling/with-perf.md @@ -60,8 +60,8 @@ cargo install --locked addr2line --features="bin" ### Gathering a perf profile from a `perf.rust-lang.org` test Often we want to analyze a specific test from `perf.rust-lang.org`. -The easiest way to do that is to use the [rustc-perf] -benchmarking suite, this approach is described [here](with-rustc-perf.md). +The easiest way to do that is to use the [rustc-perf] benchmarking suite, +this approach is described [here](with-rustc-perf.md). Instead of using the benchmark suite CLI, you can also profile the benchmarks manually. First, you need to clone the [rustc-perf] repository: @@ -96,8 +96,8 @@ CARGO_INCREMENTAL=0 cargo + check Next: we want record the execution time for *just* the clap-rs crate, running cargo check. -I tend to use `cargo rustc` for this, since it -also allows me to add explicit flags, which we'll do later on. +I tend to use `cargo rustc` for this, since it also allows me to add explicit flags, +which we'll do later on. ```bash touch src/lib.rs @@ -106,8 +106,8 @@ CARGO_INCREMENTAL=0 perf record -F99 --call-graph dwarf cargo rustc --profile ch Note that final command: it's a doozy! It uses the `cargo rustc` command, which executes rustc with (potentially) additional options; -the `--profile check` and `--lib` options specify that we are doing a -`cargo check` execution, and that this is a library (not a binary). +the `--profile check` and `--lib` options specify that we are doing a `cargo check` execution, +and that this is a library (not a binary). At this point, we can use `perf` tooling to analyze the results. For example: @@ -121,14 +121,14 @@ In simple cases, that can be helpful. For more detailed examination, the [`perf-focus` tool][pf] can be helpful; it is covered below. **A note of caution.** Each of the rustc-perf tests is its own special snowflake. - In particular, some of them are not libraries, in which - case you would want to do `touch src/main.rs` and avoid passing `--lib`. + In particular, some of them are not libraries, + in which case you would want to do `touch src/main.rs` and avoid passing `--lib`. I'm not sure how best to tell which test is which to be honest. ### Gathering NLL data -If you want to profile an NLL run, you can just pass extra options to -the `cargo rustc` command, like so: +If you want to profile an NLL run, you can just pass extra options to the `cargo rustc` command, +like so: ```bash touch src/lib.rs @@ -152,8 +152,8 @@ To understand how it works, you have to know just a bit about perf. Basically, perf works by *sampling* your process on a regular basis (or whenever some event occurs). For each sample, perf gathers a backtrace. `perf focus` lets you write a regular expression that tests -which functions appear in that backtrace, and then tells you which -percentage of samples had a backtrace that met the regular expression. +which functions appear in that backtrace, +and then tells you which percentage of samples had a backtrace that met the regular expression. It's probably easiest to explain by walking through how I would analyze NLL performance. ### Installing `perf-focus` @@ -168,8 +168,7 @@ cargo install --locked perf-focus Let's say we've gathered the NLL data for a test. We'd like to know how much time it is spending in the MIR borrow-checker. -The "main" function of the MIR borrowck is called `do_mir_borrowck`, so we can do -this command: +The "main" function of the MIR borrowck is called `do_mir_borrowck`, so we can do this command: ```bash $ perf focus '{do_mir_borrowck}' @@ -179,22 +178,22 @@ Not Matches: 542 Percentage : 29% ``` -The `'{do_mir_borrowck}'` argument is called the **matcher**. It -specifies the test to be applied on the backtrace. +The `'{do_mir_borrowck}'` argument is called the **matcher**. +It specifies the test to be applied on the backtrace. In this case, the `{X}` indicates that there must be *some* function on the backtrace that meets the regular expression `X`. -In this case, that regex is just the name of the function we want -(in fact, it's a subset of the name; +In this case, that regex is just the name of the function we want (in fact, +it's a subset of the name; the full name includes a bunch of other stuff, like the module path). In this mode, perf-focus just prints out the percentage of samples where `do_mir_borrowck` was on the stack: in this case, 29%. -**A note about c++filt.** To get the data from `perf`, `perf focus` - currently executes `perf script` (perhaps there is a better way...). +**A note about c++filt.** To get the data from `perf`, + `perf focus` currently executes `perf script` (perhaps there is a better way...). I've sometimes found that `perf script` outputs C++ mangled names. This is annoying. You can tell by running `perf script | - head` yourself — if you see names like `5rustc6middle` instead of - `rustc::middle`, then you have the same problem. + head` yourself — if you see names like `5rustc6middle` instead of `rustc::middle`, + then you have the same problem. You can solve this by doing: ```bash @@ -203,10 +202,10 @@ perf script | c++filt | perf focus --from-stdin ... This will pipe the output from `perf script` through `c++filt` and should mostly convert those names into a more friendly format. -The `--from-stdin` flag to `perf focus` tells it to get its data from -stdin, rather than executing `perf focus`. -We should make this more convenient (at worst, maybe add a `c++filt` option to `perf focus`, or -just always use it — it's pretty harmless). +The `--from-stdin` flag to `perf focus` tells it to get its data from stdin, +rather than executing `perf focus`. +We should make this more convenient (at worst, maybe add a `c++filt` option to `perf focus`, +or just always use it — it's pretty harmless). ### Example: How much time does MIR borrowck spend solving traits? @@ -275,17 +274,16 @@ Usually "total" is the more interesting number, but not always. ### Relative percentages -By default, all in perf-focus are relative to the **total program -execution**. This is useful to help you keep perspective — often as -we drill down to find hot spots, we can lose sight of the fact that, +By default, all in perf-focus are relative to the **total program execution**. +This is useful to help you keep perspective — often as we drill down to find hot spots, +we can lose sight of the fact that, in terms of overall program execution, this "hot spot" is actually not important. It also ensures that percentages between different queries are easily compared against one another. -That said, sometimes it's useful to get relative percentages, so `perf -focus` offers a `--relative` option. +That said, sometimes it's useful to get relative percentages, +so `perf focus` offers a `--relative` option. In this case, the percentages are listed only for samples that match (vs all samples). -So for example we could get our percentages relative to the borrowck itself -like so: +So, for example, we could get our percentages relative to the borrowck itself like so: ```bash $ perf focus '{do_mir_borrowck}' --tree-callees --relative --tree-max-depth 1 --tree-min-percent 5 diff --git a/src/doc/rustc-dev-guide/src/query.md b/src/doc/rustc-dev-guide/src/query.md index 471b445c14d5a..a4b9eef02f13a 100644 --- a/src/doc/rustc-dev-guide/src/query.md +++ b/src/doc/rustc-dev-guide/src/query.md @@ -9,17 +9,18 @@ Instead of entirely independent passes (parsing, type-checking, etc.), a set of function-like *queries* compute information about the input source. For example, -there is a query called `type_of` that, given the [`DefId`] of -some item, will compute the type of that item and return it to you. +there is a query called `type_of` that, given the [`DefId`] of some item, +will compute the type of that item and return it to you. [`DefId`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/def_id/struct.DefId.html [Overview of the compiler]: overview.md#queries -Query execution is *memoized*. The first time you invoke a -query, it will go do the computation, but the next time, the result is returned from a hashtable. -Moreover, query execution fits nicely into -*incremental computation*; the idea is roughly that, when you invoke a -query, the result *may* be returned to you by loading stored data from disk.[^incr-comp-detail] +Query execution is *memoized*. +The first time you invoke a query, +it will go do the computation, but the next time, the result is returned from a hashtable. +Moreover, query execution fits nicely into *incremental computation*; the idea is roughly that, +when you invoke a query, +the result *may* be returned to you by loading stored data from disk.[^incr-comp-detail] When we execute a query, we also discover (at runtime!) what other queries it depends on. @@ -39,8 +40,8 @@ For example: - That query in turn would invoke something asking for the HIR. - This keeps going further and further back until we wind up doing the actual parsing. -Although this vision is not fully realized, large sections of the -compiler (for example, generating [MIR]) currently work exactly like this. +Although this vision is not fully realized, large sections of the compiler (for example, +generating [MIR]) currently work exactly like this. [^incr-comp-detail]: The [Incremental compilation in detail] chapter gives a more in-depth description of what queries are and how they work. @@ -65,22 +66,22 @@ let ty = tcx.type_of(some_def_id); So you may be wondering what happens when you invoke a query method. The answer is that, for each query, the compiler maintains a -cache – if your query has already been executed, then, the answer is -simple: we clone the return value out of the cache and return it -(therefore, you should try to ensure that the return types of queries +cache – if your query has already been executed, then, +the answer is simple: we clone the return value out of the cache and return it (therefore, +you should try to ensure that the return types of queries are cheaply cloneable; insert an `Rc` if necessary). ### Providers -If, however, the query is *not* in the cache, then the compiler will -call the corresponding **provider** function. +If, however, the query is *not* in the cache, +then the compiler will call the corresponding **provider** function. A provider is a function implemented in a specific module and **manually registered** into either the [`Providers`][providers_struct] struct (for local crate queries) or the [`ExternProviders`][extern_providers_struct] struct (for external crate queries) during compiler initialization. The macro system generates both structs, -which act as function tables for all query implementations, where each -field is a function pointer to the actual provider. +which act as function tables for all query implementations, +where each field is a function pointer to the actual provider. **Note:** Both the `Providers` and `ExternProviders` structs are generated by macros and act as function tables for all query implementations. They are **not** Rust traits, but plain structs with function pointer fields. @@ -112,11 +113,11 @@ Providers take two arguments: the `tcx` and the query key. They return the result of the query. N.B. Most of the `rustc_*` crates only provide **local -providers**. Almost all **extern providers** wind up going through the -[`rustc_metadata` crate][rustc_metadata], which loads the information from the crate metadata. -But in some cases there are crates that -provide queries for *both* local and external crates, in which case -they define both a `provide` and a `provide_extern` function, through +providers**. +Almost all **extern providers** wind up going through the [`rustc_metadata` crate][rustc_metadata], +which loads the information from the crate metadata. +But in some cases there are crates that provide queries for *both* local and external crates, +in which case they define both a `provide` and a `provide_extern` function, through [`wasm_import_module_map`][wasm_import_module_map], that `rustc_driver` can invoke. [rustc_metadata]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_metadata/index.html diff --git a/src/doc/rustc-dev-guide/src/rustc-driver/intro.md b/src/doc/rustc-dev-guide/src/rustc-driver/intro.md index 32d27d218197d..687b2d49d094a 100644 --- a/src/doc/rustc-dev-guide/src/rustc-driver/intro.md +++ b/src/doc/rustc-dev-guide/src/rustc-driver/intro.md @@ -6,7 +6,7 @@ The [`rustc_driver`] is essentially `rustc`'s `main` function. It acts as the glue for running the various phases of the compiler in the correct order, using the interface defined in the [`rustc_interface`] crate. Where possible, using [`rustc_driver`] rather than [`rustc_interface`] is recommended. -The main entry point of [`rustc_driver`] is [`rustc_driver::run_compiler`][rd_rc]. +The main entry point of [`rustc_driver`] is [`rustc_driver::compiler_entrypoint`][rd_ce]. This builder accepts the same command-line args as rustc as well as an implementation of [`Callbacks`] and a couple of other optional options. [`Callbacks`] is a `trait` that allows for custom compiler configuration, as well as allowing custom code to run after different phases of the compilation. @@ -23,7 +23,7 @@ Inside the `closure` you can use the `Compiler` to call various functions to com You can see a minimal example of how to use [`rustc_interface`] [here][example]. You can see an example of how to use the various functions using [`rustc_interface`] needs by looking at the `rustc_driver` implementation, -specifically [`rustc_driver_impl::run_compiler`][rdi_rc] +specifically [`rustc_driver_impl::compiler_entrypoint`][rdi_ce] (not to be confused with [`rustc_interface::run_compiler`][i_rc]). > **Warning:** By its very nature, the internal compiler APIs are always going @@ -36,5 +36,5 @@ specifically [`rustc_driver_impl::run_compiler`][rdi_rc] [`Callbacks`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver/trait.Callbacks.html [example]: https://github.com/rust-lang/rustc-dev-guide/blob/main/examples/rustc-interface-example.rs [i_rc]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/interface/fn.run_compiler.html -[rd_rc]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver/fn.run_compiler.html -[rdi_rc]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver_impl/fn.run_compiler.html +[rd_ce]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver/fn.compiler_entrypoint.html +[rdi_ce]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver_impl/fn.compiler_entrypoint.html diff --git a/src/doc/rustc-dev-guide/src/sanitizers.md b/src/doc/rustc-dev-guide/src/sanitizers.md index 65e1c0c705057..5d242b8ac5d4a 100644 --- a/src/doc/rustc-dev-guide/src/sanitizers.md +++ b/src/doc/rustc-dev-guide/src/sanitizers.md @@ -1,33 +1,33 @@ # Sanitizers support -The rustc compiler contains support for following sanitizers: +The Rust compiler contains support for following sanitizers: -* [AddressSanitizer][clang-asan] a faster memory error detector. - Can detect out-of-bounds access to heap, stack, and globals, use after free, use - after return, double free, invalid free, memory leaks. -* [ControlFlowIntegrity][clang-cfi] LLVM Control Flow Integrity (CFI) provides +* [AddressSanitizer][clang-asan], a faster memory error detector. + It can detect out-of-bounds access to heap, stack, and globals, use after free, use + after return, double free, invalid free, and memory leaks. +* [ControlFlowIntegrity][clang-cfi], LLVM Control Flow Integrity (CFI), provides forward-edge control flow protection. -* [Hardware-assisted AddressSanitizer][clang-hwasan] a tool similar to - AddressSanitizer but based on partial hardware assistance. -* [KernelControlFlowIntegrity][clang-kcfi] LLVM Kernel Control Flow Integrity - (KCFI) provides forward-edge control flow protection for operating systems kernels. -* [LeakSanitizer][clang-lsan] a run-time memory leak detector. -* [MemorySanitizer][clang-msan] a detector of uninitialized reads. -* [ThreadSanitizer][clang-tsan] a fast data race detector. +* [Hardware-assisted AddressSanitizer][clang-hwasan] is similar to + AddressSanitizer, but is based on partial hardware assistance. +* [KernelControlFlowIntegrity][clang-kcfi], LLVM Kernel Control Flow Integrity + (KCFI), provides forward-edge control flow protection for operating system kernels. +* [LeakSanitizer][clang-lsan], a run-time memory leak detector. +* [MemorySanitizer][clang-msan], a detector of uninitialized reads. +* [ThreadSanitizer][clang-tsan], a fast data race detector. ## How to use the sanitizers? -To enable a sanitizer compile with `-Z sanitizer=...` option, where value is one -of `address`, `cfi`, `hwaddress`, `kcfi`, `leak`, `memory` or `thread`. +To enable a sanitizer, compile with `-Z sanitizer=...` option, where value is one +of `address`, `cfi`, `hwaddress`, `kcfi`, `leak`, `memory`, or `thread`. For more details on how to use sanitizers, -please refer to the sanitizer flag in [The Unstable Book]. +refer to the sanitizer flag in [The Unstable Book]. [The Unstable Book]: https://doc.rust-lang.org/unstable-book -## How are sanitizers implemented in rustc? +## How sanitizers are implemented in rustc The implementation of sanitizers (except CFI) relies almost entirely on LLVM. -The rustc is an integration point for LLVM compile time instrumentation passes +The Rust compiler is an integration point for LLVM compile time instrumentation passes and runtime libraries. Highlight of the most important aspects of the implementation: @@ -42,7 +42,7 @@ Highlight of the most important aspects of the implementation: The runtimes are [placed into target libdir][sanitizer-copy]. * During LLVM code generation, the functions intended for instrumentation are - [marked][sanitizer-attribute] with appropriate LLVM attribute: + [marked][sanitizer-attribute] with an appropriate LLVM attribute: `SanitizeAddress`, `SanitizeHWAddress`, `SanitizeMemory`, or `SanitizeThread`. By default, all functions are instrumented, but this behaviour can be changed with `#[sanitize(xyz = "on|off|")]`. @@ -57,7 +57,7 @@ Highlight of the most important aspects of the implementation: passes][sanitizer-pass], different for each sanitizer. Instrumentation passes are invoked after optimization passes. -* When producing an executable, the sanitizer specific runtime library is +* When producing an executable, the sanitizer-specific runtime library is [linked in][sanitizer-link]. The libraries are searched for in the target libdir. First, the search is relative to the overridden system root, and subsequently, @@ -83,7 +83,7 @@ Sanitizers are validated by code generation tests in [`tests/ui/sanitizer/`][test-ui] directory. Testing sanitizer functionality requires the sanitizer runtimes (built when -`build.sanitizer = true` in `bootstrap.toml`) and target providing support for particular a sanitizer. +`build.sanitizer = true` in `bootstrap.toml`) and a target providing support for particular a sanitizer. When a sanitizer is unsupported on a given target, sanitizer tests will be ignored. This behaviour is controlled by compiletest `needs-sanitizer-*` directives. diff --git a/src/doc/rustc-dev-guide/src/solve/sharing-crates-with-rust-analyzer.md b/src/doc/rustc-dev-guide/src/solve/sharing-crates-with-rust-analyzer.md index 2b06f5b414c1b..19db5016eb073 100644 --- a/src/doc/rustc-dev-guide/src/solve/sharing-crates-with-rust-analyzer.md +++ b/src/doc/rustc-dev-guide/src/solve/sharing-crates-with-rust-analyzer.md @@ -59,7 +59,7 @@ solver. `rustc_next_trait_solver` is intended to depend only on the abstract interfaces defined in `rustc_type_ir`. To support this, the type-system traits in `rustc_type_ir` must expose every interface the solver -requires—for example, [creating a new inference type variable][ir new_infer] +requires—for example, [creating a new inference type variable][ir new_infer] ([rustc][rustc new_infer], [rust-analyzer][r-a new_infer]). For items that do not need compiler-specific representations, `rustc_type_ir` defines them directly as structs or enums parameterized over these traits—for example, [`TraitRef`][ir tr]. @@ -77,7 +77,8 @@ Among its essential responsibilities: instantiates the shared IR, - it provides the context required by the solver (e.g., querying [lang items][ir require_lang_item], enumerating [all blanket impls for a trait][ir for_each_blanket_impl]); -- and it must implement [`IrPrint`][ir irprint] for formatting and tracing. +- and it must implement [`IrPrint`][ir irprint] for formatting and tracing. + In practice, these `IrPrint` impls simply route to existing formatting logic inside rustc or rust-analyzer. @@ -91,7 +92,8 @@ rather than rustc queries. Another notable item in `rustc_type_ir` is the [`inherent` module][ir inherent]. This module provides *forward definitions* of inherent methods—expressed as traits—corresponding to -methods that exist on compiler-specific types such as `Ty` or `GenericArg`. +methods that exist on compiler-specific types such as `Ty` or `GenericArg`. + These definitions allow the generic crates (such as `rustc_next_trait_solver`) to call methods that are implemented differently in rustc and rust-analyzer. @@ -151,9 +153,9 @@ This infrastructure is used by the external fuzzing project: - [`trait Lift` and `Lift_Generic`][lift-trait-macro] - [`trait GenericTypeVisitable`][generictypevisitable] -These traits are used heavily in `rustc_type_ir`, their associated macros -primarily exist to reduce the amount of boilerplate otherwise required to -implement `Lift`, `TypeFoldable`, `TypeVisitable` and `GenericTypeVisitable`. +These traits are used heavily in `rustc_type_ir`, their associated macros +primarily exist to reduce the amount of boilerplate otherwise required to implement `Lift`, +`TypeFoldable`, `TypeVisitable` and `GenericTypeVisitable`. ### `trait TypeVisitable` and `TypeVisitable_Generic` [type-visitable-trait-macro]: #type-visitable-trait-macro @@ -163,8 +165,7 @@ which in turn will transfer control to `TypeVisitor`, this can be [seen in detail here][rustc_typevisitable]. While ostensibly similar due to their names, `TypeVisitable_Generic` and -[`GenericTypeVisitable`][generictypevisitable] they implement two different -visiting systems. +[`GenericTypeVisitable`][generictypevisitable] they implement two different visiting systems. - `TypeVisitable_Generic` means: derive the ordinary `TypeVisitable` trait generically over an `Interner`. @@ -175,50 +176,49 @@ visiting systems. [typevisitable_generic]: #typevisitable_generic It visits the value's fields in declaration order, delegating each field to that -field's own `TypeVisitable` implementation. The traversal can stop early if -the visitor returns a residual result. +field's own `TypeVisitable` implementation. +The traversal can stop early if the visitor returns a residual result. Use `#[type_visitable(ignore)]` to ignore a field; it will not be part of the -traversal and will not need to implement `TypeVisitable`. This should only -be used when the field does not need to be traversed. +traversal and will not need to implement `TypeVisitable`. +This should only be used when the field does not need to be traversed. ### `trait TypeFoldable` and `TypeFoldable_Generic` [type-foldable-trait-macro]: #type-foldable-trait-macro -The trait is implemented by things that need to embed types. This concept is -discussed in detail [here](../ty-fold.md) and can be +The trait is implemented by things that need to embed types. +This concept is discussed in detail [here](../ty-fold.md) and can be [followed in the source][rustc_typefoldable]. -`TypeFoldable_Generic` derives `rustc_type_ir::TypeFoldable` for a struct or -enum. +`TypeFoldable_Generic` derives `rustc_type_ir::TypeFoldable` for a struct or enum. -It consumes a value and reconstructs the same struct or enum variant after -folding its fields. It generates both fallible and infallible folding methods. +It consumes a value and reconstructs the same struct or enum variant after folding its fields. +It generates both fallible and infallible folding methods. -Use `#[type_foldable(identity)]` for a field whose value must be preserved -unchanged. The macro moves that field directly into the reconstructed value -instead of passing it to the folder. Its type therefore does not need to -implement `TypeFoldable`. +Use `#[type_foldable(identity)]` for a field whose value must be preserved unchanged. +The macro moves that field directly into the reconstructed value +instead of passing it to the folder. +Its type therefore does not need to implement `TypeFoldable`. For an enum, the generated match contains one reconstruction arm per variant. ### `trait Lift` and `Lift_Generic` [lift-trait-macro]: #lift-trait-macro -The trait has a method `lift_to_interner(...)`. As the name suggests, it should -'lift' something to the interner. [See here](../memory.md) to read more about -the interner [and here for the source][rustc_lift]. +The trait has a method `lift_to_interner(...)`. +As the name suggests, it should 'lift' something to the interner. +[See here](../memory.md) to read more about the interner [and here for the source][rustc_lift]. -The macro `Lift_Generic` derives `Lift` for a struct or enum, with three -non-obvious considerations: +The macro `Lift_Generic` derives `Lift` for a struct or enum, with three non-obvious considerations: 1. The generic parameters `I` and `J` are reserved for `I: Interner` and `J` being the interner it is being lifted to. -2. `PhantomData` is handled automatically, creating a new `PhantomData`. But it - _has_ to be used in the fully unqualified form -- you cannot use +2. `PhantomData` is handled automatically, creating a new `PhantomData`. + But it _has_ to be used in the fully unqualified form -- you cannot use `std::marker::PhantomData` directly in the field. 3. The bounds are deliberately written as associated type bounds on the `Interner` - trait rather than as `where` clauses on `LiftInto`. Given only `I: LiftInto`, + trait rather than as `where` clauses on `LiftInto`. + Given only `I: LiftInto`, Rust can then treat bounds such as the following as implied: ```rust @@ -228,15 +228,14 @@ I::Const: Lift This allows `Lift_Generic` to emit the bound `I: LiftInto` while still calling `lift_to_interner` on fields of type `I::Ty`, `I::Const`, and the other -declared associated types. It also guarantees that each call produces the -destination field type expected after the derive rewrites `I::Assoc` to -`J::Assoc`. +declared associated types. +It also guarantees that each call produces the +destination field type expected after the derive rewrites `I::Assoc` to `J::Assoc`. Without `declare_lift_into!`, the derive would need to generate a separate bound -for every interner-associated type used by every field. If a new `Interner` -associated type is expected to work with `Lift_Generic`, it needs an appropriate -`Lift` implementation and normally needs to be included in the -`declare_lift_into!` invocation. +for every interner-associated type used by every field. +If a new `Interner` associated type is expected to work with `Lift_Generic`, it needs an appropriate +`Lift` implementation and normally needs to be included in the `declare_lift_into!` invocation. If you want to ignore a field, such as a primitive like a `u32` which can't be lifted you can skip the field with `#[lift(ignore)]`. @@ -245,28 +244,28 @@ lifted you can skip the field with `#[lift(ignore)]`. [generictypevisitable]: #generictypevisitable This a separate more general traversal trait purely used by `rust-analyzer`. -The visitor type is a parameter of the trait rather than a parameter of the -method, and visiting neither returns a result nor supports short-circuiting. +The visitor type is a parameter of the trait rather than a parameter of the method, +and visiting neither returns a result nor supports short-circuiting. -As such a struct or enum can derive both `TypeVisitable_Generic` and -`GenericTypeVisitable` +As such a struct or enum can derive both `TypeVisitable_Generic` and `GenericTypeVisitable` -There is intentionally no ignore attribute. The traversal must visit every -field. This is a soundness requirement for rust-analyzer's use of the traversal +There is intentionally no ignore attribute. +The traversal must visit every field. +This is a soundness requirement for rust-analyzer's use of the traversal when tracing and garbage-collecting interned types. ## Long-term plans for supporting rust-analyzer In general, we aim to support rust-analyzer just as well as rustc in these shared crates—provided -doing so does not substantially harm rustc's performance or maintainability. +doing so does not substantially harm rustc's performance or maintainability. (e.g., [#145377][pr 145377], [#146111][pr 146111], [#146182][pr 146182] and [#147723][pr 147723]) Shared crates that require nightly-only features must guard such code behind a `nightly` feature flag, since rust-analyzer is built with the stable toolchain. Looking forward, we plan to uplift more shared logic into `rustc_type_ir`. -There are still duplicated implementations between rustc and rust-analyzer—such as `ObligationCtxt` -([rustc][rustc oblctxt], [rust-analyzer][r-a oblctxt]) and type coercion logic +There are still duplicated implementations between rustc and rust-analyzer—such as `ObligationCtxt` +([rustc][rustc oblctxt], [rust-analyzer][r-a oblctxt]) and type coercion logic ([rustc][rustc coerce], [rust-analyzer][r-a coerce])—that we would like to unify over time. [rustc-auto-publish]: https://github.com/rust-analyzer/rustc-auto-publish @@ -303,5 +302,5 @@ There are still duplicated implementations between rustc and rust-analyzer—suc [rustc coerce]: https://github.com/rust-lang/rust/blob/63b1db05801271e400954e41b8600a3cf1482363/compiler/rustc_hir_typeck/src/coercion.rs [r-a coerce]: https://github.com/rust-lang/rust-analyzer/blob/34f47d9298c478c12c6c4c0348771d1b05706e09/crates/hir-ty/src/infer/coerce.rs [rustc_lift]: https://github.com/rust-lang/rust/blob/0913b18e489ac1011b580e31fa5559654be12bfc/compiler/rustc_type_ir/src/lift.rs#L18 -[rustc_typevisitable]: https://github.com/rust-lang/rust/blob/0913b18e489ac1011b580e31fa5559654be12bfc/compiler/rustc_type_ir/src/visit.rs#L62 +[rustc_typevisitable]: https://github.com/rust-lang/rust/blob/0913b18e489ac1011b580e31fa5559654be12bfc/compiler/rustc_type_ir/src/visit.rs#L62 [rustc_typefoldable]: https://github.com/rust-lang/rust/blob/0913b18e489ac1011b580e31fa5559654be12bfc/compiler/rustc_type_ir/src/fold.rs#L71 diff --git a/src/doc/rustc-dev-guide/src/test-implementation.md b/src/doc/rustc-dev-guide/src/test-implementation.md index 707ba040609e0..5df7d8d304d22 100644 --- a/src/doc/rustc-dev-guide/src/test-implementation.md +++ b/src/doc/rustc-dev-guide/src/test-implementation.md @@ -1,9 +1,7 @@ # The `#[test]` attribute - - -Many Rust programmers rely on a built-in attribute called `#[test]`. All -you have to do is mark a function and include some asserts like so: +Many Rust programmers rely on a built-in attribute called `#[test]`. +All you have to do is mark a function and include some asserts like so: ```rust,ignore @@ -14,9 +12,9 @@ fn my_test() { ``` When this program is compiled using `rustc --test` or `cargo test`, it will -produce an executable that can run this, and any other test function. This -method of testing allows tests to live alongside code in an organic way. You -can even put tests inside private modules: +produce an executable that can run this, and any other test function. +This method of testing allows tests to live alongside code in an organic way. +You can even put tests inside private modules: ```rust,ignore mod my_priv_mod { @@ -30,22 +28,23 @@ mod my_priv_mod { ``` Private items can thus be easily tested without worrying about how to expose -them to any sort of external testing apparatus. This is key to the -ergonomics of testing in Rust. Semantically, however, it's rather odd. +them to any sort of external testing apparatus. +This is key to the ergonomics of testing in Rust. +Semantically, however, it's rather odd. How does any sort of `main` function invoke these tests if they're not visible? What exactly is `rustc --test` doing? `#[test]` is implemented as a syntactic transformation inside the compiler's -[`rustc_ast`][rustc_ast]. Essentially, it's a fancy [`macro`] that -rewrites the crate in 3 steps: +[`rustc_ast`][rustc_ast]. +Essentially, it's a fancy [`macro`] that rewrites the crate in 3 steps: ## Step 1: Re-Exporting As mentioned earlier, tests can exist inside private modules, so we need a -way of exposing them to the main function, without breaking any existing -code. To that end, [`rustc_ast`][rustc_ast] will create local modules called -`__test_reexports` that recursively reexport tests. This expansion translates -the above example into: +way of exposing them to the main function, without breaking any existing code. +To that end, [`rustc_ast`][rustc_ast] will create local modules called +`__test_reexports` that recursively reexport tests. +This expansion translates the above example into: ```rust,ignore mod my_priv_mod { @@ -61,24 +60,24 @@ mod my_priv_mod { } ``` -Now, our test can be accessed as -`my_priv_mod::__test_reexports::test_priv_func`. For deeper module -structures, `__test_reexports` will reexport modules that contain tests, so a -test at `a::b::my_test` becomes -`a::__test_reexports::b::__test_reexports::my_test`. While this process seems -pretty safe, what happens if there is an existing `__test_reexports` module? +Now, our test can be accessed as `my_priv_mod::__test_reexports::test_priv_func`. +For deeper module structures, `__test_reexports` will reexport modules that contain tests, so a +test at `a::b::my_test` becomes `a::__test_reexports::b::__test_reexports::my_test`. +While this process seems pretty safe, +what happens if there is an existing `__test_reexports` module? The answer: nothing. To explain, we need to understand how Rust's [Abstract Syntax Tree][ast] -represents [identifiers][Ident]. The name of every function, variable, module, -etc. is not stored as a string, but rather as an opaque [Symbol][Symbol] which -is essentially an ID number for each identifier. The compiler keeps a separate -hashtable that allows us to recover the human-readable name of a Symbol when -necessary (such as when printing a syntax error). When the compiler generates -the `__test_reexports` module, it generates a new [Symbol][Symbol] for the -identifier, so while the compiler-generated `__test_reexports` may share a name -with your hand-written one, it will not share a [Symbol][Symbol]. This -technique prevents name collision during code generation and is the foundation +represents [identifiers][Ident]. +The name of every function, variable, module, etc. is not stored as a string, +but rather as an opaque [Symbol] which is essentially an ID number for each identifier. +The compiler keeps a separate hashtable that allows us to recover +the human-readable name of a Symbol when necessary (such as when printing a syntax error). +When the compiler generates the `__test_reexports` module, +it generates a new [Symbol] for the identifier, +so while the compiler-generated `__test_reexports` may share a name +with your hand-written one, it will not share a [Symbol]. +This technique prevents name collision during code generation and is the foundation of Rust's [`macro`] hygiene. ## Step 2: Harness generation @@ -96,20 +95,20 @@ pub fn main() { Here `path::to::test1` is a constant of type [`test::TestDescAndFn`][tdaf]. -While this transformation is simple, it gives us a lot of insight into how -tests are actually run. The tests are aggregated into an array and passed to -a test runner called `test_main_env_args`. We'll come back to exactly what -[`TestDescAndFn`][tdaf] is, but for now, the key takeaway is that there is a crate -called [`test`][test] that is part of Rust core, that implements all of the -runtime for testing. [`test`][test]'s interface is unstable, so the only stable way +While this transformation is simple, it gives us a lot of insight into how tests are actually run. +The tests are aggregated into an array and passed to a test runner called `test_main_env_args`. +We'll come back to exactly what [`TestDescAndFn`][tdaf] is, +but for now, the key takeaway is that there is a crate +called [`test`][test] that is part of Rust core, that implements all of the runtime for testing. +[`test`][test]'s interface is unstable, so the only stable way to interact with it is through the `#[test]` macro. ## Step 3: Test object generation If you've written tests in Rust before, you may be familiar with some of the -optional attributes available on test functions. For example, a test can be -annotated with `#[should_panic]` if we expect the test to cause a panic. It -looks something like this: +optional attributes available on test functions. +For example, a test can be annotated with `#[should_panic]` if we expect the test to cause a panic. +It looks something like this: ```rust,ignore #[test] @@ -120,12 +119,12 @@ fn foo() { ``` This means our tests are more than just simple functions, they have -configuration information as well. `test` encodes this configuration data into -a `struct` called [`TestDesc`]. For each test function in a crate, -[`rustc_ast`][rustc_ast] will parse its attributes and generate a [`TestDesc`] -instance. It then combines the [`TestDesc`] and test function into the -predictably named [`TestDescAndFn`][tdaf] `struct`, that [`test_main_env_args`] -operates on. +configuration information as well. +`test` encodes this configuration data into a `struct` called [`TestDesc`]. +For each test function in a crate, +[`rustc_ast`][rustc_ast] will parse its attributes and generate a [`TestDesc`] instance. +It then combines the [`TestDesc`] and test function into the +predictably named [`TestDescAndFn`][tdaf] `struct`, that [`test_main_env_args`] operates on. For a given test, the generated [`TestDescAndFn`][tdaf] instance looks like so: ```rust,ignore diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md index 4573a04d0281c..ee42cf1fa1d87 100644 --- a/src/doc/rustc-dev-guide/src/tests/compiletest.md +++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md @@ -3,8 +3,8 @@ ## Introduction `compiletest` is the main test harness of the Rust test suite. -It allows test authors to organize large numbers of tests (the Rust compiler has many -thousands), efficient test execution (parallel execution is supported), and +It allows test authors to organize large numbers of tests (the Rust compiler has many thousands), +efficient test execution (parallel execution is supported), and allows the test author to configure behavior and expected results of both individual and groups of tests. @@ -23,14 +23,14 @@ individual and groups of tests. Tests are typically organized as a Rust source file with annotations in comments before and/or within the test code. -These comments serve to direct `compiletest` -on if or how to run the test, what behavior to expect, and more. +These comments serve to direct `compiletest` on if or how to run the test, +what behavior to expect, and more. See [directives](directives.md) and the test suite documentation below for more details on these annotations. See the [Adding new tests](adding.md) and [Best practices](best-practices.md) -chapters for a tutorial on creating a new test and advice on writing a good -test, and the [Running tests](running.md) chapter on how to run the test suite. +chapters for a tutorial on creating a new test and advice on writing a good test, +and the [Running tests](running.md) chapter on how to run the test suite. Arguments can be passed to compiletest using `--test-args` or by placing them after `--`, e.g. - `x test --test-args --force-rerun` @@ -49,8 +49,8 @@ You can use `x test --test-args All of the tests are in the [`tests`] directory. The tests are organized into "suites", with each suite in a separate subdirectory. -Each test suite behaves a -little differently, with different compiler behavior and different checks for correctness. +Each test suite behaves a little differently, +with different compiler behavior and different checks for correctness. For example, the [`tests/incremental`] directory contains tests for incremental compilation. The various suites are defined in [`src/tools/compiletest/src/common.rs`] in the `pub enum Mode` declaration. @@ -114,8 +114,8 @@ The `-Z unpretty` CLI option for `rustc` causes it to translate the input source into various different formats, such as the Rust source after macro expansion. The pretty-printer tests have several [directives](directives.md) described below. -These commands can significantly change the behavior of the test, but the -default behavior without any commands is to: +These commands can significantly change the behavior of the test, +but the default behavior without any commands is to: 1. Run `rustc -Zunpretty=normal` on the source file. 2. Run `rustc -Zunpretty=normal` on the output of the previous step. @@ -133,17 +133,17 @@ The directives for pretty-printing tests are: - `pretty-compare-only` causes a pretty test to only compare the pretty-printed output (stopping after step 3 from above). It will not try to compile the expanded output to type check it. - This is needed for a pretty-mode that does - not expand to valid Rust, or for other situations where the expanded output cannot be compiled. + This is needed for a pretty-mode that does not expand to valid Rust, + or for other situations where the expanded output cannot be compiled. - `pp-exact` is used to ensure a pretty-print test results in specific output. - If specified without a value, then it means the pretty-print output should - match the original source. - If specified with a value, as in `//@ - pp-exact:foo.pp`, it will ensure that the pretty-printed output matches the + If specified without a value, + then it means the pretty-print output should match the original source. + If specified with a value, as in `//@ pp-exact:foo.pp`, + it will ensure that the pretty-printed output matches the contents of the given file. Otherwise, if `pp-exact` is not specified, then - the pretty-printed output will be pretty-printed one more time, and the output - of the two pretty-printing rounds will be compared to ensure that the + the pretty-printed output will be pretty-printed one more time, + and the output of the two pretty-printing rounds will be compared to ensure that the pretty-printed output converges to a steady state. [`tests/pretty`]: https://github.com/rust-lang/rust/tree/HEAD/tests/pretty @@ -166,8 +166,8 @@ Each revision name must start with one of: To make the revisions unique, you should add a suffix like `rpass1` and `rpass2`. -To simulate changing the source, compiletest also passes a `--cfg` flag with the -current revision name. +To simulate changing the source, +compiletest also passes a `--cfg` flag with the current revision name. For example, this will run twice, simulating changing a function: @@ -221,9 +221,10 @@ A simple example of a test using `rustc_clean` is the [hello_world test]. > opt-in. For further context, see: > [Stabilizing the state of the debuginfo test suite](https://github.com/rust-lang/compiler-team/issues/1012) -The tests in [`tests/debuginfo`] test how debuginfo is interpreted by the supported debuggers, and -confirm our visualizers still work as expected. They build a program, launch a debugger, and issue -commands to the debugger. A single test can work with cdb, gdb, and lldb. +The tests in [`tests/debuginfo`] test how debuginfo is interpreted by the supported debuggers, +and confirm our visualizers still work as expected. +They build a program, launch a debugger, and issue commands to the debugger. +A single test can work with cdb, gdb, and lldb. Most tests should have the `//@ compile-flags: -g` directive or something similar to generate the appropriate debuginfo. @@ -245,8 +246,8 @@ The debugger values can be: The command to check the output are of the form `//@ $DEBUGGER-check:$OUTPUT` where `$OUTPUT` is the output to expect. -For example, the following will build the test, start the debugger, set a -breakpoint, launch the program, inspect a value, and check what the debugger prints: +For example, the following will build the test, start the debugger, set a breakpoint, +launch the program, inspect a value, and check what the debugger prints: ```rust,ignore //@ compile-flags: -g @@ -265,7 +266,8 @@ fn b() {} Additionally, there is a special command, `//@ $DEBUGGER-repr:$VAR_NAME` intended to verify variables (and their visualizers) with more granularity than can be achieved with simple string -comparison. This directive should be preferred over the `-command`/`-check` whenever possible. +comparison. +This directive should be preferred over the `-command`/`-check` whenever possible. > [!NOTE] > At time of writing (July 2026) this command is limited to LLDB, with an implementation coming soon @@ -279,8 +281,8 @@ This command effectivly desugars into: ``` The `repr $VAR_NAME` command is intercepted by special logic that uses the debuggers' API to inspect -data that isn't reflected in the variable's printed output. The variable in memory is compared -against input data stored in +data that isn't reflected in the variable's printed output. +The variable in memory is compared against input data stored in `tests/debuginfo//input/_input/.json` and provides detailed error messages on failure. @@ -305,8 +307,9 @@ the debugger currently being used: - `min-apple-lldb-version: 1703.0.236.21`/`min-llvm-lldb-version: 21.1.0` — ignores the test if the version of lldb is below the given version. Note: Apple's fork of LLDB (distributed with Xcode) uses a different versioning scheme that is not - easily mappable to LLVM's LLDB version numbers. As such, the version gates are specified by - vendor. Further info on manually checking version equivalence is available [here](../debuginfo/testing.md#lldb-versioning) + easily mappable to LLVM's LLDB version numbers. + As such, the version gates are specified by vendor. + Further info on manually checking version equivalence is available [here](../debuginfo/testing.md#lldb-versioning) - `rust-lldb` — ignores the test if lldb is not contain the Rust plugin. NOTE: The "Rust" version of LLDB doesn't exist anymore, so this will always be ignored. This should probably be removed. @@ -361,14 +364,14 @@ See the [FileCheck] documentation for a tutorial and more information. See also the [assembly tests](#assembly-tests) for a similar set of tests. By default, codegen tests will have `//@ needs-target-std` *implied* (that the -target needs to support std), *unless* the `#![no_std]`/`#![no_core]` attribute -was specified in the test source. +target needs to support std), +*unless* the `#![no_std]`/`#![no_core]` attribute was specified in the test source. You can override this behavior and explicitly -write `//@ needs-target-std` to only run the test when target supports std, even -if the test is `#![no_std]`/`#![no_core]`. +write `//@ needs-target-std` to only run the test when target supports std, +even if the test is `#![no_std]`/`#![no_core]`. -If you need to work with `#![no_std]` cross-compiling tests, consult the -[`minicore` test auxiliary](./minicore.md) chapter. +If you need to work with `#![no_std]` cross-compiling tests, +consult the [`minicore` test auxiliary](./minicore.md) chapter. [`tests/codegen-llvm`]: https://github.com/rust-lang/rust/tree/HEAD/tests/codegen-llvm [FileCheck]: https://llvm.org/docs/CommandGuide/FileCheck.html @@ -388,8 +391,8 @@ See the [FileCheck] documentation for a tutorial and more information. See also the [codegen tests](#codegen-tests) for a similar set of tests. -If you need to work with `#![no_std]` cross-compiling tests, consult the -[`minicore` test auxiliary](./minicore.md) chapter. +If you need to work with `#![no_std]` cross-compiling tests, +consult the [`minicore` test auxiliary](./minicore.md) chapter. [`tests/assembly-llvm`]: https://github.com/rust-lang/rust/tree/HEAD/tests/assembly-llvm @@ -444,10 +447,10 @@ There are several forms the `EMIT_MIR` comment can take: interested in the final state after an optimization. Some rare cases may want to use the "before" file for completeness. -- `// EMIT_MIR $MIR_PATH.diff` — where `$MIR_PATH` is the filename of the MIR - dump, such as `my_test_name.my_function.EarlyOtherwiseBranch`. - Compiletest will diff the `.before.mir` and `.after.mir` files, and compare the diff - output to the expected `.diff` file from the `EMIT_MIR` comment. +- `// EMIT_MIR $MIR_PATH.diff` — where `$MIR_PATH` is the filename of the MIR dump, + such as `my_test_name.my_function.EarlyOtherwiseBranch`. + Compiletest will diff the `.before.mir` and `.after.mir` files, + and compare the diff output to the expected `.diff` file from the `EMIT_MIR` comment. This is useful if you want to see how an optimization changes the MIR. @@ -457,8 +460,8 @@ There are several forms the `EMIT_MIR` comment can take: By default 32 bit and 64 bit targets use the same dump files, which can be problematic in the presence of pointers in constants or other bit width dependent things. -In that case you can add `// EMIT_MIR_FOR_EACH_BIT_WIDTH` to -your test, causing separate files to be generated for 32bit and 64bit systems. +In that case you can add `// EMIT_MIR_FOR_EACH_BIT_WIDTH` to your test, +causing separate files to be generated for 32bit and 64bit systems. [`tests/mir-opt`]: https://github.com/rust-lang/rust/tree/HEAD/tests/mir-opt @@ -491,8 +494,8 @@ Each test should be in a separate directory with a `rmake.rs` Rust program, called the *recipe*. A recipe will be compiled and executed by compiletest with the `run_make_support` library linked in. -If you need new utilities or functionality, consider extending and improving the -[`run_make_support`] library. +If you need new utilities or functionality, +consider extending and improving the [`run_make_support`] library. Compiletest directives like `//@ only-` or `//@ ignore-` are supported in `rmake.rs`, like in UI tests. @@ -524,11 +527,11 @@ Of course, some tests will not successfully *run* in this way. #### Using rust-analyzer with `rmake.rs` -Like other test programs, the `rmake.rs` scripts used by run-make tests do not -have rust-analyzer integration by default. +Like other test programs, +the `rmake.rs` scripts used by run-make tests do not have rust-analyzer integration by default. -To work around this when working on a particular test, temporarily create a -`Cargo.toml` file in the test's directory +To work around this when working on a particular test, +temporarily create a `Cargo.toml` file in the test's directory (e.g. `tests/run-make/sysroot-crates-are-unstable/Cargo.toml`) with these contents: @@ -589,8 +592,8 @@ Each mode also has an alias to run the coverage tests in just that mode: ./x test coverage-map -- tests/coverage/if.rs # runs the specified test in "coverage-map" mode only ``` -If a particular test should not be run in one of the coverage test modes for -some reason, use the `//@ ignore-coverage-map` or `//@ ignore-coverage-run` directives. +If a particular test should not be run in one of the coverage test modes for some reason, +use the `//@ ignore-coverage-map` or `//@ ignore-coverage-run` directives. #### `coverage-map` suite @@ -598,11 +601,11 @@ In `coverage-map` mode, these tests verify the mappings between source code regions and coverage counters that are emitted by LLVM. They compile the test with `--emit llvm-ir`, then use a custom tool ([`src/tools/coverage-dump`]) to extract and pretty-print the coverage mappings embedded in the IR. -These tests don't require the profiler runtime, so they run in PR CI jobs and are easy to -run/bless locally. +These tests don't require the profiler runtime, +so they run in PR CI jobs and are easy to run/bless locally. -These coverage map tests can be sensitive to changes in MIR lowering or MIR -optimizations, producing mappings that are different but produce identical coverage reports. +These coverage map tests can be sensitive to changes in MIR lowering or MIR optimizations, +producing mappings that are different but produce identical coverage reports. As a rule of thumb, any PR that doesn't change coverage-specific code should **feel free to re-bless** the `coverage-map` tests as necessary, without @@ -612,19 +615,19 @@ worrying about the actual changes, as long as the `coverage-run` tests still pas In `coverage-run` mode, these tests perform an end-to-end test of coverage reporting. They compile a test program with coverage instrumentation, run that -program to produce raw coverage data, and then use LLVM tools to process that -data into a human-readable code coverage report. +program to produce raw coverage data, +and then use LLVM tools to process that data into a human-readable code coverage report. -Instrumented binaries need to be linked against the LLVM profiler runtime, so -`coverage-run` tests are **automatically skipped** unless the profiler runtime +Instrumented binaries need to be linked against the LLVM profiler runtime, +so `coverage-run` tests are **automatically skipped** unless the profiler runtime is enabled in `bootstrap.toml`: ```toml build.profiler = true ``` -This also means that they typically don't run in PR CI jobs, though they do run -as part of the full set of CI jobs used for merging. +This also means that they typically don't run in PR CI jobs, +though they do run as part of the full set of CI jobs used for merging. #### `coverage-run-rustdoc` suite @@ -638,8 +641,8 @@ This avoids having to build rustdoc when only running the main `coverage` suite. ### Crash tests -[`tests/crashes`] serve as a collection of tests that are expected to cause the -compiler to ICE, panic or crash in some other way, so that accidental fixes are tracked. +[`tests/crashes`] serve as a collection of tests that are expected to cause the compiler to ICE, +panic, or crash in some other way, so that accidental fixes are tracked. Formerly, this was done at but doing it inside the rust-lang/rust testsuite is more convenient. @@ -659,8 +662,8 @@ When you do so, each issue number should be noted in the file name (`12345.rs` should suffice) and also inside the file by means of a `//@ known-bug: #12345` directive. Please [label][labeling] the relevant issues with `S-bug-has-test` once your PR is merged. -If you happen to fix one of the crashes, please move it to a fitting -subdirectory in `tests/ui` and give it a meaningful name. +If you happen to fix one of the crashes, +please move it to a fitting subdirectory in `tests/ui` and give it a meaningful name. Please add a doc comment at the top of the file explaining why this test exists. Even better will be if you can briefly explain how the example caused rustc to crash previously, and what was done to fix it. @@ -711,8 +714,8 @@ The `-L` flag is used to find the extern crates. `aux-crate` is very similar to `aux-build`. However, it uses the `--extern` flag to link to the extern crate to make the crate be available as an extern prelude. -That allows you to specify the additional syntax of the `--extern` flag, such as -renaming a dependency. +That allows you to specify the additional syntax of the `--extern` flag, +such as renaming a dependency. For example, `//@ aux-crate: foo=bar.rs` will compile `auxiliary/bar.rs` and make it available under the name `foo` within the test. This is similar to how Cargo does dependency renaming. @@ -722,8 +725,8 @@ For example, `//@ aux-crate: noprelude:foo=bar.rs`. `aux-bin` is similar to `aux-build` but will build a binary instead of a library. The binary will be available in `auxiliary/bin` relative to the working directory of the test. -`aux-codegen-backend` is similar to `aux-build`, but will then pass the compiled -dylib to `-Zcodegen-backend` when building the main file. +`aux-codegen-backend` is similar to `aux-build`, +but will then pass the compiled dylib to `-Zcodegen-backend` when building the main file. This will only work for tests in `tests/ui-fulldeps`, since it requires the use of compiler crates. ### Auxiliary proc-macro @@ -740,8 +743,8 @@ preset behavior compared to `aux-build` for the proc-macro test auxiliary: to produce a dylib for the aux crate. 3. The aux crate is made available to the test file via extern prelude with `--extern `. - Note that since UI tests default to edition - 2015, you still need to specify `extern ` unless the main + Note that since UI tests default to edition 2015, + you still need to specify `extern ` unless the main test file is using an edition that is 2018 or newer if you want to use the aux crate name in a `use` import. 4. The `proc_macro` crate is made available as an extern prelude module. @@ -794,8 +797,8 @@ This is done by adding a special directive at the top of the file: //@ revisions: foo bar baz ``` -This will result in the test being compiled (and tested) three times, once with -`--cfg foo`, once with `--cfg bar`, and once with `--cfg baz`. +This will result in the test being compiled (and tested) three times, once with `--cfg foo`, +once with `--cfg bar`, and once with `--cfg baz`. You can therefore use `#[cfg(foo)]` etc within the test to tweak each of these results. You can also customize directives and expected error messages to a particular revision. @@ -814,8 +817,8 @@ fn test_foo() { Multiple revisions can be specified in a comma-separated list, such as `//[foo,bar,baz]~^`. -In test suites that use the LLVM [FileCheck] tool, the current revision name is -also registered as an additional prefix for FileCheck directives: +In test suites that use the LLVM [FileCheck] tool, +the current revision name is also registered as an additional prefix for FileCheck directives: ```rust,ignore //@ revisions: NORMAL COVERAGE @@ -848,8 +851,8 @@ Normally, revision names mentioned in other directives and error annotations must correspond to an actual revision declared in a `revisions` directive. This is enforced by an `./x test tidy` check. -If a revision name needs to be temporarily removed from the revision list for -some reason, the above check can be suppressed by adding the revision name to an +If a revision name needs to be temporarily removed from the revision list for some reason, +the above check can be suppressed by adding the revision name to an `//@ unused-revision-names:` header instead. Specifying an unused name of `*` (i.e. `//@ unused-revision-names: *`) will @@ -857,10 +860,10 @@ permit any unused revision name to be mentioned. ## Compare modes -Compiletest can be run in different modes, called _compare modes_, which can be -used to compare the behavior of all tests with different compiler flags enabled. -This can help highlight what differences might appear with certain flags, and -check for any problems that might arise. +Compiletest can be run in different modes, called _compare modes_, +which can be used to compare the behavior of all tests with different compiler flags enabled. +This can help highlight what differences might appear with certain flags, +and check for any problems that might arise. To run the tests in a different mode, you need to pass the `--compare-mode` CLI flag: @@ -885,8 +888,8 @@ In CI, compare modes are only used in one Linux builder, and only with the follo This helps ensure that none of the debuginfo tests are affected when enabling split-DWARF. Note that compare modes are separate to [revisions](#revisions). -All revisions are tested when running `./x test tests/ui`, however compare-modes must be -manually run individually via the `--compare-mode` flag. +All revisions are tested when running `./x test tests/ui`, +however compare-modes must be manually run individually via the `--compare-mode` flag. ## Parallel frontend diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index 22735c124fb43..a12b602d9c2d6 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -148,8 +148,7 @@ Some examples of `X` in `ignore-X` or `only-X`: - When particular debuggers are being tested: `cdb`, `gdb`, `lldb` - When particular debugger versions are matched: `ignore-gdb-version` - When the [parallel frontend] is enabled: `ignore-parallel-frontend` -- Specific [compare modes]: `compare-mode-polonius`, - `compare-mode-next-solver`, `compare-mode-next-solver-coherence`, +- Specific [compare modes]: `compare-mode-polonius`, `compare-mode-next-solver`, `compare-mode-split-dwarf`, `compare-mode-split-dwarf-single` - The two different test modes used by coverage tests: `ignore-coverage-map`, `ignore-coverage-run` diff --git a/src/doc/rustc-dev-guide/src/tests/ecosystem-test-jobs/fuchsia.md b/src/doc/rustc-dev-guide/src/tests/ecosystem-test-jobs/fuchsia.md index 3f274464c6e63..19e3fb44bb8ed 100644 --- a/src/doc/rustc-dev-guide/src/tests/ecosystem-test-jobs/fuchsia.md +++ b/src/doc/rustc-dev-guide/src/tests/ecosystem-test-jobs/fuchsia.md @@ -14,12 +14,11 @@ Please contact the [fuchsia][fuchsia-ping] ping group and ask them for help. ## Building Fuchsia in CI -Fuchsia builds as part of the suite of bors tests that run before a pull request -is merged. +Fuchsia builds as part of the suite of bors tests that run before a pull request is merged. If you are worried that a pull request might break the Fuchsia builder and want -to test it out before submitting it to the bors queue, simply ask bors to run -the try job that builds the Fuchsia integration: +to test it out before submitting it to the bors queue, +simply ask bors to run the try job that builds the Fuchsia integration: ```text @bors try jobs=test-x86_64-fuchsia @@ -27,13 +26,13 @@ the try job that builds the Fuchsia integration: ## Building Fuchsia locally -Because Fuchsia uses languages other than Rust, it does not use Cargo as a build -system. It also requires the toolchain build to be configured in a [certain -way][build-toolchain]. +Because Fuchsia uses languages other than Rust, it does not use Cargo as a build system. +It also requires the toolchain build to be configured in a [certain way][build-toolchain]. The recommended way to build Fuchsia is to use the Docker scripts that check out -and run a Fuchsia build for you. If you've run Docker tests before, you can -simply run this command from your Rust checkout to download and build Fuchsia +and run a Fuchsia build for you. +If you've run Docker tests before, +you can simply run this command from your Rust checkout to download and build Fuchsia using your local Rust toolchain. ``` @@ -44,20 +43,20 @@ See the [Testing with Docker](../docker.md) chapter for more details on how to r and debug jobs with Docker. Note that a Fuchsia checkout is *large* – as of this writing, a checkout and -build takes over 67G of space – and as you might imagine, it takes a while to -complete. +build takes over 67G of space – and as you might imagine, it takes a while to complete. ### Modifying the Fuchsia checkout The main reason you would want to build Fuchsia locally is because you need to -investigate a regression. After running a Docker build, you'll find the Fuchsia -checkout inside the `obj/test-x86_64-fuchsia/fuchsia` directory of your Rust -checkout. If you modify the `KEEP_CHECKOUT` line in the [build-fuchsia.sh] -script to `KEEP_CHECKOUT=1`, you can change the checkout as needed and rerun -the build command above. This will reuse all the build results from before. +investigate a regression. +After running a Docker build, +you'll find the Fuchsia checkout inside the `obj/test-x86_64-fuchsia/fuchsia` directory of your Rust +checkout. +If you modify the `KEEP_CHECKOUT` line in the [build-fuchsia.sh] script to `KEEP_CHECKOUT=1`, +you can change the checkout as needed and rerun the build command above. +This will reuse all the build results from before. -You can find more options to customize the Fuchsia checkout in the -[build-fuchsia.sh] script. +You can find more options to customize the Fuchsia checkout in the [build-fuchsia.sh] script. ### Customizing the Fuchsia build @@ -73,14 +72,15 @@ to add this to your `$PATH` for some workflows. There are a few `fx` subcommands that are relevant, including: -- `fx set` accepts build arguments, writes them to `out/default/args.gn`, and - runs GN. -- `fx build` builds the Fuchsia project using Ninja. It will automatically pick - up changes to build arguments and rerun GN. By default it builds everything, +- `fx set` accepts build arguments, writes them to `out/default/args.gn`, and runs GN. +- `fx build` builds the Fuchsia project using Ninja. + It will automatically pick up changes to build arguments and rerun GN. + By default, it builds everything, but it also accepts target paths to build specific targets (see below). -- `fx clippy` runs Clippy on specific Rust targets (or all of them). We use this - in the Rust CI build to avoid running codegen on most Rust targets. Underneath - it invokes Ninja, just like `fx build`. The clippy results are saved in json +- `fx clippy` runs Clippy on specific Rust targets (or all of them). + We use this in the Rust CI build to avoid running codegen on most Rust targets. + Underneath, it invokes Ninja, just like `fx build`. + The clippy results are saved in json files inside the build output directory before being printed. #### Target paths @@ -91,20 +91,20 @@ GN uses paths like the following to identify build targets: //src/starnix/kernel:starnix_core ``` -The initial `//` means the root of the checkout, and the remaining slashes are -directory names. The string after `:` is the _target name_ of a target defined +The initial `//` means the root of the checkout, and the remaining slashes are directory names. +The string after `:` is the _target name_ of a target defined in the `BUILD.gn` file of that directory. -The target name can be omitted if it is the same as the directory name. In other -words, `//src/starnix/kernel` is the same as `//src/starnix/kernel:kernel`. +The target name can be omitted if it is the same as the directory name. +In other words, `//src/starnix/kernel` is the same as `//src/starnix/kernel:kernel`. These target paths are used inside `BUILD.gn` files to reference dependencies, and can also be used in `fx build`. #### Modifying compiler flags -You can put custom compiler flags inside a GN `config` that is added to a -target. As a simple example: +You can put custom compiler flags inside a GN `config` that is added to a target. +As a simple example: ``` config("everybody_loops") { @@ -118,20 +118,20 @@ rustc_binary("example") { } ``` -This will add the flag `-Zeverybody-loops` to rustc when building the `example` -target. Note that you can also use [`public_configs`] for a config to be added +This will add the flag `-Zeverybody-loops` to rustc when building the `example` target. +Note that you can also use [`public_configs`] for a config to be added to every target that depends on that target. -If you want to add a flag to every Rust target in the build, you can add -rustflags to the [`//build/config:compiler`] config or to the OS-specific -configs referenced in that file. Note that `cflags` and `ldflags` are ignored on -Rust targets. +If you want to add a flag to every Rust target in the build, +you can add rustflags to the [`//build/config:compiler`] config or to the OS-specific +configs referenced in that file. +Note that `cflags` and `ldflags` are ignored on Rust targets. #### Running ninja and rustc commands directly -Going down one layer, `fx build` invokes `ninja`, which in turn eventually -invokes `rustc`. All build actions are run inside the out directory, which is -usually `out/default` inside the Fuchsia checkout. +Going down one layer, `fx build` invokes `ninja`, which in turn eventually invokes `rustc`. +All build actions are run inside the out directory, +which is usually `out/default` inside the Fuchsia checkout. You can get ninja to print the actual command it invokes by forcing that command to fail, e.g. by adding a syntax error to one of the source files of the target. @@ -139,26 +139,25 @@ Once you have the command, you can run it from inside the output directory. After changing the toolchain itself, the build setting `rustc_version_string` in `out/default/args.gn` needs to be changed so that `fx build` or `ninja` will -rebuild all the Rust targets. This can be done in a text editor and the contents -of the string do not matter, as long as it changes from one build to the next. -[build_fuchsia_from_rust_ci.sh] does this for you by hashing the toolchain -directory. +rebuild all the Rust targets. +This can be done in a text editor, and the contents of the string do not matter, +as long as it changes from one build to the next. +[build_fuchsia_from_rust_ci.sh] does this for you by hashing the toolchain directory. The Fuchsia website has more detailed documentation of the [build system]. #### Other tips and tricks When using `build_fuchsia_from_rust_ci.sh` you can comment out the `fx set` -command after the initial run so it won't rerun GN each time. If you do this you -can also comment out the version_string line to save a couple seconds. +command after the initial run so it won't rerun GN each time. +If you do this, you can also comment out the version_string line to save a couple seconds. -`export NINJA_PERSISTENT_MODE=1` to get faster ninja startup times after the -initial build. +`export NINJA_PERSISTENT_MODE=1` to get faster ninja startup times after the initial build. ## Fuchsia target support -To learn more about Fuchsia target support, see the Fuchsia chapter in [the -rustc book][platform-support]. +To learn more about Fuchsia target support, +see the Fuchsia chapter in [the rustc book][platform-support]. [regressions]: https://gist.github.com/tmandry/7103eba4bd6a6fb0c439b5a90ae355fa [build-toolchain]: https://fuchsia.dev/fuchsia-src/development/build/rust_toolchain @@ -173,5 +172,5 @@ rustc book][platform-support]. [fuchsia-ping]: ../../notification-groups/fuchsia.md [^loc]: As of June 2024, Fuchsia had about 2 million lines of first-party Rust -code and a roughly equal amount of third-party code, as counted by tokei -(excluding comments and blanks). +code and a roughly equal amount of third-party code, +as counted by tokei (excluding comments and blanks). diff --git a/src/doc/rustc-dev-guide/src/tests/ecosystem-test-jobs/rust-for-linux.md b/src/doc/rustc-dev-guide/src/tests/ecosystem-test-jobs/rust-for-linux.md index a6a7374b811be..e2d3bec5d86f7 100644 --- a/src/doc/rustc-dev-guide/src/tests/ecosystem-test-jobs/rust-for-linux.md +++ b/src/doc/rustc-dev-guide/src/tests/ecosystem-test-jobs/rust-for-linux.md @@ -7,23 +7,22 @@ support for the Rust programming language into the Linux kernel. If a PR breaks the Rust for Linux CI job, then: -- If the breakage was unintentional and seems spurious, then let [RfL][rfl-ping] - know and retry. - - If the PR is urgent and retrying doesn't fix it, then disable the CI job - temporarily (comment out the `image: x86_64-rust-for-linux` job in +- If the breakage was unintentional and seems spurious, then let [RfL][rfl-ping] know and retry. + - If the PR is urgent and retrying doesn't fix it, + then disable the CI job temporarily (comment out the `image: x86_64-rust-for-linux` job in `src/ci/github-actions/jobs.yml`). - If the breakage was unintentional, then change the PR to resolve the breakage. -- If the breakage was intentional, then let [RfL][rfl-ping] know and discuss - what will the kernel need to change. +- If the breakage was intentional, + then let [RfL][rfl-ping] know and discuss what will the kernel need to change. - If the PR is urgent, then disable the CI job temporarily (comment out the `image: x86_64-rust-for-linux` job in `src/ci/github-actions/jobs.yml`). - If the PR can wait a few days, then wait for RfL maintainers to provide a - new Linux kernel commit hash with the needed changes done, and apply it to - the PR, which would confirm the changes work (update the `LINUX_VERSION` + new Linux kernel commit hash with the needed changes done, and apply it to the PR, + which would confirm the changes work (update the `LINUX_VERSION` environment variable in `src/ci/docker/scripts/rfl-build.sh`). -If you need to contact the RfL developers, you can ping the [Rust for Linux][rfl-ping] -ping group to ask for help: +If you need to contact the RfL developers, +you can ping the [Rust for Linux][rfl-ping] ping group to ask for help: ```text @rustbot ping rfl @@ -31,17 +30,17 @@ ping group to ask for help: ## Building Rust for Linux in CI -Rust for Linux builds as part of the suite of bors tests that run before a pull -request is merged. +Rust for Linux builds as part of the suite of bors tests that run before a pull request is merged. -The workflow builds a stage1 sysroot of the Rust compiler, downloads the Linux -kernel, and tries to compile several Rust for Linux drivers and examples using -this sysroot. RfL uses several unstable compiler/language features, therefore -this workflow notifies us if a given compiler change would break it. +The workflow builds a stage1 sysroot of the Rust compiler, downloads the Linux kernel, +and tries to compile several Rust for Linux drivers and examples using +this sysroot. +RfL uses several unstable compiler/language features, +therefore this workflow notifies us if a given compiler change would break it. If you are worried that a pull request might break the Rust for Linux builder -and want to test it out before submitting it to the bors queue, simply ask -bors to run the try job that builds the Rust for Linux integration: +and want to test it out before submitting it to the bors queue, +simply ask bors to run the try job that builds the Rust for Linux integration: `@bors try jobs=x86_64-rust-for-linux`. [rfl-ping]: ../../notification-groups/rust-for-linux.md diff --git a/src/doc/rustc-dev-guide/src/tests/ecosystem.md b/src/doc/rustc-dev-guide/src/tests/ecosystem.md index 9e5b3a1e1c11d..ff9aca59ebe49 100644 --- a/src/doc/rustc-dev-guide/src/tests/ecosystem.md +++ b/src/doc/rustc-dev-guide/src/tests/ecosystem.md @@ -7,9 +7,10 @@ regressions and make informed decisions about the evolution of the language. ### Crater -Crater is a tool which runs tests on many thousands of public projects. This -tool has its own separate infrastructure for running, and is not run as part of -CI. See the [Crater chapter](crater.md) for more details. +Crater is a tool which runs tests on many thousands of public projects. +This tool has its own separate infrastructure for running, and is not run as part of +CI. +See the [Crater chapter](crater.md) for more details. ### `cargotest` @@ -23,8 +24,8 @@ there aren't any significant regressions: ### Large OSS Project builders -We have CI jobs that build large open-source Rust projects that are used as -regression tests in CI. Our integration jobs build the following projects: +We have CI jobs that build large open-source Rust projects that are used as regression tests in CI. +Our integration jobs build the following projects: - [Fuchsia](./ecosystem-test-jobs/fuchsia.md) - [Rust for Linux](./ecosystem-test-jobs/rust-for-linux.md) diff --git a/src/doc/rustc-dev-guide/src/tests/intro.md b/src/doc/rustc-dev-guide/src/tests/intro.md index 82fb9597cb500..8c1e6c65b1042 100644 --- a/src/doc/rustc-dev-guide/src/tests/intro.md +++ b/src/doc/rustc-dev-guide/src/tests/intro.md @@ -1,9 +1,9 @@ # Testing the compiler -The Rust project runs a wide variety of different tests, orchestrated by the -build system (`./x test`). This section gives a brief overview of the different -testing tools. Subsequent chapters dive into [running tests](running.md) and -[adding new tests](adding.md). +The Rust project runs a wide variety of different tests, +orchestrated by the build system (`./x test`). +This section gives a brief overview of the different testing tools. +Subsequent chapters dive into [running tests](running.md) and [adding new tests](adding.md). ## Kinds of tests @@ -12,12 +12,12 @@ Almost all of them are driven by `./x test`, with some exceptions noted below. ### Compiletest -The main test harness for testing the compiler itself is a tool called -[compiletest]. +The main test harness for testing the compiler itself is a tool called [compiletest]. -[compiletest] supports running different styles of tests, organized into *test -suites*. A *test mode* may provide common presets/behavior for a set of *test -suites*. [compiletest]-supported tests are located in the [`tests`] directory. +[compiletest] supports running different styles of tests, +organized into *test suites*. +A *test mode* may provide common presets/behavior for a set of *test suites*. +[compiletest]-supported tests are located in the [`tests`] directory. The [Compiletest chapter][compiletest] goes into detail on how to use this tool. @@ -28,9 +28,9 @@ The [Compiletest chapter][compiletest] goes into detail on how to use this tool. ### Package tests -The standard library and many of the compiler packages include typical Rust -`#[test]` unit tests, integration tests, and documentation tests. You can pass a -path to `./x test` for almost any package in the `library/` or `compiler/` +The standard library and many of the compiler packages include typical Rust `#[test]` unit tests, +integration tests, and documentation tests. +You can pass a path to `./x test` for almost any package in the `library/` or `compiler/` directory, and `x` will essentially run `cargo test` on that package. Examples: @@ -41,13 +41,14 @@ Examples: | `./x test library/core` | Runs tests on `core` only | | `./x test compiler/rustc_data_structures` | Runs tests on `rustc_data_structures` | -The standard library relies very heavily on documentation tests to cover its -functionality. However, unit tests and integration tests can also be used as -needed. Almost all of the compiler packages have doctests disabled. +The standard library relies very heavily on documentation tests to cover its functionality. +However, unit tests and integration tests can also be used as needed. +Almost all of the compiler packages have doctests disabled. All standard library and compiler unit tests are placed in separate `tests` file -(which is enforced in [tidy][tidy-unit-tests]). This ensures that when the test -file is changed, the crate does not need to be recompiled. For example: +(which is enforced in [tidy][tidy-unit-tests]). +This ensures that when the test file is changed, the crate does not need to be recompiled. +For example: ```rust,ignore #[cfg(test)] @@ -55,11 +56,9 @@ mod tests; ``` If it wasn't done this way, and you were working on something like `core`, that -would require recompiling the entire standard library, and the entirety of -`rustc`. +would require recompiling the entire standard library, and the entirety of `rustc`. -`./x test` includes some CLI options for controlling the behavior with these -package tests: +`./x test` includes some CLI options for controlling the behavior with these package tests: * `--doc` — Only runs documentation tests in the package. * `--all-targets` — Run all tests *except* documentation tests. @@ -69,8 +68,9 @@ package tests: ### Tidy -Tidy is a custom tool used for validating source code style and formatting -conventions, such as rejecting long lines. There is more information in the +Tidy is a custom tool used for validating source code style and formatting conventions, +such as rejecting long lines. +There is more information in the [section on coding conventions](../conventions.md#formatting) or the [Tidy Readme]. > Examples: `./x test tidy` @@ -80,9 +80,8 @@ conventions, such as rejecting long lines. There is more information in the ### Formatting -Rustfmt is integrated with the build system to enforce uniform style across the -compiler. The formatting check is automatically run by the Tidy tool mentioned -above. +Rustfmt is integrated with the build system to enforce uniform style across the compiler. +The formatting check is automatically run by the Tidy tool mentioned above. Examples: @@ -94,10 +93,10 @@ Examples: ### Book documentation tests -All of the books that are published have their own tests, primarily for -validating that the Rust code examples pass. Under the hood, these are -essentially using `rustdoc --test` on the markdown files. The tests can be run -by passing a path to a book to `./x test`. +All of the books that are published have their own tests, +primarily for validating that the Rust code examples pass. +Under the hood, these are essentially using `rustdoc --test` on the markdown files. +The tests can be run by passing a path to a book to `./x test`. > Example: `./x test src/doc/book` @@ -114,8 +113,8 @@ This requires building all of the documentation, which might take a while. ### `distcheck` -`distcheck` verifies that the source distribution tarball created by the build -system will unpack, build, and run all tests. +`distcheck` verifies that the source distribution tarball created by the build system will unpack, +build, and run all tests. ```console ./x test distcheck @@ -123,25 +122,24 @@ system will unpack, build, and run all tests. ### Tool tests -Packages that are included with Rust have all of their tests run as well. This -includes things such as cargo, clippy, rustfmt, miri, bootstrap (testing the +Packages that are included with Rust have all of their tests run as well. +This includes things such as cargo, clippy, rustfmt, miri, bootstrap (testing the Rust build system itself), etc. -Most of the tools are located in the [`src/tools`] directory. To run the tool's -tests, just pass its path to `./x test`. +Most of the tools are located in the [`src/tools`] directory. +To run the tool's tests, just pass its path to `./x test`. > Example: `./x test src/tools/cargo` Usually these tools involve running `cargo test` within the tool's directory. -If you want to run only a specified set of tests, append `--test-args -FILTER_NAME` to the command. +If you want to run only a specified set of tests, append `--test-args FILTER_NAME` to the command. > Example: `./x test src/tools/miri --test-args padding` -In CI, some tools are allowed to fail. Failures send notifications to the -corresponding teams, and is tracked on the [toolstate website]. More information -can be found in the [toolstate documentation]. +In CI, some tools are allowed to fail. +Failures send notifications to the corresponding teams, and are tracked on the [toolstate website]. +More information can be found in the [toolstate documentation]. [`src/tools`]: https://github.com/rust-lang/rust/tree/HEAD/src/tools/ [toolstate documentation]: https://forge.rust-lang.org/infra/toolstate.html @@ -150,14 +148,14 @@ can be found in the [toolstate documentation]. ### Ecosystem testing Rust tests integration with real-world code to catch regressions and make -informed decisions about the evolution of the language. There are several kinds -of ecosystem tests, including Crater. See the [Ecosystem testing -chapter](ecosystem.md) for more details. +informed decisions about the evolution of the language. +There are several kinds of ecosystem tests, including Crater. +See the [Ecosystem testing chapter](ecosystem.md) for more details. ### Performance testing -A separate infrastructure is used for testing and tracking performance of the -compiler. See the [Performance testing chapter](perf.md) for more details. +A separate infrastructure is used for testing and tracking performance of the compiler. +See the [Performance testing chapter](perf.md) for more details. ### Codegen backend testing diff --git a/src/doc/rustc-dev-guide/src/tests/misc.md b/src/doc/rustc-dev-guide/src/tests/misc.md index 34f9faa9cc722..8fe49c82e9873 100644 --- a/src/doc/rustc-dev-guide/src/tests/misc.md +++ b/src/doc/rustc-dev-guide/src/tests/misc.md @@ -12,15 +12,10 @@ This is a bootstrap/compiler implementation detail, but it can also be useful fo This is useful because some behaviors of the compiler (e.g. diagnostics) can differ depending on whether the compiler is nightly or not. -In `ui` tests and other test suites that support `//@ rustc-env`, you can specify - -```rust,ignore -// Force unstable features to be usable on stable rustc -//@ rustc-env:RUSTC_BOOTSTRAP=1 - -// Or force nightly rustc to pretend it is a stable rustc -//@ rustc-env:RUSTC_BOOTSTRAP=-1 -``` +Note that setting `RUSTC_BOOTSTRAP` in ui tests (e.g. via `//@ rustc-env`) is pointless since ui +tests inherit the bootstrap env var from bootstrap (so it's set to `1` by default), and since +compiletest itself passes `-Z` flags (so `-1` breaks the test). To test stable-only behavior, you +need to write a `run-make` test instead. For `run-make`/`run-make-cargo` tests, `//@ rustc-env` is not supported. You can do something like the following for individual `rustc` invocations. diff --git a/src/doc/rustc-dev-guide/src/thir.md b/src/doc/rustc-dev-guide/src/thir.md index 1ba30d86a6913..8b80d0be16874 100644 --- a/src/doc/rustc-dev-guide/src/thir.md +++ b/src/doc/rustc-dev-guide/src/thir.md @@ -136,7 +136,7 @@ Thir { kind: Scope { region_scope: Node(5), hir_id: HirId(DefId(0:3 ~ main[26fd]::main).5), - // reference to expression 0 above + // reference to expression 2 above value: e2, }, ty: i32, diff --git a/src/doc/rustc-dev-guide/src/tracing.md b/src/doc/rustc-dev-guide/src/tracing.md index ae819003e1f3e..0e2f29f698cba 100644 --- a/src/doc/rustc-dev-guide/src/tracing.md +++ b/src/doc/rustc-dev-guide/src/tracing.md @@ -1,10 +1,9 @@ # Using tracing to debug the compiler -The compiler has a lot of [`debug!`] (or `trace!`) calls, which print out logging information -at many points. -These are very useful to at least narrow down the location of -a bug if not to find it entirely, or just to orient yourself as to why the -compiler is doing a particular thing. +The compiler has a lot of [`debug!`] (or `trace!`) calls, +which print out logging information at many points. +These are very useful to at least narrow down the location of a bug if not to find it entirely, +or just to orient yourself as to why the compiler is doing a particular thing. [`debug!`]: https://docs.rs/tracing/0.1/tracing/macro.debug.html @@ -66,8 +65,8 @@ RUSTC_LOG=rustc_borrowck[do_mir_borrowck] ### I don't want all calls -If you are compiling libcore, you likely don't want *all* borrowck dumps, but only one -for a specific function. +If you are compiling libcore, you likely don't want *all* borrowck dumps, +but only one for a specific function. You can filter function calls by their arguments by regexing them. ``` @@ -75,8 +74,8 @@ RUSTC_LOG=[do_mir_borrowck{id=\.\*from_utf8_unchecked\.\*}] ``` will only give you the logs of borrowchecking `from_utf8_unchecked`. -Note that you will -still get a short message per ignored `do_mir_borrowck`, but none of the things inside those calls. +Note that you will still get a short message per ignored `do_mir_borrowck`, +but none of the things inside those calls. This helps you in looking through the calls that are happening and helps you adjust your regex if you mistyped it. @@ -105,40 +104,39 @@ You can find a list of queries and their arguments in ## Broad module level filters -You can also use filters similar to the `log` crate's filters, which will enable -everything within a specific module. +You can also use filters similar to the `log` crate's filters, +which will enable everything within a specific module. This is often too verbose and too unstructured, so it is recommended to use function level filters. Your log filter can be just `debug` to get all `debug!` output and higher (e.g., it will also include `info!`), or `path::to::module` to get *all* -output (which will include `trace!`) from a particular module, or -`path::to::module=debug` to get `debug!` output and higher from a particular module. +output (which will include `trace!`) from a particular module, +or `path::to::module=debug` to get `debug!` output and higher from a particular module. -For example, to get the `debug!` output and higher for a specific module, you -can run the compiler with `RUSTC_LOG=path::to::module=debug rustc my-file.rs`. +For example, to get the `debug!` output and higher for a specific module, +you can run the compiler with `RUSTC_LOG=path::to::module=debug rustc my-file.rs`. All `debug!` output will then appear in standard error. Note that you can use a partial path and the filter will still work. -For example, if you want to see `info!` output from only -`rustdoc::passes::collect_intra_doc_links`, you could use -`RUSTDOC_LOG=rustdoc::passes::collect_intra_doc_links=info` *or* you could use +For example, if you want to see `info!` output from only `rustdoc::passes::collect_intra_doc_links`, +you could use `RUSTDOC_LOG=rustdoc::passes::collect_intra_doc_links=info` *or* you could use `RUSTDOC_LOG=rustdoc::passes::collect_intra=info`. If you are developing rustdoc, use `RUSTDOC_LOG` instead. If you are developing Miri, use `MIRI_LOG` instead. You get the idea :) -See the [`tracing`] crate's docs, and specifically the docs for [`debug!`] to -see the full syntax you can use. -(Note: unlike the compiler, the [`tracing`] -crate and its examples use the `RUSTC_LOG` environment variable. +See the [`tracing`] crate's docs, +and specifically the docs for [`debug!`] to see the full syntax you can use. +(Note: unlike the compiler, +the [`tracing`] crate and its examples use the `RUSTC_LOG` environment variable. rustc, rustdoc, and other tools set custom environment variables.) -**Note that unless you use a very strict filter, the logger will emit a lot of -output, so use the most specific module(s) you can (comma-separated if -multiple)**. It's typically a good idea to pipe standard error to a file and +**Note that unless you use a very strict filter, the logger will emit a lot of output, +so use the most specific module(s) you can (comma-separated if multiple)**. +It's typically a good idea to pipe standard error to a file and look at the log output with a text editor. So, to put it together: @@ -180,14 +178,13 @@ $ RUSTDOC_LOG=rustdoc=debug rustdoc +stage1 my-file.rs ## Log colors -By default, rustc (and other tools, like rustdoc and Miri) will be smart about -when to use ANSI colors in the log output. +By default, rustc (and other tools, +like rustdoc and Miri) will be smart about when to use ANSI colors in the log output. If they are outputting to a terminal, -they will use colors, and if they are outputting to a file or being piped -somewhere else, they will not. -However, it's hard to read log output in your -terminal unless you have a very strict filter, so you may want to pipe the -output to a pager like `less`. +they will use colors, and if they are outputting to a file or being piped somewhere else, +they will not. +However, it's hard to read log output in your terminal unless you have a very strict filter, +so you may want to pipe the output to a pager like `less`. But then there won't be any colors, which makes it hard to pick out what you're looking for! You can override whether to have colors in log output with the `RUSTC_LOG_COLOR` @@ -201,18 +198,18 @@ So, if you want to enable colors when piping to `less`, use something similar to $ RUSTC_LOG=debug RUSTC_LOG_COLOR=always rustc +stage1 ... | less -R ``` -Note that `MIRI_LOG_COLOR` will only color logs that come from Miri, not logs -from rustc functions that Miri calls. +Note that `MIRI_LOG_COLOR` will only color logs that come from Miri, +not logs from rustc functions that Miri calls. Use `RUSTC_LOG_COLOR` to color logs from rustc. ## How to keep or remove `debug!` and `trace!` calls from the resulting binary While calls to `error!`, `warn!` and `info!` are included in every build of the compiler, -calls to `debug!` and `trace!` are only included in the program if -`rust.debug-logging=true` is turned on in bootstrap.toml (it is -turned off by default), so if you don't see `DEBUG` logs, especially -if you run the compiler with `RUSTC_LOG=rustc rustc some.rs` and only see -`INFO` logs, make sure that `rust.debug-logging=true` is turned on in your bootstrap.toml. +calls to `debug!` and `trace!` are only included in the program if you have +`rust.debug-logging = true` in bootstrap.toml (it is turned off by default), +so if you don't see `DEBUG` logs, +especially if you run the compiler with `RUSTC_LOG=rustc rustc some.rs` and only see `INFO` logs, +make sure that `rust.debug-logging` is turned on in your bootstrap.toml. ## Logging etiquette and conventions @@ -220,11 +217,11 @@ Because calls to `debug!` are removed by default, in most cases, don't worry about the performance of adding "unnecessary" calls to `debug!` and leaving them in code you commit - they won't slow down the performance of what we ship. -That said, there can also be excessive tracing calls, especially -when they are redundant with other calls nearby or in functions called from here. +That said, there can also be excessive tracing calls, +especially when they are redundant with other calls nearby or in functions called from here. There is no perfect balance to hit here, and it is left to the reviewer's -discretion to decide whether to let you leave `debug!` statements in, or whether to ask -you to remove them before merging. +discretion to decide whether to let you leave `debug!` statements in, +or whether to ask you to remove them before merging. It may be preferable to use `trace!` over `debug!` for very noisy logs. @@ -243,8 +240,8 @@ If in the module `rustc::foo` you have a statement debug!(x = ?random_operation(tcx)); ``` -Then if someone runs a debug `rustc` with `RUSTC_LOG=rustc::foo`, then -`random_operation()` will run. +Then if someone runs a debug `rustc` with `RUSTC_LOG=rustc::foo`, +then `random_operation()` will run. `RUSTC_LOG` filters that do not enable this debug statement will not execute `random_operation`. This means that you should not put anything too expensive or likely to crash diff --git a/src/doc/rustc-dev-guide/src/ty-fold.md b/src/doc/rustc-dev-guide/src/ty-fold.md index bf0a51e6b7cfb..f7fce9a981af1 100644 --- a/src/doc/rustc-dev-guide/src/ty-fold.md +++ b/src/doc/rustc-dev-guide/src/ty-fold.md @@ -7,13 +7,10 @@ to find any usages of the bound vars in order to replace them. Binders can wrap an arbitrary Rust type `T`, not just a `Ty`. So, how do we implement the `instantiate` methods on the `Early/Binder` types? -The answer is a couple of traits: -[`TypeFoldable`] -and -[`TypeFolder`]. +The answer is a couple of traits: [`TypeFoldable`] and [`TypeFolder`]. -- `TypeFoldable` is implemented by types that embed type information. It allows you to recursively - process the contents of the `TypeFoldable` and do stuff to them. +- `TypeFoldable` is implemented by types that embed type information. + It allows you to recursively process the contents of `TypeFoldable` and do stuff to them. - `TypeFolder` defines what you want to do with the types you encounter while processing the `TypeFoldable`. @@ -38,8 +35,10 @@ So to reiterate: In the case of `subst`, we can see that it is implemented as a `TypeFolder`: [`ArgFolder`]. Looking at its implementation, we see where the actual substitutions are happening. -However, you might also notice that the implementation calls this `super_fold_with` method. What is -that? It is a method of `TypeFoldable`. Consider the following `TypeFoldable` type `MyFoldable`: +However, you might also notice that the implementation calls this `super_fold_with` method. +What is that? +It is a method of `TypeFoldable`. +Consider the following `TypeFoldable` type `MyFoldable`: ```rust,ignore struct MyFoldable<'tcx> { @@ -49,12 +48,13 @@ struct MyFoldable<'tcx> { ``` The `TypeFolder` can call `super_fold_with` on `MyFoldable` if it just wants to replace some of the -fields of `MyFoldable` with new values. If it instead wants to replace the whole `MyFoldable` with a -different one, it would call `fold_with` instead (a different method on `TypeFoldable`). +fields of `MyFoldable` with new values. +If it instead wants to replace the whole `MyFoldable` with a different one, +it would call `fold_with` instead (a different method on `TypeFoldable`). In almost all cases, we don’t want to replace the whole struct; we only want to replace `ty::Ty`s in -the struct, so usually we call `super_fold_with`. A typical implementation that `MyFoldable` could -have might do something like this: +the struct, so usually we call `super_fold_with`. +A typical implementation that `MyFoldable` could have might do something like this: ```rust,ignore my_foldable: MyFoldable<'tcx> @@ -72,24 +72,30 @@ impl TypeFoldable for MyFoldable { } ``` -Notice that here, we implement `super_fold_with` to go over the fields of `MyFoldable` and call -`fold_with` on *them*. That is, a folder may replace `def_id` and `ty`, but not the whole -`MyFoldable` struct. - -Here is another example to put things together: suppose we have a type like `Vec>`. The -`ty::Ty` would look like: `Adt(Vec, &[Adt(Vec, &[Param(X)])])`. If we want to do `subst(X => u32)`, -then we would first look at the overall type. We would see that there are no substitutions to be -made at the outer level, so we would descend one level and look at `Adt(Vec, &[Param(X)])`. There -are still no substitutions to be made here, so we would descend again. Now we are looking at -`Param(X)`, which can be substituted, so we replace it with `u32`. We can’t descend any more, so we -are done, and the overall result is `Adt(Vec, &[Adt(Vec, &[u32])])`. - -One last thing to mention: often when folding over a `TypeFoldable`, we don’t want to change most -things. We only want to do something when we reach a type. That means there may be a lot of +Notice that here, +we implement `super_fold_with` to go over the fields of `MyFoldable` and call `fold_with` on *them*. +That is, a folder may replace `def_id` and `ty`, but not the whole `MyFoldable` struct. + +Here is another example to put things together: suppose we have a type like `Vec>`. +The `ty::Ty` would look like: `Adt(Vec, &[Adt(Vec, &[Param(X)])])`. +If we want to do `subst(X => u32)`, +then we would first look at the overall type. +We would see that there are no substitutions to be made at the outer level, +so we would descend one level and look at `Adt(Vec, &[Param(X)])`. +There are still no substitutions to be made here, so we would descend again. +Now we are looking at `Param(X)`, which can be substituted, so we replace it with `u32`. +We can’t descend any more, so we are done, +and the overall result is `Adt(Vec, &[Adt(Vec, &[u32])])`. + +One last thing to mention: often when folding over a `TypeFoldable`, +we don’t want to change most things. +We only want to do something when we reach a type. +That means there may be a lot of `TypeFoldable` types whose implementations basically just forward to their fields’ `TypeFoldable` -implementations. Such implementations of `TypeFoldable` tend to be pretty tedious to write by hand. -For this reason, there is a `derive` macro that allows you to `#![derive(TypeFoldable)]`. It is -defined [here]. +implementations. +Such implementations of `TypeFoldable` tend to be pretty tedious to write by hand. +For this reason, there is a `derive` macro that allows you to `#![derive(TypeFoldable)]`. +It is defined [here]. **`subst`** In the case of substitutions the [actual folder] is going to be doing the indexing we’ve already mentioned. diff --git a/src/doc/rustc-dev-guide/src/ty.md b/src/doc/rustc-dev-guide/src/ty.md index ef2ea37b7ca46..887dd822e6b33 100644 --- a/src/doc/rustc-dev-guide/src/ty.md +++ b/src/doc/rustc-dev-guide/src/ty.md @@ -1,8 +1,8 @@ # The `ty` module: representing types The `ty` module defines how the Rust compiler represents types internally. -It also defines the -*typing context* (`tcx` or `TyCtxt`), which is the central data structure in the compiler. +It also defines the *typing context* (`tcx` or `TyCtxt`), +which is the central data structure in the compiler. ## `ty::Ty` @@ -22,12 +22,13 @@ The distinction is important, so we will discuss it first before going into the The HIR in rustc can be thought of as the high-level intermediate representation. It is more or less the AST (see [this chapter](hir.md)) as it represents the -syntax that the user wrote, and is obtained after parsing and some *desugaring*. It has a -representation of types, but in reality it reflects more of what the user wrote, that is, what they -wrote so as to represent that type. +syntax that the user wrote, and is obtained after parsing and some *desugaring*. +It has a representation of types, +but in reality, it reflects more of what the user wrote, +so as to represent that type. -In contrast, `ty::Ty` represents the semantics of a type, that is, the *meaning* of what the user -wrote. +In contrast, `ty::Ty` represents the semantics of a type; +that is, the *meaning* of what the user wrote. For example, `rustc_hir::Ty` would record the fact that a user used the name `u32` twice in their program, but the `ty::Ty` would record the fact that both usages refer to the same type. @@ -46,16 +47,16 @@ That is, they have two different [`Span`s][span] (locations). **Example: `fn foo(x: &u32) -> &u32`** In addition, HIR might have information left out. -This type -`&u32` is incomplete, since in the full Rust type there is actually a lifetime, but we didn’t need +This type `&u32` is incomplete, +since in the full Rust type there is actually a lifetime, but we didn’t need to write those lifetimes. There are also some elision rules that insert information. The result may look like `fn foo<'a>(x: &'a u32) -> &'a u32`. In the HIR level, these things are not spelled out and you can say the picture is rather incomplete. However, at the `ty::Ty` level, these details are added and it is complete. -Moreover, we will have -exactly one `ty::Ty` for a given type, like `u32`, and that `ty::Ty` is used for all `u32`s in the +Moreover, we will have exactly one `ty::Ty` for a given type, +like `u32`, and that `ty::Ty` is used for all `u32`s in the whole program, not a specific usage, unlike `rustc_hir::Ty`. Here is a summary: @@ -74,8 +75,8 @@ Here is a summary: HIR is built directly from the AST, so it happens before any `ty::Ty` is produced. After HIR is built, some basic type inference and type checking is done. -During the type inference, we -figure out what the `ty::Ty` of everything is and we also check if the type of something is +During the type inference, +we figure out what the `ty::Ty` of everything is and we also check if the type of something is ambiguous. The `ty::Ty` is then used for type checking while making sure everything has the expected type. The [`hir_ty_lowering` module][hir_ty_lowering] is where the code responsible for @@ -102,8 +103,8 @@ Consider another example: `fn foo(x: T) -> u32`. Suppose that someone invokes `foo::(0)`. This means that `T` and `u32` (in this invocation) actually turns out to be the same type, so we would eventually end up with the same `ty::Ty` in the end, but we have distinct `rustc_hir::Ty`. -(This is a bit over-simplified, though, since during type checking, we would check the function -generically and would still have a `T` distinct from `u32`. +(This is a bit over-simplified, though, since during type checking, +we would check the function generically and would still have a `T` distinct from `u32`. Later, when doing code generation, we would always be handling "monomorphized" (fully substituted) versions of each function, and hence we would know what `T` represents (and specifically that it is `u32`).) @@ -125,8 +126,8 @@ Here the type `X` will vary depending on context, clearly. If you look at the `rustc_hir::Ty`, you will get back that `X` is an alias in both cases (though it will be mapped via name resolution to distinct aliases). -But if you look at the `ty::Ty` signature, it will be either `fn(u32) -> u32` -or `fn(i32) -> i32` (with type aliases fully expanded). +But if you look at the `ty::Ty` signature, +it will be either `fn(u32) -> u32` or `fn(i32) -> i32` (with type aliases fully expanded). ## `ty::Ty` implementation @@ -140,8 +141,7 @@ We always hide them within `Ty` and skip over it via `Deref` impls or methods. They are convenient hacks for efficiency and summarize information about the type that we may want to know, but they don’t come into the picture as much here. -Finally, [`Interned`](./memory.md) allows the `ty::Ty` to be a thin pointer-like -type. +Finally, [`Interned`](./memory.md) allows the `ty::Ty` to be a thin pointer-like type. This allows us to do cheap comparisons for equality, along with the other benefits of interning. [tykind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/ty_kind/enum.TyKind.html @@ -169,9 +169,9 @@ You can also find various common types in the `tcx` itself by accessing its fiel ## Comparing types -Because types are interned, it is possible to compare them for equality efficiently using `==` -– however, this is almost never what you want to do unless you happen to be hashing and looking -for duplicates. +Because types are interned, it is possible to compare them for equality efficiently using `==`. +However, this is almost never what you want to do, +unless you happen to be hashing and looking for duplicates. This is because often in Rust there are multiple ways to represent the same type, particularly once inference is involved. @@ -182,11 +182,11 @@ diagnostics code). `==` on them will return `false` though, since they are different types. The simplest way to compare two types correctly requires an inference context (`infcx`). -If you have one, you can use `infcx.can_eq(param_env, ty1, ty2)` -to check whether the types can be made equal. +If you have one, +you can use `infcx.can_eq(param_env, ty1, ty2)` to check whether the types can be made equal. This is typically what you want to check during diagnostics, which is concerned with questions such -as whether two types can be assigned to each other, not whether they're represented identically in -the compiler's type-checking layer. +as whether two types can be assigned to each other, +not whether they're represented identically in the compiler's type-checking layer. When working with an inference context, you have to be careful to ensure that potential inference variables inside the types actually belong to that inference context. @@ -199,24 +199,24 @@ To compare them correctly, you have to normalize the types first. This is primarily a concern during HIR type checking and with all types from a `TyCtxt` query (for example from `tcx.type_of()`). -When a `FnCtxt` or an `ObligationCtxt` is available during type checking, `.normalize(ty)` -should be used on them to normalize the type. +When a `FnCtxt` or an `ObligationCtxt` is available during type checking, +`.normalize(ty)` should be used on them to normalize the type. After type checking, diagnostics code can use `tcx.normalize_erasing_regions(ty)`. There are also cases where using `==` on `Ty` is fine. -This is, for example, the case in late lints -or after monomorphization, since type checking has been completed, meaning all inference variables +This is, for example, the case in late lints or after monomorphization, +since type checking has been completed, meaning all inference variables are resolved and all regions have been erased. -In these cases, if you know that inference variables -or normalization won't be a concern, `#[allow]` or `#[expect]`ing the lint is recommended. +In these cases, if you know that inference variables or normalization won't be a concern, +`#[allow]` or `#[expect]`ing the lint is recommended. When diagnostics code does not have access to an inference context, it should be threaded through the function calls if one is available in some place (like during type checking). -If no inference context is available at all, then one can be created as described in -[type-inference]. -But this is only useful when the involved types (for example, if -they came from a query like `tcx.type_of()`) are actually substituted with fresh +If no inference context is available at all, +then one can be created as described in [type-inference]. +But this is only useful when the involved types (for example, +if they came from a query like `tcx.type_of()`) are actually substituted with fresh inference variables using [`fresh_args_for_item`]. This can be used to answer questions like "can `Vec` for any `T` be unified with `Vec`?". @@ -237,8 +237,8 @@ fn foo(x: Ty<'tcx>) { } ``` -The `kind` field is of type `TyKind<'tcx>`, which is an enum defining all of the different kinds of -types in the compiler. +The `kind` field is of type `TyKind<'tcx>`, +which is an enum defining all of the different kinds of types in the compiler. > N.B. inspecting the `kind` field on types during type inference can be risky, as there may be > inference variables and other things to consider, or sometimes types are not yet known and will @@ -247,8 +247,8 @@ types in the compiler. There are a lot of related types, and we’ll cover them in time (e.g regions/lifetimes, “substitutions”, etc). -There are many variants on the `TyKind` enum, which you can see by looking at its -[documentation][tykind]. +There are many variants on the `TyKind` enum, +which you can see by looking at its [documentation][tykind]. Here is a sampling: - [**Algebraic Data Types (ADTs)**][kindadt] An [*algebraic data type*][wikiadt] is a `struct`, @@ -264,8 +264,8 @@ Here is a sampling: - [**Array**][kindarray] Corresponds to `[T; n]`. - [**RawPtr**][kindrawptr] Corresponds to `*mut T` or `*const T`. - [**Ref**][kindref] `Ref` stands for safe references, `&'a mut T` or `&'a T`. - `Ref` has some - associated parts, like `Ty<'tcx>` which is the type that the reference references. + `Ref` has some associated parts, + like `Ty<'tcx>`, which is the type that the reference references. `Region<'tcx>` is the lifetime or region of the reference and `Mutability` if the reference is mutable or not. - [**Param**][kindparam] Represents a type parameter (e.g. the `T` in `Vec`). @@ -314,15 +314,15 @@ which case the error should've been reported when that error type was produced). It's important to maintain this invariant because the whole point of the `Error` type is to suppress other errors -- i.e., we don't report them. If we were to produce an `Error` type without actually -emitting an error to the user, then this could cause later errors to be suppressed, and the -compilation might inadvertently succeed! +emitting an error to the user, then this could cause later errors to be suppressed, +and the compilation might inadvertently succeed! Sometimes there is a third case. You believe that an error has been reported, but you believe it would've been reported earlier in the compilation, not locally. In that case, you can create a "delayed bug" with [`delayed_bug`] or [`span_delayed_bug`]. -This will make a note that you expect -compilation to yield an error -- if, however, compilation should succeed, then it will trigger a +This will make a note that you expect compilation to yield an error -- if, +however, compilation should succeed, then it will trigger a compiler bug report. [`delayed_bug`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.DiagCtxt.html#method.delayed_bug diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index f15c712c32b91..1c2935ff264e0 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -133,6 +133,7 @@ - [\*-unikraft-linux-musl](platform-support/unikraft-linux-musl.md) - [\*-unknown-helenos](platform-support/helenos.md) - [\*-unknown-hermit](platform-support/hermit.md) + - [\*-unknown-hyperlight](platform-support/hyperlight.md) - [\*-unknown-freebsd](platform-support/freebsd.md) - [\*-unknown-managarm-mlibc](platform-support/managarm.md) - [\*-unknown-motor](platform-support/motor.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index c2a86d85dfcde..11a6ada2c4f89 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -273,6 +273,7 @@ target | std | host | notes [`aarch64-oe-linux-gnu`](platform-support/oe-linux-gnu.md) | ✓ | | ARM64 OpenEmbedded/Yocto Linux (GNU) [`aarch64-unknown-helenos`](platform-support/helenos.md) | ✓ | | ARM64 HelenOS [`aarch64-unknown-hermit`](platform-support/hermit.md) | ✓ | | ARM64 Hermit +[`aarch64-unknown-hyperlight`](platform-support/hyperlight.md) | * | | ARM64 Hyperlight [`aarch64-unknown-illumos`](platform-support/illumos.md) | ✓ | ✓ | ARM64 illumos [`aarch64-unknown-l4re-uclibc`](platform-support/l4re.md) | ✓ | | ARM64 L4Re with uclibc `aarch64-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (ILP32 ABI) @@ -461,6 +462,7 @@ target | std | host | notes `x86_64-unknown-haiku` | ✓ | ✓ | 64-bit Haiku [`x86_64-unknown-hermit`](platform-support/hermit.md) | ✓ | | x86_64 Hermit [`x86_64-unknown-helenos`](platform-support/helenos.md) | ✓ | | x86_64 (amd64) HelenOS +[`x86_64-unknown-hyperlight`](platform-support/hyperlight.md) | * | | x86_64 (amd64) Hyperlight [`x86_64-unknown-hurd-gnu`](platform-support/hurd.md) | ✓ | ✓ | 64-bit GNU/Hurd [`x86_64-unknown-l4re-uclibc`](platform-support/l4re.md) | ✓ | | x86_64 L4Re with uclibc [`x86_64-unknown-linux-none`](platform-support/x86_64-unknown-linux-none.md) | * | | 64-bit Linux with no libc diff --git a/src/doc/rustc/src/platform-support/hyperlight.md b/src/doc/rustc/src/platform-support/hyperlight.md new file mode 100644 index 0000000000000..be54a87e6a0fc --- /dev/null +++ b/src/doc/rustc/src/platform-support/hyperlight.md @@ -0,0 +1,75 @@ +# `*-unknown-hyperlight` + +**Tier: 3** + +These targets are used to build guest executables for the +[Hyperlight](https://github.com/hyperlight-dev/hyperlight) sandboxing +solution. + +## Target maintainers + +- [@syntactically](https://github.com/syntactically) +- [@yoshuawuyts](https://github.com/yoshuawuyts) + +## Requirements + +These targets can only be used for cross-compilation. They should not +require anything special as host tooling, but the usual way to use +them is via the +[cargo-hyperlight](https://github.com/hyperlight-dev/cargo-hyperlight) +tool. + +Hyperlight targets do not have any `std` support, but do fully support +`alloc`. + +These targets assume ARMv8.1-A (with Neon) on aarch64 and SSE2 on +x86_64, which are required by core Hyperlight libraries. The targets +are only expected to work with recent versions of Hyperlight. + +Hyperlight uses the official hardfloat calling convention of each +architecture for `extern "C"`. + +Hyperlight guest binaries use the ELF file format. + +## Building the target + +These targets can be built by adding them to the `target` list in +`bootstrap.toml`. + +```toml +[build] +build-stage = 1 +target = [ + "", + "-unknown-hyperlight", +] + +[target.-unknown-hyperlight] +no-std = true +``` + +## Building Rust programs + +Rust does not yet ship pre-compiled artifacts for this target. To +compile for this target, you will either need to build Rust with the +target enabled (see "Building the target" above), or build your own +copy of `core` by using `build-std` or similar. When using +`cargo-hyperlight`, the tool will manage this automatically. + +## Testing + +Binaries built for this target need to use the +[`hyperlight-guest-bin`](https://crates.io/crates/hyperlight-guest-bin) +crate. A given binary can then be loaded by a host process that uses +[`hyperlight-host`](https://crates.io/crates/hyperlight-host) and +agrees with it on a host/guest interface. For more details on the +programming model and how to write a host, see those crates and +documentation on the Hyperlight [website](https://hyperlight.org) and +in the [repository](https://github.com/hyperlight-dev/hyperlight). + +## Cross-compilation toolchains and C code + +This target does support C code through +[cargo-hyperlight](https://github.com/hyperlight-dev/cargo-hyperlight), +which supports wrapping an `-unknown-none` C toolchain into +something that can build targeting Hyperlight. diff --git a/src/doc/rustc/src/platform-support/openbsd.md b/src/doc/rustc/src/platform-support/openbsd.md index e6fb23ca9eefd..75bae0e2382fd 100644 --- a/src/doc/rustc/src/platform-support/openbsd.md +++ b/src/doc/rustc/src/platform-support/openbsd.md @@ -14,7 +14,7 @@ The target names follow this format: `$ARCH-unknown-openbsd`, where `$ARCH` spec | `i686-unknown-openbsd` | libc++ | [Standard PC and clones based on the Intel i386 architecture and compatible processors](https://www.openbsd.org/i386.html) | | `powerpc64-unknown-openbsd` | libc++ | [IBM POWER-based PowerNV systems](https://www.openbsd.org/powerpc64.html) | | `riscv64gc-unknown-openbsd` | libc++ | [64-bit RISC-V systems](https://www.openbsd.org/riscv64.html) | -| `sparc64-unknown-openbsd` | estdc++ | [Sun UltraSPARC and Fujitsu SPARC64 systems](https://www.openbsd.org/sparc64.html) | +| `sparc64-unknown-openbsd` | libc++ | [Sun UltraSPARC and Fujitsu SPARC64 systems](https://www.openbsd.org/sparc64.html) | | `x86_64-unknown-openbsd` | libc++ | [AMD64-based systems](https://www.openbsd.org/amd64.html) | Note that all OS versions are *major* even if using X.Y notation (`6.8` and `6.9` are different major versions) and could be binary incompatibles (with breaking changes). diff --git a/src/doc/rustc/src/platform-support/wasm32-unknown-emscripten.md b/src/doc/rustc/src/platform-support/wasm32-unknown-emscripten.md index fcf6e42be224b..af95f9bccb94c 100644 --- a/src/doc/rustc/src/platform-support/wasm32-unknown-emscripten.md +++ b/src/doc/rustc/src/platform-support/wasm32-unknown-emscripten.md @@ -43,12 +43,13 @@ APIs, the `wasm32-unknown-emscripten` target may be preferable. ## Requirements This target is cross-compiled. The Emscripten compiler toolchain `emcc` must be -installed to link WASM binaries for this target. You can install `emcc` using: +installed to link WASM binaries for this target. Emscripten 4.0.0 or newer is +required. You can install `emcc` using: ```sh git clone https://github.com/emscripten-core/emsdk.git --depth 1 -./emsdk/emsdk install 3.1.68 -./emsdk/emsdk activate 3.1.68 +./emsdk/emsdk install latest +./emsdk/emsdk activate latest source ./emsdk/emsdk_env.sh ``` diff --git a/src/doc/unstable-book/src/language-features/f16b.md b/src/doc/unstable-book/src/language-features/f16b.md new file mode 100644 index 0000000000000..c18289921e032 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/f16b.md @@ -0,0 +1,11 @@ +# `f16b` + +The tracking issue for this feature is: [#160630] + +[#160630]: https://github.com/rust-lang/rust/issues/160630 + +--- + +Enable the `core::num::f16b` type for values stored in the bfloat16 format. + +`f16b` is a nominal library type, not a primitive floating-point type: it has no literal suffix, arithmetic operators, or numeric `as` casts. A compiler lang item connects its layout to the chosen backend's representation. diff --git a/src/doc/unstable-book/src/language-features/gca-adts.md b/src/doc/unstable-book/src/language-features/gca-adts.md new file mode 100644 index 0000000000000..49905d57a9a4c --- /dev/null +++ b/src/doc/unstable-book/src/language-features/gca-adts.md @@ -0,0 +1,21 @@ +# gca_adts + +Enables ADTs under the generic const args family of features (e.g. `gca!` macro). + +The tracking issue for this feature is: [#163420] + +[#163420]: https://github.com/rust-lang/rust/issues/163420 + +## Examples + +```rust +#![feature(gca_min_const_items, min_adt_const_params, gca_adts)] + +use std::gca; + +struct S; + +fn main() { + let _: S = S::; +} +``` diff --git a/src/doc/unstable-book/src/language-features/generic-const-args.md b/src/doc/unstable-book/src/language-features/gca-const-items.md similarity index 75% rename from src/doc/unstable-book/src/language-features/generic-const-args.md rename to src/doc/unstable-book/src/language-features/gca-const-items.md index c496c50bf0400..e773f3bdec03d 100644 --- a/src/doc/unstable-book/src/language-features/generic-const-args.md +++ b/src/doc/unstable-book/src/language-features/gca-const-items.md @@ -1,4 +1,4 @@ -# generic_const_args +# gca_const_items Allows using generics in more complex const expressions, based on definitional equality. @@ -11,23 +11,23 @@ The tracking issue for this feature is: [#151972] Warning: This feature is incomplete; its design and syntax may change. This feature enables many of the same use cases supported by [generic_const_exprs], -but based on the machinery developed for [min_generic_const_args]. In a way, it is +but based on the machinery developed for [gca_min_const_items]. In a way, it is meant to be an interim successor for GCE (though it might not currently support all the valid cases that supported by GCE). See also: [generic_const_items] -[min_generic_const_args]: min-generic-const-args.md +[gca_min_const_items]: gca-min-const-items.md [generic_const_exprs]: generic-const-exprs.md [generic_const_items]: generic-const-items.md ## Examples - + ```rust,ignore (requires-Z-next-solver) #![feature(generic_const_items)] -#![feature(min_generic_const_args)] -#![feature(generic_const_args)] +#![feature(gca_min_const_items)] +#![feature(gca_const_items)] #![expect(incomplete_features)] type const ADD1: usize = const { N + 1 }; diff --git a/src/doc/unstable-book/src/language-features/gca-macroless-args.md b/src/doc/unstable-book/src/language-features/gca-macroless-args.md new file mode 100644 index 0000000000000..d81b202d9dd3c --- /dev/null +++ b/src/doc/unstable-book/src/language-features/gca-macroless-args.md @@ -0,0 +1,77 @@ +# gca_macroless_args + +Enables using `#![feature(gca_min_const_items)]` without the `gca!` macro. + +The tracking issue for this feature is: [#159006] + +[#159006]: https://github.com/rust-lang/rust/issues/159006 + +------------------------ + +Warning: This feature is incomplete; its design and syntax may change. + +Related features: +- [gca_min_const_items]. See that doc for what the `gca!` is. This feature enables +support for directly represented const arguments without the macro. +- [gca_macroless_items]. For a version of this feature that works for const arguments +as the right hand side of a const item. + +[gca_min_const_items]: gca-min-const-items.md +[gca_macroless_items]: gca-macroless-items.md + +## Examples + +Here is an example from [gca_min_const_items]: + +[gca_min_const_items]: gca-min-const-items.md + +```rust +#![allow(incomplete_features)] +#![feature(gca_min_const_items)] + +trait Bar { + #[rustc_always_gca] + const VAL: usize; + #[rustc_always_gca] + const VAL2: usize; +} + +struct Baz; + +impl Bar for Baz { + const VAL: usize = core::gca!(2); + const VAL2: usize = core::gca!(const { Self::VAL * 2 }); +} + +struct Foo { + arr1: [usize; core::gca!(B::VAL)], + arr2: [usize; core::gca!(B::VAL2)], +} +``` + +Using `#![feature(gca_macroless_args)]` enables you to write the above without the macro: + +```rust +#![allow(incomplete_features)] +#![feature(gca_min_const_items, gca_macroless_args)] + +trait Bar { + #[rustc_always_gca] + const VAL: usize; + #[rustc_always_gca] + const VAL2: usize; +} + +struct Baz; + +impl Bar for Baz { + // note these still need a macro, macroless for these is `gca_macroless_items` + const VAL: usize = core::gca!(2); + const VAL2: usize = core::gca!(const { Self::VAL * 2 }); +} + +struct Foo { + arr1: [usize; B::VAL], + arr2: [usize; B::VAL2], +} +``` diff --git a/src/doc/unstable-book/src/language-features/gca-macroless-items.md b/src/doc/unstable-book/src/language-features/gca-macroless-items.md new file mode 100644 index 0000000000000..d683adc0b1349 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/gca-macroless-items.md @@ -0,0 +1,75 @@ +# gca_macroless_args + +Enables implementing const items under `#![feature(gca_min_const_items)]` and `#![feature(gca_const_items)]` without the `gca!` macro. + +The tracking issue for this feature is: [#162540] + +[#162540]: https://github.com/rust-lang/rust/issues/162540 + +------------------------ + +Warning: This feature is incomplete; its design and syntax may change. + +Related features: +- [gca_min_const_items]. See that doc for what the `gca!` is. This feature enables +support for directly represented const arguments as the rhs of const items without the macro. +- [gca_macroless_args]. For a version of this feature that works for const arguments in other +positions + +[gca_min_const_items]: gca-min-const-items.md +[gca_macroless_args]: gca-macroless-args.md + +## Examples + +Here is an example from [gca_min_const_items]: + +[gca_min_const_items]: gca-min-const-items.md + +```rust,ignore (needs new solver) +#![allow(incomplete_features)] +#![feature( + gca_min_const_items, + gca_const_items, + gca_macroless_args, + generic_const_items, +)] + +trait Trait { + const ASSOC: usize; +} + +impl Trait for () { + const ASSOC: usize = core::gca!(N); +} + +fn foo() { + let a: [(); <() as Trait>::ASSOC::] + = [(); N]; +} +``` + +Using `#![feature(gca_macroless_items)]` enables you to write the above without the macro: + +```rust,ignore (needs new solver) +#![allow(incomplete_features)] +#![feature( + gca_min_const_items, + gca_const_items, + gca_macroless_args, + gca_macroless_items, + generic_const_items, +)] + +trait Trait { + const ASSOC: usize; +} + +impl Trait for () { + const ASSOC: usize = N; +} + +fn foo() { + let a: [(); <() as Trait>::ASSOC::] + = [(); N]; +} +``` diff --git a/src/doc/unstable-book/src/language-features/min-generic-const-args.md b/src/doc/unstable-book/src/language-features/gca-min-const-items.md similarity index 63% rename from src/doc/unstable-book/src/language-features/min-generic-const-args.md rename to src/doc/unstable-book/src/language-features/gca-min-const-items.md index 0d33af9092cf6..486db1f50885b 100644 --- a/src/doc/unstable-book/src/language-features/min-generic-const-args.md +++ b/src/doc/unstable-book/src/language-features/gca-min-const-items.md @@ -1,6 +1,6 @@ -# min_generic_const_args +# gca_min_const_items -Enables the generic const args MVP (paths to direct const items and constructors for ADTs and primitives). +Enables the generic const args MVP (paths to direct const items and primitives). The tracking issue for this feature is: [#132980] @@ -15,44 +15,44 @@ and uses a different approach for implementation. It is intentionally more restr cases that make the `generic_const_exprs` hard to implement properly. See [Feature background][feature_background] for more details. -Related features: [macroless_generic_const_args], [generic_const_args], [generic_const_items]. +Related features: [gca_macroless_args], [gca_const_items], [generic_const_items]. [feature_background]: https://github.com/rust-lang/project-const-generics/blob/main/documents/min_const_generics_plan.md [generic_const_exprs]: generic-const-exprs.md -[macroless_generic_const_args]: macroless-generic-const-args.md -[generic_const_args]: generic-const-args.md +[gca_macroless_args]: gca-macroless-args.md +[gca_const_items]: gca-const-items.md [generic_const_items]: generic-const-items.md -## `direct_const_arg!` macro +## `gca!` macro -This feature introduces a new macro: `direct_const_arg!`. +This feature introduces a new macro: `gca!`. When an expression is used as a generic argument, it is typically lowered as an "anon const", which is an expression -that is opaque to the type system and cannot contain generics. Using `direct_const_arg!` instead represents the +that is opaque to the type system and cannot contain generics. Using `gca!` instead represents the expression "directly", i.e. without an anon const, in a way that is visible to the type system. -(Note that plain paths to generic parameters are always represented directly, without `direct_const_arg!`, as this +(Note that plain paths to generic parameters are always represented directly, without `gca!`, as this already works on stable) -See [macroless_generic_const_args] as a feature to disable the requirement of writing `direct_const_arg!`. +See [gca_macroless_args] as a feature to disable the requirement of writing `gca!`. -[macroless_generic_const_args]: macroless-generic-const-args.md +[gca_macroless_args]: gca-macroless-args.md ## direct const items -This feature introduces a new item kind: consts with a `direct_const_arg!` right-hand side. +This feature introduces a new item kind: consts with a `gca!` right-hand side. Constants with a direct right-hand side are allowed to be used in type contexts, e.g.: ```compile_fail #![allow(incomplete_features)] -#![feature(min_generic_const_args)] +#![feature(gca_min_const_items)] -const X: usize = core::direct_const_arg!(1); +const X: usize = core::gca!(1); const Y: usize = 1; struct Foo { - good_arr: [(); core::direct_const_arg!(X)], // Allowed - bad_arr: [(); core::direct_const_arg!(Y)], // Will not compile + good_arr: [(); core::gca!(X)], // Allowed + bad_arr: [(); core::gca!(Y)], // Will not compile } ``` @@ -60,7 +60,7 @@ struct Foo { ```rust #![allow(incomplete_features)] -#![feature(min_generic_const_args)] +#![feature(gca_min_const_items)] trait Bar { #[rustc_always_gca] @@ -72,13 +72,13 @@ trait Bar { struct Baz; impl Bar for Baz { - const VAL: usize = core::direct_const_arg!(2); - const VAL2: usize = core::direct_const_arg!(const { Self::VAL * 2 }); + const VAL: usize = core::gca!(2); + const VAL2: usize = core::gca!(const { Self::VAL * 2 }); } struct Foo { - arr1: [usize; core::direct_const_arg!(B::VAL)], - arr2: [usize; core::direct_const_arg!(B::VAL2)], + arr1: [usize; core::gca!(B::VAL)], + arr2: [usize; core::gca!(B::VAL2)], } ``` @@ -114,7 +114,7 @@ Use of const functions is allowed: ```rust #![allow(incomplete_features)] -#![feature(min_generic_const_args)] +#![feature(gca_min_const_items)] const VAL: usize = 1; @@ -122,7 +122,7 @@ const fn inc(val: usize) -> usize { val + 1 } -const INC: usize = core::direct_const_arg!(const { inc(VAL) }); +const INC: usize = core::gca!(const { inc(VAL) }); const ARR: [usize; INC] = [0; INC]; ``` diff --git a/src/doc/unstable-book/src/language-features/generic-const-exprs.md b/src/doc/unstable-book/src/language-features/generic-const-exprs.md index 2085ed8fed0cc..645a2df3ebad4 100644 --- a/src/doc/unstable-book/src/language-features/generic-const-exprs.md +++ b/src/doc/unstable-book/src/language-features/generic-const-exprs.md @@ -12,10 +12,10 @@ The tracking issue for this feature is: [#76560] Warning: This feature is incomplete; its design and syntax may change. -See also: [min_generic_const_args], [generic_const_args] +See also: [gca_min_const_items], [gca_const_items] -[min_generic_const_args]: min-generic-const-args.md -[generic_const_args]: generic-const-args.md +[gca_min_const_items]: gca-min-const-items.md +[gca_const_items]: gca-const-items.md ## Examples diff --git a/src/doc/unstable-book/src/language-features/generic-const-items.md b/src/doc/unstable-book/src/language-features/generic-const-items.md index c856c9f135aff..ddf8f445ae859 100644 --- a/src/doc/unstable-book/src/language-features/generic-const-items.md +++ b/src/doc/unstable-book/src/language-features/generic-const-items.md @@ -11,11 +11,11 @@ The tracking issue for this feature is: [#113521] Warning: This feature is an [experiment] and lacks an RFC. There are no guarantees that it will ever be stabilized. -See also: [generic_const_exprs], [min_generic_const_args]. +See also: [generic_const_exprs], [gca_min_const_items]. [experiment]: https://lang-team.rust-lang.org/how_to/experiment.html [generic_const_exprs]: generic-const-exprs.md -[min_generic_const_args]: min-generic-const-args.md +[gca_min_const_items]: gca-min-const-items.md ## Examples diff --git a/src/doc/unstable-book/src/language-features/intrinsics.md b/src/doc/unstable-book/src/language-features/intrinsics.md index 9e59dd889985c..c662e5831d3a0 100644 --- a/src/doc/unstable-book/src/language-features/intrinsics.md +++ b/src/doc/unstable-book/src/language-features/intrinsics.md @@ -68,5 +68,5 @@ These are written without a body: #![allow(internal_features)] #[rustc_intrinsic] -pub fn abort() -> !; +pub fn abort_immediate() -> !; ``` diff --git a/src/doc/unstable-book/src/language-features/lang-items.md b/src/doc/unstable-book/src/language-features/lang-items.md index ccc973d013e44..c88f0cb859877 100644 --- a/src/doc/unstable-book/src/language-features/lang-items.md +++ b/src/doc/unstable-book/src/language-features/lang-items.md @@ -85,7 +85,7 @@ unsafe fn allocate(size: usize, _align: usize) -> *mut u8 { // Check if `malloc` failed: if p.is_null() { - intrinsics::abort(); + intrinsics::abort_immediate(); } p @@ -102,7 +102,7 @@ extern "C" fn main(_argc: c_int, _argv: *const *const u8) -> c_int { fn rust_eh_personality() {} #[panic_handler] -fn panic_handler(_info: &PanicInfo) -> ! { intrinsics::abort() } +fn panic_handler(_info: &PanicInfo) -> ! { intrinsics::abort_immediate() } ``` Note the use of `abort`: the `exchange_malloc` lang item is assumed to diff --git a/src/doc/unstable-book/src/language-features/macroless-const-item-generic-const-args.md b/src/doc/unstable-book/src/language-features/macroless-const-item-generic-const-args.md deleted file mode 100644 index df9172e04b9ac..0000000000000 --- a/src/doc/unstable-book/src/language-features/macroless-const-item-generic-const-args.md +++ /dev/null @@ -1,75 +0,0 @@ -# macroless_generic_const_args - -Enables implementing const items under `#![feature(min_generic_const_args)]` and `#![feature(generic_const_args)]` without the `direct_const_arg!` macro. - -The tracking issue for this feature is: [#162540] - -[#162540]: https://github.com/rust-lang/rust/issues/162540 - ------------------------- - -Warning: This feature is incomplete; its design and syntax may change. - -Related features: -- [min_generic_const_args]. See that doc for what the `direct_const_arg!` is. This feature enables -support for directly represented const arguments as the rhs of const items without the macro. -- [macroless_generic_const_args]. For a version of this feature that works for const arguments in other -positions - -[min_generic_const_args]: min-generic-const-args.md -[macroless_generic_const_args]: macroless-generic-const-args.md - -## Examples - -Here is an example from [min_generic_const_args]: - -[min_generic_const_args]: min-generic-const-args.md - -```rust,ignore (needs new solver) -#![allow(incomplete_features)] -#![feature( - min_generic_const_args, - generic_const_args, - macroless_generic_const_args, - generic_const_items, -)] - -trait Trait { - const ASSOC: usize; -} - -impl Trait for () { - const ASSOC: usize = core::direct_const_arg!(N); -} - -fn foo() { - let a: [(); <() as Trait>::ASSOC::] - = [(); N]; -} -``` - -Using `#![feature(macroless_const_item_generic_const_args)]` enables you to write the above without the macro: - -```rust,ignore (needs new solver) -#![allow(incomplete_features)] -#![feature( - min_generic_const_args, - generic_const_args, - macroless_generic_const_args, - macroless_const_item_generic_const_args, - generic_const_items, -)] - -trait Trait { - const ASSOC: usize; -} - -impl Trait for () { - const ASSOC: usize = N; -} - -fn foo() { - let a: [(); <() as Trait>::ASSOC::] - = [(); N]; -} -``` diff --git a/src/doc/unstable-book/src/language-features/macroless-generic-const-args.md b/src/doc/unstable-book/src/language-features/macroless-generic-const-args.md deleted file mode 100644 index 410834a79712e..0000000000000 --- a/src/doc/unstable-book/src/language-features/macroless-generic-const-args.md +++ /dev/null @@ -1,77 +0,0 @@ -# macroless_generic_const_args - -Enables using `#![feature(min_generic_const_args)]` without the `direct_const_arg!` macro. - -The tracking issue for this feature is: [#159006] - -[#159006]: https://github.com/rust-lang/rust/issues/159006 - ------------------------- - -Warning: This feature is incomplete; its design and syntax may change. - -Related features: -- [min_generic_const_args]. See that doc for what the `direct_const_arg!` is. This feature enables -support for directly represented const arguments without the macro. -- [macroless_const_item_generic_const_args]. For a version of this feature that works for const arguments -as the right hand side of a const item. - -[min_generic_const_args]: min-generic-const-args.md -[macroless_const_item_generic_const_args]: macroless-const-item-generic-const-args.md - -## Examples - -Here is an example from [min_generic_const_args]: - -[min_generic_const_args]: min-generic-const-args.md - -```rust -#![allow(incomplete_features)] -#![feature(min_generic_const_args)] - -trait Bar { - #[rustc_always_gca] - const VAL: usize; - #[rustc_always_gca] - const VAL2: usize; -} - -struct Baz; - -impl Bar for Baz { - const VAL: usize = core::direct_const_arg!(2); - const VAL2: usize = core::direct_const_arg!(const { Self::VAL * 2 }); -} - -struct Foo { - arr1: [usize; core::direct_const_arg!(B::VAL)], - arr2: [usize; core::direct_const_arg!(B::VAL2)], -} -``` - -Using `#![feature(macroless_generic_const_args)]` enables you to write the above without the macro: - -```rust -#![allow(incomplete_features)] -#![feature(min_generic_const_args, macroless_generic_const_args)] - -trait Bar { - #[rustc_always_gca] - const VAL: usize; - #[rustc_always_gca] - const VAL2: usize; -} - -struct Baz; - -impl Bar for Baz { - // note these still need a macro, macroless for these is `macroless_const_item_generic_const_args` - const VAL: usize = core::direct_const_arg!(2); - const VAL2: usize = core::direct_const_arg!(const { Self::VAL * 2 }); -} - -struct Foo { - arr1: [usize; B::VAL], - arr2: [usize; B::VAL2], -} -``` diff --git a/src/doc/unstable-book/src/library-features/allocator-api.md b/src/doc/unstable-book/src/library-features/allocator-ext.md similarity index 72% rename from src/doc/unstable-book/src/library-features/allocator-api.md rename to src/doc/unstable-book/src/library-features/allocator-ext.md index 9f045ce08a433..6e374f0743f89 100644 --- a/src/doc/unstable-book/src/library-features/allocator-api.md +++ b/src/doc/unstable-book/src/library-features/allocator-ext.md @@ -1,8 +1,8 @@ -# `allocator_api` +# `allocator_ext` -The tracking issue for this feature is [#32838] +The tracking issue for this feature is: [#163177] -[#32838]: https://github.com/rust-lang/rust/issues/32838 +[#163177]: https://github.com/rust-lang/rust/issues/163177 ------------------------ diff --git a/src/etc/completions/x.fish b/src/etc/completions/x.fish index 58c5e4255caf6..9a9142f3540ba 100644 --- a/src/etc/completions/x.fish +++ b/src/etc/completions/x.fish @@ -920,6 +920,8 @@ complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_fro complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l exclude -d 'Select the benchmarks matching a prefix in this comma-separated list that you don\'t want to run' -r complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l scenarios -d 'Select the scenarios that should be benchmarked' -r -f -a "{Full\t'',IncrFull\t'',IncrUnchanged\t'',IncrPatched\t''}" complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l profiles -d 'Select the profiles that should be benchmarked' -r -f -a "{Check\t'',Debug\t'',Doc\t'',DocJson\t'',Opt\t'',Clippy\t''}" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l database-path -r +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l frontend-threads -d 'Comma-separated list of thread counts for the parallel frontend. Maps directly to the `-Zthreads` rustc option. If unspecified, `rustc-perf` uses only 1' -r complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l config -d 'TOML configuration file for build' -r -F complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l build-dir -d 'Build directory, overrides `build.build-dir` in `bootstrap.toml`' -r -f -a "(__fish_complete_directories)" complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l build -d 'host target of the stage0 compiler' -r -f @@ -959,6 +961,8 @@ complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_fro complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l exclude -d 'Select the benchmarks matching a prefix in this comma-separated list that you don\'t want to run' -r complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l scenarios -d 'Select the scenarios that should be benchmarked' -r -f -a "{Full\t'',IncrFull\t'',IncrUnchanged\t'',IncrPatched\t''}" complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l profiles -d 'Select the profiles that should be benchmarked' -r -f -a "{Check\t'',Debug\t'',Doc\t'',DocJson\t'',Opt\t'',Clippy\t''}" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l database-path -r +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l frontend-threads -d 'Comma-separated list of thread counts for the parallel frontend. Maps directly to the `-Zthreads` rustc option. If unspecified, `rustc-perf` uses only 1' -r complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l config -d 'TOML configuration file for build' -r -F complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l build-dir -d 'Build directory, overrides `build.build-dir` in `bootstrap.toml`' -r -f -a "(__fish_complete_directories)" complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l build -d 'host target of the stage0 compiler' -r -f @@ -998,6 +1002,8 @@ complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_fro complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l exclude -d 'Select the benchmarks matching a prefix in this comma-separated list that you don\'t want to run' -r complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l scenarios -d 'Select the scenarios that should be benchmarked' -r -f -a "{Full\t'',IncrFull\t'',IncrUnchanged\t'',IncrPatched\t''}" complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l profiles -d 'Select the profiles that should be benchmarked' -r -f -a "{Check\t'',Debug\t'',Doc\t'',DocJson\t'',Opt\t'',Clippy\t''}" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l database-path -r +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l frontend-threads -d 'Comma-separated list of thread counts for the parallel frontend. Maps directly to the `-Zthreads` rustc option. If unspecified, `rustc-perf` uses only 1' -r complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l config -d 'TOML configuration file for build' -r -F complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l build-dir -d 'Build directory, overrides `build.build-dir` in `bootstrap.toml`' -r -f -a "(__fish_complete_directories)" complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l build -d 'host target of the stage0 compiler' -r -f @@ -1037,6 +1043,8 @@ complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_fro complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l exclude -d 'Select the benchmarks matching a prefix in this comma-separated list that you don\'t want to run' -r complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l scenarios -d 'Select the scenarios that should be benchmarked' -r -f -a "{Full\t'',IncrFull\t'',IncrUnchanged\t'',IncrPatched\t''}" complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l profiles -d 'Select the profiles that should be benchmarked' -r -f -a "{Check\t'',Debug\t'',Doc\t'',DocJson\t'',Opt\t'',Clippy\t''}" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l database-path -r +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l frontend-threads -d 'Comma-separated list of thread counts for the parallel frontend. Maps directly to the `-Zthreads` rustc option. If unspecified, `rustc-perf` uses only 1' -r complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l config -d 'TOML configuration file for build' -r -F complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l build-dir -d 'Build directory, overrides `build.build-dir` in `bootstrap.toml`' -r -f -a "(__fish_complete_directories)" complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l build -d 'host target of the stage0 compiler' -r -f @@ -1072,6 +1080,7 @@ complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_fro complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l skip-stage0-validation -d 'Skip stage0 compiler validation' complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l skip-std-check-if-no-download-rustc -d 'Skip checking the standard library if `rust.download-rustc` isn\'t available. This is mostly for RA as building the stage1 compiler to check the library tree on each code change might be too much for some computers' complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -s h -l help -d 'Print help (see more with \'--help\')' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l database-path -r complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l config -d 'TOML configuration file for build' -r -F complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l build-dir -d 'Build directory, overrides `build.build-dir` in `bootstrap.toml`' -r -f -a "(__fish_complete_directories)" complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l build -d 'host target of the stage0 compiler' -r -f diff --git a/src/etc/completions/x.ps1 b/src/etc/completions/x.ps1 index bf7ed50a85b90..9fe020c8e1cf6 100644 --- a/src/etc/completions/x.ps1 +++ b/src/etc/completions/x.ps1 @@ -1093,6 +1093,8 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock { [CompletionResult]::new('--exclude', '--exclude', [CompletionResultType]::ParameterName, 'Select the benchmarks matching a prefix in this comma-separated list that you don''t want to run') [CompletionResult]::new('--scenarios', '--scenarios', [CompletionResultType]::ParameterName, 'Select the scenarios that should be benchmarked') [CompletionResult]::new('--profiles', '--profiles', [CompletionResultType]::ParameterName, 'Select the profiles that should be benchmarked') + [CompletionResult]::new('--database-path', '--database-path', [CompletionResultType]::ParameterName, 'database-path') + [CompletionResult]::new('--frontend-threads', '--frontend-threads', [CompletionResultType]::ParameterName, 'Comma-separated list of thread counts for the parallel frontend. Maps directly to the `-Zthreads` rustc option. If unspecified, `rustc-perf` uses only 1') [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') [CompletionResult]::new('--build-dir', '--build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `bootstrap.toml`') [CompletionResult]::new('--build', '--build', [CompletionResultType]::ParameterName, 'host target of the stage0 compiler') @@ -1140,6 +1142,8 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock { [CompletionResult]::new('--exclude', '--exclude', [CompletionResultType]::ParameterName, 'Select the benchmarks matching a prefix in this comma-separated list that you don''t want to run') [CompletionResult]::new('--scenarios', '--scenarios', [CompletionResultType]::ParameterName, 'Select the scenarios that should be benchmarked') [CompletionResult]::new('--profiles', '--profiles', [CompletionResultType]::ParameterName, 'Select the profiles that should be benchmarked') + [CompletionResult]::new('--database-path', '--database-path', [CompletionResultType]::ParameterName, 'database-path') + [CompletionResult]::new('--frontend-threads', '--frontend-threads', [CompletionResultType]::ParameterName, 'Comma-separated list of thread counts for the parallel frontend. Maps directly to the `-Zthreads` rustc option. If unspecified, `rustc-perf` uses only 1') [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') [CompletionResult]::new('--build-dir', '--build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `bootstrap.toml`') [CompletionResult]::new('--build', '--build', [CompletionResultType]::ParameterName, 'host target of the stage0 compiler') @@ -1187,6 +1191,8 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock { [CompletionResult]::new('--exclude', '--exclude', [CompletionResultType]::ParameterName, 'Select the benchmarks matching a prefix in this comma-separated list that you don''t want to run') [CompletionResult]::new('--scenarios', '--scenarios', [CompletionResultType]::ParameterName, 'Select the scenarios that should be benchmarked') [CompletionResult]::new('--profiles', '--profiles', [CompletionResultType]::ParameterName, 'Select the profiles that should be benchmarked') + [CompletionResult]::new('--database-path', '--database-path', [CompletionResultType]::ParameterName, 'database-path') + [CompletionResult]::new('--frontend-threads', '--frontend-threads', [CompletionResultType]::ParameterName, 'Comma-separated list of thread counts for the parallel frontend. Maps directly to the `-Zthreads` rustc option. If unspecified, `rustc-perf` uses only 1') [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') [CompletionResult]::new('--build-dir', '--build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `bootstrap.toml`') [CompletionResult]::new('--build', '--build', [CompletionResultType]::ParameterName, 'host target of the stage0 compiler') @@ -1234,6 +1240,8 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock { [CompletionResult]::new('--exclude', '--exclude', [CompletionResultType]::ParameterName, 'Select the benchmarks matching a prefix in this comma-separated list that you don''t want to run') [CompletionResult]::new('--scenarios', '--scenarios', [CompletionResultType]::ParameterName, 'Select the scenarios that should be benchmarked') [CompletionResult]::new('--profiles', '--profiles', [CompletionResultType]::ParameterName, 'Select the profiles that should be benchmarked') + [CompletionResult]::new('--database-path', '--database-path', [CompletionResultType]::ParameterName, 'database-path') + [CompletionResult]::new('--frontend-threads', '--frontend-threads', [CompletionResultType]::ParameterName, 'Comma-separated list of thread counts for the parallel frontend. Maps directly to the `-Zthreads` rustc option. If unspecified, `rustc-perf` uses only 1') [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') [CompletionResult]::new('--build-dir', '--build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `bootstrap.toml`') [CompletionResult]::new('--build', '--build', [CompletionResultType]::ParameterName, 'host target of the stage0 compiler') @@ -1277,6 +1285,7 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock { break } 'x;perf;compare' { + [CompletionResult]::new('--database-path', '--database-path', [CompletionResultType]::ParameterName, 'database-path') [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') [CompletionResult]::new('--build-dir', '--build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `bootstrap.toml`') [CompletionResult]::new('--build', '--build', [CompletionResultType]::ParameterName, 'host target of the stage0 compiler') diff --git a/src/etc/completions/x.py.fish b/src/etc/completions/x.py.fish index 292408fbe49f6..9f3056fdb3b63 100644 --- a/src/etc/completions/x.py.fish +++ b/src/etc/completions/x.py.fish @@ -920,6 +920,8 @@ complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcomma complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l exclude -d 'Select the benchmarks matching a prefix in this comma-separated list that you don\'t want to run' -r complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l scenarios -d 'Select the scenarios that should be benchmarked' -r -f -a "{Full\t'',IncrFull\t'',IncrUnchanged\t'',IncrPatched\t''}" complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l profiles -d 'Select the profiles that should be benchmarked' -r -f -a "{Check\t'',Debug\t'',Doc\t'',DocJson\t'',Opt\t'',Clippy\t''}" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l database-path -r +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l frontend-threads -d 'Comma-separated list of thread counts for the parallel frontend. Maps directly to the `-Zthreads` rustc option. If unspecified, `rustc-perf` uses only 1' -r complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l config -d 'TOML configuration file for build' -r -F complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l build-dir -d 'Build directory, overrides `build.build-dir` in `bootstrap.toml`' -r -f -a "(__fish_complete_directories)" complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l build -d 'host target of the stage0 compiler' -r -f @@ -959,6 +961,8 @@ complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcomma complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l exclude -d 'Select the benchmarks matching a prefix in this comma-separated list that you don\'t want to run' -r complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l scenarios -d 'Select the scenarios that should be benchmarked' -r -f -a "{Full\t'',IncrFull\t'',IncrUnchanged\t'',IncrPatched\t''}" complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l profiles -d 'Select the profiles that should be benchmarked' -r -f -a "{Check\t'',Debug\t'',Doc\t'',DocJson\t'',Opt\t'',Clippy\t''}" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l database-path -r +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l frontend-threads -d 'Comma-separated list of thread counts for the parallel frontend. Maps directly to the `-Zthreads` rustc option. If unspecified, `rustc-perf` uses only 1' -r complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l config -d 'TOML configuration file for build' -r -F complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l build-dir -d 'Build directory, overrides `build.build-dir` in `bootstrap.toml`' -r -f -a "(__fish_complete_directories)" complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l build -d 'host target of the stage0 compiler' -r -f @@ -998,6 +1002,8 @@ complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcomma complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l exclude -d 'Select the benchmarks matching a prefix in this comma-separated list that you don\'t want to run' -r complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l scenarios -d 'Select the scenarios that should be benchmarked' -r -f -a "{Full\t'',IncrFull\t'',IncrUnchanged\t'',IncrPatched\t''}" complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l profiles -d 'Select the profiles that should be benchmarked' -r -f -a "{Check\t'',Debug\t'',Doc\t'',DocJson\t'',Opt\t'',Clippy\t''}" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l database-path -r +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l frontend-threads -d 'Comma-separated list of thread counts for the parallel frontend. Maps directly to the `-Zthreads` rustc option. If unspecified, `rustc-perf` uses only 1' -r complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l config -d 'TOML configuration file for build' -r -F complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l build-dir -d 'Build directory, overrides `build.build-dir` in `bootstrap.toml`' -r -f -a "(__fish_complete_directories)" complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l build -d 'host target of the stage0 compiler' -r -f @@ -1037,6 +1043,8 @@ complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcomma complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l exclude -d 'Select the benchmarks matching a prefix in this comma-separated list that you don\'t want to run' -r complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l scenarios -d 'Select the scenarios that should be benchmarked' -r -f -a "{Full\t'',IncrFull\t'',IncrUnchanged\t'',IncrPatched\t''}" complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l profiles -d 'Select the profiles that should be benchmarked' -r -f -a "{Check\t'',Debug\t'',Doc\t'',DocJson\t'',Opt\t'',Clippy\t''}" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l database-path -r +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l frontend-threads -d 'Comma-separated list of thread counts for the parallel frontend. Maps directly to the `-Zthreads` rustc option. If unspecified, `rustc-perf` uses only 1' -r complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l config -d 'TOML configuration file for build' -r -F complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l build-dir -d 'Build directory, overrides `build.build-dir` in `bootstrap.toml`' -r -f -a "(__fish_complete_directories)" complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l build -d 'host target of the stage0 compiler' -r -f @@ -1072,6 +1080,7 @@ complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcomma complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l skip-stage0-validation -d 'Skip stage0 compiler validation' complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l skip-std-check-if-no-download-rustc -d 'Skip checking the standard library if `rust.download-rustc` isn\'t available. This is mostly for RA as building the stage1 compiler to check the library tree on each code change might be too much for some computers' complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -s h -l help -d 'Print help (see more with \'--help\')' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l database-path -r complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l config -d 'TOML configuration file for build' -r -F complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l build-dir -d 'Build directory, overrides `build.build-dir` in `bootstrap.toml`' -r -f -a "(__fish_complete_directories)" complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l build -d 'host target of the stage0 compiler' -r -f diff --git a/src/etc/completions/x.py.ps1 b/src/etc/completions/x.py.ps1 index 5ab4cab3a32a3..189b69df3c6f1 100644 --- a/src/etc/completions/x.py.ps1 +++ b/src/etc/completions/x.py.ps1 @@ -1093,6 +1093,8 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--exclude', '--exclude', [CompletionResultType]::ParameterName, 'Select the benchmarks matching a prefix in this comma-separated list that you don''t want to run') [CompletionResult]::new('--scenarios', '--scenarios', [CompletionResultType]::ParameterName, 'Select the scenarios that should be benchmarked') [CompletionResult]::new('--profiles', '--profiles', [CompletionResultType]::ParameterName, 'Select the profiles that should be benchmarked') + [CompletionResult]::new('--database-path', '--database-path', [CompletionResultType]::ParameterName, 'database-path') + [CompletionResult]::new('--frontend-threads', '--frontend-threads', [CompletionResultType]::ParameterName, 'Comma-separated list of thread counts for the parallel frontend. Maps directly to the `-Zthreads` rustc option. If unspecified, `rustc-perf` uses only 1') [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') [CompletionResult]::new('--build-dir', '--build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `bootstrap.toml`') [CompletionResult]::new('--build', '--build', [CompletionResultType]::ParameterName, 'host target of the stage0 compiler') @@ -1140,6 +1142,8 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--exclude', '--exclude', [CompletionResultType]::ParameterName, 'Select the benchmarks matching a prefix in this comma-separated list that you don''t want to run') [CompletionResult]::new('--scenarios', '--scenarios', [CompletionResultType]::ParameterName, 'Select the scenarios that should be benchmarked') [CompletionResult]::new('--profiles', '--profiles', [CompletionResultType]::ParameterName, 'Select the profiles that should be benchmarked') + [CompletionResult]::new('--database-path', '--database-path', [CompletionResultType]::ParameterName, 'database-path') + [CompletionResult]::new('--frontend-threads', '--frontend-threads', [CompletionResultType]::ParameterName, 'Comma-separated list of thread counts for the parallel frontend. Maps directly to the `-Zthreads` rustc option. If unspecified, `rustc-perf` uses only 1') [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') [CompletionResult]::new('--build-dir', '--build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `bootstrap.toml`') [CompletionResult]::new('--build', '--build', [CompletionResultType]::ParameterName, 'host target of the stage0 compiler') @@ -1187,6 +1191,8 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--exclude', '--exclude', [CompletionResultType]::ParameterName, 'Select the benchmarks matching a prefix in this comma-separated list that you don''t want to run') [CompletionResult]::new('--scenarios', '--scenarios', [CompletionResultType]::ParameterName, 'Select the scenarios that should be benchmarked') [CompletionResult]::new('--profiles', '--profiles', [CompletionResultType]::ParameterName, 'Select the profiles that should be benchmarked') + [CompletionResult]::new('--database-path', '--database-path', [CompletionResultType]::ParameterName, 'database-path') + [CompletionResult]::new('--frontend-threads', '--frontend-threads', [CompletionResultType]::ParameterName, 'Comma-separated list of thread counts for the parallel frontend. Maps directly to the `-Zthreads` rustc option. If unspecified, `rustc-perf` uses only 1') [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') [CompletionResult]::new('--build-dir', '--build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `bootstrap.toml`') [CompletionResult]::new('--build', '--build', [CompletionResultType]::ParameterName, 'host target of the stage0 compiler') @@ -1234,6 +1240,8 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--exclude', '--exclude', [CompletionResultType]::ParameterName, 'Select the benchmarks matching a prefix in this comma-separated list that you don''t want to run') [CompletionResult]::new('--scenarios', '--scenarios', [CompletionResultType]::ParameterName, 'Select the scenarios that should be benchmarked') [CompletionResult]::new('--profiles', '--profiles', [CompletionResultType]::ParameterName, 'Select the profiles that should be benchmarked') + [CompletionResult]::new('--database-path', '--database-path', [CompletionResultType]::ParameterName, 'database-path') + [CompletionResult]::new('--frontend-threads', '--frontend-threads', [CompletionResultType]::ParameterName, 'Comma-separated list of thread counts for the parallel frontend. Maps directly to the `-Zthreads` rustc option. If unspecified, `rustc-perf` uses only 1') [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') [CompletionResult]::new('--build-dir', '--build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `bootstrap.toml`') [CompletionResult]::new('--build', '--build', [CompletionResultType]::ParameterName, 'host target of the stage0 compiler') @@ -1277,6 +1285,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { break } 'x.py;perf;compare' { + [CompletionResult]::new('--database-path', '--database-path', [CompletionResultType]::ParameterName, 'database-path') [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') [CompletionResult]::new('--build-dir', '--build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `bootstrap.toml`') [CompletionResult]::new('--build', '--build', [CompletionResultType]::ParameterName, 'host target of the stage0 compiler') diff --git a/src/etc/completions/x.py.sh b/src/etc/completions/x.py.sh index 0c449988b0461..7bc3da600c2fe 100644 --- a/src/etc/completions/x.py.sh +++ b/src/etc/completions/x.py.sh @@ -3094,7 +3094,7 @@ _x.py() { return 0 ;; x.py__perf__benchmark) - opts="-v -q -i -j -h --include --exclude --scenarios --profiles --verbose --quiet --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." + opts="-v -q -i -j -h --include --exclude --scenarios --profiles --database-path --frontend-threads --verbose --quiet --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -3116,6 +3116,14 @@ _x.py() { COMPREPLY=($(compgen -W "Check Debug Doc DocJson Opt Clippy" -- "${cur}")) return 0 ;; + --database-path) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --frontend-threads) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; --config) local oldifs if [ -n "${IFS+x}" ]; then @@ -3292,7 +3300,7 @@ _x.py() { return 0 ;; x.py__perf__cachegrind) - opts="-v -q -i -j -h --include --exclude --scenarios --profiles --verbose --quiet --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." + opts="-v -q -i -j -h --include --exclude --scenarios --profiles --database-path --frontend-threads --verbose --quiet --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -3314,6 +3322,14 @@ _x.py() { COMPREPLY=($(compgen -W "Check Debug Doc DocJson Opt Clippy" -- "${cur}")) return 0 ;; + --database-path) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --frontend-threads) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; --config) local oldifs if [ -n "${IFS+x}" ]; then @@ -3490,12 +3506,16 @@ _x.py() { return 0 ;; x.py__perf__compare) - opts="-v -q -i -j -h --verbose --quiet --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." + opts="-v -q -i -j -h --database-path --verbose --quiet --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 fi case "${prev}" in + --database-path) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; --config) local oldifs if [ -n "${IFS+x}" ]; then @@ -3676,7 +3696,7 @@ _x.py() { return 0 ;; x.py__perf__eprintln) - opts="-v -q -i -j -h --include --exclude --scenarios --profiles --verbose --quiet --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." + opts="-v -q -i -j -h --include --exclude --scenarios --profiles --database-path --frontend-threads --verbose --quiet --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -3698,6 +3718,14 @@ _x.py() { COMPREPLY=($(compgen -W "Check Debug Doc DocJson Opt Clippy" -- "${cur}")) return 0 ;; + --database-path) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --frontend-threads) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; --config) local oldifs if [ -n "${IFS+x}" ]; then @@ -3874,7 +3902,7 @@ _x.py() { return 0 ;; x.py__perf__samply) - opts="-v -q -i -j -h --include --exclude --scenarios --profiles --verbose --quiet --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." + opts="-v -q -i -j -h --include --exclude --scenarios --profiles --database-path --frontend-threads --verbose --quiet --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -3896,6 +3924,14 @@ _x.py() { COMPREPLY=($(compgen -W "Check Debug Doc DocJson Opt Clippy" -- "${cur}")) return 0 ;; + --database-path) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --frontend-threads) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; --config) local oldifs if [ -n "${IFS+x}" ]; then diff --git a/src/etc/completions/x.py.zsh b/src/etc/completions/x.py.zsh index 9ee5843a5abd7..c06d0f316053a 100644 --- a/src/etc/completions/x.py.zsh +++ b/src/etc/completions/x.py.zsh @@ -1123,6 +1123,8 @@ _arguments "${_arguments_options[@]}" : \ '*--exclude=[Select the benchmarks matching a prefix in this comma-separated list that you don'\''t want to run]:EXCLUDE:_default' \ '*--scenarios=[Select the scenarios that should be benchmarked]:SCENARIOS:(Full IncrFull IncrUnchanged IncrPatched)' \ '*--profiles=[Select the profiles that should be benchmarked]:PROFILES:(Check Debug Doc DocJson Opt Clippy)' \ +'--database-path=[]:DATABASE_PATH:_default' \ +'*--frontend-threads=[Comma-separated list of thread counts for the parallel frontend. Maps directly to the \`-Zthreads\` rustc option. If unspecified, \`rustc-perf\` uses only 1]:FRONTEND_THREADS:_default' \ '--config=[TOML configuration file for build]:FILE:_files' \ '--build-dir=[Build directory, overrides \`build.build-dir\` in \`bootstrap.toml\`]:DIR:_files -/' \ '--build=[host target of the stage0 compiler]:BUILD:' \ @@ -1172,6 +1174,8 @@ _arguments "${_arguments_options[@]}" : \ '*--exclude=[Select the benchmarks matching a prefix in this comma-separated list that you don'\''t want to run]:EXCLUDE:_default' \ '*--scenarios=[Select the scenarios that should be benchmarked]:SCENARIOS:(Full IncrFull IncrUnchanged IncrPatched)' \ '*--profiles=[Select the profiles that should be benchmarked]:PROFILES:(Check Debug Doc DocJson Opt Clippy)' \ +'--database-path=[]:DATABASE_PATH:_default' \ +'*--frontend-threads=[Comma-separated list of thread counts for the parallel frontend. Maps directly to the \`-Zthreads\` rustc option. If unspecified, \`rustc-perf\` uses only 1]:FRONTEND_THREADS:_default' \ '--config=[TOML configuration file for build]:FILE:_files' \ '--build-dir=[Build directory, overrides \`build.build-dir\` in \`bootstrap.toml\`]:DIR:_files -/' \ '--build=[host target of the stage0 compiler]:BUILD:' \ @@ -1221,6 +1225,8 @@ _arguments "${_arguments_options[@]}" : \ '*--exclude=[Select the benchmarks matching a prefix in this comma-separated list that you don'\''t want to run]:EXCLUDE:_default' \ '*--scenarios=[Select the scenarios that should be benchmarked]:SCENARIOS:(Full IncrFull IncrUnchanged IncrPatched)' \ '*--profiles=[Select the profiles that should be benchmarked]:PROFILES:(Check Debug Doc DocJson Opt Clippy)' \ +'--database-path=[]:DATABASE_PATH:_default' \ +'*--frontend-threads=[Comma-separated list of thread counts for the parallel frontend. Maps directly to the \`-Zthreads\` rustc option. If unspecified, \`rustc-perf\` uses only 1]:FRONTEND_THREADS:_default' \ '--config=[TOML configuration file for build]:FILE:_files' \ '--build-dir=[Build directory, overrides \`build.build-dir\` in \`bootstrap.toml\`]:DIR:_files -/' \ '--build=[host target of the stage0 compiler]:BUILD:' \ @@ -1270,6 +1276,8 @@ _arguments "${_arguments_options[@]}" : \ '*--exclude=[Select the benchmarks matching a prefix in this comma-separated list that you don'\''t want to run]:EXCLUDE:_default' \ '*--scenarios=[Select the scenarios that should be benchmarked]:SCENARIOS:(Full IncrFull IncrUnchanged IncrPatched)' \ '*--profiles=[Select the profiles that should be benchmarked]:PROFILES:(Check Debug Doc DocJson Opt Clippy)' \ +'--database-path=[]:DATABASE_PATH:_default' \ +'*--frontend-threads=[Comma-separated list of thread counts for the parallel frontend. Maps directly to the \`-Zthreads\` rustc option. If unspecified, \`rustc-perf\` uses only 1]:FRONTEND_THREADS:_default' \ '--config=[TOML configuration file for build]:FILE:_files' \ '--build-dir=[Build directory, overrides \`build.build-dir\` in \`bootstrap.toml\`]:DIR:_files -/' \ '--build=[host target of the stage0 compiler]:BUILD:' \ @@ -1316,6 +1324,7 @@ _arguments "${_arguments_options[@]}" : \ ;; (compare) _arguments "${_arguments_options[@]}" : \ +'--database-path=[]:DATABASE_PATH:_default' \ '--config=[TOML configuration file for build]:FILE:_files' \ '--build-dir=[Build directory, overrides \`build.build-dir\` in \`bootstrap.toml\`]:DIR:_files -/' \ '--build=[host target of the stage0 compiler]:BUILD:' \ diff --git a/src/etc/completions/x.sh b/src/etc/completions/x.sh index f8897cc5f5ec8..a60e9083730e3 100644 --- a/src/etc/completions/x.sh +++ b/src/etc/completions/x.sh @@ -3094,7 +3094,7 @@ _x() { return 0 ;; x__perf__benchmark) - opts="-v -q -i -j -h --include --exclude --scenarios --profiles --verbose --quiet --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." + opts="-v -q -i -j -h --include --exclude --scenarios --profiles --database-path --frontend-threads --verbose --quiet --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -3116,6 +3116,14 @@ _x() { COMPREPLY=($(compgen -W "Check Debug Doc DocJson Opt Clippy" -- "${cur}")) return 0 ;; + --database-path) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --frontend-threads) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; --config) local oldifs if [ -n "${IFS+x}" ]; then @@ -3292,7 +3300,7 @@ _x() { return 0 ;; x__perf__cachegrind) - opts="-v -q -i -j -h --include --exclude --scenarios --profiles --verbose --quiet --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." + opts="-v -q -i -j -h --include --exclude --scenarios --profiles --database-path --frontend-threads --verbose --quiet --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -3314,6 +3322,14 @@ _x() { COMPREPLY=($(compgen -W "Check Debug Doc DocJson Opt Clippy" -- "${cur}")) return 0 ;; + --database-path) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --frontend-threads) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; --config) local oldifs if [ -n "${IFS+x}" ]; then @@ -3490,12 +3506,16 @@ _x() { return 0 ;; x__perf__compare) - opts="-v -q -i -j -h --verbose --quiet --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." + opts="-v -q -i -j -h --database-path --verbose --quiet --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 fi case "${prev}" in + --database-path) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; --config) local oldifs if [ -n "${IFS+x}" ]; then @@ -3676,7 +3696,7 @@ _x() { return 0 ;; x__perf__eprintln) - opts="-v -q -i -j -h --include --exclude --scenarios --profiles --verbose --quiet --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." + opts="-v -q -i -j -h --include --exclude --scenarios --profiles --database-path --frontend-threads --verbose --quiet --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -3698,6 +3718,14 @@ _x() { COMPREPLY=($(compgen -W "Check Debug Doc DocJson Opt Clippy" -- "${cur}")) return 0 ;; + --database-path) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --frontend-threads) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; --config) local oldifs if [ -n "${IFS+x}" ]; then @@ -3874,7 +3902,7 @@ _x() { return 0 ;; x__perf__samply) - opts="-v -q -i -j -h --include --exclude --scenarios --profiles --verbose --quiet --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." + opts="-v -q -i -j -h --include --exclude --scenarios --profiles --database-path --frontend-threads --verbose --quiet --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -3896,6 +3924,14 @@ _x() { COMPREPLY=($(compgen -W "Check Debug Doc DocJson Opt Clippy" -- "${cur}")) return 0 ;; + --database-path) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --frontend-threads) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; --config) local oldifs if [ -n "${IFS+x}" ]; then diff --git a/src/etc/completions/x.zsh b/src/etc/completions/x.zsh index f61942a11da4a..21857db8899ab 100644 --- a/src/etc/completions/x.zsh +++ b/src/etc/completions/x.zsh @@ -1123,6 +1123,8 @@ _arguments "${_arguments_options[@]}" : \ '*--exclude=[Select the benchmarks matching a prefix in this comma-separated list that you don'\''t want to run]:EXCLUDE:_default' \ '*--scenarios=[Select the scenarios that should be benchmarked]:SCENARIOS:(Full IncrFull IncrUnchanged IncrPatched)' \ '*--profiles=[Select the profiles that should be benchmarked]:PROFILES:(Check Debug Doc DocJson Opt Clippy)' \ +'--database-path=[]:DATABASE_PATH:_default' \ +'*--frontend-threads=[Comma-separated list of thread counts for the parallel frontend. Maps directly to the \`-Zthreads\` rustc option. If unspecified, \`rustc-perf\` uses only 1]:FRONTEND_THREADS:_default' \ '--config=[TOML configuration file for build]:FILE:_files' \ '--build-dir=[Build directory, overrides \`build.build-dir\` in \`bootstrap.toml\`]:DIR:_files -/' \ '--build=[host target of the stage0 compiler]:BUILD:' \ @@ -1172,6 +1174,8 @@ _arguments "${_arguments_options[@]}" : \ '*--exclude=[Select the benchmarks matching a prefix in this comma-separated list that you don'\''t want to run]:EXCLUDE:_default' \ '*--scenarios=[Select the scenarios that should be benchmarked]:SCENARIOS:(Full IncrFull IncrUnchanged IncrPatched)' \ '*--profiles=[Select the profiles that should be benchmarked]:PROFILES:(Check Debug Doc DocJson Opt Clippy)' \ +'--database-path=[]:DATABASE_PATH:_default' \ +'*--frontend-threads=[Comma-separated list of thread counts for the parallel frontend. Maps directly to the \`-Zthreads\` rustc option. If unspecified, \`rustc-perf\` uses only 1]:FRONTEND_THREADS:_default' \ '--config=[TOML configuration file for build]:FILE:_files' \ '--build-dir=[Build directory, overrides \`build.build-dir\` in \`bootstrap.toml\`]:DIR:_files -/' \ '--build=[host target of the stage0 compiler]:BUILD:' \ @@ -1221,6 +1225,8 @@ _arguments "${_arguments_options[@]}" : \ '*--exclude=[Select the benchmarks matching a prefix in this comma-separated list that you don'\''t want to run]:EXCLUDE:_default' \ '*--scenarios=[Select the scenarios that should be benchmarked]:SCENARIOS:(Full IncrFull IncrUnchanged IncrPatched)' \ '*--profiles=[Select the profiles that should be benchmarked]:PROFILES:(Check Debug Doc DocJson Opt Clippy)' \ +'--database-path=[]:DATABASE_PATH:_default' \ +'*--frontend-threads=[Comma-separated list of thread counts for the parallel frontend. Maps directly to the \`-Zthreads\` rustc option. If unspecified, \`rustc-perf\` uses only 1]:FRONTEND_THREADS:_default' \ '--config=[TOML configuration file for build]:FILE:_files' \ '--build-dir=[Build directory, overrides \`build.build-dir\` in \`bootstrap.toml\`]:DIR:_files -/' \ '--build=[host target of the stage0 compiler]:BUILD:' \ @@ -1270,6 +1276,8 @@ _arguments "${_arguments_options[@]}" : \ '*--exclude=[Select the benchmarks matching a prefix in this comma-separated list that you don'\''t want to run]:EXCLUDE:_default' \ '*--scenarios=[Select the scenarios that should be benchmarked]:SCENARIOS:(Full IncrFull IncrUnchanged IncrPatched)' \ '*--profiles=[Select the profiles that should be benchmarked]:PROFILES:(Check Debug Doc DocJson Opt Clippy)' \ +'--database-path=[]:DATABASE_PATH:_default' \ +'*--frontend-threads=[Comma-separated list of thread counts for the parallel frontend. Maps directly to the \`-Zthreads\` rustc option. If unspecified, \`rustc-perf\` uses only 1]:FRONTEND_THREADS:_default' \ '--config=[TOML configuration file for build]:FILE:_files' \ '--build-dir=[Build directory, overrides \`build.build-dir\` in \`bootstrap.toml\`]:DIR:_files -/' \ '--build=[host target of the stage0 compiler]:BUILD:' \ @@ -1316,6 +1324,7 @@ _arguments "${_arguments_options[@]}" : \ ;; (compare) _arguments "${_arguments_options[@]}" : \ +'--database-path=[]:DATABASE_PATH:_default' \ '--config=[TOML configuration file for build]:FILE:_files' \ '--build-dir=[Build directory, overrides \`build.build-dir\` in \`bootstrap.toml\`]:DIR:_files -/' \ '--build=[host target of the stage0 compiler]:BUILD:' \ diff --git a/src/etc/debugger_tester/common.py b/src/etc/debugger_tester/common.py index 69e4f646f23d3..cf6fe3b3b63e5 100644 --- a/src/etc/debugger_tester/common.py +++ b/src/etc/debugger_tester/common.py @@ -319,8 +319,7 @@ def print_field_errors(self, expected: "Type", error_source: str): elif types_match and names_match: reordered = "\n ".join( ( - f"{m[1].name} offset: +{m[1].offset} -> {m[0].name} offset: \n\ -+{m[0].offset}" + f"{m[1].name} offset: +{m[1].offset} -> {m[0].name} offset: +{m[0].offset}" ) for m in mismatches ) diff --git a/src/etc/debugger_tester/lldb/batchmode.py b/src/etc/debugger_tester/lldb/batchmode.py index fdf6a34c01716..ebc7b7ff91577 100644 --- a/src/etc/debugger_tester/lldb/batchmode.py +++ b/src/etc/debugger_tester/lldb/batchmode.py @@ -16,6 +16,7 @@ import _thread as thread import os import re +import signal import sys import threading import time @@ -184,6 +185,27 @@ def dispatch_repr(var_name: str, breakpoint_index: int, frame: lldb.SBFrame) -> return check(var_name, breakpoint_index, frame) == Result.Ok +def quit_with_error(debugger: lldb.SBDebugger): + """Kills the parent LLDB process with a non-0 exit code""" + + # file handles aren't guaranteed to be flushed when python doesn't return control back to LLDB, + # so we need to do it manually + debugger.GetOutputFile().Flush() + debugger.GetErrorFile().Flush() + sys.stdout.flush() + sys.stderr.flush() + + # When using a debugger created from the python script (e.g. `lldb.SBDebugger.Create()`), this + # doesn't actually work, but it doesn't hurt to try =) + debugger.HandleCommand("quit 1") + + # Returning status codes using `sys.exit` doesn't work since we're in an LLDB managed python + # instance. Instead, we kill the PID, which happens to be the parent LLDB process. + # Note: We use SIGTERM because it works on linux and windows, unlike SIGKILL, and doesn't cause + # LLDB to spit out a backtrace like SIGABRT. + os.kill(os.getpid(), signal.SIGTERM) + + #################################################################################################### # ~main #################################################################################################### @@ -203,12 +225,12 @@ def main(): # Start the timeout watchdog start_watchdog() - # This is the debugger instance of the lldb executable that imported and ran this python script. - # There is some weird behavior around LLDB reassigning, clearing, or not updating their own - # references (like `lldb.debugger`) while a python function is actively running (i.e. if control - # is not given back to the REPL). To prevent LLDB from changing things out from under us, we - # store this reference locally. - debugger = lldb.debugger + # We use a new `SBDebugger` instance, since LLDB often doesn't update internal state/python + # state while a command is being run. Since the entirety of `batchmode` is executed via a + # `script` command, the parent LLDB never gets a chance to update. + # In LLDB <23 this was less of an issue, but a change in LLDB 23 made it difficult to create + # targets from the parent debugger instance. + debugger = lldb.SBDebugger.Create() # When we step or continue, don't return from the function until the process # stops. We do this by setting the async mode to false. @@ -283,13 +305,10 @@ def main(): print(f"Could not read debugging script '{script_path}'.") traceback.print_exception(type(e), e, e.__traceback__, file=sys.stdout) print("Aborting.") - # Returning status codes using `sys.exit` doesn't work since we're in an LLDB managed python - # instance. This command sets the exit code but *does not* kill LLDB, the debugee process, - # or the SBDebugger object. - debugger.HandleCommand("quit 1") + quit_with_error(debugger) except Exception as e: traceback.print_exception(type(e), e, e.__traceback__, file=sys.stdout) - debugger.HandleCommand("quit 1") + quit_with_error(debugger) else: # Executes if the `try` block throws no exceptions. if repr_cmd_run: # We save importing these until we actually see a repr command. This prevents us @@ -329,7 +348,7 @@ def main(): ) if not tested_all_types() or not tested_all_variables(): - debugger.HandleCommand("quit 1") + quit_with_error(debugger) elif BLESS: from lldb_providers import FEATURE_FLAGS diff --git a/src/etc/debugger_tester/lldb/check_lldb.py b/src/etc/debugger_tester/lldb/check_lldb.py index 698513ffc1793..554f29a892991 100644 --- a/src/etc/debugger_tester/lldb/check_lldb.py +++ b/src/etc/debugger_tester/lldb/check_lldb.py @@ -207,24 +207,30 @@ def var_matches(var: Variable, expected: Variable, valobj: lldb.SBValue) -> Resu type_ok = var.type == expected.type + all_providers_ok = ( + summary_ok & synthetic_ok & format_ok & pretty_type_name_ok & pretty_print_ok + ) + if var.has_visualizer() or expected.has_visualizer(): type_match_ok = type_matches( - valobj.GetType(), - valobj.GetTarget(), - summary_ok - & synthetic_ok - & format_ok - & pretty_type_name_ok - & pretty_print_ok, + valobj.GetType(), valobj.GetTarget(), all_providers_ok ) else: type_match_ok = Result.Ok + # (small hack) If the type information doesn't match, BUT it doesn't affect the synthetics, we + # don't actually care that much. This prevents CI from failing when 2 different targets use 2 + # different layouts, e.g.:: + # x86_64-linux-gnu: `Vec { buf, len, marker}` + # aarch64-linux-gnu: `Vec {len, buf, marker}` + if type_match_ok == Result.Mismatch and all_providers_ok: + type_match_ok = Result.Ok + value_ok = var.value == expected.value work_list = [valobj.GetChildAtIndex(i) for i in range(valobj.GetNumChildren())] target = valobj.GetTarget() - child_types_ok = True + child_types_ok = Result.Ok while len(work_list) != 0: obj = work_list.pop() @@ -237,12 +243,17 @@ def var_matches(var: Variable, expected: Variable, valobj: lldb.SBValue) -> Resu if child.IsValid(): work_list.append(child) else: - child_types_ok = False + child_types_ok = Result.Mismatch if var.has_visualizer() or expected.has_visualizer(): - child_types_ok &= type_matches(obj.GetType(), target) == Result.Ok + child_types_ok &= type_matches(obj.GetType(), target) + + # similar to type matching, we don't actually care if there's a mismatch if the + # providers aren't affected. + if child_types_ok == Result.Mismatch and all_providers_ok: + child_types_ok = Result.Ok else: - type_match_ok = Result.Ok + child_types_ok = Result.Ok children_ok = children_match( var.children, expected.children, valobj.GetName(), valobj diff --git a/src/etc/lldb_lookup.py b/src/etc/lldb_lookup.py index 365816dc8489a..94ed47af1891f 100644 --- a/src/etc/lldb_lookup.py +++ b/src/etc/lldb_lookup.py @@ -40,6 +40,7 @@ ClangEncodedEnumSummaryProvider, StructSummaryProvider, f16SummaryProvider, + f128SummaryProvider, # re-exports get_template_args as get_template_args, resolve_msvc_template_arg as resolve_msvc_template_arg, @@ -181,6 +182,17 @@ def register_providers_compatibility(): DEFAULT_TYPE_OPTIONS | lldb.eTypeOptionHideChildren, ) + if LLDBFeature.Float128 in FEATURE_FLAGS: + # Force f128 summary on windows-msvc since most Windows debuggers don't support PDB f128 + register_summary( + f128SummaryProvider, + lldb.SBTypeNameSpecifier( + MOD_PREFIX + is_msvc_f128.__name__, + lldb.eFormatterMatchCallback, + ), + DEFAULT_TYPE_OPTIONS | lldb.eTypeOptionHideChildren, + ) + # Tuple-structs register_synth( TupleSyntheticProvider, @@ -501,6 +513,11 @@ def is_msvc_f16(type: lldb.SBType, _dict: LLDBOpaque) -> bool: return type.GetName() == "f16" and type.IsAggregateType() +def is_msvc_f128(type: lldb.SBType, _dict: LLDBOpaque) -> bool: + # Most Windows debuggers don't support PDB f128. + return type.GetName() == "f128" and type.IsAggregateType() + + def classify_rust_type(type: lldb.SBType, is_msvc: bool) -> RustType: if type.IsPointerType(): return RustType.Indirection diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py index 2791dae3600b0..5859852fb6be4 100644 --- a/src/etc/lldb_providers.py +++ b/src/etc/lldb_providers.py @@ -20,7 +20,6 @@ eBasicTypeUnsignedLongLong, eBasicTypeUnsignedShort, eFormatChar, - eTypeIsInteger, ) from rust_types import is_tuple_fields @@ -171,12 +170,6 @@ def unwrap_unique_or_non_null(unique_or_nonnull: SBValue) -> SBValue: return ptr if ptr.TypeIsPointerType() else ptr.GetChildAtIndex(0) -def unwrap_scalar_wrappers(wrapper: SBValue) -> SBValue: - while (wrapper.type.GetTypeFlags() & eTypeIsInteger) == 0: - wrapper = wrapper.GetChildAtIndex(0) - return wrapper - - class DefaultSyntheticProvider: def __init__(self, valobj: SBValue, _dict: LLDBOpaque): # logger = Logger.Logger() @@ -424,12 +417,10 @@ def StdStringSummaryProvider(valobj: SBValue, dict: LLDBOpaque): .GetNonSyntheticValue() ) - pointer = ( + pointer = unwrap_unique_or_non_null( inner_vec.GetChildMemberWithName("buf") .GetChildMemberWithName("inner") .GetChildMemberWithName("ptr") - .GetChildMemberWithName("pointer") - .GetChildMemberWithName("pointer") ) length = inner_vec.GetChildMemberWithName("len").GetValueAsUnsigned() @@ -462,12 +453,10 @@ def StdOsStringSummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str: if is_windows: inner_vec = inner_vec.GetChildAtIndex(0) - pointer = ( + pointer = unwrap_unique_or_non_null( inner_vec.GetChildMemberWithName("buf") .GetChildMemberWithName("inner") .GetChildMemberWithName("ptr") - .GetChildMemberWithName("pointer") - .GetChildMemberWithName("pointer") ) length = inner_vec.GetChildMemberWithName("len").GetValueAsUnsigned() @@ -542,6 +531,12 @@ def f16SummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str: ) +def f128SummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str: + from lldb import eBasicTypeFloat128 + + return valobj.Cast(valobj.GetTarget().GetBasicType(eBasicTypeFloat128)).GetValue() + + def sequence_formatter(output: str, valobj: SBValue, _dict: LLDBOpaque): length: int = valobj.GetNumChildren() @@ -621,12 +616,10 @@ def __init__(self, valobj: SBValue, _dict: LLDBOpaque): def update(self): inner_vec = self.valobj.GetChildMemberWithName("vec").GetNonSyntheticValue() - self.data_ptr = ( + self.data_ptr = unwrap_unique_or_non_null( inner_vec.GetChildMemberWithName("buf") .GetChildMemberWithName("inner") .GetChildMemberWithName("ptr") - .GetChildMemberWithName("pointer") - .GetChildMemberWithName("pointer") ) self.capacity = ( @@ -1132,6 +1125,10 @@ class StdVecSyntheticProvider: rust 1.62.0: struct Unique { pointer: NonNull, ... } struct NonZero(T) struct NonNull { pointer: *const T } + + FIXME: This version history has been incomplete for a long time. + Maybe either update it to reflect all the version changes, + including addition of RawVecInner and allocators, or just delete it entirely. """ def __init__(self, valobj: SBValue, _dict: LLDBOpaque): @@ -1545,9 +1542,17 @@ def has_children(self) -> bool: def StdRcSummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str: - strong = valobj.GetChildMemberWithName("strong").GetValueAsUnsigned() - weak = valobj.GetChildMemberWithName("weak").GetValueAsUnsigned() - return "strong={}, weak={}".format(strong, weak) + strong = valobj.GetChildMemberWithName("strong") + weak = valobj.GetChildMemberWithName("weak") + + if not (strong.IsValid() and weak.IsValid()): + strong = "?" + weak = "?" + else: + strong = strong.GetValueAsUnsigned() + weak = weak.GetValueAsUnsigned() + + return f"strong={strong}, weak={weak}" class StdRcSyntheticProvider: @@ -1570,8 +1575,24 @@ def __init__(self, valobj: SBValue, _dict: LLDBOpaque, is_atomic: bool = False): self.value = self.ptr.GetChildMemberWithName("data" if is_atomic else "value") - self.strong = unwrap_scalar_wrappers(self.ptr.GetChildMemberWithName("strong")) - self.weak = unwrap_scalar_wrappers(self.ptr.GetChildMemberWithName("weak")) + # infallibly gets an unsigned integer type of at least 64 bits. We don't need to worry about + # whether or not `usize` is actually smaller than that since we don't ever display the + # underlying type to the user anyway + usize_type = valobj.GetTarget().GetBasicType(eBasicTypeUnsignedLongLong) + + self.strong = self.ptr.GetChildMemberWithName("strong").Cast(usize_type) + self.weak = self.ptr.GetChildMemberWithName("weak").Cast(usize_type) + + # If the usize type isn't valid due to llvm/llvm-project#196812, not even the type's fields + # will populate. Luckily, `RcInner` is `#[repr(C)]`, so we can infallibly find the strong + # and weak values in memory + if not self.strong.IsValid() or not self.weak.IsValid(): + raw_ptr = self.ptr.Cast(usize_type.GetPointerType()) + addr = raw_ptr.GetValueAsAddress() + self.strong = self.valobj.CreateValueFromAddress("strong", addr, usize_type) + self.weak = self.valobj.CreateValueFromAddress( + "weak", addr + usize_type.GetByteSize(), usize_type + ) self.value_builder = ValueBuilder(valobj) @@ -1594,15 +1615,14 @@ def get_child_at_index(self, index: int) -> Optional[SBValue]: if index == 0: return self.value if index == 1: - return self.value_builder.from_uint("strong", self.strong_count) + return self.strong if index == 2: - return self.value_builder.from_uint("weak", self.weak_count) + return self.weak return None def update(self): - self.strong_count = self.strong.GetValueAsUnsigned() - self.weak_count = self.weak.GetValueAsUnsigned() - 1 + pass def has_children(self) -> bool: return True diff --git a/src/etc/natvis/intrinsic.natvis b/src/etc/natvis/intrinsic.natvis index 49e0ce319efac..ac9bf1c427957 100644 --- a/src/etc/natvis/intrinsic.natvis +++ b/src/etc/natvis/intrinsic.natvis @@ -59,6 +59,118 @@ {(float) (sign() * (raw_significand() + 1.0) * two_pow_exponent())} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {sign()}inf + NaN + + {sign()}0x0p+0 + + {sign()}0x1{subnormal_hex()}p{-16382 - subnormal_shift(),d} + {sign()}0x1{normal_hex()}p{normal_exponent_sign()}{normal_exponent(),d} + + + "0x" + hex128(high_bits, low_bits, 128) + + () diff --git a/src/etc/natvis/liballoc.natvis b/src/etc/natvis/liballoc.natvis index dade85dafe738..c8e143b49911b 100644 --- a/src/etc/natvis/liballoc.natvis +++ b/src/etc/natvis/liballoc.natvis @@ -7,7 +7,7 @@ buf.inner.cap.__0 len - ($T1*)buf.inner.ptr.pointer.pointer + ($T1*)buf.inner.ptr.pointer @@ -23,7 +23,7 @@ - (($T1*)buf.inner.ptr.pointer.pointer)[(i + head.__0) % buf.inner.cap.__0] + (($T1*)buf.inner.ptr.pointer)[(i + head.__0) % buf.inner.cap.__0] i = i + 1 @@ -41,17 +41,17 @@ - {(char*)vec.buf.inner.ptr.pointer.pointer,[vec.len]s8} - (char*)vec.buf.inner.ptr.pointer.pointer,[vec.len]s8 + {(char*)vec.buf.inner.ptr.pointer,[vec.len]s8} + (char*)vec.buf.inner.ptr.pointer,[vec.len]s8 vec.len vec.buf.inner.cap.__0 - {(char*)vec.buf.inner.ptr.pointer.pointer,[vec.len]s8} + {(char*)vec.buf.inner.ptr.pointer,[vec.len]s8} vec.len - (char*)vec.buf.inner.ptr.pointer.pointer + (char*)vec.buf.inner.ptr.pointer @@ -72,7 +72,7 @@ it is necessary for them. --> - + {ptr.pointer->value} @@ -87,7 +87,7 @@ - + {{ len={ptr.pointer.length} }} ptr.pointer.length @@ -102,7 +102,7 @@ - + {ptr.pointer->value} @@ -117,7 +117,7 @@ - + {{ len={ptr.pointer.length} }} ptr.pointer.length @@ -131,7 +131,7 @@ - + {ptr.pointer->data} @@ -146,7 +146,7 @@ - + {{ len={ptr.pointer.length} }} ptr.pointer.length @@ -160,7 +160,7 @@ - + {ptr.pointer->data} @@ -175,7 +175,7 @@ - + {{ len={ptr.pointer.length} }} ptr.pointer.length diff --git a/src/etc/natvis/libstd.natvis b/src/etc/natvis/libstd.natvis index 93e94e5f38261..bfad30db3cf7a 100644 --- a/src/etc/natvis/libstd.natvis +++ b/src/etc/natvis/libstd.natvis @@ -104,14 +104,14 @@ - {(char*)inner.inner.bytes.buf.inner.ptr.pointer.pointer,[inner.inner.bytes.len]} + {(char*)inner.inner.bytes.buf.inner.ptr.pointer,[inner.inner.bytes.len]} - {(char*)inner.inner.bytes.buf.inner.ptr.pointer.pointer,[inner.inner.bytes.len]} + {(char*)inner.inner.bytes.buf.inner.ptr.pointer,[inner.inner.bytes.len]} inner.inner.bytes.len - (char*)inner.inner.bytes.buf.inner.ptr.pointer.pointer + (char*)inner.inner.bytes.buf.inner.ptr.pointer diff --git a/src/gcc b/src/gcc index 6f155cc3f5a2d..badf78d09d16e 160000 --- a/src/gcc +++ b/src/gcc @@ -1 +1 @@ -Subproject commit 6f155cc3f5a2dff33afe6cc3ed6c2e0e605ae6a3 +Subproject commit badf78d09d16e66f4ca07971c51aa6a227558d4f diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index 4d7895264c158..64fff96201939 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -11,7 +11,6 @@ path = "lib.rs" # tidy-alphabetical-start arrayvec = { version = "0.7", default-features = false } askama = { version = "0.16.1", default-features = false, features = ["alloc", "config", "derive"] } -base64 = "0.21.7" indexmap = { version = "2", features = ["serde"] } itertools = "0.15" libc = "0.2" diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index db63dbaa24663..b0fb3543160e0 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -8,13 +8,11 @@ use std::sync::Arc; use std::{fmt, mem, ops}; use itertools::Either; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::thin_vec::{ThinVec, thin_vec}; -use rustc_hir as hir; -use rustc_hir::Attribute; -use rustc_hir::attrs::{ +use rustc_attr_ir::{ AttributeKind, CfgEntry, CfgHideShow, DocCfgHideShow, DocCfgHideShowValue, HideOrShow, }; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::thin_vec::{ThinVec, thin_vec}; use rustc_middle::ty::TyCtxt; use rustc_span::symbol::{Symbol, sym}; use rustc_span::{DUMMY_SP, Span}; @@ -672,6 +670,7 @@ fn human_readable_target_os(os: Symbol) -> Option<&'static str> { Hermit => "Hermit", Horizon => "Horizon", Hurd => "GNU/Hurd", + Hyperlight => "Hyperlight", IOs => "iOS", Illumos => "illumos", L4Re => "L4Re", @@ -835,7 +834,10 @@ fn handle_auto_cfg_hide_show(cfg_info: &mut CfgInfo, attr: &CfgHideShow) { } } -pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator + Clone>( +pub(crate) fn extract_cfg_from_attrs< + 'a, + I: Iterator + Clone, +>( attrs: I, tcx: TyCtxt<'_>, cfg_info: &mut CfgInfo, @@ -868,7 +870,7 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator let mut doc_cfg = attrs .clone() .filter_map(|attr| match attr { - Attribute::Parsed(AttributeKind::Doc(d)) if !d.cfg.is_empty() => Some(d), + rustc_attr_ir::Attribute::Parsed(AttributeKind::Doc(d)) if !d.cfg.is_empty() => Some(d), _ => None, }) .peekable(); @@ -892,7 +894,7 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator // We get all `doc(auto_cfg)`, `cfg` and `target_feature` attributes. for attr in attrs { - if let Attribute::Parsed(AttributeKind::Doc(d)) = attr { + if let rustc_attr_ir::Attribute::Parsed(AttributeKind::Doc(d)) = attr { for (new_value, span) in &d.auto_cfg_change { if check_changed_auto_active_status( &mut changed_auto_active_status, @@ -918,7 +920,11 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator handle_auto_cfg_hide_show(cfg_info, value); } } - } else if let hir::Attribute::Parsed(AttributeKind::TargetFeature { features, .. }) = attr { + } else if let rustc_attr_ir::Attribute::Parsed(AttributeKind::TargetFeature { + features, + .. + }) = attr + { // Treat `#[target_feature(enable = "feat")]` attributes as if they were // `#[doc(cfg(target_feature = "feat"))]` attributes as well. for (feature, _) in features { @@ -930,7 +936,7 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator } continue; } else if !cfg_info.parent_is_doc_cfg - && let hir::Attribute::Parsed(AttributeKind::CfgTrace(cfgs)) = attr + && let rustc_attr_ir::Attribute::Parsed(AttributeKind::CfgTrace(cfgs)) = attr { for (new_cfg, _) in cfgs { cfg_info.current_cfg &= Cfg(new_cfg.clone()); diff --git a/src/librustdoc/clean/cfg/tests.rs b/src/librustdoc/clean/cfg/tests.rs index a1c25d4251823..e7c6d1ff1b6c4 100644 --- a/src/librustdoc/clean/cfg/tests.rs +++ b/src/librustdoc/clean/cfg/tests.rs @@ -1,5 +1,5 @@ +use rustc_attr_ir::CfgEntry; use rustc_data_structures::thin_vec::thin_vec; -use rustc_hir::attrs::CfgEntry; use rustc_span::{DUMMY_SP, create_default_session_globals_then}; use super::*; diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 2fbfae88918a0..7f81e29180baf 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -3,11 +3,12 @@ use std::iter::once; use std::sync::Arc; +use rustc_attr_ir::{DocInline, find_attr}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::thin_vec::{ThinVec, thin_vec}; use rustc_hir::def::{DefKind, MacroKinds, Res}; use rustc_hir::def_id::{DefId, DefIdSet, LocalDefId, LocalModId}; -use rustc_hir::{self as hir, Mutability, find_attr}; +use rustc_hir::{self as hir, HirId, Mutability}; use rustc_metadata::creader::{CStore, LoadedMacro}; use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::{self, TyCtxt}; @@ -42,7 +43,7 @@ pub(crate) fn try_inline( cx: &mut DocContext<'_>, res: Res, name: Symbol, - attrs: Option<(&[hir::Attribute], Option)>, + attrs: Option<(&[rustc_attr_ir::Attribute], Option)>, visited: &mut DefIdSet, ) -> Option> { fn try_inline_inner( @@ -184,7 +185,8 @@ pub(crate) fn try_inline_glob( current_mod: LocalModId, visited: &mut DefIdSet, inlined_names: &mut FxHashSet<(ItemType, Symbol)>, - import: &hir::Item<'_>, + import_id: LocalDefId, + import_hir_id: HirId, ) -> Option> { let did = res.opt_def_id()?; if did.is_local() { @@ -203,7 +205,7 @@ pub(crate) fn try_inline_glob( .filter_map(|child| child.res.opt_def_id()) .filter(|&def_id| !cx.tcx.is_doc_hidden(def_id)) .collect(); - let attrs = cx.tcx.hir_attrs(import.hir_id()); + let attrs = cx.tcx.hir_attrs(import_hir_id); let mut items = build_module_items( cx, did, @@ -211,7 +213,7 @@ pub(crate) fn try_inline_glob( visited, inlined_names, Some(&reexports), - Some((attrs, Some(import.owner_id.def_id))), + Some((attrs, Some(import_id))), ); items.retain(|item| { if let Some(name) = item.name { @@ -229,7 +231,7 @@ pub(crate) fn try_inline_glob( } } -pub(crate) fn load_attrs<'hir>(tcx: TyCtxt<'hir>, did: DefId) -> &'hir [hir::Attribute] { +pub(crate) fn load_attrs<'hir>(tcx: TyCtxt<'hir>, did: DefId) -> &'hir [rustc_attr_ir::Attribute] { // FIXME: all uses should use `find_attr`! #[allow(deprecated)] tcx.get_all_attrs(did) @@ -346,7 +348,7 @@ pub(super) fn build_function(cx: &mut DocContext<'_>, def_id: DefId) -> Box, did: DefId, - attrs: Option<(&[hir::Attribute], Option)>, + attrs: Option<(&[rustc_attr_ir::Attribute], Option)>, ret: &mut Vec, ) { let tcx = cx.tcx; @@ -432,8 +434,8 @@ pub(crate) fn build_impls( pub(crate) fn merge_attrs( tcx: TyCtxt<'_>, - old_attrs: &[hir::Attribute], - new_attrs: Option<(&[hir::Attribute], Option)>, + old_attrs: &[rustc_attr_ir::Attribute], + new_attrs: Option<(&[rustc_attr_ir::Attribute], Option)>, cfg_info: &mut CfgInfo, ) -> (clean::Attributes, Option>) { // NOTE: If we have additional attributes (from a re-export), @@ -461,7 +463,7 @@ pub(crate) fn merge_attrs( pub(crate) fn build_impl( cx: &mut DocContext<'_>, did: DefId, - attrs: Option<(&[hir::Attribute], Option)>, + attrs: Option<(&[rustc_attr_ir::Attribute], Option)>, ret: &mut Vec, ) { if !cx.inlined.insert(did.into()) { @@ -716,7 +718,7 @@ fn build_module_items( visited: &mut DefIdSet, inlined_names: &mut FxHashSet<(ItemType, Symbol)>, allowed_def_ids: Option<&DefIdSet>, - attrs: Option<(&[hir::Attribute], Option)>, + attrs: Option<(&[rustc_attr_ir::Attribute], Option)>, ) -> Vec { let mut items = Vec::new(); @@ -784,7 +786,7 @@ fn build_module_items( && find_attr!( load_attrs(cx.tcx, reexport_def_id), Doc(d) - if d.inline.first().is_some_and(|(inline, _)| *inline == hir::attrs::DocInline::NoInline) + if d.inline.is_some_and(|(inline, _)| inline == DocInline::NoInline) ) { // We don't inline foreign `use`. diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 1078f48855fbf..1e03a0b13f79f 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -34,16 +34,15 @@ use std::borrow::Cow; use std::collections::BTreeMap; use std::mem; +use rustc_attr_ir::lang_items::LangItem; +use rustc_attr_ir::{AttributeKind, DocAttribute, DocInline, find_attr}; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet, IndexEntry}; use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::codes::*; use rustc_errors::{FatalError, struct_span_code_err}; -use rustc_hir as hir; -use rustc_hir::attrs::lang_items::LangItem; -use rustc_hir::attrs::{AttributeKind, DocAttribute, DocInline}; -use rustc_hir::def::{CtorKind, DefKind, MacroKinds, Res}; +use rustc_hir::def::{CtorKind, DefKind, MacroKinds, PerNS, Res}; use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LOCAL_CRATE, LocalDefId}; -use rustc_hir::{PredicateOrigin, find_attr}; +use rustc_hir::{self as hir, HirId, PredicateOrigin}; use rustc_hir_analysis::{lower_const_arg_for_rustdoc, lower_ty}; use rustc_middle::middle::resolve::Reexport; use rustc_middle::middle::resolve_bound_vars as rbv; @@ -64,6 +63,14 @@ use crate::core::DocContext; use crate::formats::item_type::ItemType; use crate::visit_ast; +#[derive(Copy, Clone, Debug)] +enum ImportLowerMode { + Everything, + GlobsOnly, + NoGlobs, +} + +#[instrument(level = "trace", skip(cx))] pub(crate) fn clean_doc_module<'tcx>( doc: &visit_ast::Module<'tcx>, cx: &mut DocContext<'tcx>, @@ -101,9 +108,6 @@ pub(crate) fn clean_doc_module<'tcx>( items.extend(doc.items.values().flat_map( |visit_ast::ItemEntry { item, renamed, import_ids }| { // First, lower everything other than glob imports. - if matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) { - return Vec::new(); - } let v = clean_maybe_renamed_item(cx, item, *renamed, import_ids); for item in &v { if let Some(name) = item.name @@ -119,18 +123,26 @@ pub(crate) fn clean_doc_module<'tcx>( |((_, renamed), visit_ast::InlinedForeign { res, import_id })| { let Some(def_id) = res.opt_def_id() else { return Vec::new() }; let name = renamed.unwrap_or_else(|| cx.tcx.item_name(def_id)); - let import = cx.tcx.hir_expect_item(*import_id); - match import.kind { - hir::ItemKind::Use(path, kind) => { - let hir::UsePath { segments, span, .. } = *path; - let path = hir::Path { segments, res: *res, span }; - clean_use_statement_inner( - import, + let import = cx.tcx.hir_node_by_def_id(*import_id); + match import { + hir::Node::Item(hir::Item { kind: hir::ItemKind::Use(tree), .. }) + | hir::Node::NestedUseTree(tree) => { + let hir::UsePath { segments, span, .. } = *tree.prefix; + let path = hir::UsePath { + segments, + res: PerNS { value_ns: Some(*res), type_ns: None, macro_ns: None }, + span, + }; + clean_use_statement( + *import_id, + cx.tcx.local_def_id_to_hir_id(*import_id), + tree.prefix.span, Some(name), &path, - kind, + tree.kind, cx, &mut Default::default(), + ImportLowerMode::Everything, ) } _ => unreachable!(), @@ -140,8 +152,18 @@ pub(crate) fn clean_doc_module<'tcx>( items.extend(doc.items.values().flat_map( |visit_ast::ItemEntry { item, renamed, import_ids: _ }| { // Now we actually lower the imports, skipping everything else. - if let hir::ItemKind::Use(path, hir::UseKind::Glob) = item.kind { - clean_use_statement(item, *renamed, path, hir::UseKind::Glob, cx, &mut inserted) + if let hir::ItemKind::Use(tree) = item.kind { + clean_use_statement( + item.owner_id.def_id, + item.hir_id(), + item.span, + *renamed, + tree.prefix, + tree.kind, + cx, + &mut inserted, + ImportLowerMode::GlobsOnly, + ) } else { // skip everything else Vec::new() @@ -178,10 +200,10 @@ pub(crate) fn clean_doc_module<'tcx>( } fn is_glob_import(tcx: TyCtxt<'_>, import_id: LocalDefId) -> bool { - if let hir::Node::Item(item) = tcx.hir_node_by_def_id(import_id) - && let hir::ItemKind::Use(_, use_kind) = item.kind + if let hir::Node::Item(hir::Item { kind: hir::ItemKind::Use(tree), .. }) + | hir::Node::NestedUseTree(tree) = tcx.hir_node_by_def_id(import_id) { - use_kind == hir::UseKind::Glob + matches!(tree.kind, hir::UseKind::Glob) } else { false } @@ -202,7 +224,7 @@ pub(crate) fn macro_reexport_is_inline( let is_inline = find_attr!( inline::load_attrs(tcx, reexport_def_id), Doc(d) - if d.inline.first().is_some_and(|(inline, _)| *inline == DocInline::Inline) + if d.inline.is_some_and(|(inline, _)| inline == DocInline::Inline) ); // hidden takes absolute priority over inline on the same node @@ -239,7 +261,7 @@ fn generate_item_with_correct_attrs( let import_is_inline = find_attr!( inline::load_attrs(tcx, import_id.to_def_id()), Doc(d) - if d.inline.first().is_some_and(|(inline, _)| *inline == DocInline::Inline) + if d.inline.is_some_and(|(inline, _)| inline == DocInline::Inline) ) || (is_glob_import(tcx, import_id) && (cx.document_hidden() || !tcx.is_doc_hidden(def_id))) || macro_reexport_is_inline(tcx, import_id, def_id); @@ -1099,7 +1121,7 @@ fn clean_fn_or_proc_macro<'tcx>( /// This is needed to make it more "readable" when documenting functions using /// `rustc_legacy_const_generics`. More information in /// . -fn clean_fn_decl_legacy_const_generics(func: &mut Function, attrs: &[hir::Attribute]) { +fn clean_fn_decl_legacy_const_generics(func: &mut Function, attrs: &[rustc_attr_ir::Attribute]) { let Some(indexes) = find_attr!(attrs, RustcLegacyConstGenerics{fn_indexes,..} => fn_indexes) else { return; @@ -1679,10 +1701,32 @@ fn first_non_private<'tcx>( 'reexps: for reexp in child.reexport_chain.iter() { if let Some(use_def_id) = reexp.id() && let Some(local_use_def_id) = use_def_id.as_local() - && let hir::Node::Item(item) = cx.tcx.hir_node_by_def_id(local_use_def_id) - && let hir::ItemKind::Use(path, hir::UseKind::Single(_)) = item.kind + && let hir::Node::Item(hir::Item { kind: hir::ItemKind::Use(tree), .. }) + | hir::Node::NestedUseTree(tree) = + cx.tcx.hir_node_by_def_id(local_use_def_id) + && let hir::UseKind::Single(_) = tree.kind { - for res in path.res.present_items() { + let mut segments = tree.prefix.segments.to_vec(); + let mut span = tree.prefix.span; + let mut parent = cx.tcx.local_parent(local_use_def_id); + loop { + match cx.tcx.hir_node_by_def_id(parent) { + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Use(tree), .. + }) => { + span = tree.prefix.span.to(span); + segments.splice(0..0, tree.prefix.segments.iter().copied()); + break; + } + hir::Node::NestedUseTree(tree) => { + span = tree.prefix.span.to(span); + segments.splice(0..0, tree.prefix.segments.iter().copied()); + parent = cx.tcx.local_parent(local_use_def_id); + } + _ => break, + } + } + for res in tree.prefix.res.present_items() { if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = res { continue; } @@ -1695,7 +1739,7 @@ fn first_non_private<'tcx>( { break 'reexps; } - last_path_res = Some((path, res)); + last_path_res = Some((segments, span, res)); continue 'reexps; } } @@ -1706,13 +1750,8 @@ fn first_non_private<'tcx>( // // 1. We found a public reexport. // 2. We didn't find a public reexport so it's the "end type" path. - if let Some((new_path, _)) = last_path_res { - return Some(first_non_private_clean_path( - cx, - path, - new_path.segments, - new_path.span, - )); + if let Some((segments, span, _)) = last_path_res { + return Some(first_non_private_clean_path(cx, path, &segments, span)); } // If `last_path_res` is `None`, it can mean two things: // @@ -1888,10 +1927,10 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'_>, cx: &mut DocContext<'tcx>) -> Typ match ty.kind { TyKind::Never => Primitive(PrimitiveType::Never), - TyKind::Ptr(ref m) => RawPointer(m.mutbl, Box::new(clean_ty(m.ty, cx))), - TyKind::Ref(l, ref m) => { + TyKind::Ptr(inner_ty, mutbl) => RawPointer(mutbl, Box::new(clean_ty(inner_ty, cx))), + TyKind::Ref(l, inner_ty, mutbl) => { let lifetime = if l.is_anonymous() { None } else { Some(clean_lifetime(l, cx)) }; - BorrowedRef { lifetime, mutability: m.mutbl, type_: Box::new(clean_ty(m.ty, cx)) } + BorrowedRef { lifetime, mutability: mutbl, type_: Box::new(clean_ty(inner_ty, cx)) } } TyKind::Slice(ty) => Slice(Box::new(clean_ty(ty, cx))), TyKind::Pat(inner_ty, pat) => { @@ -2743,12 +2782,20 @@ fn clean_unsafe_binder_ty<'tcx>( UnsafeBinderTy { generic_params, ty } } +#[instrument(level = "trace", skip(tcx), ret)] pub(crate) fn reexport_chain( tcx: TyCtxt<'_>, import_def_id: LocalDefId, target_def_id: DefId, ) -> &[Reexport] { - for child in tcx.module_children_local(tcx.local_parent(import_def_id)) { + let mut module_id = import_def_id; + loop { + module_id = tcx.local_parent(module_id); + if !matches!(tcx.def_kind(module_id), DefKind::Use) { + break; + } + } + for child in tcx.module_children_local(module_id) { if child.res.opt_def_id() == Some(target_def_id) && child.reexport_chain.first().and_then(|r| r.id()) == Some(import_def_id.to_def_id()) { @@ -2764,7 +2811,7 @@ fn get_all_import_attributes<'hir>( import_def_id: LocalDefId, target_def_id: DefId, is_inline: bool, -) -> Vec<(Cow<'hir, hir::Attribute>, Option)> { +) -> Vec<(Cow<'hir, rustc_attr_ir::Attribute>, Option)> { let mut attrs = Vec::new(); let mut first = true; for def_id in reexport_chain(cx.tcx, import_def_id, target_def_id) @@ -2810,17 +2857,17 @@ fn get_all_import_attributes<'hir>( /// * `doc(no_inline)` /// * `doc(hidden)` fn add_without_unwanted_attributes<'hir>( - attrs: &mut Vec<(Cow<'hir, hir::Attribute>, Option)>, - new_attrs: &'hir [hir::Attribute], + attrs: &mut Vec<(Cow<'hir, rustc_attr_ir::Attribute>, Option)>, + new_attrs: &'hir [rustc_attr_ir::Attribute], is_inline: bool, import_parent: Option, ) { for attr in new_attrs { match attr { - hir::Attribute::Parsed(AttributeKind::DocComment { .. }) => { + rustc_attr_ir::Attribute::Parsed(AttributeKind::DocComment { .. }) => { attrs.push((Cow::Borrowed(attr), import_parent)); } - hir::Attribute::Parsed(AttributeKind::Doc(d)) => { + rustc_attr_ir::Attribute::Parsed(AttributeKind::Doc(d)) => { // Remove attributes from `normal` that should not be inherited by `use` re-export. let DocAttribute { first_span: _, @@ -2850,18 +2897,20 @@ fn add_without_unwanted_attributes<'hir>( if is_inline { attr.cfg = cfg.clone(); } else { - attr.inline = inline.clone(); + attr.inline = *inline; attr.hidden = hidden.clone(); } attr.aliases = aliases.clone(); attrs.push(( - Cow::Owned(hir::Attribute::Parsed(AttributeKind::Doc(Box::new(attr)))), + Cow::Owned(rustc_attr_ir::Attribute::Parsed(AttributeKind::Doc(Box::new( + attr, + )))), import_parent, )); } // We discard `#[cfg(...)]` attributes unless we're inlining - hir::Attribute::Parsed(AttributeKind::CfgTrace(..)) if !is_inline => {} + rustc_attr_ir::Attribute::Parsed(AttributeKind::CfgTrace(..)) if !is_inline => {} // We keep all other attributes _ => { attrs.push((Cow::Borrowed(attr), import_parent)); @@ -2892,14 +2941,17 @@ fn clean_maybe_renamed_item<'tcx>( // generate an impl placeholder and not a "real" impl item. return clean_impl(impl_, item.owner_id.def_id, cx, renamed.is_some()); } - ItemKind::Use(path, kind) => { + ItemKind::Use(tree) => { return clean_use_statement( - item, + item.owner_id.def_id, + item.hir_id(), + item.span, get_name(cx.tcx, item, renamed), - path, - kind, + tree.prefix, + tree.kind, cx, &mut FxHashSet::default(), + ImportLowerMode::NoGlobs, ); } _ => {} @@ -3119,8 +3171,8 @@ fn clean_extern_crate<'tcx>( && attrs.iter().any(|a| { matches!( a, - hir::Attribute::Parsed(AttributeKind::Doc(d)) - if d.inline.first().is_some_and(|(i, _)| *i == DocInline::Inline)) + rustc_attr_ir::Attribute::Parsed(AttributeKind::Doc(d)) + if d.inline.is_some_and(|(i, _)| i == DocInline::Inline)) }) && !cx.is_json_output(); @@ -3147,27 +3199,76 @@ fn clean_extern_crate<'tcx>( } fn clean_use_statement<'tcx>( - import: &hir::Item<'tcx>, + import_def_id: LocalDefId, + import_hir_id: HirId, + import_span: rustc_span::Span, name: Option, - path: &hir::UsePath<'tcx>, - kind: hir::UseKind, + path: &hir::UsePath<'_>, + kind: hir::UseKind<'tcx>, cx: &mut DocContext<'tcx>, inlined_names: &mut FxHashSet<(ItemType, Symbol)>, + mode: ImportLowerMode, ) -> Vec { - let mut items = Vec::new(); - let hir::UsePath { segments, ref res, span } = *path; - for res in res.present_items() { - let path = hir::Path { segments, res, span }; - items.append(&mut clean_use_statement_inner(import, name, &path, kind, cx, inlined_names)); - } - items + let name = match (kind, mode) { + (hir::UseKind::Single(n), ImportLowerMode::Everything | ImportLowerMode::NoGlobs) => { + name.or(Some(n.name)) + } + (hir::UseKind::Glob, ImportLowerMode::NoGlobs) + | (hir::UseKind::Single(_), ImportLowerMode::GlobsOnly) => return vec![], + (hir::UseKind::Glob, ImportLowerMode::Everything | ImportLowerMode::GlobsOnly) => name, + (hir::UseKind::Nested { items }, _) => { + let mut all = vec![]; + for (tree, hir_id, def_id) in items { + let mut segments = path.segments.to_vec(); + segments.extend(tree.prefix.segments.iter()); + if segments.last().unwrap().ident.name == kw::SelfLower { + segments.pop(); + } + let path = hir::UsePath { + segments: &segments, + res: tree.prefix.res, + span: path.span.to(tree.prefix.span), + }; + all.append(&mut clean_use_statement( + *def_id, + *hir_id, + tree.prefix.span, + None, + &path, + tree.kind, + cx, + inlined_names, + mode, + )); + } + return all; + } + }; + path.res + .present_items() + .flat_map(|res| { + let path = hir::Path { span: path.span, res, segments: path.segments }; + clean_use_statement_leaf( + import_def_id, + import_hir_id, + import_span, + name, + &path, + kind, + cx, + inlined_names, + ) + }) + .collect() } -fn clean_use_statement_inner<'tcx>( - import: &hir::Item<'tcx>, +fn clean_use_statement_leaf<'tcx>( + import_id: LocalDefId, + import_hir_id: HirId, + import_span: rustc_span::Span, name: Option, path: &hir::Path<'_>, - kind: hir::UseKind, + kind: hir::UseKind<'tcx>, cx: &mut DocContext<'tcx>, inlined_names: &mut FxHashSet<(ItemType, Symbol)>, ) -> Vec { @@ -3177,20 +3278,20 @@ fn clean_use_statement_inner<'tcx>( // We need this comparison because some imports (for std types for example) // are "inserted" as well but directly by the compiler and they should not be // taken into account. - if import.span.ctxt().outer_expn_data().kind == ExpnKind::AstPass(AstPass::StdImports) { + if import_span.ctxt().outer_expn_data().kind == ExpnKind::AstPass(AstPass::StdImports) { return Vec::new(); } - let visibility = cx.tcx.visibility(import.owner_id); - let attrs = cx.tcx.hir_attrs(import.hir_id()); + let visibility = cx.tcx.visibility(import_id); + let attrs = cx.tcx.hir_attrs(import_hir_id); let inline_attr = find_attr!( attrs, - Doc(d) if d.inline.first().is_some_and(|(i, _)| *i == DocInline::Inline) => d + Doc(d) if d.inline.is_some_and(|(i, _)| i == DocInline::Inline) => d ) - .and_then(|d| d.inline.first()); + .and_then(|d| d.inline); let pub_underscore = visibility.is_public() && name == Some(kw::Underscore); - let current_mod = cx.tcx.parent_module_from_def_id(import.owner_id.def_id); - let import_def_id = import.owner_id.def_id; + let current_mod = cx.tcx.parent_module_from_def_id(import_id); + let import_def_id = import_id; // The parent of the module in which this import resides. This // is the same as `current_mod` if that's already the top @@ -3208,11 +3309,11 @@ fn clean_use_statement_inner<'tcx>( if pub_underscore && let Some((_, inline_span)) = inline_attr { struct_span_code_err!( cx.tcx.dcx(), - *inline_span, + inline_span, E0780, "anonymous imports cannot be inlined" ) - .with_span_label(import.span, "anonymous import") + .with_span_label(import_span, "anonymous import") .emit(); } @@ -3223,16 +3324,18 @@ fn clean_use_statement_inner<'tcx>( let mut denied = cx.is_json_output() || !(visibility.is_public() || (cx.document_private() && is_visible_from_parent_mod)) || pub_underscore - || attrs.iter().any(|a| matches!( - a, - hir::Attribute::Parsed(AttributeKind::Doc(d)) - if d.hidden.is_some() || d.inline.first().is_some_and(|(i, _)| *i == DocInline::NoInline) - )); + || attrs.iter().any(|a| { + matches!( + a, + rustc_attr_ir::Attribute::Parsed(AttributeKind::Doc(d)) + if d.hidden.is_some() || d.inline.is_some_and(|(i, _)| i == DocInline::NoInline) + ) + }); // Also check whether imports were asked to be inlined, in case we're trying to re-export a // crate in Rust 2018+ let path = clean_path(path, cx); - let inner = if kind == hir::UseKind::Glob { + let inner = if matches!(kind, hir::UseKind::Glob) { if !denied { let mut visited = DefIdSet::default(); if let Some(items) = inline::try_inline_glob( @@ -3241,7 +3344,8 @@ fn clean_use_statement_inner<'tcx>( current_mod, &mut visited, inlined_names, - import, + import_id, + import_hir_id, ) { return items; } @@ -3249,6 +3353,8 @@ fn clean_use_statement_inner<'tcx>( Import::new_glob(resolve_use_source(cx, path), true) } else { let name = name.unwrap(); + let name = if name == kw::SelfLower { path.last() } else { name }; + if inline_attr.is_none() && let Res::Def(DefKind::Mod, did) = path.res && !did.is_local() diff --git a/src/librustdoc/clean/render_macro_matchers.rs b/src/librustdoc/clean/render_macro_matchers.rs index a69e3808bd7f7..7221a169c9026 100644 --- a/src/librustdoc/clean/render_macro_matchers.rs +++ b/src/librustdoc/clean/render_macro_matchers.rs @@ -1,4 +1,4 @@ -use rustc_ast::token::{self, Delimiter, IdentIsRaw}; +use rustc_ast::token::{self, Delimiter, IdentKind}; use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_ast_pretty::pprust::PrintState; use rustc_ast_pretty::pprust::state::State as Printer; @@ -146,7 +146,7 @@ fn print_tts(printer: &mut Printer<'_>, tts: &TokenStream) { (DollarParen, _) => (false, DollarParenSep), (DollarParenSep, token::Plus | token::Star) => (false, Other), (Pound, token::Bang) => (false, PoundBang), - (_, token::Ident(symbol, IdentIsRaw::No)) + (_, token::Ident(symbol, IdentKind::Normal)) if !usually_needs_space_between_keyword_and_open_delim(*symbol, tt.span) => { (true, Ident) diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs index 98c09d1134fe6..a6d6005a18118 100644 --- a/src/librustdoc/clean/simplify.rs +++ b/src/librustdoc/clean/simplify.rs @@ -11,10 +11,10 @@ //! This module attempts to reconstruct the original where and/or parameter //! bounds by special casing scenarios such as these. Fun! +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::thin_vec::ThinVec; use rustc_hir as hir; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def_id::DefId; use rustc_middle::ty::{TyCtxt, Unnormalized}; diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 744c7eb288dfa..c34b33d9c92f5 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -8,20 +8,23 @@ use arrayvec::ArrayVec; use itertools::Either; use rustc_abi::{ExternAbi, VariantIdx}; use rustc_ast as ast; +use rustc_attr_ir::lang_items::LangItem; +use rustc_attr_ir::{ + AttributeKind, ConstStability, DeprecatedSince, Deprecation, DocAttribute, Stability, + StableSince, find_attr, +}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::thin_vec::ThinVec; use rustc_hir as hir; -use rustc_hir::attrs::lang_items::LangItem; -use rustc_hir::attrs::{AttributeKind, DeprecatedSince, Deprecation, DocAttribute}; use rustc_hir::def::{CtorKind, DefKind, MacroKinds, Res}; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId}; -use rustc_hir::{Attribute, BodyId, ConstStability, Mutability, Stability, StableSince, find_attr}; +use rustc_hir::{BodyId, Mutability}; use rustc_index::IndexVec; use rustc_metadata::rendered_const; use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::{self, Ty, TyCtxt, Visibility}; use rustc_resolve::rustdoc::{ - DocFragment, add_doc_fragment, attrs_to_doc_fragments, inner_docs, span_of_fragments, + DocFragment, add_doc_fragment, attrs_to_doc_fragments, span_of_fragments, }; use rustc_session::Session; use rustc_span::def_id::{CRATE_DEF_ID, ModId}; @@ -426,7 +429,7 @@ impl Item { // versions; the paths that are exposed through it are "deprecated" because they // were never supposed to work at all. let stab = self.stability(tcx)?; - if let rustc_hir::StabilityLevel::Stable { + if let rustc_attr_ir::StabilityLevel::Stable { allowed_through_unstable_modules: Some((note, _)), .. } = stab.level @@ -458,13 +461,15 @@ impl Item { } pub(crate) fn inner_docs(&self, tcx: TyCtxt<'_>) -> bool { + use rustc_ast::attr::AttributeExt; + self.item_id .as_def_id() .map(|did| { - inner_docs( - #[allow(deprecated)] - tcx.get_all_attrs(did), - ) + #[allow(deprecated)] + tcx.get_all_attrs(did).iter().any(|attr| { + attr.doc_resolution_scope().is_some_and(|style| style == ast::AttrStyle::Inner) + }) }) .unwrap_or(false) } @@ -576,7 +581,7 @@ impl Item { } pub(crate) fn links(&self, cx: &Context<'_>) -> Vec { - use crate::html::format::{href, link_tooltip}; + use crate::html::format::{href_with_path_check, link_tooltip}; let Some(links) = cx.cache().intra_doc_links.get(&self.item_or_reexport_id()) else { return vec![]; @@ -585,7 +590,7 @@ impl Item { .iter() .filter_map(|ItemLink { link: s, link_text, page_id: id, fragment }| { debug!(?id); - if let Ok(HrefInfo { mut url, .. }) = href(*id, cx) { + if let Ok(HrefInfo { mut url, .. }) = href_with_path_check(*id, cx, link_text) { debug!(?url); match fragment { Some(UrlFragment::Item(def_id)) => { @@ -601,7 +606,7 @@ impl Item { Some(RenderedLink { original_text: s.clone(), new_text: link_text.clone(), - tooltip: link_tooltip(*id, fragment, cx).to_string(), + tooltip: link_tooltip(*id, fragment, cx, Some(link_text)).to_string(), href: url, }) } else { @@ -1061,7 +1066,7 @@ pub struct RenderedLink { #[derive(Clone, Debug, Default)] pub(crate) struct Attributes { pub(crate) doc_strings: Vec, - pub(crate) other_attrs: ThinVec, + pub(crate) other_attrs: ThinVec, } impl Attributes { @@ -1073,13 +1078,13 @@ impl Attributes { find_attr!(&self.other_attrs, Doc(d) if d.hidden.is_some()) } - pub(crate) fn from_hir(attrs: &[hir::Attribute]) -> Attributes { + pub(crate) fn from_hir(attrs: &[rustc_attr_ir::Attribute]) -> Attributes { Attributes::from_hir_iter(attrs.iter().map(|attr| (attr, None)), false) } pub(crate) fn from_hir_with_additional( - attrs: &[hir::Attribute], - (additional_attrs, def_id): (&[hir::Attribute], DefId), + attrs: &[rustc_attr_ir::Attribute], + (additional_attrs, def_id): (&[rustc_attr_ir::Attribute], DefId), ) -> Attributes { // Additional documentation should be shown before the original documentation. let attrs1 = additional_attrs.iter().map(|attr| (attr, Some(def_id))); @@ -1088,7 +1093,7 @@ impl Attributes { } pub(crate) fn from_hir_iter<'a>( - attrs: impl Iterator)>, + attrs: impl Iterator)>, doc_only: bool, ) -> Attributes { let (doc_strings, other_attrs) = attrs_to_doc_fragments(attrs, doc_only); @@ -1118,7 +1123,7 @@ impl Attributes { let mut aliases = FxIndexSet::default(); for attr in &self.other_attrs { - if let Attribute::Parsed(AttributeKind::Doc(d)) = attr { + if let rustc_attr_ir::Attribute::Parsed(AttributeKind::Doc(d)) = attr { for (alias, _) in &d.aliases { aliases.insert(*alias); } diff --git a/src/librustdoc/clean/types/tests.rs b/src/librustdoc/clean/types/tests.rs index a0fc623c20c48..29a8fd5e2678e 100644 --- a/src/librustdoc/clean/types/tests.rs +++ b/src/librustdoc/clean/types/tests.rs @@ -12,6 +12,7 @@ fn create_doc_fragment(s: &str) -> Vec { kind: DocFragmentKind::Sugared(CommentKind::Line), indent: 0, from_expansion: false, + style: rustc_ast::AttrStyle::Outer, }] } diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 012c4997db9c1..57e5ec2c4f0fb 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -7,12 +7,11 @@ use rustc_ast as ast; use rustc_ast::join_path_idents; use rustc_ast::token::{Token, TokenKind}; use rustc_ast::tokenstream::TokenTree; +use rustc_attr_ir::{DocAttribute, find_attr}; use rustc_data_structures::thin_vec::{ThinVec, thin_vec}; use rustc_hir as hir; -use rustc_hir::attrs::DocAttribute; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; -use rustc_hir::find_attr; use rustc_metadata::rendered_const; use rustc_middle::mir; use rustc_middle::ty::{self, GenericArgKind, GenericArgsRef, TyCtxt, TypeVisitableExt}; @@ -357,11 +356,11 @@ pub(crate) fn print_const(tcx: TyCtxt<'_>, n: ty::Const<'_>) -> String { match n.kind() { ty::ConstKind::Alias(_, ty::AliasConst { kind, .. }) => { let def_id: DefId = match kind { - ty::AliasConstKind::Projection { def_id } => def_id.into(), - ty::AliasConstKind::InherentSelf { def_id } => def_id.into(), - ty::AliasConstKind::InherentImpl { def_id } => def_id.into(), - ty::AliasConstKind::Free { def_id } => def_id.into(), - ty::AliasConstKind::Anon { def_id } => def_id.into(), + ty::AliasConstKind::Projection { def_id } + | ty::AliasConstKind::InherentSelf { def_id } + | ty::AliasConstKind::InherentImpl { def_id } + | ty::AliasConstKind::Free { def_id } + | ty::AliasConstKind::Anon { def_id } => def_id, }; if let Some(local_def_id) = def_id.as_local() && let Some(body_id) = tcx.hir_maybe_body_owned_by(local_def_id) @@ -580,7 +579,7 @@ pub(crate) fn find_nearest_parent_module(tcx: TyCtxt<'_>, def_id: DefId) -> Opti } } -/// This function exists because it runs on `hir::Attributes` whereas the other is a +/// This function exists because it runs on `rustc_attr_ir::Attributes` whereas the other is a /// `clean::Attributes` method. pub(crate) fn has_doc_flag bool>( tcx: TyCtxt<'_>, diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index c2c58a345fa22..ebf488fcb31b5 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -752,12 +752,12 @@ impl Options { if !theme_file.is_file() { dcx.struct_fatal(format!("invalid argument: \"{theme_s}\"")) .with_help("arguments to --theme must be files") - .emit(); + .emit_fatal(); } if theme_file.extension() != Some(OsStr::new("css")) { dcx.struct_fatal(format!("invalid argument: \"{theme_s}\"")) .with_help("arguments to --theme must have a .css extension") - .emit(); + .emit_fatal(); } let (success, ret) = theme::test_theme_against(&theme_file, &paths, dcx); if !success { @@ -777,7 +777,7 @@ impl Options { } } - let edition = config::parse_crate_edition(early_dcx, matches); + let edition = config::parse_crate_edition(early_dcx, matches, true); let mut id_map = html::markdown::IdMap::new(); let Some(external_html) = ExternalHtml::load( diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index db5e281f376ad..caf3fb9887c3e 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -367,13 +367,14 @@ pub(crate) fn run_global_ctxt( // NOTE: These are copy/pasted from typeck/lib.rs and should be kept in sync with those changes. tcx.sess.time("wf_checking", || tcx.ensure_ok().check_type_wf(())); + tcx.sess.time("check_mod_attrs", || { + tcx.hir_for_each_module(|module| tcx.ensure_ok().check_mod_attrs(module)) + }); + rustc_interface::passes::emit_delayed_lints(tcx); tcx.dcx().abort_if_errors(); tcx.sess.time("missing_docs", || rustc_lint::check_crate(tcx)); - tcx.sess.time("check_mod_attrs", || { - tcx.hir_for_each_module(|module| tcx.ensure_ok().check_mod_attrs(module)) - }); rustc_passes::stability::check_unused_or_stable_features(tcx); let auto_traits = diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 0ecbc5927cf48..c8d1cfd599909 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -18,12 +18,12 @@ use std::{panic, str}; pub(crate) use make::{BuildDocTestBuilder, DocTestBuilder}; pub(crate) use markdown::test as test_markdown; use proc_macro2::{TokenStream, TokenTree}; +use rustc_attr_ir::{Attribute, AttributeKind}; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxHasher, FxIndexMap, FxIndexSet}; use rustc_errors::emitter::HumanReadableErrorType; use rustc_errors::{ColorConfig, DiagCtxtHandle}; -use rustc_hir::attrs::AttributeKind; +use rustc_hir::CRATE_HIR_ID; use rustc_hir::def_id::LOCAL_CRATE; -use rustc_hir::{Attribute, CRATE_HIR_ID}; use rustc_interface::interface; use rustc_lint as lint; use rustc_middle::ty::TyCtxt; @@ -423,33 +423,19 @@ pub(crate) fn run_tests( // `running 0 tests...`. if ran_edition_tests == 0 || !standalone_tests.is_empty() { standalone_tests.sort_by(|a, b| a.desc.name.as_slice().cmp(b.desc.name.as_slice())); - cfg_select! { - bootstrap => { - test::test_main_with_exit_callback(&test_args, standalone_tests, None, || { - let times = times.times_in_secs(); - // We ensure temp dir destructor is called. - std::mem::drop(temp_dir.take()); - if let Some((total_time, compilation_time)) = times { - test::print_merged_doctests_times(&test_args, total_time, compilation_time); - } - }); - } - _ => { - // We need a vector of `&TestDescAndFn`. - let standalone_test_refs = &standalone_tests.iter().collect::>(); - let exit = test::test_main(&test_args, standalone_test_refs); - let times = times.times_in_secs(); - // We ensure temp dir destructor is called. - std::mem::drop(standalone_tests); - std::mem::drop(temp_dir.take()); - if let Some((total_time, compilation_time)) = times { - test::print_merged_doctests_times(&test_args, total_time, compilation_time); - } - // Fall through on success, the caller may want to do more stuff. - if exit != std::process::ExitCode::SUCCESS { - exit.exit_process(); - } - } + // We need a vector of `&TestDescAndFn`. + let standalone_test_refs = &standalone_tests.iter().collect::>(); + let exit = test::test_main(&test_args, standalone_test_refs); + let times = times.times_in_secs(); + // We ensure temp dir destructor is called. + std::mem::drop(standalone_tests); + std::mem::drop(temp_dir.take()); + if let Some((total_time, compilation_time)) = times { + test::print_merged_doctests_times(&test_args, total_time, compilation_time); + } + // Fall through on success, the caller may want to do more stuff. + if exit != std::process::ExitCode::SUCCESS { + exit.exit_process(); } } else { // If the first condition branch exited successfully, it will @@ -1175,18 +1161,6 @@ fn generate_test_desc_and_fn( no_run: scraped_test.no_run(&rustdoc_options), test_type: test::TestType::DocTest, }, - #[cfg(bootstrap)] - testfn: test::DynTestFn(Box::new(move || { - doctest_run_fn( - &rustdoc_test_options, - &opts, - &test, - &scraped_test, - &rustdoc_options, - &unused_externs, - ) - })), - #[cfg(not(bootstrap))] testfn: test::DynTestFn(Arc::new(move || { doctest_run_fn( &rustdoc_test_options, diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs index 13f705f9c3f47..352e9a1f6adb2 100644 --- a/src/librustdoc/doctest/rust.rs +++ b/src/librustdoc/doctest/rust.rs @@ -5,8 +5,8 @@ use std::str::FromStr; use std::sync::Arc; use proc_macro2::{TokenStream, TokenTree}; +use rustc_attr_ir::{AttributeKind, CfgEntry}; use rustc_attr_parsing::eval_config_entry; -use rustc_hir::attrs::{AttributeKind, CfgEntry}; use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; use rustc_hir::{self as hir, CRATE_HIR_ID, intravisit}; use rustc_middle::hir::nested_filter; @@ -131,7 +131,7 @@ impl HirCollector<'_> { // 1. Collect `#[target_feature(...)]`. // 2. Collect `#[doc(test(attr(...)))]`. for attr in hir_attrs.iter() { - let hir::Attribute::Parsed(attr) = attr else { continue }; + let rustc_attr_ir::Attribute::Parsed(attr) = attr else { continue }; if let AttributeKind::TargetFeature { features, .. } = attr { for (feature, _) in features { found_features += 1; diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index ccee062584e01..39baca1b4c0cd 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -1,7 +1,7 @@ use std::mem; +use rustc_attr_ir::StabilityLevel; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; -use rustc_hir::StabilityLevel; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet}; use rustc_metadata::creader::CStore; use rustc_middle::ty::{self, TyCtxt}; @@ -18,6 +18,45 @@ use crate::formats::item_type::ItemType; use crate::html::render::{IndexItem, IndexItemInfo}; use crate::visit_lib::RustdocEffectiveVisibilities; +pub(crate) struct PathInfo { + /// Parts of the fully qualified path. So in `foo::bar::bib`, it will + /// be `["foo", "bar", "bib"]`. + pub(crate) parts: Vec, + pub(crate) ty: ItemType, + /// When a reexport inline an item, we can end up with the same `DefId` with multiple local + /// targets. So in case like: + /// + /// ``` + /// /// Link to [`a2`]. + /// pub use std::ffi::os_str::OsString as a1; + /// /// Link to [`a1`]. + /// pub use std::ffi::os_str::OsString as a2; + /// /// Link to [`a2`]. + /// pub use std::ffi::os_str::OsString as a3; + /// ``` + /// + /// To ensure that `a1` and `a2` links to `a1` and `a2` which have the same `DefId`, we need + /// to store both `a1` and `a2` paths. + /// + /// The path stored in `parts` is not present in `alternatives`. + pub(crate) alternatives: Vec>, +} + +impl PathInfo { + pub(crate) fn get_preferred_path(&self, preferred_name: Option<&str>) -> &[Symbol] { + if let Some(preferred_name) = preferred_name + && let Some(alternative_path) = self + .alternatives + .iter() + .find(|path| path.last().is_some_and(|last| last.as_str() == preferred_name)) + { + alternative_path + } else { + &self.parts + } + } +} + /// This cache is used to store information about the [`clean::Crate`] being /// rendered in order to provide more useful documentation. This contains /// information like all implementors of a trait, all traits a type implements, @@ -42,7 +81,7 @@ pub(crate) struct Cache { /// URLs when a type is being linked to. External paths are not located in /// this map because the `External` type itself has all the information /// necessary. - pub(crate) paths: FxIndexMap, ItemType)>, + pub(crate) paths: FxIndexMap, /// Similar to `paths`, but only holds external paths. This is only used for /// generating explicit hyperlinks to other crates. @@ -358,7 +397,8 @@ impl DocFolder for CacheBuilder<'_, '_> { | clean::ForeignTypeItem | clean::MacroItem(..) | clean::ProcMacroItem(..) - | clean::VariantItem(..) => { + | clean::VariantItem(..) + | clean::PrimitiveItem(..) => { use rustc_data_structures::fx::IndexEntry as Entry; let skip_because_unstable = matches!( @@ -376,21 +416,31 @@ impl DocFolder for CacheBuilder<'_, '_> { let item_def_id = item.item_id.expect_def_id(); match self.cache.paths.entry(item_def_id) { Entry::Vacant(entry) => { - entry.insert((self.cache.stack.clone(), item.type_())); + entry.insert(PathInfo { + parts: self.cache.stack.clone(), + ty: item.type_(), + alternatives: Vec::new(), + }); } Entry::Occupied(mut entry) => { - if entry.get().0.len() > self.cache.stack.len() { - entry.insert((self.cache.stack.clone(), item.type_())); + // Shorter paths are preferred by default. + if entry.get().parts.len() > self.cache.stack.len() { + let old_parts = std::mem::replace( + &mut entry.get_mut().parts, + self.cache.stack.clone(), + ); + // We only keep the old path if it's a different (final) name. + if old_parts.last() != self.cache.stack.last() { + entry.get_mut().alternatives.push(old_parts); + } + } + if !entry.get().alternatives.contains(&self.cache.stack) { + entry.get_mut().alternatives.push(self.cache.stack.clone()); } } } } } - clean::PrimitiveItem(..) => { - self.cache - .paths - .insert(item.item_id.expect_def_id(), (self.cache.stack.clone(), item.type_())); - } clean::ExternCrateItem { .. } | clean::ImportItem(..) @@ -570,7 +620,7 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It // in a field of the cache whose elements are added to the search index later, // after cache building is complete (see `handle_orphan_impl_child`). match cache.paths.get(&parent_did) { - Some((fqp, _)) => (Some(parent_did), &fqp[..fqp.len() - 1]), + Some(info) => (Some(parent_did), &info.parts[..info.parts.len() - 1]), None => { handle_orphan_impl_child(cache, item, parent_did); return; diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 54ff94ed1d614..7f0c4132456ad 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -13,15 +13,15 @@ use std::{iter, slice}; use itertools::{Either, Itertools}; use rustc_abi::ExternAbi; use rustc_ast::join_path_syms; +use rustc_attr_ir::{ConstStability, StabilityLevel, StableSince}; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def::{DefKind, MacroKinds}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_hir::{ConstStability, StabilityLevel, StableSince}; use rustc_metadata::creader::CStore; -use rustc_middle::ty::{self, TyCtxt, TypingMode}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypingMode}; use rustc_span::symbol::kw; -use rustc_span::{Ident, Symbol}; +use rustc_span::{Ident, Symbol, sym}; use tracing::{debug, trace}; use super::url_parts_builder::UrlPartsBuilder; @@ -348,11 +348,11 @@ pub(crate) struct HrefInfo { } /// This function is to get the external macro path because they are not in the cache used in -/// `href_with_root_path`. +/// `href_with_jump_to_def_path_depth`. fn generate_macro_def_id_path( def_id: DefId, cx: &Context<'_>, - root_path: Option<&str>, + jump_to_def_path_depth: Option, ) -> Result { let tcx = cx.tcx(); let crate_name = tcx.crate_name(def_id.krate); @@ -388,16 +388,16 @@ fn generate_macro_def_id_path( let url = match cache.extern_locations[&def_id.krate] { ExternalLocation::Remote { ref url, is_absolute } => { - let mut prefix = remote_url_prefix(url, is_absolute, cx.current.len()); + let depth = jump_to_def_path_depth.unwrap_or(cx.current.len()); + let mut prefix = remote_url_prefix(url, is_absolute, depth); prefix.extend(module_path.iter().copied()); prefix.push_fmt(format_args!("{}.{last}.html", item_type.as_str())); prefix.finish() } ExternalLocation::Local => { - // `root_path` always end with a `/`. format!( - "{root_path}{path}/{item_type}.{last}.html", - root_path = root_path.unwrap_or(""), + "{prefix}{path}/{item_type}.{last}.html", + prefix = "../".repeat(jump_to_def_path_depth.unwrap_or(0)), path = fmt::from_fn(|f| module_path.iter().joined("/", f)), item_type = item_type.as_str(), ) @@ -410,49 +410,177 @@ fn generate_macro_def_id_path( Ok(HrefInfo { url, kind: item_type, rust_path: path }) } +/// Takes an impl `DefId` and return the self `Ty` of the impl. +fn impl_self_ty(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Ty<'_> { + use rustc_middle::traits::ObligationCause; + use rustc_middle::ty; + use rustc_trait_selection::infer::TyCtxtInferExt; + use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; + + let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); + let ty = tcx.type_of(impl_def_id); + infcx + .at(&ObligationCause::dummy(), tcx.param_env(impl_def_id)) + .query_normalize(ty::Binder::dummy(ty.instantiate_identity().skip_norm_wip())) + .map(|resolved| infcx.deeply_resolve_ignoring_regions(resolved.value).skip_binder()) + .unwrap_or(ty.skip_binder()) +} + +fn transitive_reexport_path(tcx: TyCtxt<'_>, def_id: DefId) -> Option> { + transitive_reexport_path_inner(tcx, def_id, &mut Vec::new()) +} + +/// Simplified implementation of `rustc_middle::ty::print::pretty::try_print_visible_def_path_recur`. +fn transitive_reexport_path_inner( + tcx: TyCtxt<'_>, + def_id: DefId, + callers: &mut Vec, +) -> Option> { + use rustc_hir::def_id::ModId; + use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; + + if let Some(cnum) = def_id.as_crate_root() { + return Some(vec![tcx.crate_name(cnum)]); + } + + let visible_parent_map = tcx.visible_parent_map(()); + let mut cur_def_key = tcx.def_key(def_id); + + // For a constructor, we want the name of its parent rather than . + if let DefPathData::Ctor = cur_def_key.disambiguated_data.data { + let parent = DefId { + krate: def_id.krate, + index: cur_def_key + .parent + .expect("`DefPathData::Ctor` / `VariantData` missing a parent"), + }; + + cur_def_key = tcx.def_key(parent); + } + + let visible_parent = visible_parent_map.get(&def_id).cloned()?; + // FIXME: Should we also check for private items? + if tcx.is_doc_hidden(visible_parent) { + return None; + } + + let actual_parent = tcx.opt_parent(def_id); + let mut data = cur_def_key.disambiguated_data.data; + match data { + DefPathData::TypeNs(ref mut name) if Some(visible_parent) != actual_parent => { + // Item might be re-exported several times, but filter for the one + // that's public and whose identifier isn't `_`. + let reexport = tcx + .module_children(ModId::new_unchecked(visible_parent)) + .iter() + .filter(|child| child.res.opt_def_id() == Some(def_id)) + .find(|child| child.vis.is_public() && child.ident.name != kw::Underscore) + .map(|child| child.ident.name); + + if let Some(new_name) = reexport { + *name = new_name; + } else { + // There is no name that is public and isn't `_`, so bail. + return None; + } + } + // Re-exported `extern crate`. + DefPathData::CrateRoot => { + data = DefPathData::TypeNs(tcx.crate_name(def_id.krate)); + } + _ => {} + } + + if callers.contains(&visible_parent) { + return None; + } + callers.push(visible_parent); + let mut path = transitive_reexport_path_inner(tcx, visible_parent, callers)?; + callers.pop(); + path.push(DisambiguatedDefPathData { data, disambiguator: 0 }.as_sym(false)); + Some(path) +} + fn generate_item_def_id_path( mut def_id: DefId, original_def_id: DefId, cx: &Context<'_>, - root_path: Option<&str>, + jump_to_def_path_depth: Option, ) -> Result { - use rustc_middle::traits::ObligationCause; - use rustc_trait_selection::infer::TyCtxtInferExt; - use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; - let tcx = cx.tcx(); let crate_name = tcx.crate_name(def_id.krate); let mut prim = None; + let mut maybe_have_impl_not_in_def_crate = false; // No need to try to infer the actual parent item if it's not an associated item from the `impl` // block. - if def_id != original_def_id && matches!(tcx.def_kind(def_id), DefKind::Impl { .. }) { - let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); - let ty = tcx.type_of(def_id); - let ty = infcx - .at(&ObligationCause::dummy(), tcx.param_env(def_id)) - .query_normalize(ty::Binder::dummy(ty.instantiate_identity().skip_norm_wip())) - .map(|resolved| infcx.deeply_resolve_ignoring_regions(resolved.value).skip_binder()) - .unwrap_or(ty.skip_binder()); - if let Some(new_def_id) = ty.ty_adt_def().map(|adt| adt.did()) { + if def_id != original_def_id + && let DefKind::Impl { of_trait } = tcx.def_kind(def_id) + { + let ty = impl_self_ty(tcx, def_id); + // If this is a dyn trait, we want to get the actual trait from which the method comes from. + // Since a `dyn trait` (as of 2026) can only be composed of a trait plus auto traits, we + // look for the trait and ignore auto traits. + if let ty::Dynamic(traits, _) = ty.kind() + && let Some(trait_def_id) = + traits.iter().find_map(|trait_| match trait_.skip_binder() { + ty::ExistentialPredicate::Trait(t) => Some(t.def_id), + ty::ExistentialPredicate::Projection(p) => Some(p.trait_ref(tcx).def_id), + ty::ExistentialPredicate::AutoTrait(_) => None, + }) + { + def_id = trait_def_id; + } else if let Some(new_def_id) = ty.ty_adt_def().map(|adt| adt.did()) { def_id = new_def_id; + maybe_have_impl_not_in_def_crate = !of_trait + && !original_def_id.is_local() + && !def_id.is_local() + && def_id.krate != original_def_id.krate; } else { + // This hack is because primitive types are only documented in `core` and `std`. + // However, with `#[rustc_allow_incoherent_impl]`, a lot of primitive methods are + // implemented in `core`. Some of them are documented in `core` (like `[]::sort`) while + // others aren't (like `[]::to_vec`). So in case we're not documenting `core`, we link + // to `std`. + if !of_trait && crate_name != sym::core { + if ![sym::alloc, sym::std].contains(&crate_name) { + // We cannot link to this primitive's associated item as it's not part of + // `core`, `alloc` or `std` so returning early. + return Err(HrefError::UnnamableItem); + } + maybe_have_impl_not_in_def_crate = true; + } prim = PrimitiveType::from_ty(ty); } } - let mut fqp = vec![crate_name]; - let shortty = if let Some(prim) = prim { - fqp.push(prim.as_sym()); - ItemType::Primitive + let (shortty, fqp) = if let Some(prim) = prim { + let crate_name = if maybe_have_impl_not_in_def_crate { sym::std } else { crate_name }; + (ItemType::Primitive, vec![crate_name, prim.as_sym()]) } else { - fqp.append(&mut clean::inline::item_relative_path(tcx, def_id)); - ItemType::from_def_id(def_id, tcx) + ( + ItemType::from_def_id(def_id, tcx), + if maybe_have_impl_not_in_def_crate + // We have a method, not coming from a trait, implemented from a different crate + // where the original item is defined. So in short, the item is using + // `#[rustc_allow_incoherent_impl]` and we need to keep the non-final item path. + // Sadly if we use `item_relative_path` which uses `def_path`, it renders the final + // item path and not the intermediate one. + && let Some(fqp) = transitive_reexport_path(tcx, def_id) + { + fqp + } else { + let mut fqp = vec![crate_name]; + fqp.append(&mut clean::inline::item_relative_path(tcx, def_id)); + fqp + }, + ) }; let module_fqp = to_module_fqp(shortty, &fqp); - let (parts, is_absolute) = url_parts(cx.cache(), def_id, module_fqp, &cx.current)?; - let mut url = make_href(root_path, shortty, parts, &fqp, is_absolute); + let (parts, is_remote) = + url_parts(cx.cache(), def_id, module_fqp, &cx.current, jump_to_def_path_depth)?; + let mut url = make_href(jump_to_def_path_depth, shortty, parts, &fqp, is_remote); if def_id != original_def_id { let kind = ItemType::from_def_id(original_def_id, tcx); @@ -497,17 +625,33 @@ fn remote_url_prefix(url: &str, is_absolute: bool, depth: usize) -> UrlPartsBuil } } +/// Returns `(url_parts, is_remote)` where `is_remote` indicates the URL points to an +/// external location and should not use relative URLs. +/// +/// This function tries to generate minimal, relative URLs. When generating an intra-doc +/// link from one item page to another, it compares the paths of both items and generates +/// exactly the right number of "../"'s to reach the deepest common parent, then fills in +/// the rest of the path. This is done by comparing `module_fqp` with `relative_to`. +/// +/// When generating a link for jump-to-def across crates, that won't work, because sources are +/// stored under `src//.rs`. When linking jump-to-def for another crate, we just +/// write enough "../"'s to reach the doc root, then generate the entire path for `module_fqp`. +/// The right number of "../"'s is stored in `jump_to_def_path_depth`. fn url_parts( cache: &Cache, def_id: DefId, module_fqp: &[Symbol], relative_to: &[Symbol], + jump_to_def_path_depth: Option, ) -> Result<(UrlPartsBuilder, bool), HrefError> { match cache.extern_locations[&def_id.krate] { ExternalLocation::Remote { ref url, is_absolute } => { - let mut builder = remote_url_prefix(url, is_absolute, relative_to.len()); + let depth = jump_to_def_path_depth.unwrap_or(relative_to.len()); + let mut builder = remote_url_prefix(url, is_absolute, depth); builder.extend(module_fqp.iter().copied()); - Ok((builder, is_absolute)) + // Remote URLs (both absolute and relative) already encode the correct path; + // no additional depth prefix should be applied. + Ok((builder, true)) } ExternalLocation::Local => Ok((href_relative_parts(module_fqp, relative_to), false)), ExternalLocation::Unknown => Err(HrefError::DocumentationNotBuilt), @@ -515,16 +659,16 @@ fn url_parts( } fn make_href( - root_path: Option<&str>, + jump_to_def_path_depth: Option, shortty: ItemType, mut url_parts: UrlPartsBuilder, fqp: &[Symbol], - is_absolute: bool, + is_remote: bool, ) -> String { - // FIXME: relative extern URLs may break when prefixed with root_path - if !is_absolute && let Some(root_path) = root_path { - let root = root_path.trim_end_matches('/'); - url_parts.push_front(root); + // Remote URLs already have the correct depth via `remote_url_prefix`; + // only local items need `jump_to_def_path_depth` to bridge from source pages to the doc tree. + if !is_remote && let Some(depth) = jump_to_def_path_depth { + url_parts.push_front(&iter::repeat_n("..", depth).join("/")); } debug!(?url_parts); match shortty { @@ -539,10 +683,54 @@ fn make_href( url_parts.finish() } -pub(crate) fn href_with_root_path( +fn ty_inherits_doc_hidden<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { + match ty.kind() { + // If this is a dyn trait, we want to get the actual trait from which the method comes from. + // Since a `dyn trait` (as of 2026) can only be composed of a trait plus auto traits, we + // look for the trait and ignore auto traits. + ty::Dynamic(traits, _) => traits + .iter() + .find_map(|trait_| match trait_.skip_binder() { + ty::ExistentialPredicate::Trait(t) => Some(inherits_doc_hidden(tcx, t.def_id)), + ty::ExistentialPredicate::Projection(p) => { + Some(inherits_doc_hidden(tcx, p.trait_ref(tcx).def_id)) + } + ty::ExistentialPredicate::AutoTrait(_) => None, + }) + .unwrap_or(false), + ty::Adt(adt, _) => inherits_doc_hidden(tcx, adt.did()), + ty::Foreign(def_id) => inherits_doc_hidden(tcx, *def_id), + ty::Ref(_, ty, _) => ty_inherits_doc_hidden(tcx, *ty), + // For now we consider that everything doesn't inherit `#[doc(hidden)]`. To be confirmed + // later. + _ => false, + } +} + +fn inherits_doc_hidden(tcx: TyCtxt<'_>, mut def_id: DefId) -> bool { + loop { + if tcx.is_doc_hidden(def_id) { + return true; + } else if def_id.is_crate_root() { + return false; + } else if let DefKind::Impl { of_trait } = tcx.def_kind(def_id) { + // `impl` blocks stand a bit on their own: unless they have `#[doc(hidden)]` directly + // on them, they don't inherit it from the parent context. + // Instead we check that the `Self` item and the trait aren't hidden. + return ty_inherits_doc_hidden(tcx, impl_self_ty(tcx, def_id)) + || (of_trait && inherits_doc_hidden(tcx, tcx.impl_trait_id(def_id))); + } + def_id = tcx.parent(def_id); + } +} + +/// If `jump_to_def_path_depth` is `Some`, the link comes from a source page and needs that many +/// `..` segments to reach the doc root; `None` generates a link relative to the current page. +pub(crate) fn href_with_jump_to_def_path_depth( original_did: DefId, cx: &Context<'_>, - root_path: Option<&str>, + jump_to_def_path_depth: Option, + preferred_name: Option<&str>, ) -> Result { let tcx = cx.tcx(); let def_kind = tcx.def_kind(original_did); @@ -553,7 +741,14 @@ pub(crate) fn href_with_root_path( } // If this a constructor, we get the parent (either a struct or a variant) and then // generate the link for this item. - DefKind::Ctor(..) => return href_with_root_path(tcx.parent(original_did), cx, root_path), + DefKind::Ctor(..) => { + return href_with_jump_to_def_path_depth( + tcx.parent(original_did), + cx, + jump_to_def_path_depth, + preferred_name, + ); + } DefKind::ExternCrate => { // Link to the crate itself, not the `extern crate` item. if let Some(local_did) = original_did.as_local() { @@ -564,7 +759,7 @@ pub(crate) fn href_with_root_path( } _ => original_did, }; - if is_unnamable(cx.tcx(), did) { + if is_unnamable(tcx, did) { return Err(HrefError::UnnamableItem); } let cache = cx.cache(); @@ -573,8 +768,8 @@ pub(crate) fn href_with_root_path( if !original_did.is_local() { // If we are generating an href for the "jump to def" feature, then the only case we want // to ignore is if the item is `doc(hidden)` because we can't link to it. - if root_path.is_some() { - if tcx.is_doc_hidden(original_did) { + if jump_to_def_path_depth.is_some() { + if inherits_doc_hidden(tcx, original_did) { return Err(HrefError::Private); } } else if !cache.effective_visibilities.is_directly_public(tcx, did) @@ -585,44 +780,57 @@ pub(crate) fn href_with_root_path( } } - let (fqp, shortty, url_parts, is_absolute) = match cache.paths.get(&did) { - Some(&(ref fqp, shortty)) => ( - fqp, - shortty, + let (fqp, shortty, url_parts, is_remote) = match cache.paths.get(&did) { + Some(info) => ( + info.get_preferred_path(preferred_name), + info.ty, { - let module_fqp = to_module_fqp(shortty, fqp.as_slice()); - debug!(?fqp, ?shortty, ?module_fqp); + let module_fqp = to_module_fqp(info.ty, info.parts.as_slice()); + debug!(?info.parts, ?info.ty, ?module_fqp); href_relative_parts(module_fqp, relative_to) }, false, ), None => { - // Associated items are handled differently with "jump to def". The anchor is generated + // Associated items are handled differently with "jump to def": the anchor is generated // directly here whereas for intra-doc links, we have some extra computation being // performed there. - let def_id_to_get = if root_path.is_some() { original_did } else { did }; - if let Some(&(ref fqp, shortty)) = cache.external_paths.get(&def_id_to_get) { + // + // So if `jump_to_def_path_depth` is `Some()` and it's an associated item, we enter to + // enter the `else` branch to have the anchor generated here directly. + if (jump_to_def_path_depth.is_none() || original_did == did) + && let Some(&(ref fqp, shortty)) = cache.external_paths.get(&did) + { let module_fqp = to_module_fqp(shortty, fqp); - let (parts, is_absolute) = url_parts(cache, did, module_fqp, relative_to)?; - (fqp, shortty, parts, is_absolute) + let (parts, is_remote) = + url_parts(cache, did, module_fqp, relative_to, jump_to_def_path_depth)?; + (fqp.as_slice(), shortty, parts, is_remote) } else if matches!(def_kind, DefKind::Macro(_)) { - return generate_macro_def_id_path(did, cx, root_path); + return generate_macro_def_id_path(did, cx, jump_to_def_path_depth); } else if did.is_local() { return Err(HrefError::Private); } else { - return generate_item_def_id_path(did, original_did, cx, root_path); + return generate_item_def_id_path(did, original_did, cx, jump_to_def_path_depth); } } }; Ok(HrefInfo { - url: make_href(root_path, shortty, url_parts, fqp, is_absolute), + url: make_href(jump_to_def_path_depth, shortty, url_parts, fqp, is_remote), kind: shortty, - rust_path: fqp.clone(), + rust_path: fqp.to_vec(), }) } pub(crate) fn href(did: DefId, cx: &Context<'_>) -> Result { - href_with_root_path(did, cx, None) + href_with_jump_to_def_path_depth(did, cx, None, None) +} + +pub(crate) fn href_with_path_check( + did: DefId, + cx: &Context<'_>, + text: &str, +) -> Result { + href_with_jump_to_def_path_depth(did, cx, None, Some(text)) } /// Both paths should only be modules. @@ -660,14 +868,21 @@ pub(crate) fn link_tooltip( did: DefId, fragment: &Option, cx: &Context<'_>, + preferred_name: Option<&str>, ) -> impl fmt::Display { fmt::from_fn(move |f| { let cache = cx.cache(); - let Some((fqp, shortty)) = cache.paths.get(&did).or_else(|| cache.external_paths.get(&did)) + let Some((fqp, shortty)) = cache + .paths + .get(&did) + .map(|info| (info.get_preferred_path(preferred_name), info.ty)) + .or_else(|| { + cache.external_paths.get(&did).map(|(fqp, shortty)| (fqp.as_slice(), *shortty)) + }) else { return Ok(()); }; - let fqp = if *shortty == ItemType::Primitive { + let fqp = if shortty == ItemType::Primitive { // primitives are documented in a crate, but not actually part of it slice::from_ref(fqp.last().unwrap()) } else { @@ -679,7 +894,7 @@ pub(crate) fn link_tooltip( for component in fqp { write!(f, "{component}::")?; } - if *shortty == ItemType::Enum && tcx.def_kind(id) == DefKind::Field { + if shortty == ItemType::Enum && tcx.def_kind(id) == DefKind::Field { write!(f, "{}::", tcx.item_name(tcx.parent(id)))?; } write!(f, "{}", tcx.item_name(id))?; diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 89d50680c3a3b..a9b8ccc3b2106 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -33,7 +33,7 @@ pub(crate) struct HrefContext<'a, 'tcx> { pub(crate) file_span: Span, /// This field is used to know "how far" from the top of the directory we are to link to either /// documentation pages or other source pages. - pub(crate) root_path: &'a str, + pub(crate) jump_to_def_path_depth: usize, /// This field is used to calculate precise local URLs. pub(crate) current_href: String, } @@ -1297,6 +1297,7 @@ impl<'src> Classifier<'src> { TokenKind::RawIdent | TokenKind::UnknownPrefix | TokenKind::InvalidIdent => { Class::Ident(new_span(before, text, file_span)) } + TokenKind::ForcedKeywordIdent => Class::KeyWord, TokenKind::Lifetime { .. } | TokenKind::RawLifetime | TokenKind::UnknownPrefixLifetime => Class::Lifetime, @@ -1406,23 +1407,30 @@ fn generate_link_to_def( LinkFromSrc::Local(span) => { context.href_from_span_relative(*span, &href_context.current_href) } - LinkFromSrc::External(def_id) => { - format::href_with_root_path(*def_id, context, Some(href_context.root_path)) - .ok() - .map(|HrefInfo { url, .. }| url) - } - LinkFromSrc::Primitive(prim) => format::href_with_root_path( + LinkFromSrc::External(def_id) => format::href_with_jump_to_def_path_depth( + *def_id, + context, + Some(href_context.jump_to_def_path_depth), + None, + ) + .ok() + .map(|HrefInfo { url, .. }| url), + LinkFromSrc::Primitive(prim) => format::href_with_jump_to_def_path_depth( PrimitiveType::primitive_locations(context.tcx())[prim], context, - Some(href_context.root_path), + Some(href_context.jump_to_def_path_depth), + None, + ) + .ok() + .map(|HrefInfo { url, .. }| url), + LinkFromSrc::Doc(def_id) => format::href_with_jump_to_def_path_depth( + *def_id, + context, + Some(href_context.jump_to_def_path_depth), + None, ) .ok() .map(|HrefInfo { url, .. }| url), - LinkFromSrc::Doc(def_id) => { - format::href_with_root_path(*def_id, context, Some(href_context.root_path)) - .ok() - .map(|HrefInfo { url, .. }| url) - } } }) { diff --git a/src/librustdoc/html/macro_expansion.rs b/src/librustdoc/html/macro_expansion.rs index adef8e00b2a52..c72a20e6a05d5 100644 --- a/src/librustdoc/html/macro_expansion.rs +++ b/src/librustdoc/html/macro_expansion.rs @@ -103,7 +103,7 @@ impl<'ast> ExpandedCodeVisitor<'ast> { } fn compute_expanded(mut self) -> FxHashMap> { - self.expanded_codes.sort_unstable_by(|item1, item2| item1.span.cmp(&item2.span)); + self.expanded_codes.sort_unstable_by_key(|item| item.span.lo_hi()); let mut expanded: FxHashMap> = FxHashMap::default(); for ExpandedCodeInfo { span, code, original_span, .. } in self.expanded_codes { if let Ok(lines) = self.source_map.span_to_lines(span) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index af1cd195266cb..43c825c889907 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -875,7 +875,7 @@ impl<'doc, 'tcx> ExtraInfo<'doc, 'tcx> { fn error_invalid_codeblock_attr_with_help( &self, msg: impl Into, - f: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>), + f: impl for<'a, 'b> FnOnce(&'b mut Diag<'a>), ) { self.tcx.emit_node_span_lint( crate::lint::INVALID_CODEBLOCK_ATTRIBUTES, diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 56dd665177a93..f828327b4b985 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -7,9 +7,8 @@ use std::sync::mpsc::{Receiver, channel}; use askama::Template; use rustc_ast::join_path_syms; +use rustc_attr_ir::{Attribute, AttributeKind}; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; -use rustc_hir::Attribute; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::{DefIdMap, LOCAL_CRATE}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; @@ -296,19 +295,19 @@ impl<'tcx> Context<'tcx> { &self.shared.style_files, ) } else { - if let Some(&(ref names, ty)) = self.cache().paths.get(&it.item_id.expect_def_id()) - && (self.current.len() + 1 != names.len() - || self.current.iter().zip(names.iter()).any(|(a, b)| a != b)) + if let Some(info) = self.cache().paths.get(&it.item_id.expect_def_id()) + && (self.current.len() + 1 != info.parts.len() + || self.current.iter().zip(info.parts.iter()).any(|(a, b)| a != b)) { // We checked that the redirection isn't pointing to the current file, // preventing an infinite redirection loop in the generated // documentation. let path = fmt::from_fn(|f| { - for name in &names[..names.len() - 1] { + for name in &info.parts[..info.parts.len() - 1] { write!(f, "{name}/")?; } - write!(f, "{}", print_ty_path(ty, names.last().unwrap().as_str())) + write!(f, "{}", print_ty_path(info.ty, info.parts.last().unwrap().as_str())) }); match self.shared.redirections { Some(ref redirections) => { @@ -320,7 +319,7 @@ impl<'tcx> Context<'tcx> { let _ = write!( current_path, "{}", - print_ty_path(ty, names.last().unwrap().as_str()) + print_ty_path(info.ty, info.parts.last().unwrap().as_str()) ); redirections.borrow_mut().insert(current_path, path.to_string()); } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index a9ecf6d66d001..c2d8cc9ebae3e 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -51,12 +51,14 @@ use askama::Template; use indexmap::IndexMap; use itertools::Either; use rustc_ast::join_path_syms; +use rustc_attr_ir::{ + AttributeKind, ConstStability, DeprecatedSince, Deprecation, RustcVersion, StabilityLevel, + StableSince, find_attr, +}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; -use rustc_hir as hir; -use rustc_hir::attrs::{AttributeKind, DeprecatedSince, Deprecation, RustcVersion}; +use rustc_hir::Mutability; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, DefIdSet}; -use rustc_hir::{ConstStability, Mutability, StabilityLevel, StableSince}; use rustc_middle::ty::print::PrintTraitRefExt; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::DUMMY_SP; @@ -98,14 +100,23 @@ pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display { #[derive(Copy, Clone, Debug)] enum AssocItemRender<'a> { All, - DerefFor { trait_: &'a clean::Path, type_: &'a clean::Type, deref_mut_: bool }, + DerefFor { + trait_: &'a clean::Path, + type_: &'a clean::Type, + deref_mut_: bool, + deref_impl_def_id: DefId, + }, } impl AssocItemRender<'_> { - fn render_mode(&self) -> RenderMode { + fn render_mode(&self, tcx: TyCtxt<'_>) -> RenderMode { match self { Self::All => RenderMode::Normal, - &Self::DerefFor { deref_mut_, .. } => RenderMode::ForDeref { mut_: deref_mut_ }, + &Self::DerefFor { deref_mut_, deref_impl_def_id, .. } => { + let is_deref_target_copy = + compute_if_deref_target_implements_copy(tcx, deref_impl_def_id); + RenderMode::ForDeref { mut_: deref_mut_, is_deref_target_copy } + } } } @@ -119,7 +130,7 @@ impl AssocItemRender<'_> { #[derive(Copy, Clone, PartialEq)] enum RenderMode { Normal, - ForDeref { mut_: bool }, + ForDeref { mut_: bool, is_deref_target_copy: bool }, } // Helper structs for rendering items/sidebars and carrying along contextual @@ -1483,7 +1494,7 @@ fn render_assoc_items_inner( let (mut inherent_impls, trait_impls): (Vec<_>, _) = impls.iter().partition(|i| i.inner_impl().trait_.is_none()); if !inherent_impls.is_empty() { - let render_mode = what.render_mode(); + let render_mode = what.render_mode(cx.tcx()); let class_html = what .class() .map(|class| fmt::from_fn(move |f| write!(f, r#" class="{class}""#))) @@ -1621,8 +1632,12 @@ fn render_deref_methods( "Render deref methods for {for_:#?}, target {target:#?}", for_ = impl_.inner_impl().for_ ); - let what = - AssocItemRender::DerefFor { trait_: deref_type, type_: real_target, deref_mut_: deref_mut }; + let what = AssocItemRender::DerefFor { + trait_: deref_type, + type_: real_target, + deref_mut_: deref_mut, + deref_impl_def_id: impl_.def_id(), + }; if let Some(did) = target.def_id(cache) { if let Some(type_did) = impl_.inner_impl().for_.def_id(cache) { // `impl Deref for S` @@ -1640,29 +1655,31 @@ fn render_deref_methods( Ok(()) } -fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) -> bool { +fn should_render_item( + item: &clean::Item, + deref_mut_: bool, + tcx: TyCtxt<'_>, + target_is_copy: bool, +) -> bool { let self_type_opt = match item.kind { clean::MethodItem(ref method, _) => method.decl.receiver_type(), clean::RequiredMethodItem(ref method, _) => method.decl.receiver_type(), _ => None, }; - if let Some(self_ty) = self_type_opt { - let (by_mut_ref, by_box, by_value) = match *self_ty { - clean::Type::BorrowedRef { mutability, .. } => { - (mutability == Mutability::Mut, false, false) - } - clean::Type::Path { ref path } => { - (false, Some(path.def_id()) == tcx.lang_items().owned_box(), false) - } - clean::Type::SelfTy => (false, false, true), - _ => (false, false, false), - }; + let Some(self_ty) = self_type_opt else { return false }; + let (by_mut_ref, by_box, by_value) = match *self_ty { + clean::Type::BorrowedRef { mutability, .. } => { + (mutability == Mutability::Mut, false, false) + } + clean::Type::Path { ref path } => { + (false, Some(path.def_id()) == tcx.lang_items().owned_box(), false) + } + clean::Type::SelfTy => (false, false, true), + _ => (false, false, false), + }; - (deref_mut_ || !by_mut_ref) && !by_box && !by_value - } else { - false - } + (deref_mut_ || !by_mut_ref) && !by_box && (!by_value || target_is_copy) } /// `Box` has pass-through impls for `Read`, `Write`, `Iterator`, and `Future` when the @@ -1885,8 +1902,8 @@ fn render_impl( let render_method_item = rendering_params.show_non_assoc_items && match render_mode { RenderMode::Normal => true, - RenderMode::ForDeref { mut_: deref_mut_ } => { - should_render_item(item, deref_mut_, cx.tcx()) + RenderMode::ForDeref { mut_: deref_mut_, is_deref_target_copy } => { + should_render_item(item, deref_mut_, cx.tcx(), is_deref_target_copy) } }; @@ -1995,117 +2012,125 @@ fn render_impl( } } clean::RequiredAssocConstItem(generics, ty) => { - let source_id = format!("{item_type}.{name}"); - let id = cx.derive_id(&source_id); - write!( - w, - "

    \ - {}", - render_rightside(cx, item, render_mode) - )?; - if trait_.is_some() { - // Anchors are only used on trait impls. - write!(w, "§")?; + if !matches!(render_mode, RenderMode::ForDeref { .. }) { + let source_id = format!("{item_type}.{name}"); + let id = cx.derive_id(&source_id); + write!( + w, + "
    \ + {}", + render_rightside(cx, item, render_mode) + )?; + if trait_.is_some() { + // Anchors are only used on trait impls. + write!(w, "§")?; + } + write!( + w, + "

    {}

    ", + assoc_const( + item, + generics, + ty, + AssocConstValue::None, + link.anchor(if trait_.is_some() { &source_id } else { &id }), + 0, + cx, + ), + )?; } - write!( - w, - "

    {}

    ", - assoc_const( - item, - generics, - ty, - AssocConstValue::None, - link.anchor(if trait_.is_some() { &source_id } else { &id }), - 0, - cx, - ), - )?; } clean::ProvidedAssocConstItem(ci) | clean::ImplAssocConstItem(ci) => { - let source_id = format!("{item_type}.{name}"); - let id = cx.derive_id(&source_id); - write!( - w, - "
    \ - {}", - render_rightside(cx, item, render_mode), - )?; - if trait_.is_some() { - // Anchors are only used on trait impls. - write!(w, "§")?; + if !matches!(render_mode, RenderMode::ForDeref { .. }) { + let source_id = format!("{item_type}.{name}"); + let id = cx.derive_id(&source_id); + write!( + w, + "
    \ + {}", + render_rightside(cx, item, render_mode), + )?; + if trait_.is_some() { + // Anchors are only used on trait impls. + write!(w, "§")?; + } + write!( + w, + "

    {}

    ", + assoc_const( + item, + &ci.generics, + &ci.type_, + match item.kind { + clean::ProvidedAssocConstItem(_) => + AssocConstValue::TraitDefault(&ci.kind), + clean::ImplAssocConstItem(_) => AssocConstValue::Impl(&ci.kind), + _ => unreachable!(), + }, + link.anchor(if trait_.is_some() { &source_id } else { &id }), + 0, + cx, + ), + )?; } - write!( - w, - "

    {}

    ", - assoc_const( - item, - &ci.generics, - &ci.type_, - match item.kind { - clean::ProvidedAssocConstItem(_) => - AssocConstValue::TraitDefault(&ci.kind), - clean::ImplAssocConstItem(_) => AssocConstValue::Impl(&ci.kind), - _ => unreachable!(), - }, - link.anchor(if trait_.is_some() { &source_id } else { &id }), - 0, - cx, - ), - )?; } clean::RequiredAssocTypeItem(generics, bounds) => { - let source_id = format!("{item_type}.{name}"); - let id = cx.derive_id(&source_id); - write!( - w, - "
    \ - {}", - render_rightside(cx, item, render_mode), - )?; - if trait_.is_some() { - // Anchors are only used on trait impls. - write!(w, "§")?; + if !matches!(render_mode, RenderMode::ForDeref { .. }) { + let source_id = format!("{item_type}.{name}"); + let id = cx.derive_id(&source_id); + write!( + w, + "
    \ + {}", + render_rightside(cx, item, render_mode), + )?; + if trait_.is_some() { + // Anchors are only used on trait impls. + write!(w, "§")?; + } + write!( + w, + "

    {}

    ", + assoc_type( + item, + generics, + bounds, + None, + link.anchor(if trait_.is_some() { &source_id } else { &id }), + 0, + cx, + ), + )?; } - write!( - w, - "

    {}

    ", - assoc_type( - item, - generics, - bounds, - None, - link.anchor(if trait_.is_some() { &source_id } else { &id }), - 0, - cx, - ), - )?; } clean::AssocTypeItem(tydef, _bounds) => { - let source_id = format!("{item_type}.{name}"); - let id = cx.derive_id(&source_id); - write!( - w, - "
    \ - {}", - render_rightside(cx, item, render_mode), - )?; - if trait_.is_some() { - // Anchors are only used on trait impls. - write!(w, "§")?; + if !matches!(render_mode, RenderMode::ForDeref { .. }) { + let source_id = format!("{item_type}.{name}"); + let id = cx.derive_id(&source_id); + write!( + w, + "
    \ + {}", + render_rightside(cx, item, render_mode), + )?; + if trait_.is_some() { + // Anchors are only used on trait impls. + write!(w, "§")?; + } + write!( + w, + "

    {}

    ", + assoc_type( + item, + &tydef.generics, + &[], // intentionally leaving out bounds + Some(tydef.item_type.as_ref().unwrap_or(&tydef.type_)), + link.anchor(if trait_.is_some() { &source_id } else { &id }), + 0, + cx, + ), + )?; } - write!( - w, - "

    {}

    ", - assoc_type( - item, - &tydef.generics, - &[], // intentionally leaving out bounds - Some(tydef.item_type.as_ref().unwrap_or(&tydef.type_)), - link.anchor(if trait_.is_some() { &source_id } else { &id }), - 0, - cx, - ), - )?; } clean::StrippedItem(..) => return Ok(()), _ => panic!("can't make docs for trait item with name {:?}", item.name), @@ -2901,7 +2926,7 @@ fn render_call_locations( contents_subset, file_span, cx, - &cx.root_path(), + cx.current.len(), &highlight::DecorationInfo(decoration_info), &sources::SourceContext::Embedded(sources::ScrapedInfo { needs_expansion, @@ -3021,7 +3046,7 @@ pub(super) fn render_attributes_in_code_with_options( render_code_attribute(&prefix, "#[doc(hidden)]", w)?; } for attr in &item.attrs.other_attrs { - let hir::Attribute::Parsed(kind) = attr else { continue }; + let rustc_attr_ir::Attribute::Parsed(kind) = attr else { continue }; let attr = match kind { AttributeKind::LinkSection { name, .. } => { Cow::Owned(format!("#[unsafe(link_section = {})]", Escape(&format!("{name:?}")))) @@ -3089,7 +3114,7 @@ fn repr_attribute<'tcx>( // - the item is annotated with internal attribute `#[rustc_pub_transparent]` if repr.transparent() { let is_public = 'is_public: { - if hir::find_attr!(tcx, def_id, AttributeKind::RustcPubTransparent(_)) { + if find_attr!(tcx, def_id, RustcPubTransparent(_)) { break 'is_public true; } @@ -3156,3 +3181,21 @@ fn repr_attribute<'tcx>( (!result.is_empty()).then(|| format!("#[repr({})]", result.join(", ")).into()) } + +pub(crate) fn compute_if_deref_target_implements_copy(tcx: TyCtxt<'_>, impl_def_id: DefId) -> bool { + if let Some(impl_def_id) = impl_def_id.as_local() + && let item = tcx.hir_expect_item(impl_def_id) + && let rustc_hir::ItemKind::Impl(impl_item) = item.kind + { + for item in impl_item.items { + let item = tcx.hir_impl_item(*item); + if matches!(item.kind, rustc_hir::ImplItemKind::Type(_)) { + let item_def_id = item.owner_id.to_def_id(); + let ty = tcx.type_of(item_def_id).instantiate_identity().skip_norm_wip(); + let typing_env = ty::TypingEnv::non_body_analysis(tcx, item_def_id); + return tcx.type_is_copy_modulo_regions(typing_env, ty); + } + } + } + false +} diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 6f66dcf9eae83..f6d5ada07f364 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1296,79 +1296,71 @@ fn item_trait_alias( fn item_type_alias(cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) -> impl fmt::Display { fmt::from_fn(|w| { + let def_id = it.def_id().unwrap(); + wrap_item(w, |w| { render_attributes_in_code(w, it, "", cx)?; write!( w, - "{vis}type {name}{generics}{where_clause} = {type_};", + "{vis}type {name}{generics}", vis = visibility_print_with_space(it, cx), name = it.name.unwrap(), generics = print_generics(&t.generics, cx), - where_clause = - print_where_clause(&t.generics, cx, 0, Ending::Newline).maybe_display(), - type_ = print_type(&t.type_, cx), - ) + )?; + + let type_ = print_type(&t.type_, cx); + let where_clause = + print_where_clause(&t.generics, cx, 0, Ending::NoNewline).maybe_display(); + + if cx.tcx().type_alias_is_checked(def_id) { + write!(w, " = {type_}{where_clause}") + } else { + write!(w, "{where_clause} = {type_}") + }?; + + w.write_str(";") })?; write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?; if let Some(inner_type) = &t.inner_type { - write!(w, "{}", write_section_heading("Aliased Type", "aliased-type", None, ""),)?; + write!(w, "{}", write_section_heading("Aliased Type", "aliased-type", None, ""))?; + + let ty = cx.tcx().type_of(def_id).instantiate_identity().skip_norm_wip(); + let adt_def_id = ty.ty_adt_def().unwrap().did(); match inner_type { clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive } => { - let ty = cx - .tcx() - .type_of(it.def_id().unwrap()) - .instantiate_identity() - .skip_norm_wip(); - let enum_def_id = ty.ty_adt_def().unwrap().did(); - DisplayEnum { variants, generics: &t.generics, is_non_exhaustive: *is_non_exhaustive, - def_id: enum_def_id, + def_id: adt_def_id, } .render_into(cx, it, true, w)?; } clean::TypeAliasInnerType::Union { fields } => { - let ty = cx - .tcx() - .type_of(it.def_id().unwrap()) - .instantiate_identity() - .skip_norm_wip(); - let union_def_id = ty.ty_adt_def().unwrap().did(); - ItemUnion { cx, it, fields, generics: &t.generics, is_type_alias: true, - def_id: union_def_id, + def_id: adt_def_id, } .render_into(w)?; } clean::TypeAliasInnerType::Struct { ctor_kind, fields } => { - let ty = cx - .tcx() - .type_of(it.def_id().unwrap()) - .instantiate_identity() - .skip_norm_wip(); - let struct_def_id = ty.ty_adt_def().unwrap().did(); - DisplayStruct { ctor_kind: *ctor_kind, generics: &t.generics, fields, - def_id: struct_def_id, + def_id: adt_def_id, } .render_into(cx, it, true, w)?; } } } else { - let def_id = it.item_id.expect_def_id(); // Render any items associated directly to this alias, as otherwise they // won't be visible anywhere in the docs. It would be nice to also show // associated items from the aliased type (see discussion in #32077), but @@ -1455,12 +1447,12 @@ fn item_type_alias(cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) -> // [^115718]: https://github.com/rust-lang/rust/issues/115718 let cache = &cx.shared.cache; if let Some(target_did) = t.type_.def_id(cache) - && let get_extern = { || cache.external_paths.get(&target_did) } - && let Some(&(ref target_fqp, target_type)) = - cache.paths.get(&target_did).or_else(get_extern) + && let get_extern = { || cache.external_paths.get(&target_did).map(|(fqp, shortty)| (fqp, *shortty)) } + && let Some((target_fqp, target_type)) = + cache.paths.get(&target_did).map(|info| (&info.parts, info.ty)).or_else(get_extern) && target_type.is_adt() // primitives cannot be inlined && let Some(self_did) = it.item_id.as_def_id() - && let get_local = { || cache.paths.get(&self_did).map(|(p, _)| p) } + && let get_local = { || cache.paths.get(&self_did).map(|info| &info.parts) } && let Some(self_fqp) = cache.exact_paths.get(&self_did).or_else(get_local) { let mut js_src_path: UrlPartsBuilder = @@ -2017,13 +2009,11 @@ fn item_constant( write!( w, - "{vis}const {name}{generics}: {typ}{where_clause}", + "{vis}const {name}{generics}: {typ}", vis = visibility_print_with_space(it, cx), name = it.name.unwrap(), generics = print_generics(generics, cx), typ = print_type(ty, cx), - where_clause = - print_where_clause(generics, cx, 0, Ending::NoNewline).maybe_display(), )?; // FIXME: The code below now prints @@ -2039,9 +2029,7 @@ fn item_constant( let is_literal = c.is_literal(tcx); let expr = c.expr(tcx); if value.is_some() || is_literal { - write!(w, " = {expr};", expr = Escape(&expr))?; - } else { - w.write_str(";")?; + write!(w, " = {expr}", expr = Escape(&expr))?; } if !is_literal && let Some(value) = &value { @@ -2051,9 +2039,14 @@ fn item_constant( if value_lowercase != expr_lowercase && value_lowercase.trim_end_matches("i32") != expr_lowercase { - write!(w, " // {value}", value = Escape(value))?; + write!(w, " /* {value} */", value = Escape(value))?; } } + + print_where_clause(generics, cx, 0, Ending::NoNewline).maybe_display().fmt(w)?; + + w.write_str(";")?; + Ok::<(), fmt::Error>(()) })?; diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 4c93e632ab467..38e91050fc9bc 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -11,10 +11,10 @@ use ::serde::de::{self, Deserializer, Error as _}; use ::serde::ser::{SerializeSeq, Serializer}; use ::serde::{Deserialize, Serialize}; use rustc_ast::join_path_syms; +use rustc_attr_ir::find_attr; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::thin_vec::ThinVec; use rustc_hir::def_id::{CrateNum, DefIndex, LOCAL_CRATE}; -use rustc_hir::find_attr; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::DefId; use rustc_span::sym; @@ -1279,7 +1279,7 @@ pub(crate) fn build_index( for &OrphanImplItem { impl_id, parent, trait_parent, ref item, ref impl_generics } in &cache.orphan_impl_items { - if let Some((fqp, _)) = cache.paths.get(&parent) { + if let Some(path_info) = cache.paths.get(&parent) { let info = IndexItemInfo::new( tcx, cache, @@ -1291,7 +1291,7 @@ pub(crate) fn build_index( search_index.push(IndexItem { defid: item.item_id.as_def_id(), name: item.name.unwrap(), - module_path: fqp[..fqp.len() - 1].to_vec(), + module_path: path_info.parts[..path_info.parts.len() - 1].to_vec(), parent: Some(parent), parent_idx: None, trait_parent, @@ -1418,8 +1418,15 @@ pub(crate) fn build_index( cache .paths .get(&defid) - .or_else(|| check_external.then(|| cache.external_paths.get(&defid)).flatten()) - .map(|&(ref fqp, ty)| { + .map(|info| (&info.parts, info.ty)) + .or_else(|| { + check_external + .then(|| { + cache.external_paths.get(&defid).map(|(parts, ty)| (parts, *ty)) + }) + .flatten() + }) + .map(|(fqp, ty)| { let pathid = serialized_index.names.len(); match serialized_index.crate_paths_index.entry((ty, fqp.clone())) { Entry::Occupied(entry) => *entry.get(), @@ -1661,8 +1668,10 @@ pub(crate) fn build_index( used_in_function_signature, )), RenderTypeId::DefId(defid) => { - if let Some(&(ref fqp, item_type)) = - paths.get(&defid).or_else(|| external_paths.get(&defid)) + if let Some((fqp, item_type)) = paths + .get(&defid) + .map(|info| (&info.parts, info.ty)) + .or_else(|| external_paths.get(&defid).map(|(parts, ty)| (parts, *ty))) { if tcx.lang_items().fn_mut_trait() == Some(defid) || tcx.lang_items().fn_once_trait() == Some(defid) @@ -1974,8 +1983,11 @@ pub(crate) fn get_function_type_for_search( let impl_or_trait_generics = impl_generics.or_else(|| { if let Some(def_id) = parent && let Some(trait_) = cache.traits.get(&def_id) - && let Some((path, _)) = - cache.paths.get(&def_id).or_else(|| cache.external_paths.get(&def_id)) + && let Some((path, _)) = cache + .paths + .get(&def_id) + .map(|info| (&info.parts, info.ty)) + .or_else(|| cache.external_paths.get(&def_id).map(|(parts, ty)| (parts, *ty))) { let path = clean::Path { res: rustc_hir::def::Res::Def(rustc_hir::def::DefKind::Trait, def_id), diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index 6fa033f927c85..3befbf2cff8ff 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -3,7 +3,7 @@ use std::cmp::Ordering; use std::fmt; use askama::Template; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def::CtorKind; use rustc_hir::def_id::{DefIdMap, DefIdSet}; use rustc_middle::ty::TyCtxt; @@ -442,7 +442,7 @@ fn sidebar_assoc_items<'a>( let mut assoc_fns = Vec::new(); let mut methods = Vec::new(); if let Some(v) = cache.impls.get(&did) { - let mut used_links = FxHashSet::default(); + let mut used_links = UsedLinks::default(); let mut id_map = IdMap::new(); { @@ -524,7 +524,7 @@ fn sidebar_deref_methods<'a>( impl_: &Impl, v: &[Impl], derefs: &mut DefIdSet, - used_links: &mut FxHashSet, + used_links: &mut UsedLinks, deref_id_map: &'a DefIdMap, ) { let c = cx.cache(); @@ -548,7 +548,8 @@ fn sidebar_deref_methods<'a>( // Avoid infinite cycles return; } - let deref_mut = v.iter().any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait()); + let tcx = cx.tcx(); + let deref_mut = v.iter().any(|i| i.trait_did() == tcx.lang_items().deref_mut_trait()); let inner_impl = target .def_id(c) .or_else(|| { @@ -557,6 +558,8 @@ fn sidebar_deref_methods<'a>( .and_then(|did| c.impls.get(&did)); if let Some(impls) = inner_impl { debug!("found inner_impl: {impls:?}"); + let is_deref_target_copy = + super::compute_if_deref_target_implements_copy(tcx, impl_.def_id()); let mut ret = impls .iter() .filter(|i| { @@ -566,9 +569,9 @@ fn sidebar_deref_methods<'a>( .flat_map(|i| { get_methods( i.inner_impl(), - GetMethodsMode::Deref { deref_mut }, + GetMethodsMode::Deref { deref_mut, is_deref_target_copy }, used_links, - cx.tcx(), + tcx, ) .collect::>() }) @@ -602,7 +605,7 @@ fn sidebar_deref_methods<'a>( i.inner_impl() .trait_ .as_ref() - .map(|t| Some(t.def_id()) == cx.tcx().lang_items().deref_trait()) + .map(|t| Some(t.def_id()) == tcx.lang_items().deref_trait()) .unwrap_or(false) && !i.is_negative_trait_impl() }) @@ -745,26 +748,25 @@ fn sidebar_render_assoc_items( ]); } -fn get_next_url(used_links: &mut FxHashSet, url: String) -> String { - if used_links.insert(url.clone()) { - return url; - } - let mut add = 1; - while !used_links.insert(format!("{url}-{add}")) { - add += 1; - } - format!("{url}-{add}") +/// Tracks sidebar link anchors so duplicates get unique names. +type UsedLinks = FxHashMap; + +fn get_next_url(used_links: &mut UsedLinks, url: String) -> String { + let count = used_links.entry(url.clone()).or_insert(0); + let res = if *count == 0 { url } else { format!("{url}-{count}") }; + *count += 1; + res } enum GetMethodsMode<'r, 'l> { - Deref { deref_mut: bool }, + Deref { deref_mut: bool, is_deref_target_copy: bool }, AlsoCollectAssocFns { assoc_fns: &'r mut Vec> }, } fn get_methods<'a>( i: &'a clean::Impl, mut mode: GetMethodsMode<'_, 'a>, - used_links: &mut FxHashSet, + used_links: &mut UsedLinks, tcx: TyCtxt<'_>, ) -> impl Iterator> { i.items.iter().filter_map(move |item| { @@ -778,8 +780,8 @@ fn get_methods<'a>( ) }; match &mut mode { - &mut GetMethodsMode::Deref { deref_mut } => { - if super::should_render_item(item, deref_mut, tcx) { + &mut GetMethodsMode::Deref { deref_mut, is_deref_target_copy } => { + if super::should_render_item(item, deref_mut, tcx, is_deref_target_copy) { Some(build_link()) } else { None @@ -802,7 +804,7 @@ fn get_methods<'a>( fn get_associated_constants<'a>( i: &'a clean::Impl, - used_links: &mut FxHashSet, + used_links: &mut UsedLinks, ) -> impl Iterator> { i.items.iter().filter_map(|item| { if let Some(ref name) = item.name @@ -820,7 +822,7 @@ fn get_associated_constants<'a>( fn get_associated_types<'a>( i: &'a clean::Impl, - used_links: &mut FxHashSet, + used_links: &mut UsedLinks, ) -> impl Iterator> { i.items.iter().filter_map(|item| { if let Some(ref name) = item.name diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index 5c963afd3a221..258970bbb3b8f 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -834,12 +834,17 @@ impl TraitAliasPart { // FIXME: this is a vague explanation for why this can't be a `get`, in // theory it should be... let (remote_path, remote_item_type) = match cache.exact_paths.get(&did) { - Some(p) => match cache.paths.get(&did).or_else(|| cache.external_paths.get(&did)) { + Some(p) => match cache + .paths + .get(&did) + .map(|info| (&info.parts, info.ty)) + .or_else(|| cache.external_paths.get(&did).map(|(parts, ty)| (parts, *ty))) + { Some((_, t)) => (p, t), None => continue, }, None => match cache.external_paths.get(&did) { - Some((p, t)) => (p, t), + Some((p, t)) => (p, *t), None => continue, }, }; @@ -987,8 +992,10 @@ impl<'item> DocVisitor<'item> for TypeImplCollector<'_, '_, 'item> { return; } let Some(target_did) = t.type_.def_id(cache) else { return }; - let get_extern = { || cache.external_paths.get(&target_did) }; - let Some(&(ref target_fqp, target_type)) = cache.paths.get(&target_did).or_else(get_extern) + let get_extern = + { || cache.external_paths.get(&target_did).map(|(parts, ty)| (parts, *ty)) }; + let Some((target_fqp, target_type)) = + cache.paths.get(&target_did).map(|info| (&info.parts, info.ty)).or_else(get_extern) else { return; }; @@ -1004,7 +1011,7 @@ impl<'item> DocVisitor<'item> for TypeImplCollector<'_, '_, 'item> { .collect(); AliasedType { target_fqp: &target_fqp[..], target_type, impl_ } }); - let get_local = { || cache.paths.get(&self_did).map(|(p, _)| p) }; + let get_local = { || cache.paths.get(&self_did).map(|info| &info.parts) }; let Some(self_fqp) = cache.exact_paths.get(&self_did).or_else(get_local) else { return; }; diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index ea52fe51c5358..2328f0e8b9a11 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -1,4 +1,4 @@ -use std::cell::RefCell; +use std::cell::{Cell, RefCell}; use std::ffi::OsStr; use std::path::{Component, Path, PathBuf}; use std::{fmt, fs}; @@ -193,31 +193,28 @@ impl SourceCollector<'_, '_> { let shared = &self.cx.shared; // Create the intermediate directories let cur = RefCell::new(PathBuf::new()); - let root_path = RefCell::new(PathBuf::new()); + // Tracks the extra directory depth accumulated during path traversal. + // Starts at 0; `PathBuf::pop()` on empty is a no-op, so we mirror that + // with `saturating_sub`. The base depth of 2 (for `src//`) is added after. + let extra_depth = Cell::new(0usize); clean_path( &shared.src_root, &p, |component| { cur.borrow_mut().push(component); - root_path.borrow_mut().push(".."); + extra_depth.set(extra_depth.get() + 1); }, || { cur.borrow_mut().pop(); - root_path.borrow_mut().pop(); + extra_depth.set(extra_depth.get().saturating_sub(1)); }, ); + let jump_to_def_path_depth = 2 + extra_depth.get(); + let src_fname = p.file_name().expect("source has no filename").to_os_string(); let mut fname = src_fname.clone(); - - let root_path = PathBuf::from("../../").join(root_path.into_inner()); - let mut root_path = root_path.to_string_lossy(); - if let Some(c) = root_path.as_bytes().last() - && *c != b'/' - { - root_path += "/"; - } let mut file_path = Path::new(&self.crate_name).join(&*cur.borrow()); file_path.push(&fname); fname.push(".html"); @@ -228,6 +225,7 @@ impl SourceCollector<'_, '_> { let title = format!("{} - source", src_fname.to_string_lossy()); let desc = format!("Source of the Rust file `{}`.", p.to_string_lossy()); + let root_path = "../".repeat(jump_to_def_path_depth); let page = layout::Page { title: &title, short_title: &src_fname.to_string_lossy(), @@ -251,7 +249,7 @@ impl SourceCollector<'_, '_> { contents, file_span, self.cx, - &root_path, + jump_to_def_path_depth, &highlight::DecorationInfo::default(), &source_context, ) @@ -330,7 +328,7 @@ pub(crate) fn print_src( s: &str, file_span: rustc_span::Span, cx: &Context<'_>, - root_path: &str, + jump_to_def_path_depth: usize, decoration_info: &highlight::DecorationInfo, source_context: &SourceContext<'_>, ) -> fmt::Result { @@ -358,7 +356,7 @@ pub(crate) fn print_src( Some(highlight::HrefContext { context: cx, file_span: file_span.into(), - root_path, + jump_to_def_path_depth, current_href, }), Some(decoration_info), diff --git a/src/librustdoc/html/span_map.rs b/src/librustdoc/html/span_map.rs index 424b3c94fcbb1..45eb7529896b1 100644 --- a/src/librustdoc/html/span_map.rs +++ b/src/librustdoc/html/span_map.rs @@ -8,16 +8,18 @@ use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{ExprKind, HirId, Item, ItemKind, Mod, Node, QPath}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, TyCtxt}; +use rustc_span::def_id::LocalModId; use rustc_span::{BytePos, ExpnKind}; use crate::clean::{self, PrimitiveType, rustc_span}; use crate::html::sources; -/// This is a stripped down version of [`rustc_span::Span`] that only contains the start and end byte positions of the span. +/// This is a stripped down version of [`rustc_span::Span`] that only contains the start and end +/// byte positions of the span. /// -/// Profiling showed that the `Span` interner was taking up a lot of the run-time when highlighting, and since we -/// never actually use the context and parent that are stored in a normal `Span`, we can replace its usages with this -/// one, which is much cheaper to construct. +/// Profiling showed that the `Span` interner was taking up a lot of the run-time when highlighting, +/// and since we never actually use the context and parent that are stored in a normal `Span`, we +/// can replace its usages with this one, which is much cheaper to construct. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub(crate) struct Span { lo: BytePos, @@ -309,13 +311,13 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { } } - fn visit_mod(&mut self, m: &'tcx Mod<'tcx>, span: rustc_span::Span, id: HirId) { + fn visit_mod(&mut self, m: &'tcx Mod<'tcx>, span: rustc_span::Span, id: LocalModId) { // To make the difference between "mod foo {}" and "mod foo;". In case we "import" another // file, we want to link to it. Otherwise no need to create a link. if !span.overlaps(m.spans.inner_span) { // Now that we confirmed it's a file import, we want to get the span for the module // name only and not all the "mod foo;". - if let Node::Item(item) = self.tcx.hir_node(id) { + if let Node::Item(item) = self.tcx.hir_node_by_def_id(id.into()) { let (ident, _) = item.expect_mod(); self.matches.insert( ident.span.into(), @@ -324,7 +326,7 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { } } else { // If it's a "mod foo {}", we want to look to its documentation page. - self.extract_info_from_hir_id(id); + self.extract_info_from_hir_id(id.into()); } intravisit::walk_mod(self, m); } diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 588a61f8ad3cc..556a922f7044c 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -4,15 +4,15 @@ use rustc_abi::ExternAbi; use rustc_ast::ast; -use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::thin_vec::ThinVec; -use rustc_hir as hir; -use rustc_hir::attrs::{ +use rustc_attr_ir::{ self, DeprecatedSince, DocAttribute, DocCfgHideShow, DocInline, HideOrShow, RustcVersion, + find_attr, }; +use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::thin_vec::ThinVec; use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::DefId; -use rustc_hir::{HeaderSafety, Safety, find_attr, intravisit}; +use rustc_hir::{HeaderSafety, Safety, intravisit}; use rustc_hir_pretty::PpAnn; use rustc_metadata::rendered_const; use rustc_middle::ty::{self, TyCtxt}; @@ -222,9 +222,9 @@ impl FromClean>> for Visibility { } } -impl FromClean for Deprecation { - fn from_clean(deprecation: &attrs::Deprecation, _renderer: &JsonRenderer<'_>) -> Self { - let attrs::Deprecation { since, note, suggestion: _ } = deprecation; +impl FromClean for Deprecation { + fn from_clean(deprecation: &rustc_attr_ir::Deprecation, _renderer: &JsonRenderer<'_>) -> Self { + let rustc_attr_ir::Deprecation { since, note, suggestion: _ } = deprecation; let since = match since { DeprecatedSince::RustcVersion(version) => Some(version.to_string()), DeprecatedSince::Future => Some("TBD".to_string()), @@ -235,45 +235,48 @@ impl FromClean for Deprecation { } } -impl FromClean for Stability { - fn from_clean(stab: &hir::Stability, _renderer: &JsonRenderer<'_>) -> Self { +impl FromClean for Stability { + fn from_clean(stab: &rustc_attr_ir::Stability, _renderer: &JsonRenderer<'_>) -> Self { let feature = stab.feature.to_string(); let level = match stab.level { - hir::StabilityLevel::Stable { since, .. } => StabilityLevel::Stable { + rustc_attr_ir::StabilityLevel::Stable { since, .. } => StabilityLevel::Stable { since: match since { - hir::StableSince::Version(since) => Some(since.to_string()), - hir::StableSince::Current => Some(RustcVersion::CURRENT.to_string()), + rustc_attr_ir::StableSince::Version(since) => Some(since.to_string()), + rustc_attr_ir::StableSince::Current => Some(RustcVersion::CURRENT.to_string()), // Match rustdoc HTML: malformed stable-since values are omitted. - hir::StableSince::Err(_) => None, + rustc_attr_ir::StableSince::Err(_) => None, }, }, - hir::StabilityLevel::Unstable { .. } => StabilityLevel::Unstable, + rustc_attr_ir::StabilityLevel::Unstable { .. } => StabilityLevel::Unstable, }; Stability { feature, level } } } -impl FromClean for Stability { - fn from_clean(stab: &hir::ConstStability, _renderer: &JsonRenderer<'_>) -> Self { +impl FromClean for Stability { + fn from_clean(stab: &rustc_attr_ir::ConstStability, _renderer: &JsonRenderer<'_>) -> Self { let feature = stab.feature.to_string(); let level = match stab.level { - hir::StabilityLevel::Stable { since, .. } => StabilityLevel::Stable { + rustc_attr_ir::StabilityLevel::Stable { since, .. } => StabilityLevel::Stable { since: match since { - hir::StableSince::Version(since) => Some(since.to_string()), - hir::StableSince::Current => Some(RustcVersion::CURRENT.to_string()), + rustc_attr_ir::StableSince::Version(since) => Some(since.to_string()), + rustc_attr_ir::StableSince::Current => Some(RustcVersion::CURRENT.to_string()), // Match rustdoc HTML: malformed stable-since values are omitted. - hir::StableSince::Err(_) => None, + rustc_attr_ir::StableSince::Err(_) => None, }, }, - hir::StabilityLevel::Unstable { .. } => StabilityLevel::Unstable, + rustc_attr_ir::StabilityLevel::Unstable { .. } => StabilityLevel::Unstable, }; Stability { feature, level } } } -impl FromClean for Box { - fn from_clean(stab: &hir::DefaultBodyStability, _renderer: &JsonRenderer<'_>) -> Self { - let hir::StabilityLevel::Unstable { .. } = stab.level else { +impl FromClean for Box { + fn from_clean( + stab: &rustc_attr_ir::DefaultBodyStability, + _renderer: &JsonRenderer<'_>, + ) -> Self { + let rustc_attr_ir::StabilityLevel::Unstable { .. } = stab.level else { bug!( "unexpected stable default-body stability, \ there's no stable equivalent of `#[rustc_default_body_unstable]`" @@ -1013,15 +1016,18 @@ impl FromClean for ItemKind { fn default_body_stability_for_def_id( tcx: TyCtxt<'_>, def_id: DefId, -) -> Option { +) -> Option { let stability = tcx.lookup_default_body_stability(def_id)?; match stability.level { - hir::StabilityLevel::Unstable { .. } => Some(stability), - hir::StabilityLevel::Stable { .. } => None, + rustc_attr_ir::StabilityLevel::Unstable { .. } => Some(stability), + rustc_attr_ir::StabilityLevel::Stable { .. } => None, } } -fn const_stability_for_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option { +fn const_stability_for_def_id( + tcx: TyCtxt<'_>, + def_id: DefId, +) -> Option { if !tcx.is_conditionally_const(def_id) { // The item cannot be conditionally-const. No const stability here. // @@ -1064,7 +1070,7 @@ fn const_stability_for_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option, def_id: DefId) -> Option) -> Vec { - use attrs::AttributeKind as AK; +fn maybe_from_hir_attr( + attr: &rustc_attr_ir::Attribute, + item_id: ItemId, + tcx: TyCtxt<'_>, +) -> Vec { + use rustc_attr_ir::AttributeKind as AK; let kind = match attr { - hir::Attribute::Parsed(kind) => kind, + rustc_attr_ir::Attribute::Parsed(kind) => kind, - hir::Attribute::Unparsed(_) => { + rustc_attr_ir::Attribute::Unparsed(_) => { // FIXME: We should handle `#[doc(hidden)]`. return vec![other_attr(tcx, attr)]; } @@ -1162,7 +1172,7 @@ fn maybe_from_hir_attr(attr: &hir::Attribute, item_id: ItemId, tcx: TyCtxt<'_>) ret.push(Attribute::Other(format!("#[doc(alias = {:?})]", alias.as_str()))); } toggle_attr(&mut ret, "hidden", hidden); - if let Some(inline) = inline.first() { + if let Some(inline) = inline { ret.push(Attribute::Other(format!( "#[doc({})]", match inline.0 { @@ -1242,7 +1252,7 @@ fn maybe_from_hir_attr(attr: &hir::Attribute, item_id: ItemId, tcx: TyCtxt<'_>) }] } -fn other_attr(tcx: TyCtxt<'_>, attr: &hir::Attribute) -> Attribute { +fn other_attr(tcx: TyCtxt<'_>, attr: &rustc_attr_ir::Attribute) -> Attribute { let mut s = rustc_hir_pretty::attribute_to_string( &(&tcx as &dyn intravisit::HirTyCtxt<'_>) as &dyn PpAnn, attr, diff --git a/src/librustdoc/json/ids.rs b/src/librustdoc/json/ids.rs index 31043b8028f02..6c3e05e5d7399 100644 --- a/src/librustdoc/json/ids.rs +++ b/src/librustdoc/json/ids.rs @@ -5,9 +5,9 @@ //! [`rustdoc_json_types::Item`] doesn't correspond exactly to what //! other phases think of as an "item". +use rustc_attr_ir::find_attr; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; -use rustc_hir::find_attr; use rustc_span::Symbol; use rustdoc_json_types as types; diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs index f161b31f94dcb..bdd2b7d416d80 100644 --- a/src/librustdoc/json/mod.rs +++ b/src/librustdoc/json/mod.rs @@ -113,8 +113,9 @@ impl<'tcx> JsonRenderer<'tcx> { .cache .paths .iter() - .chain(&self.cache.external_paths) - .map(|(&k, &(ref path, kind))| { + .map(|(k, info)| (k, (&info.parts, info.ty))) + .chain(self.cache.external_paths.iter().map(|(k, (parts, ty))| (k, (parts, *ty)))) + .map(|(&k, (path, kind))| { ( self.id_from_item_default(k.into()), types::ItemSummary { @@ -195,8 +196,9 @@ impl<'tcx> JsonRenderer<'tcx> { self.cache .paths .get(&item_id) - .or_else(|| self.cache.external_paths.get(&item_id)) - .map(|(path, _)| path.iter().map(|name| name.to_string()).collect()) + .map(|info| &info.parts) + .or_else(|| self.cache.external_paths.get(&item_id).map(|(parts, _)| parts)) + .map(|path| path.iter().map(|name| name.to_string()).collect()) } } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 24427ff5cb3d3..57e4b9c9d8c54 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -1,6 +1,4 @@ // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(trim_prefix_suffix))] -#![cfg_attr(not(bootstrap), feature(exitcode_exit_method))] #![doc( html_root_url = "https://doc.rust-lang.org/nightly/", html_playground_url = "https://play.rust-lang.org/" @@ -8,6 +6,7 @@ #![feature(ascii_char)] #![feature(ascii_char_variants)] #![feature(deref_patterns)] +#![feature(exitcode_exit_method)] #![feature(file_buffered)] #![feature(formatting_options)] #![feature(iter_intersperse)] @@ -32,6 +31,7 @@ extern crate rustc_abi; extern crate rustc_ast; extern crate rustc_ast_pretty; +extern crate rustc_attr_ir; extern crate rustc_attr_parsing; extern crate rustc_data_structures; extern crate rustc_driver; @@ -1022,8 +1022,6 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { cache.crate_version = crate_version; - rustc_interface::passes::emit_delayed_lints(tcx); - if render_opts.dep_info().is_some() { rustc_interface::passes::write_dep_info(tcx); } diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 1ae23d58448a3..ea131574b17e3 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -8,21 +8,21 @@ use std::mem; use std::ops::Range; use rustc_ast::util::comments::may_have_doc_links; +use rustc_attr_ir::{Attribute, AttributeKind, find_attr}; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::intern::Interned; use rustc_errors::{Applicability, Diag, DiagMessage}; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::Namespace::*; use rustc_hir::def::{DefKind, MacroKinds, Namespace, PerNS}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LOCAL_CRATE}; -use rustc_hir::{Attribute, Mutability, Safety, find_attr}; +use rustc_hir::{Mutability, Safety}; use rustc_lint::Lint; use rustc_middle::ty; use rustc_middle::ty::{Ty, TyCtxt}; use rustc_resolve::rustdoc::pulldown_cmark::LinkType; use rustc_resolve::rustdoc::{ - MalformedGenerics, has_primitive_or_keyword_or_attribute_docs, prepare_to_doc_link_resolution, - source_span_for_markdown_range, strip_generics_from_path, + DocStrings, MalformedGenerics, has_primitive_or_keyword_or_attribute_docs, + prepare_to_doc_link_resolution, source_span_for_markdown_range, strip_generics_from_path, }; use rustc_span::def_id::ModId; use rustc_span::edit_distance::find_best_match_for_name; @@ -1091,14 +1091,7 @@ impl LinkCollector<'_, '_> { return; } - let mut try_insert_links = |item_id, doc: &str| { - if should_skip_link_resolution(item_id) { - return; - } - let module_id = match tcx.def_kind(item_id) { - DefKind::Mod if item.inner_docs(tcx) => ModId::new_unchecked(item_id), - _ => find_nearest_parent_module(tcx, item_id).unwrap(), - }; + let mut try_insert_links_inner = |item_id, module_id, doc: &str| { for md_link in preprocessed_markdown_links(&doc) { let link = self.resolve_link(&doc, item, item_id, module_id, &md_link); if let Some(link) = link { @@ -1112,16 +1105,45 @@ impl LinkCollector<'_, '_> { } }; + let mut try_insert_links = |item_id, docs: &DocStrings| { + if should_skip_link_resolution(item_id) { + return; + } + match tcx.def_kind(item_id) { + DefKind::Mod => { + if !docs.outer.is_empty() { + try_insert_links_inner( + item_id, + find_nearest_parent_module(tcx, item_id).unwrap(), + &docs.outer, + ); + } + if !docs.inner.is_empty() { + try_insert_links_inner(item_id, ModId::new_unchecked(item_id), &docs.inner); + } + } + _ => { + // It's the same handling for non-module items. + let module_id = find_nearest_parent_module(tcx, item_id).unwrap(); + try_insert_links_inner( + item_id, + module_id, + &format!("{}{}", docs.outer, docs.inner), + ); + } + } + }; + // We want to resolve in the lexical scope of the documentation. // In the presence of re-exports, this is not the same as the module of the item. // Rather than merging all documentation into one, resolve it one attribute at a time // so we know which module it came from. for (item_id, doc) in prepare_to_doc_link_resolution(&item.attrs.doc_strings) { - if !may_have_doc_links(&doc) { + if !may_have_doc_links(&doc.inner) && !may_have_doc_links(&doc.outer) { continue; } - debug!("combined_docs={doc}"); + debug!("combined_docs=outer: {} inner: {}", doc.outer, doc.inner); // NOTE: if there are links that start in one crate and end in another, this will not resolve them. // This is a degenerate case and it's not supported by rustdoc. let item_id = item_id.unwrap_or_else(|| item.item_id.expect_def_id()); @@ -1163,7 +1185,7 @@ impl LinkCollector<'_, '_> { } else { item.item_id.expect_def_id() }; - try_insert_links(item_id, note) + try_insert_links(item_id, &DocStrings { outer: note.to_owned(), inner: String::new() }); } } @@ -1229,7 +1251,7 @@ impl LinkCollector<'_, '_> { /// Returns `true` if a link could be generated from the given intra-doc information. /// - /// This is a very light version of `format::href_with_root_path` since we're only interested + /// This is a very light version of `format::href_with_jump_to_def_path_depth` since we're only interested /// about whether we can generate a link to an item or not. /// /// * If `original_did` is local, then we check if the item is reexported or public. @@ -1471,7 +1493,7 @@ impl LinkCollector<'_, '_> { ) { // The resolved item did not match the disambiguator; give a better error than 'not found' let msg = format!("incompatible link kind for `{path_str}`"); - let callback = |diag: &mut Diag<'_, ()>, sp: Option, link_range| { + let callback = |diag: &mut Diag<'_>, sp: Option, link_range| { let note = format!( "this link resolved to {} {}, which is not {} {}", resolved.article(), @@ -1929,7 +1951,7 @@ fn report_diagnostic( lint: &'static Lint, msg: impl Into + Display, DiagnosticInfo { item, ori_link: _, dox, link_range }: &DiagnosticInfo<'_>, - decorate: impl FnOnce(&mut Diag<'_, ()>, Option, MarkdownLinkRange), + decorate: impl FnOnce(&mut Diag<'_>, Option, MarkdownLinkRange), ) { let Some(hir_id) = DocContext::as_local_hir_id(tcx, item.item_id) else { // If non-local, no need to check anything. @@ -2007,7 +2029,7 @@ fn report_diagnostic( fn suggest_path_name_typo( collector: &LinkCollector<'_, '_>, - diag: &mut Diag<'_, ()>, + diag: &mut Diag<'_>, span: Option, link_range: &MarkdownLinkRange, dox: &str, @@ -2522,7 +2544,7 @@ fn ambiguity_error( /// disambiguator. fn suggest_disambiguator( res: Res, - diag: &mut Diag<'_, ()>, + diag: &mut Diag<'_>, path_str: &str, link_range: MarkdownLinkRange, sp: Option, diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index 1bfac8e67748c..8720e09bd85cd 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -3,11 +3,10 @@ //! For example, if a crate defines a struct that implements a trait, //! this pass will note that the struct implements that trait. +use rustc_attr_ir::{Attribute, AttributeKind, DocAttribute, find_attr}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::FatalError; -use rustc_hir::attrs::{AttributeKind, DocAttribute}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_hir::{Attribute, find_attr}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::kw; use tracing::debug; diff --git a/src/librustdoc/passes/lint/bare_urls.rs b/src/librustdoc/passes/lint/bare_urls.rs index 0928980e390a8..0b78be0e6dc8d 100644 --- a/src/librustdoc/passes/lint/bare_urls.rs +++ b/src/librustdoc/passes/lint/bare_urls.rs @@ -2,13 +2,14 @@ //! Suggests wrapping the link with angle brackets: `Go to .` to linkify it. use core::ops::Range; -use std::mem; use std::sync::LazyLock; use regex::Regex; use rustc_errors::{Applicability, DiagDecorator}; use rustc_hir::HirId; -use rustc_resolve::rustdoc::pulldown_cmark::{Event, Parser, Tag}; +use rustc_resolve::rustdoc::pulldown_cmark::{ + DefaultBrokenLinkCallback, Event, Tag, TextMergeWithOffset, +}; use rustc_resolve::rustdoc::source_span_for_markdown_range; use tracing::trace; @@ -55,21 +56,20 @@ pub(super) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: & ); }; - let mut p = Parser::new_ext(dox, main_body_opts()).into_offset_iter(); + // pulldown-cmark can split a URL into multiple `Text` events while processing + // characters such as `_` according to CommonMark's emphasis rules. + // `TextMergeWithOffset` merges these events so we can check the complete URL. + let mut p = TextMergeWithOffset::::new_ext(dox, main_body_opts()); while let Some((event, range)) = p.next() { match event { - Event::Text(s) => find_raw_urls(cx, dox, &s, range, &report_diag), + Event::Text(_s) => find_raw_urls(cx, dox, range, &report_diag), // We don't want to check the text inside code blocks or links. Event::Start(tag @ (Tag::CodeBlock(_) | Tag::Link { .. })) => { + let end = tag.to_end(); for (event, _) in p.by_ref() { - match event { - Event::End(end) - if mem::discriminant(&end) == mem::discriminant(&tag.to_end()) => - { - break; - } - _ => {} + if matches!(event, Event::End(tag) if tag == end) { + break; } } } @@ -78,12 +78,11 @@ pub(super) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: & } } -static URL_REGEX: LazyLock = LazyLock::new(|| { +static URL_SCHEME_HOST_REGEX: LazyLock = LazyLock::new(|| { Regex::new(concat!( r"https?://", // url scheme r"([-a-zA-Z0-9@:%._\+~#=]{2,256}\.)+", // one or more subdomains r"[a-zA-Z]{2,63}", // root domain - r"\b([-a-zA-Z0-9@:%_\+.~#?&/=]*)", // optional query or url fragments )) .expect("failed to build regex") }); @@ -91,16 +90,64 @@ static URL_REGEX: LazyLock = LazyLock::new(|| { fn find_raw_urls( cx: &DocContext<'_>, dox: &str, - text: &str, range: Range, f: &impl Fn(&DocContext<'_>, &'static str, Range, Option<&str>), ) { - trace!("looking for raw urls in {text}"); + trace!("looking for raw urls in {text}", text = &dox[range.clone()]); // For now, we only check "full" URLs (meaning, starting with "http://" or "https://"). - for match_ in URL_REGEX.find_iter(text) { + for match_ in URL_SCHEME_HOST_REGEX.find_iter(&dox[range.clone()]) { let mut url_range = match_.range(); + // We have a range within `dox[range]`. + // We need a range within `dox` to report the diagnostic. url_range.start += range.start; url_range.end += range.start; + // We found the scheme and host. Find the path, query, or fragment. + // We want to check for matching, balanced parens, + // but regex isn't powerful enough for that. + let mut paren_stack = Vec::with_capacity(3); + 'parts: while let Some(&sep) = dox.as_bytes().get(url_range.end) { + // The hostname must be immediately followed by a path, query, + // or fragment-declaring separator. + if !matches!(sep, b'/' | b'?' | b'#') { + break; + } + url_range.end += 1; + while let Some(&c) = dox.as_bytes().get(url_range.end) { + if c == b'(' { + paren_stack.push(url_range.end); + } else if c == b')' { + // We assume the first unmatched parenthesis marks the end of the url, + // as urls rarely contain unbalanced parenthesis in practice. + if paren_stack.pop().is_none() { + break 'parts; + } + } else if !matches!( + c, + b'-' + | b'a'..=b'z' + | b'A'..=b'Z' + | b'0'..=b'9' + | b'@' + | b':' + | b'%' + | b'_' + | b'\\' + | b'+' + | b'.' + | b'~' + | b'&' + | b'=' + ) { + break; + } + url_range.end += 1; + } + } + // We assume the first unmatched parenthesis marks the end of the url, + // as urls rarely contain unbalanced parenthesis in practice. + if let Some(&end) = paren_stack.first() { + url_range.end = end; + } let mut without_brackets = None; // If the link is contained inside `[]`, then we need to replace the brackets and // not just add `<>`. @@ -117,7 +164,7 @@ fn find_raw_urls( // period out of the link, so that `Visit https://example.com/docs.` is linkified as // `Visit .`. let trailing_periods = - match_.as_str().len() - match_.as_str().trim_end_matches('.').len(); + dox[url_range.clone()].len() - dox[url_range.clone()].trim_end_matches('.').len(); url_range.end -= trailing_periods; } f(cx, "this URL is not a hyperlink", url_range, without_brackets); diff --git a/src/librustdoc/passes/lint/check_code_block_syntax.rs b/src/librustdoc/passes/lint/check_code_block_syntax.rs index b25d081f6ae80..cd838a4a83b20 100644 --- a/src/librustdoc/passes/lint/check_code_block_syntax.rs +++ b/src/librustdoc/passes/lint/check_code_block_syntax.rs @@ -41,8 +41,8 @@ fn check_rust_syntax( is_precise_span: bool, } - impl<'a, 'b> Diagnostic<'a, ()> for CodeblockError<'b> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + impl<'a, 'b> Diagnostic<'a> for CodeblockError<'b> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let Self { buffer, code_block, span, is_precise_span } = self; let mut lint = Diag::new( diff --git a/src/librustdoc/passes/lint/redundant_explicit_links.rs b/src/librustdoc/passes/lint/redundant_explicit_links.rs index d2f244e7bba0b..e02fbae3e0568 100644 --- a/src/librustdoc/passes/lint/redundant_explicit_links.rs +++ b/src/librustdoc/passes/lint/redundant_explicit_links.rs @@ -30,10 +30,11 @@ struct LinkData { pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId) { let hunks = prepare_to_doc_link_resolution(&item.attrs.doc_strings); for (item_id, doc) in hunks { - if let Some(item_id) = item_id.or(item.def_id()) - && !doc.is_empty() - { - check_redundant_explicit_link_for_did(cx, item, item_id, hir_id, &doc); + if let Some(item_id) = item_id.or(item.def_id()) { + if !doc.outer.is_empty() || !doc.inner.is_empty() { + let doc = format!("{}{}", doc.outer, doc.inner); + check_redundant_explicit_link_for_did(cx, item, item_id, hir_id, &doc); + } } } } @@ -164,8 +165,8 @@ struct RedundantExplicitLinksWithoutSuggestion { dest_link: String, } -impl<'a> Diagnostic<'a, ()> for RedundantExplicitLinksWithoutSuggestion { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { +impl<'a> Diagnostic<'a> for RedundantExplicitLinksWithoutSuggestion { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let Self { attr_span, display_link, dest_link } = self; Diag::new(dcx, level, "redundant explicit link target") @@ -199,8 +200,8 @@ fn check_inline_or_reference_unknown_redundancy( display_link: String, } - impl<'a> Diagnostic<'a, ()> for RedundantExplicitLinks { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + impl<'a> Diagnostic<'a> for RedundantExplicitLinks { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let Self { explicit_span, display_span, link_span, display_link } = self; Diag::new(dcx, level, "redundant explicit link target") @@ -318,8 +319,8 @@ fn check_reference_redundancy( display_link: String, } - impl<'a> Diagnostic<'a, ()> for RedundantExplicitLinkTarget { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + impl<'a> Diagnostic<'a> for RedundantExplicitLinkTarget { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let Self { explicit_span, display_span, def_span, link_span, display_link } = self; Diag::new(dcx, level, "redundant explicit link target") diff --git a/src/librustdoc/passes/lint/unescaped_backticks.rs b/src/librustdoc/passes/lint/unescaped_backticks.rs index 9c86c50dcca7c..dd6a1b26de76f 100644 --- a/src/librustdoc/passes/lint/unescaped_backticks.rs +++ b/src/librustdoc/passes/lint/unescaped_backticks.rs @@ -413,7 +413,7 @@ fn suggest_insertion( cx: &DocContext<'_>, item: &Item, dox: &str, - lint: &mut Diag<'_, ()>, + lint: &mut Diag<'_>, insert_index: usize, suggestion: char, message: &'static str, diff --git a/src/librustdoc/passes/propagate_doc_cfg.rs b/src/librustdoc/passes/propagate_doc_cfg.rs index 0d16141fa09a1..64306c0a6104c 100644 --- a/src/librustdoc/passes/propagate_doc_cfg.rs +++ b/src/librustdoc/passes/propagate_doc_cfg.rs @@ -2,9 +2,8 @@ //! //! [RFC 3631]: https://rust-lang.github.io/rfcs/3631-rustdoc-cfgs-handling.html +use rustc_attr_ir::{Attribute, AttributeKind, DocAttribute, find_attr}; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::attrs::{AttributeKind, DocAttribute}; -use rustc_hir::{Attribute, find_attr}; use rustc_span::{ExpnKind, MacroKind}; use crate::clean::inline::{load_attrs, merge_attrs}; @@ -89,14 +88,12 @@ impl CfgPropagator<'_, '_> { // We also need to merge an item attributes with its parent's in case it's a macro with // the `#[macro_export]` attribute, because it might not be defined at crate root. else if matches!(item.kind, ItemKind::MacroItem(_, _)) - && item.inner.attrs.other_attrs.iter().any(|attr| { - matches!( - attr, - rustc_hir::Attribute::Parsed( - rustc_hir::attrs::AttributeKind::MacroExport { .. } - ) - ) - }) + && item + .inner + .attrs + .other_attrs + .iter() + .any(|attr| matches!(attr, Attribute::Parsed(AttributeKind::MacroExport { .. }))) { for parent_def_id in &item.cfg_parent_ids_for_detached_item(self.cx.tcx) { let mut parent_attrs = Vec::new(); diff --git a/src/librustdoc/passes/propagate_stability.rs b/src/librustdoc/passes/propagate_stability.rs index 9afde1e6195e7..8aa24e0474890 100644 --- a/src/librustdoc/passes/propagate_stability.rs +++ b/src/librustdoc/passes/propagate_stability.rs @@ -6,8 +6,8 @@ //! [`core::error`] module is marked as stable since 1.81.0, so we want to show //! [`core::error::Error`] as stable since 1.81.0 as well. +use rustc_attr_ir::{Stability, StabilityLevel}; use rustc_hir::def_id::CRATE_DEF_ID; -use rustc_hir::{Stability, StabilityLevel}; use crate::clean::{Crate, Item, ItemId, ItemKind}; use crate::core::DocContext; @@ -37,9 +37,9 @@ impl DocFolder for StabilityPropagator<'_, '_> { matches!( self.cx.tcx.hir_node(hir_id), rustc_hir::Node::Item(rustc_hir::Item { - kind: rustc_hir::ItemKind::Use(_, rustc_hir::UseKind::Glob), + kind: rustc_hir::ItemKind::Use(tree), .. - }) + }) | rustc_hir::Node::NestedUseTree(tree) if matches!(tree.kind, rustc_hir::UseKind::Glob) ) }); let own_stability = if let Some(item_stab) = item_stability diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index ae5a76545eefb..df55626ffb05d 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -4,19 +4,18 @@ use std::mem; use rustc_ast::attr::AttributeExt; +use rustc_attr_ir::{DocInline, find_attr}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; -use rustc_hir as hir; -use rustc_hir::attrs::DocInline; use rustc_hir::def::{DefKind, MacroKinds, Res}; use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId, LocalDefIdSet}; use rustc_hir::intravisit::{Visitor, walk_body, walk_item}; -use rustc_hir::{Node, find_attr}; +use rustc_hir::{self as hir, HirId, Node}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; use rustc_span::Span; -use rustc_span::def_id::{CRATE_DEF_ID, LOCAL_CRATE}; +use rustc_span::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalModId}; use rustc_span::symbol::{Symbol, kw}; -use tracing::debug; +use tracing::{debug, instrument, trace}; use crate::clean::reexport_chain; use crate::clean::utils::{inherits_doc_hidden, should_ignore_res}; @@ -119,6 +118,16 @@ fn def_id_to_path(tcx: TyCtxt<'_>, did: DefId) -> Vec { std::iter::once(crate_name).chain(relative).collect() } +#[derive(Copy, Clone)] +enum GlobMode { + // Globs and everything else + Everything, + // Skip all globs + NoGlob, + // Skip all items except for globs + Only, +} + pub(crate) struct RustdocVisitor<'a, 'tcx> { cx: &'a mut core::DocContext<'tcx>, view_item_stack: LocalDefIdSet, @@ -129,6 +138,7 @@ pub(crate) struct RustdocVisitor<'a, 'tcx> { modules: Vec>, is_importable_from_parent: bool, inside_body: bool, + glob_mode: GlobMode, } impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { @@ -153,6 +163,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { modules: vec![om], is_importable_from_parent: true, inside_body: false, + glob_mode: GlobMode::Everything, } } @@ -202,31 +213,31 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { /// This method will go through the given module items in two passes: /// 1. The items which are not glob imports/reexports. /// 2. The glob imports/reexports. + #[instrument(level = "debug", skip(self))] fn visit_mod_contents(&mut self, def_id: LocalDefId, m: &'tcx hir::Mod<'tcx>) { - debug!("Going through module {m:?}"); // Keep track of if there were any private modules in the path. let orig_inside_public_path = self.inside_public_path; self.inside_public_path &= self.cx.tcx.local_visibility(def_id).is_public(); // Reimplementation of `walk_mod` because we need to do it in two passes (explanations in // the second loop): + let old_glob_mode = mem::replace(&mut self.glob_mode, GlobMode::NoGlob); for &i in m.item_ids { let item = self.cx.tcx.hir_item(i); - if !matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) { - self.visit_item(item); - } + self.visit_item(item); } + self.glob_mode = GlobMode::Only; for &i in m.item_ids { let item = self.cx.tcx.hir_item(i); // To match the way import precedence works, visit glob imports last. // Later passes in rustdoc will de-duplicate by name and kind, so if glob- // imported items appear last, then they'll be the ones that get discarded. - if matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) { - self.visit_item(item); + if matches!(item.kind, hir::ItemKind::Use(..)) { + self.visit_item_inner(item, None, None); } } + self.glob_mode = old_glob_mode; self.inside_public_path = orig_inside_public_path; - debug!("Leaving module {m:?}"); } /// Tries to resolve the target of a `pub use` statement and inlines the @@ -238,6 +249,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { /// and follows different rules. /// /// Returns `true` if the target has been inlined. + #[instrument(level = "debug", skip(self), ret)] fn maybe_inline_local( &mut self, def_id: LocalDefId, @@ -246,9 +258,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { please_inline: bool, import_id: Option, ) -> bool { - debug!("maybe_inline_local (renamed: {renamed:?}) res: {res:?}"); - if renamed == Some(kw::Underscore) { + debug!("not inlining `_` reexports"); // We never inline `_` reexports. return false; } @@ -259,6 +270,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { let tcx = self.cx.tcx; let Some(ori_res_did) = res.opt_def_id() else { + debug!("no resolution"); return false; }; @@ -268,16 +280,18 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { let is_no_inline = find_attr!( use_attrs, Doc(d) - if d.inline.first().is_some_and(|(inline, _)| *inline == DocInline::NoInline) + if d.inline.is_some_and(|(inline, _)| inline == DocInline::NoInline) ) || (document_hidden && use_attrs.iter().any(|attr| attr.is_doc_hidden())); if is_no_inline { + debug!("doc::no_inline or doc::hidden"); return false; } let is_glob = renamed.is_none(); let is_hidden = !document_hidden && tcx.is_doc_hidden(ori_res_did); + debug!(?is_hidden, ?is_glob); let Some(res_did) = ori_res_did.as_local() else { // For cross-crate impl inlining we need to know whether items are // reachable in documentation -- a previously unreachable item can be @@ -301,10 +315,12 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { }; let is_private = !self.cx.cache.effective_visibilities.is_directly_public(tcx, ori_res_did); + debug!(?is_private); let item = tcx.hir_node_by_def_id(res_did); if !please_inline { let inherits_hidden = !document_hidden && inherits_doc_hidden(tcx, res_did, None); + debug!(?inherits_hidden); // Only inline if requested or if the item would otherwise be stripped. if (!is_private && !inherits_hidden) || ( is_hidden && @@ -328,23 +344,30 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { return false; } + trace!(?item); + let inlined = match item { // Bang macros are handled a bit on their because of how they are handled by the // compiler. If they have `#[doc(hidden)]` and the re-export doesn't have // `#[doc(inline)]`, then we don't inline it. - Node::Item(_) if is_bang_macro && !please_inline && !is_glob && is_hidden => { + Node::NestedUseTree(..) | Node::Item(_) + if is_bang_macro && !please_inline && !is_glob && is_hidden => + { return false; } Node::Item(&hir::Item { kind: hir::ItemKind::Mod(_, m), .. }) if is_glob => { let prev = mem::replace(&mut self.inlining, true); + let prev_glob = mem::replace(&mut self.glob_mode, GlobMode::Everything); for &i in m.item_ids { let i = tcx.hir_item(i); self.visit_item_inner(i, None, Some(import_id.unwrap_or(def_id))); } + self.glob_mode = prev_glob; self.inlining = prev; true } Node::Item(it) if !is_glob => { + debug!("inlining item"); let prev = mem::replace(&mut self.inlining, true); self.visit_item_inner(it, renamed, Some(import_id.unwrap_or(def_id))); self.inlining = prev; @@ -369,6 +392,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { /// /// This function takes into account the entire re-export `use` chain, so it needs the /// ID of the "leaf" `use` and the ID of the "root" item. + #[instrument(level = "debug", skip(self), ret)] fn reexport_public_and_not_hidden( &self, import_def_id: LocalDefId, @@ -384,6 +408,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { .map(|id| id.expect_local()) .nth(1) .unwrap_or(target_def_id); + debug!(?item_def_id); + item_def_id != import_def_id && self.cx.cache.effective_visibilities.is_directly_public(tcx, item_def_id.to_def_id()) && !tcx.is_doc_hidden(item_def_id) @@ -404,6 +430,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { } #[inline] + #[instrument(level = "debug", skip(self))] fn add_to_current_mod( &mut self, item: &'tcx hir::Item<'_>, @@ -425,31 +452,24 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { renamed = None; } let key = (item.owner_id.def_id, renamed); - if let Some(import_id) = import_id { - self.modules - .last_mut() - .unwrap() - .items - .entry(key) - .and_modify(|v| v.import_ids.push(import_id)) - .or_insert_with(|| ItemEntry { item, renamed, import_ids: vec![import_id] }); - } else { - self.modules - .last_mut() - .unwrap() - .items - .insert(key, ItemEntry { item, renamed, import_ids: Vec::new() }); - } + self.modules + .last_mut() + .unwrap() + .items + .entry(key) + .or_insert_with(|| ItemEntry { item, renamed, import_ids: vec![] }) + .import_ids + .extend(import_id); } } + #[instrument(level = "debug", skip(self))] fn visit_item_inner( &mut self, item: &'tcx hir::Item<'_>, renamed: Option, import_id: Option, ) { - debug!("visiting item {item:?}"); if self.inside_body { // Only impls can be "seen" outside a body. For example: // @@ -486,53 +506,16 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { // If we're inlining, skip private items. _ if self.inlining && !is_pub => {} hir::ItemKind::GlobalAsm { .. } => {} - hir::ItemKind::Use(_, hir::UseKind::ListStem) => {} - hir::ItemKind::Use(path, kind) => { - for res in path.res.present_items() { - // Struct and variant constructors and proc macro stubs always show up alongside - // their definitions, we've already processed them so just discard these. - if should_ignore_res(res) { - continue; - } - - let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(item.owner_id.def_id)); - - // If there was a private module in the current path then don't bother inlining - // anything as it will probably be stripped anyway. - if is_pub && self.inside_public_path { - let please_inline = if let Some(res_did) = res.opt_def_id() - && matches!(tcx.def_kind(res_did), DefKind::Macro(MacroKinds::BANG)) - { - crate::clean::macro_reexport_is_inline( - tcx, - item.owner_id.def_id, - res_did, - ) - } else { - find_attr!( - attrs, - Doc(d) - if d.inline.first().is_some_and(|(inline, _)| *inline == DocInline::Inline) - ) - }; - let ident = match kind { - hir::UseKind::Single(ident) => Some(ident.name), - hir::UseKind::Glob => None, - hir::UseKind::ListStem => unreachable!(), - }; - if self.maybe_inline_local( - item.owner_id.def_id, - res, - ident, - please_inline, - import_id, - ) { - debug!("Inlining {:?}", item.owner_id.def_id); - continue; - } - } - self.add_to_current_mod(item, renamed, import_id); - } + hir::ItemKind::Use(ref tree) => { + self.visit_use_inner( + item, + renamed, + import_id, + is_pub, + item.owner_id.def_id, + item.hir_id(), + tree, + ); } hir::ItemKind::Macro(_, macro_def, _) => { // `#[macro_export] macro_rules!` items are handled separately in `visit()`, @@ -586,6 +569,66 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { } } + #[instrument(level = "debug", skip(self))] + fn visit_use_inner( + &mut self, + item: &'tcx hir::Item<'tcx>, + renamed: Option, + import_id: Option, + is_pub: bool, + def_id: LocalDefId, + hir_id: HirId, + tree: &hir::UseTree<'tcx>, + ) { + let tcx = self.cx.tcx; + for res in tree.prefix.res.present_items() { + // Struct and variant constructors and proc macro stubs always show up alongside + // their definitions, we've already processed them so just discard these. + if should_ignore_res(res) { + continue; + } + + let attrs = tcx.hir_attrs(hir_id); + + // If there was a private module in the current path then don't bother inlining + // anything as it will probably be stripped anyway. + if is_pub && self.inside_public_path { + let please_inline = if let Some(res_did) = res.opt_def_id() + && matches!(tcx.def_kind(res_did), DefKind::Macro(MacroKinds::BANG)) + { + crate::clean::macro_reexport_is_inline(tcx, def_id, res_did) + } else { + find_attr!( + attrs, + Doc(d) + if d.inline.is_some_and(|(inline, _)| inline == DocInline::Inline) + ) + }; + let ident = match (tree.kind, self.glob_mode) { + (hir::UseKind::Single(ident), GlobMode::NoGlob | GlobMode::Everything) => { + Some(ident.name) + } + (hir::UseKind::Glob, GlobMode::Only | GlobMode::Everything) => None, + (hir::UseKind::Single(_), GlobMode::Only) + | (hir::UseKind::Glob, GlobMode::NoGlob) => continue, + (hir::UseKind::Nested { items }, _) => { + for (tree, hir_id, def_id) in items { + self.visit_use_inner( + item, renamed, import_id, is_pub, *def_id, *hir_id, tree, + ); + } + continue; + } + }; + if self.maybe_inline_local(def_id, res, ident, please_inline, import_id) { + debug!("Inlining {:?}", def_id); + continue; + } + } + self.add_to_current_mod(item, renamed, import_id); + } + } + fn visit_foreign_item_inner( &mut self, item: &'tcx hir::ForeignItem<'_>, @@ -627,6 +670,7 @@ impl<'tcx> Visitor<'tcx> for RustdocVisitor<'_, 'tcx> { self.cx.tcx } + #[instrument(level = "debug", skip(self))] fn visit_item(&mut self, i: &'tcx hir::Item<'tcx>) { self.visit_item_inner(i, None, None); let new_value = self.is_importable_from_parent @@ -642,11 +686,16 @@ impl<'tcx> Visitor<'tcx> for RustdocVisitor<'_, 'tcx> { self.is_importable_from_parent = prev; } - fn visit_mod(&mut self, _: &hir::Mod<'tcx>, _: Span, _: hir::HirId) { + fn visit_mod(&mut self, _: &hir::Mod<'tcx>, _: Span, _: LocalModId) { // Handled in `visit_item_inner` } - fn visit_use(&mut self, _: &hir::UsePath<'tcx>, _: hir::HirId) { + fn visit_use( + &mut self, + _: &hir::UseTree<'tcx>, + _: hir::HirId, + _: rustc_span::def_id::LocalDefId, + ) { // Handled in `visit_item_inner` } diff --git a/src/llvm-project b/src/llvm-project index 1b9c0d5ff9bbe..aab11ee0ea5ea 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit 1b9c0d5ff9bbe7634aead059efe6b11a7eeba145 +Subproject commit aab11ee0ea5ea9a1d462ad0bc1fae9d9bb643019 diff --git a/src/stage0 b/src/stage0 index 3426edfafbf16..65357a133fe09 100644 --- a/src/stage0 +++ b/src/stage0 @@ -13,605 +13,588 @@ nightly_branch=main # All changes below this comment will be overridden the next time the # tool is executed. -compiler_channel_manifest_hash=1612da194f5a1c699d7a20519341ed9aa62e29f28bf3f303ab6801e8e266b103 -compiler_git_commit_hash=cbae9b4cae2b108f6a3d18cfe6075714bb739463 -compiler_date=2026-08-30 +compiler_channel_manifest_hash=748739f565e7977153cc87b1bf606072e31088a751ed351c1a076cfc4ebd04ed +compiler_git_commit_hash=e3feeb59cd1bdd011a2bfcc0747b55125d9b8527 +compiler_date=2026-09-29 compiler_version=beta -rustfmt_channel_manifest_hash=d950b3b494cde6b8448964e1795a86530bff7ee5e729a48ed6341c6ec3ee5ad1 -rustfmt_git_commit_hash=fd7ed57dfd3bdebb745a1d8158638727b0e7047a -rustfmt_date=2026-08-30 +rustfmt_channel_manifest_hash=4ac7009a2d787fa804178e5a7a067b1f828f85623a160ef7cc417d378814ef3e +rustfmt_git_commit_hash=c1070d69382b8d2f2eb65119c738a77d9e324c9e +rustfmt_date=2026-09-29 rustfmt_version=nightly -dist/2026-08-30/rustc-beta-aarch64-apple-darwin.tar.gz=63da74142f0e917ccbc13cf182bae60d9ec3d545b712ac6150fe8b4e633a2c7a -dist/2026-08-30/rustc-beta-aarch64-apple-darwin.tar.xz=0c20c4730544923b2ba9ab4ccf98cd22db6759d1d6cc2e5b8c99662b953163ac -dist/2026-08-30/rustc-beta-aarch64-pc-windows-gnullvm.tar.gz=1d67701059998ff993a42c3781b2b735bf537d9db46bdb0b94e6cd9313f69b0c -dist/2026-08-30/rustc-beta-aarch64-pc-windows-gnullvm.tar.xz=093d13372b7bc06246e63a1e18aac59464e71939b951e14ae0ce500f467be92c -dist/2026-08-30/rustc-beta-aarch64-pc-windows-msvc.tar.gz=d1a51ad8e37b8f482c994527f7062a4b3ac9d1734be81b677a5e270ff121747e -dist/2026-08-30/rustc-beta-aarch64-pc-windows-msvc.tar.xz=f019195e5c72d6b6e699416d8851555965ccf5f108bddae999e9bf37fec19693 -dist/2026-08-30/rustc-beta-aarch64-unknown-freebsd.tar.gz=f9b8e19c81bb9cbb2c75d841f043b084aa9c3c239b359c761d02d255c16ec928 -dist/2026-08-30/rustc-beta-aarch64-unknown-freebsd.tar.xz=fd02f9f057e75b2f6e5c763dcad6dfd9ec39c8e03a40f2cffb82823edbb2da26 -dist/2026-08-30/rustc-beta-aarch64-unknown-linux-gnu.tar.gz=1f44bbb85ebb6d4c218016661afa545a8e038f1b937e49f81bebd915ba10c608 -dist/2026-08-30/rustc-beta-aarch64-unknown-linux-gnu.tar.xz=e2e2bf7f32709df924efe7b5f530781ea1428ce163e92270a3ee476b7b3a3d1a -dist/2026-08-30/rustc-beta-aarch64-unknown-linux-musl.tar.gz=5452dc6c27dab927dab0afb5be5f300f1cc9af3c7ccfb88dd605227851fd65d7 -dist/2026-08-30/rustc-beta-aarch64-unknown-linux-musl.tar.xz=1f7c588a39f096f7578fc6bca93f0c85ae40976cf26a51913070119a8b910e07 -dist/2026-08-30/rustc-beta-aarch64-unknown-linux-ohos.tar.gz=8aa1b28d25e4c7a21d3fcb85254d1e2c169ff55b3536a290edacecb6c051ab4b -dist/2026-08-30/rustc-beta-aarch64-unknown-linux-ohos.tar.xz=27208aaaebbcea8e819d7d9fbf20dd994cc24f727dbee339d34e6a995e2b9864 -dist/2026-08-30/rustc-beta-arm-unknown-linux-gnueabi.tar.gz=73f04b6601665531d79900f59a8f92f3e7a90f196d62e1ea10e20a3e6ca6f8f4 -dist/2026-08-30/rustc-beta-arm-unknown-linux-gnueabi.tar.xz=64b0fb006d621f6003527b41e9e31f9141f3df09a0dfe979ee68054d1346016e -dist/2026-08-30/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz=19e1f450d4aa8d169898170c9bcd2ab7fba9d3453a631c57a5148236805f101e -dist/2026-08-30/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz=f7715866e5e05360279365dc39eefc96436a53f429cd5b283662a81634f7371f -dist/2026-08-30/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz=2e765b6a53cabb0077ad4f8f563862d115c9f152165dc92615a727bdf0d2998b -dist/2026-08-30/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz=09a111ae1c8861099a39d495fcea86c65780bb2b7a6c7bc19fff56915b4f11a5 -dist/2026-08-30/rustc-beta-i686-pc-windows-gnu.tar.gz=02d8f06789c7f48a19e31ddc7c3f8dab995197f7857d410f6df60dad1067b8cf -dist/2026-08-30/rustc-beta-i686-pc-windows-gnu.tar.xz=91bf9711b7551cb3caf6660824ad5c04f27ed9a0fd086211b3aed56628311d45 -dist/2026-08-30/rustc-beta-i686-pc-windows-msvc.tar.gz=70525d3d60e2ebddb9e7b3341314caa0df4e34bdfcb47bae46927fa13a6ed327 -dist/2026-08-30/rustc-beta-i686-pc-windows-msvc.tar.xz=59d9ecfad856f135df42afd957024ede0a3eb263a7ec4ffbf517e16696054d83 -dist/2026-08-30/rustc-beta-i686-unknown-linux-gnu.tar.gz=317c5379344a6e473a29e432d4aab7399ff3dbe86a72dd279c0c82c495d98c65 -dist/2026-08-30/rustc-beta-i686-unknown-linux-gnu.tar.xz=af6b78184886cff946f408dcd27ce4332fe39b514c49a6c285aa12ee8a895194 -dist/2026-08-30/rustc-beta-loongarch64-unknown-linux-gnu.tar.gz=0894c6b942bad197888f596f7d518595f5db700cc21089b39d9b923c0bc2c902 -dist/2026-08-30/rustc-beta-loongarch64-unknown-linux-gnu.tar.xz=d10527ec270b021405f4c7c8853296837efa3d4f1804d7e9aaf22c7f76b3ac8d -dist/2026-08-30/rustc-beta-loongarch64-unknown-linux-musl.tar.gz=82edc27720b2001ef4ccfaa8638c839bd76ca9294723516749544a794a963f76 -dist/2026-08-30/rustc-beta-loongarch64-unknown-linux-musl.tar.xz=efa4d683554b36de9d19d1aa6a6bcd39223e4b8726d5108895071169f976abf6 -dist/2026-08-30/rustc-beta-powerpc-unknown-linux-gnu.tar.gz=453f91a3f885305cdc98ae91bb06fe0cd3bc2f2cad2db05ffc51a9957c5d7dd5 -dist/2026-08-30/rustc-beta-powerpc-unknown-linux-gnu.tar.xz=acb98b89f961c5b309f54e6dd647f68c105f0f95aabbddbe6363055ab842fb4f -dist/2026-08-30/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz=791dc39fa441ecbd5e976da99d988796ad5382f62a85856722dba4fdc4f81491 -dist/2026-08-30/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz=005c0e756b757b0d49b6ec9777a9997799d5328f826a4d168a67994591323037 -dist/2026-08-30/rustc-beta-powerpc64-unknown-linux-musl.tar.gz=d3b6748b0471837b8f2f7118e739f4870f2875fb061253c71dbeadcb5a88253d -dist/2026-08-30/rustc-beta-powerpc64-unknown-linux-musl.tar.xz=9c681f8f3efd7f8130ee8c2a40c39d83f6bd58858e134c8df8bba2b65fc3da1a -dist/2026-08-30/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz=3924195349e7b1b31f6334f0d07d66ca4f3c423fb57b8bdded25fa1746b4d1b1 -dist/2026-08-30/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz=3f0decdada02b892f63920829a4af3541f149d44b162b3a98d2aa006f2bf47cb -dist/2026-08-30/rustc-beta-powerpc64le-unknown-linux-musl.tar.gz=c8c0a5745dfcecc41a52876a9658badf7a35417173f143f50a6d304eb99c4dd6 -dist/2026-08-30/rustc-beta-powerpc64le-unknown-linux-musl.tar.xz=47992ff2ff6725eac8e6322c1d76df258a54fc928cbcc8d175acfe16b5a4058f -dist/2026-08-30/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz=91747193505f48d0eba8653bf14855c81999afefb632207eac3d7d6f355fd2bd -dist/2026-08-30/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz=95f5717569d3b07ed6808c82a5a3849284ce9b404792806fc06356a11ae54895 -dist/2026-08-30/rustc-beta-riscv64gc-unknown-linux-musl.tar.gz=ff705b94a62127be2bbbaf20a036fd4d9c3fcc23b852bf50b814120637eb28d9 -dist/2026-08-30/rustc-beta-riscv64gc-unknown-linux-musl.tar.xz=da5837ddd0b7ab0b10be836fcf757ce117bbe54e57d7ce696c38e8779be64328 -dist/2026-08-30/rustc-beta-s390x-unknown-linux-gnu.tar.gz=7282b673f837c2998f70a47e44c67d2d88d75f55be6c363a43706f37acde0676 -dist/2026-08-30/rustc-beta-s390x-unknown-linux-gnu.tar.xz=2890fbd6092fac747d44f525623e77c87d5924a878d20be664e20068f729f01c -dist/2026-08-30/rustc-beta-sparcv9-sun-solaris.tar.gz=b1526015253711f6b54e5e2081e5503461ca94e66edffa3226899c357350475d -dist/2026-08-30/rustc-beta-sparcv9-sun-solaris.tar.xz=dc7adedded9d7c4022057709f33de27a6ace3d08a264f17bee7c8e1a92e975b8 -dist/2026-08-30/rustc-beta-x86_64-apple-darwin.tar.gz=9f06cb6793158449b822f07b86a2a57c73d12ad05dee33d188027ae20559f197 -dist/2026-08-30/rustc-beta-x86_64-apple-darwin.tar.xz=d506f9e4bfe7d3d223a88392d5416ac0203924e31b6e3f68a32adc1578d74e0b -dist/2026-08-30/rustc-beta-x86_64-pc-solaris.tar.gz=60825508456e01c09e82986c2c3574dd954e2fbdb16f20db1751437eb2294414 -dist/2026-08-30/rustc-beta-x86_64-pc-solaris.tar.xz=7aa295d6f6ab2eca3d72e1703bc533ac01908dd5080a859c7ab01003d0634d9d -dist/2026-08-30/rustc-beta-x86_64-pc-windows-gnu.tar.gz=01bda7ca1de22ac2881f0da5bd1db0c31500eab3e4b5273d16b581193788e32c -dist/2026-08-30/rustc-beta-x86_64-pc-windows-gnu.tar.xz=65229fa0b642f511ecae17f456d5a67aac0338adcb07ad0746eaec97cd0b2d25 -dist/2026-08-30/rustc-beta-x86_64-pc-windows-gnullvm.tar.gz=90021d8fc771da2febbc00152c6dd7c3b58845a9a522f74573a2acab9f55a016 -dist/2026-08-30/rustc-beta-x86_64-pc-windows-gnullvm.tar.xz=cec531ec82cd3630818985aa6e2e2afdd553e195c87c26873b914c2a159929a2 -dist/2026-08-30/rustc-beta-x86_64-pc-windows-msvc.tar.gz=2d41c0c375f565315155c80e0d241bd8ea132e994b5d87ded8a21302f29d5ab4 -dist/2026-08-30/rustc-beta-x86_64-pc-windows-msvc.tar.xz=6d82171d5e4453c466313f6541fbb62dc74d0c7863651413c687b7f4c27b2a4e -dist/2026-08-30/rustc-beta-x86_64-unknown-freebsd.tar.gz=0509d78b60e4743b1953f12a70a91adc7ef48cf886826dcb16e87695999c7699 -dist/2026-08-30/rustc-beta-x86_64-unknown-freebsd.tar.xz=a5714f58f8651919a51c8ce28536ce83f86fd7ac72bf067dd97189c1d389c07b -dist/2026-08-30/rustc-beta-x86_64-unknown-illumos.tar.gz=883ab0b98ded267ff1b7a8fd0d9ffc7529ff6a5e6b1e73c2168faf1c90d92ef0 -dist/2026-08-30/rustc-beta-x86_64-unknown-illumos.tar.xz=58178ca6b6c9ced54a0f7991fbd94f6d92c32eacffbf774444429ea46b1f58d3 -dist/2026-08-30/rustc-beta-x86_64-unknown-linux-gnu.tar.gz=2a164be1423d98988d4c3e9c7635af9150a961b5fb3d2052517987266978fc65 -dist/2026-08-30/rustc-beta-x86_64-unknown-linux-gnu.tar.xz=66764b74fe55573ea5884ac7ac98fd7e5bff95f4ee0f389e3f5ee07eb75e1623 -dist/2026-08-30/rustc-beta-x86_64-unknown-linux-musl.tar.gz=4b7216393d167874d030e24efd637717f8451c89506c4964220632556522e881 -dist/2026-08-30/rustc-beta-x86_64-unknown-linux-musl.tar.xz=645c594846ac499aa29bef08dacf4402f2479413fb98ab255419bbcbaa39440b -dist/2026-08-30/rustc-beta-x86_64-unknown-netbsd.tar.gz=a1044a20bd4e0894f94d0d2a2f2601bd9e8315be561bb196360950fc959c9ab0 -dist/2026-08-30/rustc-beta-x86_64-unknown-netbsd.tar.xz=c90e60e75e4d2420bb58df22483f028c92eead983b2a5f73e1c87f66196a80d1 -dist/2026-08-30/rust-std-beta-aarch64-apple-darwin.tar.gz=9f02f6d297a31bb6a6bca2648f7e6e381d386365ec2548619f200fce3d1d7109 -dist/2026-08-30/rust-std-beta-aarch64-apple-darwin.tar.xz=d8f4620f3672cae11fdb841a86d44215aeba2e656244286056f7742e966797e3 -dist/2026-08-30/rust-std-beta-aarch64-apple-ios.tar.gz=547510c49fc9c4d993ca76d7d2a38e6e0fc791a8b838fb30e246df78c3480ce8 -dist/2026-08-30/rust-std-beta-aarch64-apple-ios.tar.xz=b54fc751131000c13c58b2863ed238a6d6f750fdb1b1a5e6e1110e05bedc5f7e -dist/2026-08-30/rust-std-beta-aarch64-apple-ios-macabi.tar.gz=c2c93323193b2f6d796934916c002c0d98203745c98458678d70eaff2546566d -dist/2026-08-30/rust-std-beta-aarch64-apple-ios-macabi.tar.xz=ab88ae96f66a15ce8fb00296e8f7f292d31f941e89455529ee7544220187cdec -dist/2026-08-30/rust-std-beta-aarch64-apple-ios-sim.tar.gz=dff0cf3e961505ada60f6fad68cc5f5881b725bb7ef0f0d23b6ae4fb8722f5a1 -dist/2026-08-30/rust-std-beta-aarch64-apple-ios-sim.tar.xz=b6c00ebacb1758e8f3b830a054df06d120e270c4b2a0ef88866b06be2bb95e53 -dist/2026-08-30/rust-std-beta-aarch64-apple-tvos.tar.gz=7f6c1cb3f8b5870449fdbfe1139f27c93b810b1f52da01a93abb8c8656ecc14d -dist/2026-08-30/rust-std-beta-aarch64-apple-tvos.tar.xz=e40b47e96b7ad06f7e13fa0272b4b8014eb4799bd4c7237f35df513eb7eed7cf -dist/2026-08-30/rust-std-beta-aarch64-apple-tvos-sim.tar.gz=158c8066d75a93d770ca620ffb64b92d39630b401a909f17e99765b9871ec25c -dist/2026-08-30/rust-std-beta-aarch64-apple-tvos-sim.tar.xz=635d4e479727b51957b0bbfa14ac5ce7b3282ae146aa04a92ff28963cbf9635f -dist/2026-08-30/rust-std-beta-aarch64-apple-visionos.tar.gz=28f4a6e679995ac6b4037f29c9cdc161183012edb39e484d31ce24ef044128d5 -dist/2026-08-30/rust-std-beta-aarch64-apple-visionos.tar.xz=b3d6255fb1e052747ddf5c3b3484040cbf03dbc882e92cde98c8656442f84cc5 -dist/2026-08-30/rust-std-beta-aarch64-apple-visionos-sim.tar.gz=09facc091183c715daf223d9bd158d6333c6e3f837627bf318f2c0e1be0851b9 -dist/2026-08-30/rust-std-beta-aarch64-apple-visionos-sim.tar.xz=4943e00b8e0496786ec9c2bfa526da1f44aa162f874f7084f51c95c3ec3a44b1 -dist/2026-08-30/rust-std-beta-aarch64-apple-watchos.tar.gz=ba3e478886aa3b34101334e3ab9a3543a0f5e3d44f44efa48bc7b56c14a3951a -dist/2026-08-30/rust-std-beta-aarch64-apple-watchos.tar.xz=2e4e7035ab8464d8ba3775b66433b1888044394bf18343bbbb16304ada735368 -dist/2026-08-30/rust-std-beta-aarch64-apple-watchos-sim.tar.gz=7f8ffe07832cc648c8be54b79bc6923594a34e254245e07fe706fccd52b28195 -dist/2026-08-30/rust-std-beta-aarch64-apple-watchos-sim.tar.xz=343aef2c9816bd2007837916a8ee5101c1b0b97692066c5cf844a7230a42a346 -dist/2026-08-30/rust-std-beta-aarch64-linux-android.tar.gz=94b3a90192944d2483e2c4e3bf02a03e43b00540c16873d869a75b6e6d7a675e -dist/2026-08-30/rust-std-beta-aarch64-linux-android.tar.xz=e26b35a45e9d2331b4ee37f6f9b70f927dc89cd4b956ec76c556577c7b0fa1e4 -dist/2026-08-30/rust-std-beta-aarch64-pc-windows-gnullvm.tar.gz=24eef069090edc04f787002c971bccbc328775b54d98e3ab63b247a4c1e0e99c -dist/2026-08-30/rust-std-beta-aarch64-pc-windows-gnullvm.tar.xz=88cf54b25e9a76e303413bb6d32e064d784e6b83dc282a42d45983d57842ca08 -dist/2026-08-30/rust-std-beta-aarch64-pc-windows-msvc.tar.gz=726999c2b5dbec5ca64e56abbce4d246dcb265d709df96d48a4653e863531ac7 -dist/2026-08-30/rust-std-beta-aarch64-pc-windows-msvc.tar.xz=22e04ac2cd44daff5e5873f8492a1a3345f83034021923ff223cb3905c81e16b -dist/2026-08-30/rust-std-beta-aarch64-unknown-freebsd.tar.gz=9593926d80f8b3adfa59b2073f48234001d70c08225d03e0f128f0188d2878c3 -dist/2026-08-30/rust-std-beta-aarch64-unknown-freebsd.tar.xz=f43da9dc7737f5881391e92013f773c6fc3888dece33eac141797617d590ecc1 -dist/2026-08-30/rust-std-beta-aarch64-unknown-fuchsia.tar.gz=9d177fcaf3805830b0b0a1661d3306509f5620195877185b0630193220a95feb -dist/2026-08-30/rust-std-beta-aarch64-unknown-fuchsia.tar.xz=b1c017c1cc05c334f74dbf747d29e692cbf4bdb9c08d5778067266d7816841f4 -dist/2026-08-30/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz=b443b40dbe16c1c0805778d34a652b16dd891e5d641ece48f988be00d4ae8c25 -dist/2026-08-30/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz=295de2ccd9c9ffaf6648e76a4bd358289fc78ed0ed162d38a69632a6bfae240a -dist/2026-08-30/rust-std-beta-aarch64-unknown-linux-musl.tar.gz=8fdf744594d28f7425df836f3ad3289ea052e26aa98e0d9659c8f03a85d9713a -dist/2026-08-30/rust-std-beta-aarch64-unknown-linux-musl.tar.xz=e87932897c88f458d35e53873b2c019f2cebbf11e634689f8e9b97c9320adcd3 -dist/2026-08-30/rust-std-beta-aarch64-unknown-linux-ohos.tar.gz=33e0b8fbe21128edbaa3c2da91fac703107964f818624dd87a03179c9a6b4a62 -dist/2026-08-30/rust-std-beta-aarch64-unknown-linux-ohos.tar.xz=8c79addb67a5c8b8181fa25df85516bdc4fa69003c2f8c9e8a6c6c60eaba6920 -dist/2026-08-30/rust-std-beta-aarch64-unknown-none.tar.gz=39cafc68fb5044aa42fa274fc7c49bc210472d5cd97ed82cd09c98b73042f66d -dist/2026-08-30/rust-std-beta-aarch64-unknown-none.tar.xz=78c289e2984f4b99bb9caa4d61833dc94b0588be7aac2309c4c9cf5d75b56703 -dist/2026-08-30/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz=b6012253ae93c1a13ea4e8cab50873c206056fd9325d26de402e45669950994d -dist/2026-08-30/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz=d9fb182de0cffbb3a74fe309ba65d79323901da154e9766a88a569247f260d2a -dist/2026-08-30/rust-std-beta-aarch64-unknown-uefi.tar.gz=f54892f078518f520b32bf76a932cce2c450289cd70c826542477401109eb3dc -dist/2026-08-30/rust-std-beta-aarch64-unknown-uefi.tar.xz=672c8e38330262341209232c73bb600f2949b3d5a1f5c93d0a95e18539fb94de -dist/2026-08-30/rust-std-beta-arm-linux-androideabi.tar.gz=887d61d7764ea35bcb2fdd608940a0d3b0d888cd2b8c94f33d242ae71d2269d7 -dist/2026-08-30/rust-std-beta-arm-linux-androideabi.tar.xz=29f1dea85c828ae819c5ebca3d76dd451cf72e9498f9f207f3272162cc159cd1 -dist/2026-08-30/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz=b735d7f9cfac1d3a7ad22e0b79979f78aeded3c538dee686728ff9e8c64d6787 -dist/2026-08-30/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz=878c84e83139426b209f0ae84a898e005f499d38700f5d8446a6aa4ad81feabf -dist/2026-08-30/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz=bdaf3e6c1fba51bd37e7d23b547642885b97e3e66e3e5805f60ee03332e1548d -dist/2026-08-30/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz=26446f3d1d9e235e11ebe6e21e3c8bb23efe5e8d31671bf2d573af5e39ae7ba3 -dist/2026-08-30/rust-std-beta-arm-unknown-linux-musleabi.tar.gz=21a1317f24f27d3c567c08c8306f3c97a38dad185bde37229e7c279adb52875f -dist/2026-08-30/rust-std-beta-arm-unknown-linux-musleabi.tar.xz=51ad360481ab758d3023dc615351afba30cf99a162e3f207f5275b5995258412 -dist/2026-08-30/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz=57d7204c68646f489e1db251e69d5fbb329e71954100a9a58bc4eea5bd526c34 -dist/2026-08-30/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz=01b476bcd78cfc757d9687ad65cad8c15f4bc5bd2e3149a04314e1604257169a -dist/2026-08-30/rust-std-beta-arm64ec-pc-windows-msvc.tar.gz=3b94aba1d51f5a6f039a555122d358bdd8e8f3a1a13e3622842a56acd9e48765 -dist/2026-08-30/rust-std-beta-arm64ec-pc-windows-msvc.tar.xz=c814c2d7b7de9f97457932139960d40d869ae6783528a6e0d7edf3af0d34da2d -dist/2026-08-30/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz=2783998a08bf439cc4f6113a2d7f454791a8437bc9bf9b8d3a9d991bf830eddf -dist/2026-08-30/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz=bf7bb1f7e676b0f70786ba671a687701879a7b34f3ebee99e6faab139444c0ca -dist/2026-08-30/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz=0d39dc704bb80630048ca2b6b3706c4e9bbf8c16a1eb24c41e2c818e2c89222d -dist/2026-08-30/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz=98058d9812f279e9797e6e2163d706dfa6676647d6abd64efae52d9602978bfb -dist/2026-08-30/rust-std-beta-armv7-linux-androideabi.tar.gz=906e3d6b21ca38b6358a9c381aba7aa8e2894a2abe649d12dcef91cc5ab83dca -dist/2026-08-30/rust-std-beta-armv7-linux-androideabi.tar.xz=bd9c5e2dc8542dd0ebcdb53b40422d364aec9399b93b731879c5cada4947583b -dist/2026-08-30/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz=a4f1149965d18e306ae5fb29382daa6b0e17b1667d8eae08f0342267d7aa6857 -dist/2026-08-30/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz=58121e6668b93a7bd853dff02ff26fe0c4f5e721f23acb7f092cbdac35835a87 -dist/2026-08-30/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz=7e3ce83fea162bd4eab433bf35402ecc2099a46afeb66b86ea5e54f0b53a93d5 -dist/2026-08-30/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz=4737ebecd9ded896ce25f72c7d2397759bd9243d7399bd6955b90e3fdb4c27d2 -dist/2026-08-30/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz=26d2c719cb467295e3effaeae939241325d6fdfd6d32784e90b6c955cf294ffc -dist/2026-08-30/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz=d2ec9ee496d74deafee30846ce577333154d4c4221943d53129f3a4c8365118e -dist/2026-08-30/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz=60007d5b87b54b4f500ce686b1c6740ab7641dda28676a9c56df7b9a36c34141 -dist/2026-08-30/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz=1befc6a2e3b6d7e23bc132ad6fcf4e59934d386e2e467dbbc5ec425d628db846 -dist/2026-08-30/rust-std-beta-armv7-unknown-linux-ohos.tar.gz=6ee69a47a0ce552fb9b69edeca089004c84de1d380ea45554964be8dc5b854d7 -dist/2026-08-30/rust-std-beta-armv7-unknown-linux-ohos.tar.xz=6a594a15e7a1962f4417997d1b1fcdfe72a3786f019242567a915237a8f12112 -dist/2026-08-30/rust-std-beta-armv7a-none-eabi.tar.gz=af06b8910b044e0c804802c316904d53d0a6072d601353927bf5d733cbb10664 -dist/2026-08-30/rust-std-beta-armv7a-none-eabi.tar.xz=dda99a3c47b6fdaa000172e3a9f52876d168499a24e33716a1277a2c1d3e303b -dist/2026-08-30/rust-std-beta-armv7a-none-eabihf.tar.gz=42799d4c5f6b596b6f4c565358adacd2982433cf26f16bde99047f63b07ab97a -dist/2026-08-30/rust-std-beta-armv7a-none-eabihf.tar.xz=0cb9cfeae9a30ec848ef007dd9144398c66be3a755ffe94d9898f2da9057170c -dist/2026-08-30/rust-std-beta-armv7r-none-eabi.tar.gz=b248ea8f35a5557e53882f362ab00211f15124688f56e57474c66bece0bc5c77 -dist/2026-08-30/rust-std-beta-armv7r-none-eabi.tar.xz=9427d716b04a5b178788fead61b3b41abb4acf482bc3def3c1f9b3513243b156 -dist/2026-08-30/rust-std-beta-armv7r-none-eabihf.tar.gz=b8f8e1359af035a33afde195fe4c3de23412c950e216d1ecf6a80fe471f5eadc -dist/2026-08-30/rust-std-beta-armv7r-none-eabihf.tar.xz=1a8aa055f437a8e6dc258d60697f10664b2e28e74ee450d256fd38243730a101 -dist/2026-08-30/rust-std-beta-armv8r-none-eabihf.tar.gz=3b512dfdc3365c1e7d19e42755506a2aca6efa880d754d7838e69b19f6a8d607 -dist/2026-08-30/rust-std-beta-armv8r-none-eabihf.tar.xz=f12bca87642b53c9ef4672ad070913a411de0b772a1f4437b9a258a989d8298c -dist/2026-08-30/rust-std-beta-i586-unknown-linux-gnu.tar.gz=af08b969901c84b02a2501a667bd5cb89454a0817ba66c9fd9a00b03503c922c -dist/2026-08-30/rust-std-beta-i586-unknown-linux-gnu.tar.xz=ec871b92b19cef0f63baea61298575f50ee568046470d0bc12bfc7bee830e846 -dist/2026-08-30/rust-std-beta-i586-unknown-linux-musl.tar.gz=b6805cd6642b6b1de3e637581313cb26241526112114adca8157179948f967e4 -dist/2026-08-30/rust-std-beta-i586-unknown-linux-musl.tar.xz=5c2827bdd87154ce42ad9939dae78a022d3c3045f9b8698f60292e5ba0a0ff14 -dist/2026-08-30/rust-std-beta-i686-linux-android.tar.gz=c568edbc22aba9c2e2360bcf821e9c60894ef3836fc7846cff4c178b2d746fc9 -dist/2026-08-30/rust-std-beta-i686-linux-android.tar.xz=0bd654f6591c99d139bb83ab7e2699327697feddb12edec50bf4f834c0edcffc -dist/2026-08-30/rust-std-beta-i686-pc-windows-gnu.tar.gz=701a3087c3810884d3a83345d2ae8b3de6b1514fbf97bbed99a0421abf2fa2b1 -dist/2026-08-30/rust-std-beta-i686-pc-windows-gnu.tar.xz=b8e8bb023d34012e82d708a0d6e4a558c32f0e489b3eb381b88919aa554578fb -dist/2026-08-30/rust-std-beta-i686-pc-windows-gnullvm.tar.gz=a36eafa511ea809368df67889b2fc0b5a18ed7c2a1e9b21118b160f2b8a63a5b -dist/2026-08-30/rust-std-beta-i686-pc-windows-gnullvm.tar.xz=f1b0c50faf927e5ba97cf0533287cd132f1e3a2b9cb0338f7a56e1ed4311b0bb -dist/2026-08-30/rust-std-beta-i686-pc-windows-msvc.tar.gz=847c2c7aed2ca9f03bc2a8a46ce9b2e48e968e8bb9d947349f83ecde27464631 -dist/2026-08-30/rust-std-beta-i686-pc-windows-msvc.tar.xz=e49a11c577a0e860cfac07eaf0fe1a0f502ada16ec7c05c02d0c60293c8aff8d -dist/2026-08-30/rust-std-beta-i686-unknown-freebsd.tar.gz=c2435372ce00925ed17120ba1586b5d3dac6b4ce979e02698d87a74ad71a7a7e -dist/2026-08-30/rust-std-beta-i686-unknown-freebsd.tar.xz=113874727b99b497e5b346e908407f41b7b2f85cae272cc555dd3699fe8de8fb -dist/2026-08-30/rust-std-beta-i686-unknown-linux-gnu.tar.gz=78385dde39126b0eb524a3681bb8607c016110985ef0b2f2da00c86a5961d64d -dist/2026-08-30/rust-std-beta-i686-unknown-linux-gnu.tar.xz=52480e55a90a54d3174232b4b76f6aaef9055da0ed49733b19ea6c3b8a6db1ea -dist/2026-08-30/rust-std-beta-i686-unknown-linux-musl.tar.gz=350e1a7c2925462c4c2a8b796874f17fd0e284680f2dc2e930017b585ed661c0 -dist/2026-08-30/rust-std-beta-i686-unknown-linux-musl.tar.xz=fd62e7c69e1d0388aaeba25a9a65c8e327e60dbbc3f57948b9f7000981eb4172 -dist/2026-08-30/rust-std-beta-i686-unknown-uefi.tar.gz=41dd818f059414729cd8978a4b79995b765493885dd89a0587be88b83f844aa0 -dist/2026-08-30/rust-std-beta-i686-unknown-uefi.tar.xz=6fb74e7c1b4b20c8b28966e2ee89ab9b1208fb43f57d8f9fe91e5c5bdfa9e046 -dist/2026-08-30/rust-std-beta-loongarch32-unknown-none.tar.gz=5cf23bf844fd1157b9360303459beed6fb1ca77a2bdefd7d394a1d50385670f8 -dist/2026-08-30/rust-std-beta-loongarch32-unknown-none.tar.xz=d952d3f950b776c68528bba94c76a06fb2d55d363fcedeecb1155d6de3a31d3b -dist/2026-08-30/rust-std-beta-loongarch32-unknown-none-softfloat.tar.gz=1967abcc458e9f5efc46f052733490cde533ea074a3ddc0d2fcbf002be4ddc79 -dist/2026-08-30/rust-std-beta-loongarch32-unknown-none-softfloat.tar.xz=bcc63e232b44d653196104d93629ead81c19c1facd03d97207da744c353167d7 -dist/2026-08-30/rust-std-beta-loongarch64-unknown-linux-gnu.tar.gz=d8946832080fa2e13498bbe7e0cc5611f1c93da1a7b5e3b19c836d03fe784c15 -dist/2026-08-30/rust-std-beta-loongarch64-unknown-linux-gnu.tar.xz=fda84558d60dae4383d6556545bfd0f292f4ca7226db012948c3c2c32b514c6f -dist/2026-08-30/rust-std-beta-loongarch64-unknown-linux-musl.tar.gz=63e4aaee66fdf0444becbab59ecbd7a1955d27e5e7420507bfe78e482558a4e7 -dist/2026-08-30/rust-std-beta-loongarch64-unknown-linux-musl.tar.xz=6bc088d81bf425614dea2f31fb0bcf229566cfd912078ba997e272492f5dfeb8 -dist/2026-08-30/rust-std-beta-loongarch64-unknown-none.tar.gz=aed26cd5efdefbc5b636d3a31566c816eda9dde7061efff48b327254b2fc9653 -dist/2026-08-30/rust-std-beta-loongarch64-unknown-none.tar.xz=dd470e7c8ed1a37cd140161988578a31297de2f3739a1d0a1994618f61c4c290 -dist/2026-08-30/rust-std-beta-loongarch64-unknown-none-softfloat.tar.gz=be92ce71185bb0bf4d0b220806e6d0251c80eb70d6ef133c205e8121863d0f41 -dist/2026-08-30/rust-std-beta-loongarch64-unknown-none-softfloat.tar.xz=1417b6cf3730f228ac07f045b8ee6506c2df0d8ae7b262e61ea7625c2ee866af -dist/2026-08-30/rust-std-beta-nvptx64-nvidia-cuda.tar.gz=1b10411f90f9ceb94ba6e901ea347a7b14c784250b26c7ebf578ea0f0f774c34 -dist/2026-08-30/rust-std-beta-nvptx64-nvidia-cuda.tar.xz=5998d419985fe62e015ff1d01f83907a7606c28364c0edbf540db598dd446ec6 -dist/2026-08-30/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz=efba4705562534e2f6ef97857cef856c4d1c88281cb307c7f6696bac05eac943 -dist/2026-08-30/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz=d716a06bf4bc9ad2f2d3ae17997cc3b377b8dbd1dfefaf93ae7d4f8c6a913af8 -dist/2026-08-30/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz=7576dd106e174a21eaad65bf48351233ad8cdec1c5f172f6531efc1edeb40f59 -dist/2026-08-30/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz=e187119c3fda1b444c340f4d123f95fb23274b624683c844676a15ca2457c209 -dist/2026-08-30/rust-std-beta-powerpc64-unknown-linux-musl.tar.gz=43e4647b626a5c8eb34ef930c70b89ae1efc4d7cb93ea35ea91ab82d52e20ae5 -dist/2026-08-30/rust-std-beta-powerpc64-unknown-linux-musl.tar.xz=ca4204801a3326475c726e3916d9a89c1b8f4f9eb8f2115dac4f005f080355de -dist/2026-08-30/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz=a5d389199f5e64c7f25a97cbd80082a9b07481d79bdea6052ccac4d5e46ee390 -dist/2026-08-30/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz=8979ec9fa392b43e9e0e166a68dfb4a0fdf23e28bb54abcd305b5e0e99811a72 -dist/2026-08-30/rust-std-beta-powerpc64le-unknown-linux-musl.tar.gz=39b9c96f6c01f8ad15e7b5e3cb1550258d9380ea43126c25745c38b7fdb57583 -dist/2026-08-30/rust-std-beta-powerpc64le-unknown-linux-musl.tar.xz=cf68b7783e218d0b5fac2bc6a431b3c8dedbf016c10c510558771cbdb8c473a0 -dist/2026-08-30/rust-std-beta-riscv32i-unknown-none-elf.tar.gz=adac96c3877930f85c8a9ae7a3afce44bf9030c1bf389dc3e21e7ab0c83c9cf7 -dist/2026-08-30/rust-std-beta-riscv32i-unknown-none-elf.tar.xz=138e96b975fd2bcf54525775a02ab05e1f9ad80095d01116f33693b4359bf0d6 -dist/2026-08-30/rust-std-beta-riscv32im-unknown-none-elf.tar.gz=e7be47333fba57452ed7bf22bdb165a60ea243f9a593df5e8eac32796b0d5d7f -dist/2026-08-30/rust-std-beta-riscv32im-unknown-none-elf.tar.xz=9b4ed1df7fc906cac3f1c9bbb5e546b58e9c6402b9573c4f69c160df8c5e00b8 -dist/2026-08-30/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz=43898c8585bea709bcd682eeea33e914ab8be343ca27dfb82c3524e5ece2d37c -dist/2026-08-30/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz=af236521d4556f90cc003dd17f692de787befc62e8c2df11242262f7c2afff22 -dist/2026-08-30/rust-std-beta-riscv32imafc-unknown-none-elf.tar.gz=2f8125c2d07bb57d821662f06b7eaf00d8b98a7386be372cbd6cd517eb6c6e7b -dist/2026-08-30/rust-std-beta-riscv32imafc-unknown-none-elf.tar.xz=1a86efb62f35f60d7b1860c37430e039cecfdad2053f6e449577200d715cbe58 -dist/2026-08-30/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz=6f09dc0d0bb9775d0f4b52277913e5fe8795c3d77e178e1682af563f3ab3a030 -dist/2026-08-30/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz=0de9f4b1fe08f33dba23effeefa4695a4201094cc587bfdebfb4408e9d58cc8b -dist/2026-08-30/rust-std-beta-riscv64a23-unknown-linux-gnu.tar.gz=dd2e782f2bda2c8f220727b85f8d702efb70977c2232c48bc5d9858264501de3 -dist/2026-08-30/rust-std-beta-riscv64a23-unknown-linux-gnu.tar.xz=d1ba75a8d572591380bba39dd6fed42025f481642993329dc7c20479ea92a6b8 -dist/2026-08-30/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz=d01ef2e3a1ec1c69b3a80aad302d066ea4fe406ff752c9b65f7ef1beebba0c22 -dist/2026-08-30/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz=fb919571c31f43ef03b35908bf9ca85ba6bcc053ca289380ba2552c4e64104ba -dist/2026-08-30/rust-std-beta-riscv64gc-unknown-linux-musl.tar.gz=b187f1deba8ea1bb3d3e793c3cb2a6b7f309940e4edbe8b406a023bce82d8785 -dist/2026-08-30/rust-std-beta-riscv64gc-unknown-linux-musl.tar.xz=741cbbf9d91e955303f253ecd885168737dc01369feb33f738290668be01b146 -dist/2026-08-30/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz=ed5c5a046e9896c7d9ad35c76db1c0216ed31c0661d30e29f3f7b5c8211aeb48 -dist/2026-08-30/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz=1ae3c3dd12bc5e669d953521862fff3cec12b6f9f71bb8e3e7b24290d05114cd -dist/2026-08-30/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz=3d3bd0667e139054169ec2ece3d16219269c858ffef37c4e9615b5bcf1e3c83b -dist/2026-08-30/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz=d350e91efad53a550ef8c825e7f7ce12e99bf250afd9a93ed9fd155b72287947 -dist/2026-08-30/rust-std-beta-s390x-unknown-linux-gnu.tar.gz=eac3ec8b4eb9a3b9a127d2e6fffc4b4a752449eb7595950c9af0dc83ea969b72 -dist/2026-08-30/rust-std-beta-s390x-unknown-linux-gnu.tar.xz=d081593cc03f52a6937fb3bef0a2103a02b43cb4705d1747b8b513e1f0983834 -dist/2026-08-30/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz=17db3b6de8e015e9c572899bedea98996a47d725c59664e338053ed590e4e33b -dist/2026-08-30/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz=2e79e7c3eb7515581a8bbee2d02dd8242d7d3d1529eb067a387338b2ae5a63e6 -dist/2026-08-30/rust-std-beta-sparcv9-sun-solaris.tar.gz=72474e747dbdc104c817c90e96e209cf4adc2a441aea37d9c6dc3f89ceaa1a5b -dist/2026-08-30/rust-std-beta-sparcv9-sun-solaris.tar.xz=65cb719203ec265623fffd66f905257c0eb284c0795e736adcc8dfd31750378d -dist/2026-08-30/rust-std-beta-thumbv6m-none-eabi.tar.gz=25f399a7944d8db8a44c49c0adfe7800fa4b12585c49fe2ed69a1b417571b786 -dist/2026-08-30/rust-std-beta-thumbv6m-none-eabi.tar.xz=02183d46df50acf3edbd6778eccf24e357f9d49d87ae0d8808ef47f98cfbfd51 -dist/2026-08-30/rust-std-beta-thumbv7a-none-eabi.tar.gz=839004d3d6ac180bec0e467a7bbd2ea1ddb47a0f29a92f8b2a074f628cfd0395 -dist/2026-08-30/rust-std-beta-thumbv7a-none-eabi.tar.xz=7b9b6c32078adb895fd07f3f72b2db25e9358bfa4a0470d7cfdc3cc7b5a827d4 -dist/2026-08-30/rust-std-beta-thumbv7a-none-eabihf.tar.gz=5e962fec3cc8661e9a62f15c688ac76bc83685e19c163d3b899ec423766429de -dist/2026-08-30/rust-std-beta-thumbv7a-none-eabihf.tar.xz=8db16b6186b89b3e9e0c5560a12bb09d08263e04aa0f9ee878d25dba9852447b -dist/2026-08-30/rust-std-beta-thumbv7em-none-eabi.tar.gz=0150636fdc64ec355916eec21d1dec8f5e73881dd21cb78907a2f06c14a896e8 -dist/2026-08-30/rust-std-beta-thumbv7em-none-eabi.tar.xz=9e4dc0a8492ea92703df7e00994c0e1728d2eb3152947e9b8550d6ae05a76d0d -dist/2026-08-30/rust-std-beta-thumbv7em-none-eabihf.tar.gz=fe3fd11562d4c428ae2757d42f17ee17bf9485c7e7fda2f6a54406ac6ae14444 -dist/2026-08-30/rust-std-beta-thumbv7em-none-eabihf.tar.xz=97735c5e6f77e59ca7540eb303eaa2432f632f867fc7567f1d25540504e889b6 -dist/2026-08-30/rust-std-beta-thumbv7m-none-eabi.tar.gz=e8afd6861c77eff9e8296e1846c01c06d6b83a858bf76099a79588f167999efb -dist/2026-08-30/rust-std-beta-thumbv7m-none-eabi.tar.xz=0421b81a17afc3af31e46a8bfe32a9b178a6a29ba58e25d71be991d1ab84babf -dist/2026-08-30/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz=9612f99fa78f59fcaed30feb3c8660c3ff7e731011226d6114cc49e3f765bb2d -dist/2026-08-30/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz=405ab112b26871dc310a7fe3c67d821544f48b4acace5d406c491529555cc890 -dist/2026-08-30/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz=884b25035258fc7cfc7b2a0e95358553a3c7812954ec7a18dde82fd77cad24b4 -dist/2026-08-30/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz=3648213c47d2e29931f4837914b3aa5d00575ffda7d75654c6d3cd1aece8474a -dist/2026-08-30/rust-std-beta-thumbv7r-none-eabi.tar.gz=680435027aab539feae6b3904717139e20640eb783c7da2ee31f3d06c1c6ac1d -dist/2026-08-30/rust-std-beta-thumbv7r-none-eabi.tar.xz=8fad51ba8f85f7762f40382c1a177c86f8b7ae1a23e41871f560449f34ac099a -dist/2026-08-30/rust-std-beta-thumbv7r-none-eabihf.tar.gz=0a187507505715a5a4497c0422adba69c03e496618e20023d46aa1a261a7e793 -dist/2026-08-30/rust-std-beta-thumbv7r-none-eabihf.tar.xz=b8bdb79d81f1fd5dbdc090b3306f4ec33673dcfe24350c44e1544d00bb8bbe42 -dist/2026-08-30/rust-std-beta-thumbv8m.base-none-eabi.tar.gz=430646899a6bba8c9233eec0987224e2ed42b6cee3aa37ce12bd8aae66097a21 -dist/2026-08-30/rust-std-beta-thumbv8m.base-none-eabi.tar.xz=36576d200890f00ba9e22cbdb337262ef12dc99271c8006832ebe0752c333ccb -dist/2026-08-30/rust-std-beta-thumbv8m.main-none-eabi.tar.gz=2b3cdc315cfbb7bb1a55705cd30934ceb14b7b098ac9e9b86af2091acc1b46ed -dist/2026-08-30/rust-std-beta-thumbv8m.main-none-eabi.tar.xz=ecbee4d541806d2f4559e53e4da549293f4bb881c2f5d99107043251a1f80790 -dist/2026-08-30/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz=8f925d1d913c67ffd9d28c8d02b47aade2388472015f4917a0f3166a8a789bb7 -dist/2026-08-30/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz=c9ba654ccafd0cf8e876770f3133ae143da10f37058978cb67a547f64aa5812f -dist/2026-08-30/rust-std-beta-thumbv8r-none-eabihf.tar.gz=615b2c19357545860fb64ec3b6715c004a4bf29299d8d2130b022acdc7c46ccc -dist/2026-08-30/rust-std-beta-thumbv8r-none-eabihf.tar.xz=27d47d56201190afbaf2e2339d3f56964ff59972a90c994ea551438ed4703eea -dist/2026-08-30/rust-std-beta-wasm32-unknown-emscripten.tar.gz=8ccf1bbfc69d4e8e452294a838adf848d32d57621350a7791d8ac8e1e72f8336 -dist/2026-08-30/rust-std-beta-wasm32-unknown-emscripten.tar.xz=5bd2be0368720436ace1cf02569ca50fa222f936bdda7761954108756151b3bb -dist/2026-08-30/rust-std-beta-wasm32-unknown-unknown.tar.gz=fbdcf8103c16966255aa5a1a65ed02fe6e39862ef2b6e6eade8af07d51654392 -dist/2026-08-30/rust-std-beta-wasm32-unknown-unknown.tar.xz=5c4f71b3be2808511727536c13b6af98fb61661c092031df3f24c568f8bd31d2 -dist/2026-08-30/rust-std-beta-wasm32-wasip1.tar.gz=254037cb663a97b20471a3c87f95d62d1be7b2810a35e53acb219a552e8b58fc -dist/2026-08-30/rust-std-beta-wasm32-wasip1.tar.xz=5f651662f78216db6d2db5a8ac7f8ed04349a82530c08aab2fe3b43b8ea8bf8a -dist/2026-08-30/rust-std-beta-wasm32-wasip1-threads.tar.gz=6093fe10ebfc96c45909e85ae72f98e4f8093cdf2ce9bd720ac1a1aabddbd60c -dist/2026-08-30/rust-std-beta-wasm32-wasip1-threads.tar.xz=175403a655dc2afa2f7b41bdaaac4cb371a6c5cd99a043178fa93f97ab02f604 -dist/2026-08-30/rust-std-beta-wasm32-wasip2.tar.gz=5248648157e3ec5c37d12a40caae0adac59aaae47438585e79a7fc91c294ddf3 -dist/2026-08-30/rust-std-beta-wasm32-wasip2.tar.xz=46705306104b7c5d25a15c0f3cc94f5e208d4da2b07eb516d63533194e901b5c -dist/2026-08-30/rust-std-beta-wasm32v1-none.tar.gz=746d7fa3e93d255a395baaf67d3d318e6419732698ab0089692bdbed5ec4a13b -dist/2026-08-30/rust-std-beta-wasm32v1-none.tar.xz=97d8daf857aa2da8f8daff5f7137c4cf442f3cf05a019b5608a20a4cf5e5d291 -dist/2026-08-30/rust-std-beta-x86_64-apple-darwin.tar.gz=8a5ca2b36f9f4b2ba1dfde1539e95c30a20173137b9822e7d0ec5b2c12aa89ec -dist/2026-08-30/rust-std-beta-x86_64-apple-darwin.tar.xz=2daa0aa9b738f036815b667c664e152750acbb9d14ab41e4ea994000867caae6 -dist/2026-08-30/rust-std-beta-x86_64-apple-ios.tar.gz=4eb04a86e00e50becf154d88eac41feeda32c76730297fd05178a1e941dce0ec -dist/2026-08-30/rust-std-beta-x86_64-apple-ios.tar.xz=80dd8be8e4c1a83b66a0cce6cb5bdea61640e471e755c314a549b56dad10d2d6 -dist/2026-08-30/rust-std-beta-x86_64-apple-ios-macabi.tar.gz=5080a59c173281ec57ff14c32401cb769b1a577ae7710cc54c820120b35c5564 -dist/2026-08-30/rust-std-beta-x86_64-apple-ios-macabi.tar.xz=fae0fb63364548777b9e644e83e3c6d7aeab37953efea5ac27cd2558c828a551 -dist/2026-08-30/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz=fdc91ac8f59e98eac84f79059a486c2ada66890f22b7e66ef7e0b899f65b36e9 -dist/2026-08-30/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz=9ead7033def70e9f455e4079d8a8b1c870f8871b80ae721a41f11e53106a6b64 -dist/2026-08-30/rust-std-beta-x86_64-linux-android.tar.gz=c143b388083353883d88d109db9131c1196d2874a0195b66aaa4427365bd4563 -dist/2026-08-30/rust-std-beta-x86_64-linux-android.tar.xz=f877fbc3b099bccfc531661cf4a5d67a69c0cf426b7d6d3269b4aefb85304f9e -dist/2026-08-30/rust-std-beta-x86_64-pc-solaris.tar.gz=719cb1ac855967c03120c7408d05d88ff101e34de40a65f2d37828f496a84eb6 -dist/2026-08-30/rust-std-beta-x86_64-pc-solaris.tar.xz=647430147f7b7903111ef24a90d2f326198ca70bfa0bb0ee5b1e76d297e9a81e -dist/2026-08-30/rust-std-beta-x86_64-pc-windows-gnu.tar.gz=520d157e438e557e8f37792b9c59c913a732c77149b534a1c7d2769b52e932dd -dist/2026-08-30/rust-std-beta-x86_64-pc-windows-gnu.tar.xz=830bf07e7f1b80e535ddfc2c269c8bd4ca3c63853faa6ba39c7360b0fad976d6 -dist/2026-08-30/rust-std-beta-x86_64-pc-windows-gnullvm.tar.gz=74c1ea656656a624eb84650a833f6d4d3428230435c67ce0a92798a524c55124 -dist/2026-08-30/rust-std-beta-x86_64-pc-windows-gnullvm.tar.xz=8866757f9e3508bd74c724c754511413697229daf50da755411ddc8f9de78fbc -dist/2026-08-30/rust-std-beta-x86_64-pc-windows-msvc.tar.gz=8b3ed1fbdf18c25a2900568f9c974154750a187d5385e06537fc54c607e8e305 -dist/2026-08-30/rust-std-beta-x86_64-pc-windows-msvc.tar.xz=f5250ac89f4ee3b68b86e125e64b2209360040189b39e34671aee03b9de3abad -dist/2026-08-30/rust-std-beta-x86_64-unknown-freebsd.tar.gz=711017150d461a85f3dcf28a37c7fa354ae08dbfa9ffa049576e71b224d7c189 -dist/2026-08-30/rust-std-beta-x86_64-unknown-freebsd.tar.xz=5f8a081aa1b6e7ab3bf88b6548ed55a0c1021b26060ee2655da38556b54a0e06 -dist/2026-08-30/rust-std-beta-x86_64-unknown-fuchsia.tar.gz=40d0e66f5b6c1831684ec5c6946e2467e9c81c38d5a7ceffbdc9441397d6c214 -dist/2026-08-30/rust-std-beta-x86_64-unknown-fuchsia.tar.xz=2632a2393f2f51884db1c07a3f193f19942bd20ab7a3bce6f654444028188aab -dist/2026-08-30/rust-std-beta-x86_64-unknown-illumos.tar.gz=bfe5417f3575d1fd0302e6caef380e33c71fc07ab1b9a98fc80d66cf8997006f -dist/2026-08-30/rust-std-beta-x86_64-unknown-illumos.tar.xz=bff9b2786581d340b0e801b085bdfb40d19309e07a99a97f92cf2016d77db81f -dist/2026-08-30/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz=76d75f97d7b0f3421029d078473529de626d1413d5ac8c0473325c15f2765573 -dist/2026-08-30/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz=1ec3eb585a38de48d12e05f78c82394493c9c640f8c6fdf487cc8c29ca88742d -dist/2026-08-30/rust-std-beta-x86_64-unknown-linux-gnuasan.tar.gz=7d12531568c430c670d4933955b767985504a88c3da513b832b857f28e59fbf2 -dist/2026-08-30/rust-std-beta-x86_64-unknown-linux-gnuasan.tar.xz=ade92cb3f5ca0088d93eeac74ec29b8532d285a730eb87350086e9cda4f7d60b -dist/2026-08-30/rust-std-beta-x86_64-unknown-linux-gnumsan.tar.gz=6280a11a0e153f98dfbd4892a90ee60f086c812ded8177e21e6fa3fd9f733441 -dist/2026-08-30/rust-std-beta-x86_64-unknown-linux-gnumsan.tar.xz=51792543085e26437577f4c3cabd226571c9f9d5b85d142a4d7ec19b992c4bf2 -dist/2026-08-30/rust-std-beta-x86_64-unknown-linux-gnutsan.tar.gz=85bdd806ddf39fe35df389895b3c531df21b26dd60945f2b92a3f7f7627dfe77 -dist/2026-08-30/rust-std-beta-x86_64-unknown-linux-gnutsan.tar.xz=076be1c27da9f50254ed8ae748c0ebe01a21af0c47920e035ee25121327cc607 -dist/2026-08-30/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz=e0c5acc46aac350663adc8b6b73e097299a94574785baf5487181b321979b34a -dist/2026-08-30/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz=f9470afacdce7db3d057bf13af6b6e45574b4c6cd8a1c75adbce2732ab61515e -dist/2026-08-30/rust-std-beta-x86_64-unknown-linux-musl.tar.gz=f207346769473385b1efd771abcc0e2a95d73dc93e5039a0ce4cc329daf1e83b -dist/2026-08-30/rust-std-beta-x86_64-unknown-linux-musl.tar.xz=25d752aed1d312ab614cd065826c688440aaca70cc7e67c4578ca82398f951f6 -dist/2026-08-30/rust-std-beta-x86_64-unknown-linux-ohos.tar.gz=fc511b73ed5c0da3303efb8b5e01d6e4465cf028543a9ebcd5b1ba273e3e5104 -dist/2026-08-30/rust-std-beta-x86_64-unknown-linux-ohos.tar.xz=9ada977527a2fb632cfecd9414226664b47514adc988fbb0e3cb86cf1f81f81b -dist/2026-08-30/rust-std-beta-x86_64-unknown-netbsd.tar.gz=9268af9ae7ed919e0955a1162376ed84c65906304f3dcc64762a6f8dc764031c -dist/2026-08-30/rust-std-beta-x86_64-unknown-netbsd.tar.xz=49f7e2808660320fcde2f1ffe23d4e5d44f26c9d08124a97ffbd715d24c0d21e -dist/2026-08-30/rust-std-beta-x86_64-unknown-none.tar.gz=7f2dea659c4d5ff038118307cd2fc6286a95a6dc12a85686bd958806e3d49647 -dist/2026-08-30/rust-std-beta-x86_64-unknown-none.tar.xz=e96d78c8025876bdb25613fb81cf27b54803ab0a809d761909fc56e626153e1c -dist/2026-08-30/rust-std-beta-x86_64-unknown-redox.tar.gz=7874b37200d60c3986588f6178a623817acbcb66bbbfb9b81c9e4ae348cbc0bf -dist/2026-08-30/rust-std-beta-x86_64-unknown-redox.tar.xz=a6d454739b2fcd6728a7c19afc6e95e6ce2dfb02a569619a071b02b3fb7a6e33 -dist/2026-08-30/rust-std-beta-x86_64-unknown-uefi.tar.gz=6b4c4371089aa7d2be7dea927dfc623c9958998cf02952b8455dc4f05c466ba4 -dist/2026-08-30/rust-std-beta-x86_64-unknown-uefi.tar.xz=1dd395befcbdd4f379a2f78e498c15113e07934b46dfede8dc1075276d7c7cf2 -dist/2026-08-30/cargo-beta-aarch64-apple-darwin.tar.gz=9e761054280cc664f95355b3ff7c9828d6b98fcb63c6cc65b3ef07f78fa98a2c -dist/2026-08-30/cargo-beta-aarch64-apple-darwin.tar.xz=473eed6c6004b83a306c4fcf34002560c292050a6c25247a8c1cbe3ad05b93b4 -dist/2026-08-30/cargo-beta-aarch64-pc-windows-gnullvm.tar.gz=079698fcd5ee509d6b733e7119a8023d9b5e538ea526b64fb286cfdbb85fe16d -dist/2026-08-30/cargo-beta-aarch64-pc-windows-gnullvm.tar.xz=86018bbd5e4ce4a2ed6fe33c520fbdaa1e1cb93a86cc47f8fb8a84f139fbf77b -dist/2026-08-30/cargo-beta-aarch64-pc-windows-msvc.tar.gz=136513f5d9caa1d9a982c5731f07c52ed33ffcca18cf8c76f65b15193788f4df -dist/2026-08-30/cargo-beta-aarch64-pc-windows-msvc.tar.xz=faaa5028d993cfe8ad0c86b03e8ce2718bf20e8cee68d8185ecfa39cfa3870ad -dist/2026-08-30/cargo-beta-aarch64-unknown-freebsd.tar.gz=f9c7edc721b9e36377248325ec7944df39cf2ca005fb02a91a4994d717ad4783 -dist/2026-08-30/cargo-beta-aarch64-unknown-freebsd.tar.xz=581acbb17a497d6b76b99cc94833a33d72e104f2efff7d408d69550f912da462 -dist/2026-08-30/cargo-beta-aarch64-unknown-linux-gnu.tar.gz=33647f90516de973cbfda8c553497ae5721e4412c4aecbdba1af4ff414093ca6 -dist/2026-08-30/cargo-beta-aarch64-unknown-linux-gnu.tar.xz=9741da1d26464d27e5566220ac240ece0c94d95e0458e8e945c1d29cc198746b -dist/2026-08-30/cargo-beta-aarch64-unknown-linux-musl.tar.gz=92e567bb8a7eeee6e001bd47b39263473d69a6591a9e64f9f48736a3ee631669 -dist/2026-08-30/cargo-beta-aarch64-unknown-linux-musl.tar.xz=69bd4e038b62ca7a94d85b3673286fe9a2ff57d4fd750e35cc0f4edf2e74b701 -dist/2026-08-30/cargo-beta-aarch64-unknown-linux-ohos.tar.gz=58627d33a59fcd84a33e71e121f267e675d78a181fdabd8bfd645b0c21e36c33 -dist/2026-08-30/cargo-beta-aarch64-unknown-linux-ohos.tar.xz=4dd725fb1d9eb9f7f1434edf563642dd4357b4622b7990ca835c62db9b26bf55 -dist/2026-08-30/cargo-beta-arm-unknown-linux-gnueabi.tar.gz=86ca22788c633215cdd0ca96986136504dc7525c1d6d12da5d2d37944986ea21 -dist/2026-08-30/cargo-beta-arm-unknown-linux-gnueabi.tar.xz=99f26d926f354f3a72fb422bcd8a2f0b8379ac727d5c3f16b57b433fe9f5d656 -dist/2026-08-30/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz=681839287ba9bcccbff125e21f9b1381915ebf1e2748d558cf4ff1dcace861fe -dist/2026-08-30/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz=cf1e4cb9ebb5c86ad2090a9f08862fc85ee00f0c0a4dce50df4e688112e2b3d7 -dist/2026-08-30/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz=9cd65395b781a22ec66e2e49e78d5a972360c93b5da7ed22b837635c68899a0f -dist/2026-08-30/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz=b3c6b3a99f537303f2419cf7379cbdbacb899a956ad0653b0726d558f893f481 -dist/2026-08-30/cargo-beta-i686-pc-windows-gnu.tar.gz=055b916329d7848a521f63cbe6c373a4ad94a36e9c2cefe155283bb67b2c655a -dist/2026-08-30/cargo-beta-i686-pc-windows-gnu.tar.xz=728142c61e80ed2884a41446fb5ef9784ae66c6a5ae01d34548ad3722bf17547 -dist/2026-08-30/cargo-beta-i686-pc-windows-msvc.tar.gz=902d87163289b5f7c8876d79b4bcf6a524f445007afb41478c3d446c47e8efd9 -dist/2026-08-30/cargo-beta-i686-pc-windows-msvc.tar.xz=f8c2377f283109ba8db1d15925ebf549a0297d33245d67efc2f178fbbc13a766 -dist/2026-08-30/cargo-beta-i686-unknown-linux-gnu.tar.gz=acb2edc9a1e93476848d22725017ca6c80906875cb3c3cd5f23972a9a96c109d -dist/2026-08-30/cargo-beta-i686-unknown-linux-gnu.tar.xz=6600fd8770b01333c808210c5fe794cac10f302e4d256d1c76d1ad3b86b2c63c -dist/2026-08-30/cargo-beta-loongarch64-unknown-linux-gnu.tar.gz=98b4d2d3c9684c4477cbab01edad84970f9bbef0032249800f48cb4aa6259eae -dist/2026-08-30/cargo-beta-loongarch64-unknown-linux-gnu.tar.xz=3ab812af126bc73f9da76ed94436b856b3a5b778a0594773217ede933c136e89 -dist/2026-08-30/cargo-beta-loongarch64-unknown-linux-musl.tar.gz=d5058dc322f0f947bd5b8ac8644df85ff760797283d4cd62a08c6137fb1b31b8 -dist/2026-08-30/cargo-beta-loongarch64-unknown-linux-musl.tar.xz=34dfe36b152df195bf161ebf151e7166096b8cf9581b8972b49b17cd9be6f101 -dist/2026-08-30/cargo-beta-powerpc-unknown-linux-gnu.tar.gz=7402338613366b038279a45a2390a403fd7479cca7e38fae1614e3356b1ed17b -dist/2026-08-30/cargo-beta-powerpc-unknown-linux-gnu.tar.xz=6637409067ce9667b02142a3866bacac5b378a7d3f4c1198ad63b8dd44e13683 -dist/2026-08-30/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz=bef6d62721d7cb4a5e8d16b4ce8dd0b64100991ff86e4228ccfd2baef6bc1d13 -dist/2026-08-30/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz=ab6ebfc224a8b5732b009ea5fcd082f6765ba92c1912b4507ad6b7020d366f31 -dist/2026-08-30/cargo-beta-powerpc64-unknown-linux-musl.tar.gz=07db627041a31120cb4c416ba8d8cd9497e496830568f06770aaf3bd2388cf89 -dist/2026-08-30/cargo-beta-powerpc64-unknown-linux-musl.tar.xz=c5438cb0e1df9c6dbfcb31d9b578d8421dffea54ce84fbb16619b57eaac22fac -dist/2026-08-30/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz=9ec047abc63b03ade31cad80b0e3c247855ebbdeb42081f0e7d1cd0fa2d6a591 -dist/2026-08-30/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz=59850205528f58ec1df682519b33d149dacfd34c6820188344166be741b6496a -dist/2026-08-30/cargo-beta-powerpc64le-unknown-linux-musl.tar.gz=b51d3d280d5c7db88697f5cf62a16bdf87debc3a36e169e1613b19c787684460 -dist/2026-08-30/cargo-beta-powerpc64le-unknown-linux-musl.tar.xz=42fed8ea76cdef7ef55cea3f5afa7b65bbbd0f64a7dd91c5128b4d0a459a1d61 -dist/2026-08-30/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz=674a081bb7e9264aadd9877cd56e354023bacbd3d7645f47b5e56fd929b2893c -dist/2026-08-30/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz=cad87b60d098bdc83f26039d2a7b01cecd72b454414423786e69d7830fbc2f11 -dist/2026-08-30/cargo-beta-riscv64gc-unknown-linux-musl.tar.gz=0020a7c5575ac0aed412219257ac4ed7af155ce4696382ee4a5b20113331b8e5 -dist/2026-08-30/cargo-beta-riscv64gc-unknown-linux-musl.tar.xz=ceb3b28ee22ad4b7e893583774680f5be45be9376fb56d23d48ac6201bc972e8 -dist/2026-08-30/cargo-beta-s390x-unknown-linux-gnu.tar.gz=59836d761c03a77477236199be5b0bd06d2ba5ab1d0f39176e1d7c8434eb45e2 -dist/2026-08-30/cargo-beta-s390x-unknown-linux-gnu.tar.xz=38dd22d489aecdf755bb667b4e79d2cb2ec8b6c2710d01686d600f1273e8d59e -dist/2026-08-30/cargo-beta-sparcv9-sun-solaris.tar.gz=c9df6b1cf54638837c2ab060d81241b1cc9c0fe415927c3df281a6c27a46fa5b -dist/2026-08-30/cargo-beta-sparcv9-sun-solaris.tar.xz=e1da5f2fc4dfa042c2819cc20ec8a7fe223aec9a3632899d83965b69c0742048 -dist/2026-08-30/cargo-beta-x86_64-apple-darwin.tar.gz=466f22d638541dfd8a067ae8b01d1d5ba3fe53e77191da3f3f676d4a90d88cca -dist/2026-08-30/cargo-beta-x86_64-apple-darwin.tar.xz=239a1222ddc7c24648f902799dfd258ae334a37f3f58675e94a62879fa51d302 -dist/2026-08-30/cargo-beta-x86_64-pc-solaris.tar.gz=7623f134d775dc42c669f1f23dbd22415036927efe13b183edfc303003d714e1 -dist/2026-08-30/cargo-beta-x86_64-pc-solaris.tar.xz=126bd01ff0c9bb942e211a8fdf6712199979c35d1b6b2b19bbd2a21fbbea966d -dist/2026-08-30/cargo-beta-x86_64-pc-windows-gnu.tar.gz=bfb4fedc712bceeaf74cf23245fd3efdd68728d77db8e04a32ddc329995bdd97 -dist/2026-08-30/cargo-beta-x86_64-pc-windows-gnu.tar.xz=b703ae1050c9a7ebb6b6ddb0523c59b2e5b38466716346e85e7c63c26588927b -dist/2026-08-30/cargo-beta-x86_64-pc-windows-gnullvm.tar.gz=562f4d368d2d854ac580e07738411afb563373e247514e944bcd98f9967319ae -dist/2026-08-30/cargo-beta-x86_64-pc-windows-gnullvm.tar.xz=f165e4bfef2e6bafeb4825961c0fc41ac7ac23d7457feb444dfb8c1e1e0d012c -dist/2026-08-30/cargo-beta-x86_64-pc-windows-msvc.tar.gz=9a3c7ad87e5f3b98d02167c449437c0aba21d174a6ecfa2f0e4817b8c93ad91c -dist/2026-08-30/cargo-beta-x86_64-pc-windows-msvc.tar.xz=831c421d5c5d187047920ccf7ce76300f4d9e3a36960e347d179f31b08f9b49f -dist/2026-08-30/cargo-beta-x86_64-unknown-freebsd.tar.gz=3dd99be35b587a0334d71bc92dfe15e06ff0a052d66782a2425895684dcfe669 -dist/2026-08-30/cargo-beta-x86_64-unknown-freebsd.tar.xz=e4220bc3ccfb034fb1b40d506c9a9637153c943e0a0f9dea4bd56065df2698cd -dist/2026-08-30/cargo-beta-x86_64-unknown-illumos.tar.gz=98de23e890441294de89857063f2810f81df29c02cb9da755793f78eaa8ca8fd -dist/2026-08-30/cargo-beta-x86_64-unknown-illumos.tar.xz=9dbc424c16bed26b8d2ea662c41f156ef14636cbc2006c36af151ea68c932551 -dist/2026-08-30/cargo-beta-x86_64-unknown-linux-gnu.tar.gz=2d2cdc1af3afe2816da895ed1ef0f27a31ee31f938a9385864c0c8e1385a2ec9 -dist/2026-08-30/cargo-beta-x86_64-unknown-linux-gnu.tar.xz=62bcbf31738317455017be5dadb592ce9a6b937bf61303c30c56d4f6d8f1ac3c -dist/2026-08-30/cargo-beta-x86_64-unknown-linux-musl.tar.gz=32247e6fb942d39444cab2b35ce2b64a44c84b2fa934acb781ea9c682f60f14b -dist/2026-08-30/cargo-beta-x86_64-unknown-linux-musl.tar.xz=8c155d690af2600d9a4dcf00d62b6a5f52524e999d25d02eb873a6ab28f373e5 -dist/2026-08-30/cargo-beta-x86_64-unknown-netbsd.tar.gz=c588c5407c06a36da78587b6ee7baddc72b45ecb6fac9290e333fd0eadab1152 -dist/2026-08-30/cargo-beta-x86_64-unknown-netbsd.tar.xz=09c84a0118c5d533b442f39c33265a04abb72133c94f662654095f984f0e85e8 -dist/2026-08-30/clippy-beta-aarch64-apple-darwin.tar.gz=cc03b3970d8e2f5b055fab29f46e9120fdd520f6db45a6213a699a9a4367997e -dist/2026-08-30/clippy-beta-aarch64-apple-darwin.tar.xz=7c6c789f64225fdc45aad2c8eb596aa679e855abb5c4d79573a458f2da7309ac -dist/2026-08-30/clippy-beta-aarch64-pc-windows-gnullvm.tar.gz=b404caee9abf7e945985ccd5379f0e2f41c3b7dbe6e999d722b55fdf9d08c6ac -dist/2026-08-30/clippy-beta-aarch64-pc-windows-gnullvm.tar.xz=36c9f84de6b071aa5f1c0bae438ae3ef132c4add9d35ee5a69e8a754501b568e -dist/2026-08-30/clippy-beta-aarch64-pc-windows-msvc.tar.gz=bebf9f956d1974779ab06d7bdd25a7ea053811523ba2ff4ae18267bad7c80494 -dist/2026-08-30/clippy-beta-aarch64-pc-windows-msvc.tar.xz=fb4274e7618fe49031cb6f4021a0e7dc4bf1ee8900eb45cef118600c8137ad4f -dist/2026-08-30/clippy-beta-aarch64-unknown-freebsd.tar.gz=79e922d0c5bbd69c86d7a459552e47511481eb5faee8a7a2eb893ccc5f7b6788 -dist/2026-08-30/clippy-beta-aarch64-unknown-freebsd.tar.xz=ed65f1ac492dafebfae2ee367135e1f56f57af9b2d284739fa57b18793e7386a -dist/2026-08-30/clippy-beta-aarch64-unknown-linux-gnu.tar.gz=d4aa6c22318d181ff4ef17f0ef2b8aea6641efac232f8a074cf4948e76c68802 -dist/2026-08-30/clippy-beta-aarch64-unknown-linux-gnu.tar.xz=1d679549e6cb70f5acac81d255aabfbb6afef0c94973747e791f27e1097dfb8a -dist/2026-08-30/clippy-beta-aarch64-unknown-linux-musl.tar.gz=0fecf358ff0e7725ec6df42b194a1912b91b358aad683fc48d36b1768c88042f -dist/2026-08-30/clippy-beta-aarch64-unknown-linux-musl.tar.xz=443cb8a078231aa3bba0b8082cd5f20ce36caee120d1060e8f3cf73fd05d8907 -dist/2026-08-30/clippy-beta-aarch64-unknown-linux-ohos.tar.gz=f8b624585864c6bd679db4d505b8c84d46682b8cccb29fcd5bc5fcc74c7a79e7 -dist/2026-08-30/clippy-beta-aarch64-unknown-linux-ohos.tar.xz=8e09d294c73ff186be62d2db49a06240b6d66c674c81c2471646222a5d607e01 -dist/2026-08-30/clippy-beta-arm-unknown-linux-gnueabi.tar.gz=106efc64c2e21abc149264e3a98c4b836c1430d0240f3489d5605a2cc17760fb -dist/2026-08-30/clippy-beta-arm-unknown-linux-gnueabi.tar.xz=b45eaa6f6586aca7e622c5ad60ae9ec92ddd935dff5d6c94ba3a78e0dc6dc304 -dist/2026-08-30/clippy-beta-arm-unknown-linux-gnueabihf.tar.gz=8bb3d211d9d17c5acb2867cd154f3dcecdbfb36d723c05f35d91f2e33ea00067 -dist/2026-08-30/clippy-beta-arm-unknown-linux-gnueabihf.tar.xz=08c78ecc4b5489c5480fe6d3832298686f782f9e45bb265946b9bb1176733f54 -dist/2026-08-30/clippy-beta-armv7-unknown-linux-gnueabihf.tar.gz=1875b1f3c267c634cd48933c98891a53b350f7142462cb56bfc6513326024c89 -dist/2026-08-30/clippy-beta-armv7-unknown-linux-gnueabihf.tar.xz=a647ca6600707b40a1e97f7fd0e39bf43c863ea60224eb28beee0622f74253e3 -dist/2026-08-30/clippy-beta-i686-pc-windows-gnu.tar.gz=772d98712f108d6d7eb7730e26ec04e66a972daf3ac5352c30160e20c1861ba6 -dist/2026-08-30/clippy-beta-i686-pc-windows-gnu.tar.xz=d75cdb626c3104552b122e79d2ed07bad9ec04f084f58bde332d029feb3ae90d -dist/2026-08-30/clippy-beta-i686-pc-windows-msvc.tar.gz=e7ecdd93739741d769a1fb4ea33b9dd3c935008d7ad5e773d3673d5ea7dbd6d6 -dist/2026-08-30/clippy-beta-i686-pc-windows-msvc.tar.xz=8b1223c943d378e201e83d61298527e2d7307f96ce609c6e1a5483f086490bec -dist/2026-08-30/clippy-beta-i686-unknown-linux-gnu.tar.gz=f326afa8e228964581df0c4a0096eb33fc132df0254b2648f43f6589466e72bd -dist/2026-08-30/clippy-beta-i686-unknown-linux-gnu.tar.xz=7e868c12873c5e451ebdfe67e4474538a9d3f46d5dde8ad4139c8f5056a44685 -dist/2026-08-30/clippy-beta-loongarch64-unknown-linux-gnu.tar.gz=e185f345c34e54bc100b1df955c047b53db30b9b3b054a2cf5a264f0e3ae2405 -dist/2026-08-30/clippy-beta-loongarch64-unknown-linux-gnu.tar.xz=270c4f7d7763180bfb8a7dea1d97259398c9b7c6560fdd3b58192748ffb40cf2 -dist/2026-08-30/clippy-beta-loongarch64-unknown-linux-musl.tar.gz=22d484c3ae2ade651e12ce943780b51da3b0dd6bf04c6f8403540cc7f382ced0 -dist/2026-08-30/clippy-beta-loongarch64-unknown-linux-musl.tar.xz=a669d05d6efc4e77f05b417d00272f2a0221e05b37f921b9e0f9769a8b48830e -dist/2026-08-30/clippy-beta-powerpc-unknown-linux-gnu.tar.gz=3d782e904a64856dcae5de5d2be509f2ccc3109e9067ddd540bc8e1049ea3ceb -dist/2026-08-30/clippy-beta-powerpc-unknown-linux-gnu.tar.xz=dd867a9fca45df5ee0d3ef602802f8accaf9fcc1ab1fe52b74ea72e03b91d67f -dist/2026-08-30/clippy-beta-powerpc64-unknown-linux-gnu.tar.gz=a890aa398977d62b10513350e2c918fb41ffa76a2733b2f08ec829f15b8dfd0d -dist/2026-08-30/clippy-beta-powerpc64-unknown-linux-gnu.tar.xz=c8ae48e109ae2f4daf3c878056641acacc457618afe42c9af53eaa4bf9a4faff -dist/2026-08-30/clippy-beta-powerpc64-unknown-linux-musl.tar.gz=010d6114089273e267645340437d74b962c7f16ad21914b689100241e1e5879d -dist/2026-08-30/clippy-beta-powerpc64-unknown-linux-musl.tar.xz=1ff705f8d502081d85e8f85a177b4d00a3f102475c6148f7609040efe8f95cf0 -dist/2026-08-30/clippy-beta-powerpc64le-unknown-linux-gnu.tar.gz=226cdb2a14e6f7ac8962326d6f0a4459a99d5f43ed6516f296da74a4c96504e5 -dist/2026-08-30/clippy-beta-powerpc64le-unknown-linux-gnu.tar.xz=6f0d5341554b0b12be7138962ad1f02cf534b197923f791e8489e09c2ec0fa31 -dist/2026-08-30/clippy-beta-powerpc64le-unknown-linux-musl.tar.gz=aa41e1b12199dbe2597c1614908911bc936b77e4697d6f47a73e99b3fc6d3cbb -dist/2026-08-30/clippy-beta-powerpc64le-unknown-linux-musl.tar.xz=f0084a572996014050b886ea7defe6c6f3dbb8055c840c9f3e0f09474f1cd87e -dist/2026-08-30/clippy-beta-riscv64gc-unknown-linux-gnu.tar.gz=35d0c03ff88bbc60059f1014c65f8803a5e649fcef0ffecf90c73db0b5e29d04 -dist/2026-08-30/clippy-beta-riscv64gc-unknown-linux-gnu.tar.xz=f2233e50c2c976941b30f41859494c3b47a4a92022b50f924eb3aafbb10ad1c6 -dist/2026-08-30/clippy-beta-riscv64gc-unknown-linux-musl.tar.gz=8f34275dea87439be7c820cf5c239168c5641711e439afe873615ebc26e92f20 -dist/2026-08-30/clippy-beta-riscv64gc-unknown-linux-musl.tar.xz=7346c56c1ff8cadbb633d0890dfea12918344f035075a5ade3fb44869da7ce26 -dist/2026-08-30/clippy-beta-s390x-unknown-linux-gnu.tar.gz=064c99e94f0dd2da50e8445cba0f6bd3b6de9a78f860fde838097f26f93c0a86 -dist/2026-08-30/clippy-beta-s390x-unknown-linux-gnu.tar.xz=b5237ae854270214b81270f1a6dcd26fab19470b98450b8503a1647d83f19d66 -dist/2026-08-30/clippy-beta-sparcv9-sun-solaris.tar.gz=e8cb900135fcaecc87565e6279f3b50db957370b73f7bdaee39401bb2236ec93 -dist/2026-08-30/clippy-beta-sparcv9-sun-solaris.tar.xz=a1315c1d6b3ab19a6351527f07d62bc375d937571af49f1130b030440f96ad74 -dist/2026-08-30/clippy-beta-x86_64-apple-darwin.tar.gz=4adb22b7d502339fa505fe7ecf18167fd7a054e15211d28f78d7f27c05fcfb4c -dist/2026-08-30/clippy-beta-x86_64-apple-darwin.tar.xz=0e9c5bd310a8c6d797cc741a3368c4e18ebdd138fb0035bcec94ca3563fee966 -dist/2026-08-30/clippy-beta-x86_64-pc-solaris.tar.gz=ec8ea5558cd47e0324ab46fc8d7f06b55e3a5b015e1b06f7d8372ef63846e2e5 -dist/2026-08-30/clippy-beta-x86_64-pc-solaris.tar.xz=2ce6978bb89c20fd4d92d9a513c60ed8a4ecf243d68e9ed5e76a06f46374725c -dist/2026-08-30/clippy-beta-x86_64-pc-windows-gnu.tar.gz=269d4722bf8604755727de19c3e92932976797ee07a382a4241d598e11f1dcc3 -dist/2026-08-30/clippy-beta-x86_64-pc-windows-gnu.tar.xz=bd37185b9ea6c75df3fa873bb7bb961ea48cd7eb2e1e537df78fa23560a4e542 -dist/2026-08-30/clippy-beta-x86_64-pc-windows-gnullvm.tar.gz=7a84636be94207fe6bad2a162bb9f412944f07d87e65aaea8defa01500be420d -dist/2026-08-30/clippy-beta-x86_64-pc-windows-gnullvm.tar.xz=6f3cd9590fde49d3d32473d7363b8bd9e1298adb87dbef2fc8ba2aa6565cf7f8 -dist/2026-08-30/clippy-beta-x86_64-pc-windows-msvc.tar.gz=9d33b21d67155a006f8ba2de17892812519ce9ddbb6531656b314c710363f7d1 -dist/2026-08-30/clippy-beta-x86_64-pc-windows-msvc.tar.xz=a6805eaf9f9d0d5d9f98d6fafb4b4e96accfb2676acca907ee704f84745c5768 -dist/2026-08-30/clippy-beta-x86_64-unknown-freebsd.tar.gz=b8e66bc833089b1b23cd0e73c2e1b3409864d2b00c7ba533378b45eba07a8dc9 -dist/2026-08-30/clippy-beta-x86_64-unknown-freebsd.tar.xz=593633c48f833329e86613601cbb4db25071eb19b33a4561f7104c0cba86f03a -dist/2026-08-30/clippy-beta-x86_64-unknown-illumos.tar.gz=dbfa333d2cf958b0d39983bdf3fd2d55623903d82f34d70d06ad31561f51cf97 -dist/2026-08-30/clippy-beta-x86_64-unknown-illumos.tar.xz=53a0ec434ba0664dcd967d02a13daab11c23eb294b3e80befb80667f8983c586 -dist/2026-08-30/clippy-beta-x86_64-unknown-linux-gnu.tar.gz=17a88086e382454d70bf08e33149b8e0ba97a34500b9f56dbbfd9ff4c3fc535d -dist/2026-08-30/clippy-beta-x86_64-unknown-linux-gnu.tar.xz=b0237d0782a9b8938769e85dbbcf1a6eec91554f665b90acf00a60f4dd0714bf -dist/2026-08-30/clippy-beta-x86_64-unknown-linux-musl.tar.gz=2d7ff9aa37dbcfdec585dea69555c973430b7e6f4f6b55e450cb34c05b59baef -dist/2026-08-30/clippy-beta-x86_64-unknown-linux-musl.tar.xz=9ae39943b49d89ef53153b2921fe7dd6ed244acd5e39297ba7cf108c38ec644b -dist/2026-08-30/clippy-beta-x86_64-unknown-netbsd.tar.gz=7c8c9387e3f75a917dd721cf96a118cd8924baae1b476e9da545aa6c4cf06609 -dist/2026-08-30/clippy-beta-x86_64-unknown-netbsd.tar.xz=0d85f1338a69ad620c923bec29c3fcad6225f957578f0a41a11568c154e091aa -dist/2026-08-30/rust-beta-aarch64-pc-windows-gnullvm.msi=99a2ea82e19f436cdf4dec0f0aaf315c16a3229fb6d9b103ee9d1a2c06b74090 -dist/2026-08-30/rust-beta-aarch64-pc-windows-msvc.msi=c0a27c16075e29239e68b31d76946a41a452acce1140c1e509a189c2e3f56eff -dist/2026-08-30/rust-beta-i686-pc-windows-gnu.msi=8f742fd4920a3fcf5d41dc113f885e441f8681d83a745a5b12195c2782a4c93b -dist/2026-08-30/rust-beta-i686-pc-windows-msvc.msi=513fb7fe5d4289ddbb58260863ba3821d2cb2a3baabc0bea1a65ef2885c413ec -dist/2026-08-30/rust-beta-x86_64-pc-windows-gnu.msi=2d9f279983d1a50913e7777de880899143190413292801266b95ac2ed69a4e4e -dist/2026-08-30/rust-beta-x86_64-pc-windows-gnullvm.msi=5bf8e7568a27395a4b879dd6534c141a92cdf12536d1201a175d84453963699b -dist/2026-08-30/rust-beta-x86_64-pc-windows-msvc.msi=23463fbf570caf314427b3ff8e666a2d9b3cfdf7947aced459187eb59c645e48 -dist/2026-08-30/rust-beta-aarch64-apple-darwin.pkg=e933472ceac42d94982728bedda2976bc77da11805a236cd93cab0c159dafe5e -dist/2026-08-30/rust-beta-x86_64-apple-darwin.pkg=bb9d97c74eb28bae220ed548e46b91c478d66301801c51d4d170d45989d24cc1 -dist/2026-08-30/rustc-beta-src.tar.gz=baa6964c75882e0393efbed2f19e8303b46101336adfdf44bede282dae15ef0d -dist/2026-08-30/rustc-beta-src.tar.xz=f835b92620f3802afdaa41fa49de6e253c9db0de0234070833011ce65c14d0e2 -dist/2026-08-30/rustfmt-nightly-aarch64-apple-darwin.tar.gz=22d6733e4325fe6fa25399b651e77cd5ed1a7f370f7558aaa08e8b6447f55264 -dist/2026-08-30/rustfmt-nightly-aarch64-apple-darwin.tar.xz=fcc49f0698c09f8ff89ca044d0426dad2399da253d287c3aaabebf103b4a2820 -dist/2026-08-30/rustfmt-nightly-aarch64-pc-windows-gnullvm.tar.gz=34a0e1f6d068d30cca9099c44736657de307ea3041970fde751aeda3781d02f7 -dist/2026-08-30/rustfmt-nightly-aarch64-pc-windows-gnullvm.tar.xz=8f7955c05e2a5324fba961df617ca2d70d473565cd3389fcd52814e9cd5c4f65 -dist/2026-08-30/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz=7df7e3f128e4d4f88a5777e9e736fce117f5f47ef8cb505713aba9db09b8cecc -dist/2026-08-30/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz=094323c8a19c6840d13d9e3a1668c94dc249ff0c720634da1fef8cf844cded71 -dist/2026-08-30/rustfmt-nightly-aarch64-unknown-freebsd.tar.gz=bd932f456a46de862f1aa3fc5b936a5e364a714a6cb8b71c7cf3920f180177a0 -dist/2026-08-30/rustfmt-nightly-aarch64-unknown-freebsd.tar.xz=4c14e353f8f5982ba3b3828578e412f433ca3f16e94c9aa0f6efdf2949b53215 -dist/2026-08-30/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz=efb14d13c092a4dfd1d2229aaf850cb2e6c56bac9c31ec322e22f515050567da -dist/2026-08-30/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz=7df63718528c050a0e306ab08979ca787b6f0522a442d34355cf683f3bb259f0 -dist/2026-08-30/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz=b0a231d0eb6bb006bfc144a954be07ed9886202967843b0e8475b19429902884 -dist/2026-08-30/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz=b98a8ba107231b4d8832d6f1cb679661e9ee0faf13c81dc3eaa576853580582c -dist/2026-08-30/rustfmt-nightly-aarch64-unknown-linux-ohos.tar.gz=dec01a69341b8d512eb17ec28687b4ee43174de63e31a90b91e99b9eaf17e345 -dist/2026-08-30/rustfmt-nightly-aarch64-unknown-linux-ohos.tar.xz=e598a9559c169794bec2443e7842e62728e09fcaa6614b1a3f5ebd6803f0ef6a -dist/2026-08-30/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz=18a5a8ca79e449f7daf52f534cd2ab137e33204280ac7da35e0bd31f6ba2674e -dist/2026-08-30/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz=00e6e3d4d555ee786545b5f0a542d7b5bb9260781a80e4598d50bdf856457a4a -dist/2026-08-30/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz=421735da37fcf7a85c2bccb27b13c15105f98a95333734c1d383e18b7a01842c -dist/2026-08-30/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz=a3936b0c6a67da7180c10554dbe6427beaf05480c40b3a57cc07082085987cc1 -dist/2026-08-30/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz=817768d8aae4d3d09b952631982051eba84b889e87367c2d740a14e35261fb4f -dist/2026-08-30/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz=5d905fdbcc0d3d2f5949c0e32980172fa8216a51974945248e293f8c81b58203 -dist/2026-08-30/rustfmt-nightly-i686-pc-windows-msvc.tar.gz=e2cae9d3e6b6c6cf72bf0e12510358984adb5f5279f49af8f4a29869614f0a84 -dist/2026-08-30/rustfmt-nightly-i686-pc-windows-msvc.tar.xz=bfb5472037dd5d1031384c70d58bf841ff4a9e33716b55d8384c47d27a31ac04 -dist/2026-08-30/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz=b18fa3d4a5469b7736655c842af84ad44a07fbd6146669c6f2b5f5c3f26a4e33 -dist/2026-08-30/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz=c7978ac60291efc21bc77c2aed4d14c0bbfd6dbccb461f0499202fe8b8dd0818 -dist/2026-08-30/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.gz=e1e69785fadaa294f92d7f5e42b046a47c4b508a806804048c3c2b1cfcda25d1 -dist/2026-08-30/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.xz=c6371931d1bbc56f94f17d55ef0e8a756388606599a6f8828b1269504b57f0ac -dist/2026-08-30/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.gz=e2763a910eb0305fbad6200fdb70e21c80f6ce61a29c9ef6cfdd134e54fa149e -dist/2026-08-30/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.xz=b8ff26519a2b6087969497e02e1a958b283b2dbd5741c5f9a531bfadb7f55b4e -dist/2026-08-30/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz=f83eba99f2c4ca3c8e72debd3abfc81c4e8e12b78f33030a2ec0bca467ff1026 -dist/2026-08-30/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz=9da6df6e5b98e59dad134fa7eb6fc74cdd7590799e5d77b885675d3301cb5ae5 -dist/2026-08-30/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz=ab48c3b5e6a1c920d2776ada7f2c7a262c6456c70c4051c55f24067768f36011 -dist/2026-08-30/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz=d6a6ff842d3004c14a39349c62453b5279085bcf44f2c3f101bba074249b6393 -dist/2026-08-30/rustfmt-nightly-powerpc64-unknown-linux-musl.tar.gz=7790a116d7598a29c083e8defa140b3c69dff2fa8632abe16c0814e3fdceb771 -dist/2026-08-30/rustfmt-nightly-powerpc64-unknown-linux-musl.tar.xz=090ff92dbd36a9d051e48545672099c40917f5cc781fad0075d1db73bacc9b6b -dist/2026-08-30/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz=6a448b720e8b8904a7f6b160891dc711165b043aa2f2700f1001220841da7383 -dist/2026-08-30/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz=47850b2456b8f28493268987e10ee2197f6bc49517b9338c469346e42c4ea524 -dist/2026-08-30/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.gz=51fa8c6d2e9144b5086ebcd91580a7b2338e42294a0db614c2a5b40c2817f074 -dist/2026-08-30/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.xz=09af68ab10528ac45be29d2d7671a5cbbd0c508d842c811448d1b1a0818e4ebe -dist/2026-08-30/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz=4ba0481a7d6111f1197c44f9475ccfe814a32cd13ff218b7a6f96fa6b188dc07 -dist/2026-08-30/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz=cb078f0a92905a6ce410866e2c0f93ecdc12fc2b17716e8ab503531b72cd2f41 -dist/2026-08-30/rustfmt-nightly-riscv64gc-unknown-linux-musl.tar.gz=5c8f989fc890a8d40b88d03002819dba392def655ff4a297eae83e24635599be -dist/2026-08-30/rustfmt-nightly-riscv64gc-unknown-linux-musl.tar.xz=ac6a2d15f43100e71a401f334eadc59d76e554fc7e2e4c67870c24f05c7ac682 -dist/2026-08-30/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz=78f43dc32a140e6b84c5b50e319b5d779d8d3a6a63172df5b0a7df984cda2919 -dist/2026-08-30/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz=cdc738d4e7db3489ec4be89db19fb6beeefedc09f0f99bfc3f4177e5a0e29e39 -dist/2026-08-30/rustfmt-nightly-sparcv9-sun-solaris.tar.gz=8b04309e8d8ed92f52ee9656b0c3b694681c9c867bbadb781c14df8d2f25a189 -dist/2026-08-30/rustfmt-nightly-sparcv9-sun-solaris.tar.xz=fa880983d380d71b52468551aeea00502c04a1c84c1976b82ad137031b63e61e -dist/2026-08-30/rustfmt-nightly-x86_64-apple-darwin.tar.gz=0faa8f222a300d58a8d815a49625202da2c93b89d655de9f8105fac8fa3b49c2 -dist/2026-08-30/rustfmt-nightly-x86_64-apple-darwin.tar.xz=860418646c51c8a4309b5f64700e9cddbaad55edd33776bce10341d069a4842b -dist/2026-08-30/rustfmt-nightly-x86_64-pc-solaris.tar.gz=70dd11166440c56c5d39ad393f22904b0d043c4e128ceed9b35276b1f7c88814 -dist/2026-08-30/rustfmt-nightly-x86_64-pc-solaris.tar.xz=0c0224c46c41018eee3fd6373a3a50869faca604acf5985627a43d381bd023bc -dist/2026-08-30/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz=e3ae658cd8daf76fba66422c5b63d335a73c6d1abd0b0f6b88c385467779f876 -dist/2026-08-30/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz=372750b8dca20904cc7716f6c999a581ecbf67537bbc1a340226cc4d1cf5f01f -dist/2026-08-30/rustfmt-nightly-x86_64-pc-windows-gnullvm.tar.gz=3edebbe5ca7cc0e9cdac3dbd715caea022c890f2e16dc0798f8517c324dccc8b -dist/2026-08-30/rustfmt-nightly-x86_64-pc-windows-gnullvm.tar.xz=bb6cb837ef4e0355b946a9e8af2d8a4e9a89e06f68f6a0f92adfdf56eef1d583 -dist/2026-08-30/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz=7b5a4c70a3223e4987e216f9853833c87d347c5e4daf6328eda4f2e434e3dcc8 -dist/2026-08-30/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz=4040814b80224dd9ee4ad051a491c5c7cb345ed41c2bce1070c13faf785cffb9 -dist/2026-08-30/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz=529dcbadb35869dd2dd39e4b81929dcd5fc74d983e5544b61e66a663dfc40fda -dist/2026-08-30/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz=20c86d4a327b5b227dddd1d9a900f5314f25728b5f1cbbf970d7a8bf2c8b039c -dist/2026-08-30/rustfmt-nightly-x86_64-unknown-illumos.tar.gz=b6951008db1f2b95e35dd207bdf95040bb0bc6875a710a744d81a9769e24f91d -dist/2026-08-30/rustfmt-nightly-x86_64-unknown-illumos.tar.xz=c502feb9fb19f971450201f5d396d9e622169884ab2fa42b14ca4685e2ec485e -dist/2026-08-30/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz=76acd957f09291ee38b993aea1511d7ed4247fedeed07b90368efdd4cd216e3a -dist/2026-08-30/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz=5a9b8925bfbe5c244e6f1c84ca821bad2227ec374db11f0d593830f2419742c8 -dist/2026-08-30/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz=da93547e78b5b8d587e35d15df2eb789ad4f075bb82d4605b5f4abe49b7fe49d -dist/2026-08-30/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz=688e31843695905b5e67b2a186fa4e509c860befc70ee533195710f784787b64 -dist/2026-08-30/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz=229cd762e308882adbfaee3c50e1a32bf88a3e1c488ab9998558a90716340a45 -dist/2026-08-30/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz=47e7a7cf0eebb9c62185e5506ff1aef012709b36c7433181d774b8cd1a75567c -dist/2026-08-30/rustc-nightly-aarch64-apple-darwin.tar.gz=25ffe92502f44e574a72b9493ee0b1a4e0e873c1c931f1a07aa0e478ceb170e1 -dist/2026-08-30/rustc-nightly-aarch64-apple-darwin.tar.xz=9d494a6b72761dee6ec3c0ec085d5b29838565ef45bbb67b097caf0aa4750304 -dist/2026-08-30/rustc-nightly-aarch64-pc-windows-gnullvm.tar.gz=576b8ba2a8aab889df0e673d434402c38d22efcaa4a8cd2f005c01063cd80601 -dist/2026-08-30/rustc-nightly-aarch64-pc-windows-gnullvm.tar.xz=dbd428ee786b17c9a91039a2274e01d1adf14d6af747f78c32d68a62a5752e35 -dist/2026-08-30/rustc-nightly-aarch64-pc-windows-msvc.tar.gz=395f1fd4b0286f70d4d48d5b1aae4a8f7ceb0e0a916fdfb69cbe2c00a1cd31c6 -dist/2026-08-30/rustc-nightly-aarch64-pc-windows-msvc.tar.xz=33fbd7251609d17dcedff1de1d6b34e5aa257411229e9e7c0066d51b3b22d23c -dist/2026-08-30/rustc-nightly-aarch64-unknown-freebsd.tar.gz=954f471480070c222a6316e1646770b6472c007b89f68ea61a713b153210b960 -dist/2026-08-30/rustc-nightly-aarch64-unknown-freebsd.tar.xz=9f19a67717d4710b3e445fbe84591da5fc5be7b6c6f07e5e5bbcf978243c4070 -dist/2026-08-30/rustc-nightly-aarch64-unknown-linux-gnu.tar.gz=d1cdfc3cbcb3f131b4307b83389da6c0385f733ef6c68e569f280c1d10bc6a18 -dist/2026-08-30/rustc-nightly-aarch64-unknown-linux-gnu.tar.xz=c967d3a8cf286f5197759160f90f823eb82c0dcfbdae4e43580c4e4336fee294 -dist/2026-08-30/rustc-nightly-aarch64-unknown-linux-musl.tar.gz=667e1683c157443a221f00467c927651756a593f5bec5f438b55aaadd740dd27 -dist/2026-08-30/rustc-nightly-aarch64-unknown-linux-musl.tar.xz=e4680cff6ef155809ede44b4c4a5f23ade8f920fd54e877ed93b6166010930cb -dist/2026-08-30/rustc-nightly-aarch64-unknown-linux-ohos.tar.gz=fd6c0c4450fe0b8f586452046c80b424935c1fa83181358c70c19b25ddb0dbed -dist/2026-08-30/rustc-nightly-aarch64-unknown-linux-ohos.tar.xz=b2c7e0cf7f1b71e141b248e62b7d10f0999f48fffcd6dc07318c3205c79ca505 -dist/2026-08-30/rustc-nightly-arm-unknown-linux-gnueabi.tar.gz=df20767bfd43a0fd4c5665ab4c0d0f0511b3356918f54b7c3074f6a1b7298aad -dist/2026-08-30/rustc-nightly-arm-unknown-linux-gnueabi.tar.xz=c43366c230813ae776aa094e3422a445b4a3d513f98ba6d7b78cb5ad2d0a2a8e -dist/2026-08-30/rustc-nightly-arm-unknown-linux-gnueabihf.tar.gz=0c0c872b14aa8dc461989326b53c2692b46bc24bb8125d4f90e5b977f5db3bcc -dist/2026-08-30/rustc-nightly-arm-unknown-linux-gnueabihf.tar.xz=82e0eceb946f0a69f2d766c3fe2a5467852caddff960a66c5fcf9d14fa6daf92 -dist/2026-08-30/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.gz=4783a2cc2a99596fd187bad7f486f97018ffce918c34b4b022fd88f27053c967 -dist/2026-08-30/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.xz=c078badef6ab76e69f0fcbe6ca76f1037cb64d67e5a1feecee898ef6aca91521 -dist/2026-08-30/rustc-nightly-i686-pc-windows-msvc.tar.gz=340b68ba0802676c99da1784b0f03eb9e3cd7f78cf40fa579f97601c07dacb24 -dist/2026-08-30/rustc-nightly-i686-pc-windows-msvc.tar.xz=ac4c0562021b4f3076b39856eb16ab288f3d16d25c557cf954ce66f389449f9e -dist/2026-08-30/rustc-nightly-i686-unknown-linux-gnu.tar.gz=5ac857921af0b1b8d290a059eeb1d510ad4cc5f450b513b26a11be67885558ee -dist/2026-08-30/rustc-nightly-i686-unknown-linux-gnu.tar.xz=a01fb1c1721998e3bffe377a15663e911bcaaf497fc2a7f1dbef06125466febd -dist/2026-08-30/rustc-nightly-loongarch64-unknown-linux-gnu.tar.gz=46d1aa393ee0f736bf46cb6d8e975e66774a797a78918fc106cb1baf2115c1ee -dist/2026-08-30/rustc-nightly-loongarch64-unknown-linux-gnu.tar.xz=66ea19498d78291393f20ad6313fdb2359b7a685bb358d28c498042f957b7ce9 -dist/2026-08-30/rustc-nightly-loongarch64-unknown-linux-musl.tar.gz=27e138e7e0ecff208f09780f6cb51d4447fb75b5891c2e961c3b380670bf43e2 -dist/2026-08-30/rustc-nightly-loongarch64-unknown-linux-musl.tar.xz=16bade7c4f0644f4474622591f1ae83be1c7d6ad86aa23f1cd0bc29959e41555 -dist/2026-08-30/rustc-nightly-powerpc-unknown-linux-gnu.tar.gz=44207e156b7d05f8ffd05394cd0a5f2c4cf290e8a7d4be07a95e6242be738f85 -dist/2026-08-30/rustc-nightly-powerpc-unknown-linux-gnu.tar.xz=98c9309ccd0bc473ab1c373232c0ad483ce959a9b7b31cfc0b948c0dcff4ae55 -dist/2026-08-30/rustc-nightly-powerpc64-unknown-linux-gnu.tar.gz=bcaf935990ad326f9469b18514330bb219fac62773b1ca44cfd9f2639e22442e -dist/2026-08-30/rustc-nightly-powerpc64-unknown-linux-gnu.tar.xz=68242588d61346c4001c4bade2daec0611f3f079fe1d1e5debf08746d8ca2217 -dist/2026-08-30/rustc-nightly-powerpc64-unknown-linux-musl.tar.gz=34e4d626d1e484c3d8bbe2be39a6324b0a02cfc60b7bf13c561e6c41eb6b3917 -dist/2026-08-30/rustc-nightly-powerpc64-unknown-linux-musl.tar.xz=8b35dd518df6b7225dc7b8da30ef9fce97181bf3781e69fbd5fca67833c54457 -dist/2026-08-30/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.gz=66aca4805b3450a90bde12ddf6f2b9139fcab333bf4a33825d9bedf3842fccaa -dist/2026-08-30/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.xz=6861be8d2ca2fe398e461ec1d68eb4b6e5a589b5eb39b60ca4083579a54ccc07 -dist/2026-08-30/rustc-nightly-powerpc64le-unknown-linux-musl.tar.gz=7644e739c1afbe9a7be15878444e56340dbadea64f06799066e8d14c1e736ebf -dist/2026-08-30/rustc-nightly-powerpc64le-unknown-linux-musl.tar.xz=fe4e05894da7fb3bff0d4cc725e0e80dc78fa55d97bbb0588ce5383fdec72407 -dist/2026-08-30/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.gz=e59bb87d54ca41779b984fad84ad20144e4bb224ca187ca6a51ee1d15ec5bff4 -dist/2026-08-30/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.xz=d997aa633692996dac26f68a246b3dff4a597595a08808dd965e01f05bc502b6 -dist/2026-08-30/rustc-nightly-riscv64gc-unknown-linux-musl.tar.gz=3d5692e095f9b23a05aa57b1de83fee4a6ab9ba1ba85f0d6a7a59997814f762c -dist/2026-08-30/rustc-nightly-riscv64gc-unknown-linux-musl.tar.xz=85b6a0d7e0405537ef929819b957a37723010e8cefa82c558a7d0c42e645fa59 -dist/2026-08-30/rustc-nightly-s390x-unknown-linux-gnu.tar.gz=668c203cfeed5b0286a5928d9b98e289f0ec32fbaf21fe61534ac9bb5c9236fb -dist/2026-08-30/rustc-nightly-s390x-unknown-linux-gnu.tar.xz=8fd2005a2f7233db1399af86d9c8b48b959b3627faa5c8a3dde1d6db67f72562 -dist/2026-08-30/rustc-nightly-sparcv9-sun-solaris.tar.gz=d8767b7abc10f45dcc53258adf4ca2c062d3f063f830791b6c475537a7009704 -dist/2026-08-30/rustc-nightly-sparcv9-sun-solaris.tar.xz=58ed2a29ed983d6dc632ea7dd68532fef902dbc44a68e2cd668323fa9676438d -dist/2026-08-30/rustc-nightly-x86_64-apple-darwin.tar.gz=27bd68fc60a35b8f56b538cfe9d3cafe156dba2b8cb25bc04d488505882157b6 -dist/2026-08-30/rustc-nightly-x86_64-apple-darwin.tar.xz=a452c953f1641c6d2ffaf59d129f38b7fa2c6f8c29882c3d82408b50e4c4c95e -dist/2026-08-30/rustc-nightly-x86_64-pc-solaris.tar.gz=c12868cc994812742945591b906f761f9a5ca0d09a809da69f3b0f7de731a707 -dist/2026-08-30/rustc-nightly-x86_64-pc-solaris.tar.xz=9974a6f3bf2b784771114ae846f7fb54746525e5dd921f4832410e40a00502b3 -dist/2026-08-30/rustc-nightly-x86_64-pc-windows-gnu.tar.gz=6ce5a31fd706d532679c5db6bfca375fb21f34df077bd7e182f6e38cfb852561 -dist/2026-08-30/rustc-nightly-x86_64-pc-windows-gnu.tar.xz=72cb129e77c6c8f48e82a82d894a5fdced57d74c8a7ade88589c3baf1385ef26 -dist/2026-08-30/rustc-nightly-x86_64-pc-windows-gnullvm.tar.gz=e896320dd35b2b43943a29491bacfc696d5e68c3c9a7c0a4fbab6cc57ca5afe7 -dist/2026-08-30/rustc-nightly-x86_64-pc-windows-gnullvm.tar.xz=93b8fcf4ed8aaa3a9ad1ed14086ed4bf44cc38db920f86eef0c1f42da37c9665 -dist/2026-08-30/rustc-nightly-x86_64-pc-windows-msvc.tar.gz=5368d930648a6f04b20967d6183e53925bb930d047271b3683cf3d663d893f23 -dist/2026-08-30/rustc-nightly-x86_64-pc-windows-msvc.tar.xz=e696551fc4f4b0573c60750826ee93113a95a4ee7361af3752e49c5a386abf69 -dist/2026-08-30/rustc-nightly-x86_64-unknown-freebsd.tar.gz=c2ef207e1ebd2c408c91a63cffd5986ae01cf265e535947d12bc43a2015eae5f -dist/2026-08-30/rustc-nightly-x86_64-unknown-freebsd.tar.xz=5025d42e80e6d377a9dba466ef3e81ab817ff6e098c2bbde4923afedf0fd7893 -dist/2026-08-30/rustc-nightly-x86_64-unknown-illumos.tar.gz=876fbafdf17e645e870d4b6afcd5465ee155e910309cc91bad10dc885d178b0a -dist/2026-08-30/rustc-nightly-x86_64-unknown-illumos.tar.xz=f380f5b0a7ec8c537b8bd53528b3a407cd69449baf0d6c513f03c2de047ba3fd -dist/2026-08-30/rustc-nightly-x86_64-unknown-linux-gnu.tar.gz=4e010b6ff2f3923b31c44cfcd74d33389dd2de01521e4f22b90c58294fa490fa -dist/2026-08-30/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz=d87f9b9bc000e028ce81b49d64352c59eccc99cbf282c9c3bc834a72d9aeaff1 -dist/2026-08-30/rustc-nightly-x86_64-unknown-linux-musl.tar.gz=2332302e83712fa814b119bea274fdb1f70494fc7e8ea6a23c88f20bac3ac6fe -dist/2026-08-30/rustc-nightly-x86_64-unknown-linux-musl.tar.xz=2a43c8666489856bf8ce91729c2eeceb88a3d38bc6cacccc8c598373afad1247 -dist/2026-08-30/rustc-nightly-x86_64-unknown-netbsd.tar.gz=094c2005eef2f8ca0e466da77af70f7b1cc6c837e8b2e933c26d66b8bee3e06d -dist/2026-08-30/rustc-nightly-x86_64-unknown-netbsd.tar.xz=a68f04797c3ae9db0e31d9670fa9ce17efd5ca3054bdc4de06ca97b143cb59f3 -dist/2026-08-30/rust-nightly-aarch64-pc-windows-gnullvm.msi=4334c750cc57337150cf11dad2b10c5d65f1866e7f672093550322fa696d888f -dist/2026-08-30/rust-nightly-aarch64-pc-windows-msvc.msi=b2c56f022de67eb6045b1137709a5055c139d88e9fae8175b932a7da1f2fc1d7 -dist/2026-08-30/rust-nightly-i686-pc-windows-msvc.msi=bb18feb2dfdd9495289b70a6bb41584489dfe14de19961fe32fe6d249ff60c7a -dist/2026-08-30/rust-nightly-x86_64-pc-windows-gnu.msi=18d50140987657604cbf1a980b2249243afc6a734f725475a25a3f96c0edf72c -dist/2026-08-30/rust-nightly-x86_64-pc-windows-gnullvm.msi=83cb268a0ee1c120357082f9711d2a5eb05e58d433bd52dabfabfcbb31df6200 -dist/2026-08-30/rust-nightly-x86_64-pc-windows-msvc.msi=b19b6e4ef4b022f183c45ebba60ea8b6bcbcb06c60345231fc602dc1aae0ba5a -dist/2026-08-30/rust-nightly-aarch64-apple-darwin.pkg=2fa1f66fd223a57405047c498614d71fd3bf087a98063e2f652c9899d2a5960a -dist/2026-08-30/rust-nightly-x86_64-apple-darwin.pkg=20a2d983a8ef414f2c61773f056bed1d300c24e5f65252c1a0b01fc230030e28 -dist/2026-08-30/rustc-nightly-src.tar.gz=a1768b2d3627caf5f7fbdf5918fc1586180a7a2f2076403d1cb4ab11ea3eaec6 -dist/2026-08-30/rustc-nightly-src.tar.xz=b03a014a5a4f8bb1d0fb14d9f01e3a5cb9df64c4d4208463f4a6b977d2287d9e +dist/2026-09-29/rustc-beta-aarch64-apple-darwin.tar.gz=08cb5170859d6ebf335c279df46cd8421e6478a0199d0cd2709c0afbe8a92dc2 +dist/2026-09-29/rustc-beta-aarch64-apple-darwin.tar.xz=5ec30352066404071c4c9a077e19b211e1e2865babec422c1b5305882fccb2f8 +dist/2026-09-29/rustc-beta-aarch64-pc-windows-gnullvm.tar.gz=24630c62a81890608c84b0c5d1ccba809c7414c9cfa6737b092bdcb3defb6797 +dist/2026-09-29/rustc-beta-aarch64-pc-windows-gnullvm.tar.xz=b9db7c497276274a7e5af4ae6666d4d77bb9ebe8a389513c1628488f4f86b950 +dist/2026-09-29/rustc-beta-aarch64-pc-windows-msvc.tar.gz=75b429e1d1f34317877ce3838e4e5c79ea579a051314fce9755ecd92782031e4 +dist/2026-09-29/rustc-beta-aarch64-pc-windows-msvc.tar.xz=04fb7d879ff7b00ed5cc3a054bfd8f4dfcbcc039c1000334db34284b6b7d0d27 +dist/2026-09-29/rustc-beta-aarch64-unknown-freebsd.tar.gz=4a4ece6cb6b686d844d3d93c6b5cc50dcc6fd1a2aac17d7ff7606cc3fe103980 +dist/2026-09-29/rustc-beta-aarch64-unknown-freebsd.tar.xz=2bfd413768c42feade1655007488e4cb913fc5b295763dd7835224e5a57ab198 +dist/2026-09-29/rustc-beta-aarch64-unknown-linux-gnu.tar.gz=c60b31aec10e123a0c461b6f86b0f7d18dd22e706abe76ed4d9ea010c76e1fc1 +dist/2026-09-29/rustc-beta-aarch64-unknown-linux-gnu.tar.xz=ecc23a873a11fcbe2b81e47763204f93a1f91e551c0fa8766262ffa2cc3d0538 +dist/2026-09-29/rustc-beta-aarch64-unknown-linux-musl.tar.gz=1634c9acebc04a266987633062e0db4d4b5a92a568ae55cb146cffba8108038d +dist/2026-09-29/rustc-beta-aarch64-unknown-linux-musl.tar.xz=4361c19fcc2cf8e26bb52489e7dd3e96500c6e6d54bbeaa1493d0b8c8d6a066e +dist/2026-09-29/rustc-beta-aarch64-unknown-linux-ohos.tar.gz=318e6fae90b8da5cad5eea22d358b848746a8f4fcf60d164a2d630f03d12facf +dist/2026-09-29/rustc-beta-aarch64-unknown-linux-ohos.tar.xz=7d80d8a51de0f9a8f0af2ac170e5156a14dba2f3952946a845917f772e9c7dbb +dist/2026-09-29/rustc-beta-arm-unknown-linux-gnueabi.tar.gz=7dc6dcfa998512791269d52f9ee0097bfea867c3a02a54c676c88a69a40f8a23 +dist/2026-09-29/rustc-beta-arm-unknown-linux-gnueabi.tar.xz=b791b3ed832746088ed4ec8378f9b226a7900c4fd4cbe6436f7425191ef1013f +dist/2026-09-29/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz=0a1bab07aafcc98cbb130b81b3ad9eceff1194037110fc8ed7fa6650a572ab07 +dist/2026-09-29/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz=677ee4e95a57d1e498ac4bd9ac05084675c03c58de6e85320d7d6d0c725e6ebc +dist/2026-09-29/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz=e7de0b8ec0715ee54866b533f22ffe23b560e58884228f145e0458a3dfb0450b +dist/2026-09-29/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz=6316b4bc4931c2811c90d7dff9df5bf7875531c6cf985c065f78b8a0577e370f +dist/2026-09-29/rustc-beta-i686-unknown-linux-gnu.tar.gz=8229d9b5bb6497abdedf15731c15c2ae85c8050bf0995a576685716e9d929ac8 +dist/2026-09-29/rustc-beta-i686-unknown-linux-gnu.tar.xz=428ba34f2281ddbb794a2062e446ca6e5b0cd90a84829186f769bb8d2190a28c +dist/2026-09-29/rustc-beta-loongarch64-unknown-linux-gnu.tar.gz=04ab37c4e7e9dde5a2332975b6f7cfe6c02319e89f9bab2128945656ec81110a +dist/2026-09-29/rustc-beta-loongarch64-unknown-linux-gnu.tar.xz=50d6c00c82dfb7f10bd2c49057822e082c3bc1f6cb7a08e06ac2a5a350e1d1eb +dist/2026-09-29/rustc-beta-loongarch64-unknown-linux-musl.tar.gz=9fc8d551462b4a845c18f163c2787e256d00d4edcaca0721da63ab57002f418c +dist/2026-09-29/rustc-beta-loongarch64-unknown-linux-musl.tar.xz=ce74c12908e44b8b87fdb371533b6a81b5cbc8967e04209b833c87940f4a1420 +dist/2026-09-29/rustc-beta-powerpc-unknown-linux-gnu.tar.gz=e7417324e10e59b5768daa4b5cd01fe92a624739a9b450693fbd5902227a3561 +dist/2026-09-29/rustc-beta-powerpc-unknown-linux-gnu.tar.xz=68dbbc9c7f6c1f2725140c0edd58e3607cd31d4406e7514cb32b9a894f3af39e +dist/2026-09-29/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz=93b02c260b6ace5ea43fd716a100abfb2ed3de24a53e3032794c35858fb0f618 +dist/2026-09-29/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz=e78037526d62c567cb2aa1336c0ed2bfde01e2948833c10a3585040dcf26f343 +dist/2026-09-29/rustc-beta-powerpc64-unknown-linux-musl.tar.gz=3ee5c15579228dc36bef6b254fc235ff57426be8dba7bb358beaed0f1791afc0 +dist/2026-09-29/rustc-beta-powerpc64-unknown-linux-musl.tar.xz=053fbdbfdce20029dedadf7218daabee5b181a69c20b46c2849970f9fa91a7a2 +dist/2026-09-29/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz=a0d9ad02f3f031d54080f3a64f97d099f38de9b324e3906ecd9f873f8353167f +dist/2026-09-29/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz=8a81d01e78892fbfadfb22b59c6264bcaf4343e36afa806d94b2c6f51eedde43 +dist/2026-09-29/rustc-beta-powerpc64le-unknown-linux-musl.tar.gz=5780592d8b617c2a2b3c1a0c142e71ba589ce5a6ca8d05840668ed9b0217e807 +dist/2026-09-29/rustc-beta-powerpc64le-unknown-linux-musl.tar.xz=e71f58d7383c5ce823ede7d7d032349a3616ec3c3fcae212786b24ec4e427fcd +dist/2026-09-29/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz=3f113901c2996c2bc39891cc6146fa4c2d8800029258186805b20a34dac0ab13 +dist/2026-09-29/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz=2b7bfe0f71378a76c4b5bc04224f728c02b0cf21d66999d86a33b754ff80b52a +dist/2026-09-29/rustc-beta-riscv64gc-unknown-linux-musl.tar.gz=b4e6d98741d7293f16ab9218e5492664765f43cbc7f8537b45ef6815ca83b0bb +dist/2026-09-29/rustc-beta-riscv64gc-unknown-linux-musl.tar.xz=98141ecd5a1426d8b168e5f181bacf083150895e446840d2243747159bdc3773 +dist/2026-09-29/rustc-beta-s390x-unknown-linux-gnu.tar.gz=5b821594334d8f9b1d07264df899f22fe7a041d7417618b8e7809973fd45ae22 +dist/2026-09-29/rustc-beta-s390x-unknown-linux-gnu.tar.xz=5c52006d530dbd13711e01402dc533c1d664f77e4f1c997179b900ce29ccc617 +dist/2026-09-29/rustc-beta-sparcv9-sun-solaris.tar.gz=7db0a06f0450e2f47ebae3912fff144d416cfe58877ff8996d1335af634bc7db +dist/2026-09-29/rustc-beta-sparcv9-sun-solaris.tar.xz=3d8c85b04fa3d1947840867c5ffb750ae341ad17982aca302abc754e43e1ff42 +dist/2026-09-29/rustc-beta-x86_64-apple-darwin.tar.gz=2bf2b522df594f578642b9c1670b265a02b937b48b5b47ace32a53ad7b569200 +dist/2026-09-29/rustc-beta-x86_64-apple-darwin.tar.xz=ed18e8348eba7bbc6adfd6b95292d802834022a243bd9739cfdbc5d69d8e7e2f +dist/2026-09-29/rustc-beta-x86_64-pc-solaris.tar.gz=cce6ce17920028ed75d83134bae4e02704bf497002b654ff629dedd8294a8335 +dist/2026-09-29/rustc-beta-x86_64-pc-solaris.tar.xz=6d5cb6a91f23044f79c73954bf392989d031412e4d6b43e18d4256d7a3c1fb0c +dist/2026-09-29/rustc-beta-x86_64-pc-windows-gnu.tar.gz=efea45514b6313626db60fa3e922fbd812454004c34c79e3770050826da93022 +dist/2026-09-29/rustc-beta-x86_64-pc-windows-gnu.tar.xz=5a32405ad9ffe040516efd73d961ca53f89c1cd8b4bd8eadb900caef8303e0c3 +dist/2026-09-29/rustc-beta-x86_64-pc-windows-gnullvm.tar.gz=6e911af2547846c6baa414b7d85288d954293df34b5cc87805aa6bb06570dc48 +dist/2026-09-29/rustc-beta-x86_64-pc-windows-gnullvm.tar.xz=e1ad982eccf000e89da01d6910cf918d2231e3cdcc3738f6696d3d57dfa5f353 +dist/2026-09-29/rustc-beta-x86_64-pc-windows-msvc.tar.gz=fd2d7a0809aa431f3f2365f6a98901f359514d47967067c936ba68bc47437d5c +dist/2026-09-29/rustc-beta-x86_64-pc-windows-msvc.tar.xz=ea968def39864e80a462e6be752a7fab2a8c9da7f14799b138ddf0e9bc1f534d +dist/2026-09-29/rustc-beta-x86_64-unknown-freebsd.tar.gz=87deb753d7a5af1afc02cc988d23ea0d42a9573b5e9abbed23ff8f345a91056e +dist/2026-09-29/rustc-beta-x86_64-unknown-freebsd.tar.xz=2dbaa50d6542748cd31dc9f13d4fb82874825634f027317b830121937b583d76 +dist/2026-09-29/rustc-beta-x86_64-unknown-illumos.tar.gz=f682c3ad10ae60d43e2a54a3f624782c9ef0ae61fb0e9ed95a867794e0bff256 +dist/2026-09-29/rustc-beta-x86_64-unknown-illumos.tar.xz=0ad43335ebc8b0d1dbdaa34aa53fbc0cba9a8186b5edac080f5a7bf7ed7cde7d +dist/2026-09-29/rustc-beta-x86_64-unknown-linux-gnu.tar.gz=c567617a07be89de49b009162604ef9bfe4bc542419d405c444e39714591f4de +dist/2026-09-29/rustc-beta-x86_64-unknown-linux-gnu.tar.xz=e562beb9a7ccda3e99c79b2f37149fe5dacdb1f3bb7bdb77ec94b1a947daf009 +dist/2026-09-29/rustc-beta-x86_64-unknown-linux-musl.tar.gz=cea2a33fbacd6b80a7e19592a2ca8f58eb1613589dbd8c906ae78b5723cebe6e +dist/2026-09-29/rustc-beta-x86_64-unknown-linux-musl.tar.xz=e0a1889de08bc4c91006e1d335ed7215257de80bbf6bae375906021ada6dd95b +dist/2026-09-29/rustc-beta-x86_64-unknown-netbsd.tar.gz=262db7c3e54259876fa7f03775b73a5f190cd4812060feac04c13a03db1c5343 +dist/2026-09-29/rustc-beta-x86_64-unknown-netbsd.tar.xz=547adcdda9c63d0f63f8351dee197ca843628bfb1f965e8305f947ca128a2f69 +dist/2026-09-29/rust-std-beta-aarch64-apple-darwin.tar.gz=12a712c301ab944900438c0da63b45ec133efea74b398e708faed172e2960042 +dist/2026-09-29/rust-std-beta-aarch64-apple-darwin.tar.xz=d9705141deb514ad2d721ae34174c33ab5b64ab7181294886cbbfdc3f9b999ae +dist/2026-09-29/rust-std-beta-aarch64-apple-ios.tar.gz=3a65781d05da1559788ea8bc2df4d84f12fdc4194cfcece0863ce786ead5ec13 +dist/2026-09-29/rust-std-beta-aarch64-apple-ios.tar.xz=728a2be2bbbacf3866e007d92b9f0857a9b7da0d0c47c16e296fba25839afbf4 +dist/2026-09-29/rust-std-beta-aarch64-apple-ios-macabi.tar.gz=b445edeeddab84fac96c089d73f3a87e485415583ddbff217092058c14c79c9b +dist/2026-09-29/rust-std-beta-aarch64-apple-ios-macabi.tar.xz=db73dd68d6a0458b7ab9e49f2e331a460f14eb4598af19f2a6f642b85db3c947 +dist/2026-09-29/rust-std-beta-aarch64-apple-ios-sim.tar.gz=09d2bbd7f44f6245ff9e7ffdccec0a491e9195075232cfca2f247329b0371aa8 +dist/2026-09-29/rust-std-beta-aarch64-apple-ios-sim.tar.xz=1faee83ec196e8a2876c06dd5377a542aac1aa32c7d3b00ecf31a3713b50555c +dist/2026-09-29/rust-std-beta-aarch64-apple-tvos.tar.gz=e8d1cd23d78eacb8a4be5cb465b94902b5351573b4c0a47073205ec4c8028397 +dist/2026-09-29/rust-std-beta-aarch64-apple-tvos.tar.xz=42f013a0c3ad79263d651b194dab7b08734f6a96dd5943535706f4a8990ca819 +dist/2026-09-29/rust-std-beta-aarch64-apple-tvos-sim.tar.gz=a5381d8ed927c6e1c720833462898ca6c4bdae6702e308352f0dc5698e3fb93f +dist/2026-09-29/rust-std-beta-aarch64-apple-tvos-sim.tar.xz=6ffd6d6d7bc230e28a78f7230b0b91fc71dda6af018feae186b0eb48910234c6 +dist/2026-09-29/rust-std-beta-aarch64-apple-visionos.tar.gz=90a82a478d671ed7269f7cfb5eeaa70cb914d9424dd741c7a2ad2b4a26840b6b +dist/2026-09-29/rust-std-beta-aarch64-apple-visionos.tar.xz=1c700b45a80f7304fe00977cb2fe46975ce16ac31494f2e5afc825465b475d02 +dist/2026-09-29/rust-std-beta-aarch64-apple-visionos-sim.tar.gz=50113f0e4b4cbd8c528978a6ead8fc3fcf15dc6b1c4bf97488a61da8d9245dfe +dist/2026-09-29/rust-std-beta-aarch64-apple-visionos-sim.tar.xz=fbd9564bbdd9d0d43fb00fe67a83fad16a58d4545982344bdbacad93dd53b110 +dist/2026-09-29/rust-std-beta-aarch64-apple-watchos.tar.gz=6ba080aa355f112b74c180ee6ef1ed182bad260f35e4816bf22f758af1782286 +dist/2026-09-29/rust-std-beta-aarch64-apple-watchos.tar.xz=979686c0acca1dfe4bbc1906bbb7dd268b90b9aac3dd97a06f105fbc3260f78d +dist/2026-09-29/rust-std-beta-aarch64-apple-watchos-sim.tar.gz=4d3459fa110abab0d95e1748f5426f162d746635529839d88360dd7c186a9a0b +dist/2026-09-29/rust-std-beta-aarch64-apple-watchos-sim.tar.xz=303623e7c027952c164dae9e8662c1141914a35c7af148de8e7b0cd813594384 +dist/2026-09-29/rust-std-beta-aarch64-linux-android.tar.gz=cf7c1de2dbdfd3ac05131e63cc6285c5d76e8ae85c186350a164ce1017a6a1c9 +dist/2026-09-29/rust-std-beta-aarch64-linux-android.tar.xz=790d34c2a756240cad1da2662132eb11ffa2b3e844ba639c3561aa5ba0d3ae97 +dist/2026-09-29/rust-std-beta-aarch64-pc-windows-gnullvm.tar.gz=dde06b88182f2f6bad4d7b2cfd40a99ecccac1fdc2dee3ef5e65f425e706484f +dist/2026-09-29/rust-std-beta-aarch64-pc-windows-gnullvm.tar.xz=f901b2689461d3b38b5f2eec0a6f5c6795ee3638da97a433c1fa84ecff11a082 +dist/2026-09-29/rust-std-beta-aarch64-pc-windows-msvc.tar.gz=943394d1133a8c82da94d97ae738472666a9ae4f2d04c504171d8bae34a13f21 +dist/2026-09-29/rust-std-beta-aarch64-pc-windows-msvc.tar.xz=6777866c103e45b453d7ad36d3addc3bfa09684b6e6e4abe18ea5ef782ade595 +dist/2026-09-29/rust-std-beta-aarch64-unknown-freebsd.tar.gz=5b2e8bf0dfa7a5be26a3d969c708e5181149982877a3d571d75ad2c118b56ef8 +dist/2026-09-29/rust-std-beta-aarch64-unknown-freebsd.tar.xz=b5146618aee2bdeea3f3da4c6afe4b07d6b8af0440f6d954985973d12dc7ec9e +dist/2026-09-29/rust-std-beta-aarch64-unknown-fuchsia.tar.gz=e92c49005a09ba23a21cc74364f30769997f7b6b81c2205ee7e9d9413fb38fb7 +dist/2026-09-29/rust-std-beta-aarch64-unknown-fuchsia.tar.xz=fca1e29e1856abe923cccef58d560074d2d5adac5211007c00e0657677ea7f57 +dist/2026-09-29/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz=9d02af2c761d752a1371e301753a432031984cfaf53d9812c8ce540bfbf6d716 +dist/2026-09-29/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz=e991bfc2818af00b53cfac0535d094135712339b8f1289186a6d0f8d209ff351 +dist/2026-09-29/rust-std-beta-aarch64-unknown-linux-musl.tar.gz=405f757b12a00629b2f2dfd0ebf3c372d257047fd8ddf348088ed78235fc0e7b +dist/2026-09-29/rust-std-beta-aarch64-unknown-linux-musl.tar.xz=c8dd6d868b2f6b316433bb1849a809450bae0fab9305c60b2123816066e69d71 +dist/2026-09-29/rust-std-beta-aarch64-unknown-linux-ohos.tar.gz=4fb73dd34675a6f27b6426f58ad7be879ff49ab8e98c3ce003e49651bbacee4a +dist/2026-09-29/rust-std-beta-aarch64-unknown-linux-ohos.tar.xz=a89e58073a28be60905bb7647f0e60b4520639f5460cfc3c6f1e25c581939864 +dist/2026-09-29/rust-std-beta-aarch64-unknown-none.tar.gz=b7bb5e2d07dfb49786bc1f9de858c3a41a6c4723df4e48152905616867389617 +dist/2026-09-29/rust-std-beta-aarch64-unknown-none.tar.xz=0c0fc594ad1d67be737ec709d3e10ed319d3e5a4263d1951a9bfe664bd2504b3 +dist/2026-09-29/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz=25ac7161cf32dad13f4bcefc64cedb25963f4f61995e9531650c8a2636536263 +dist/2026-09-29/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz=440165cb33ab38c651e798c0ddca0195f36f7ab6de981c37b4f41c98c2cd6f38 +dist/2026-09-29/rust-std-beta-aarch64-unknown-uefi.tar.gz=b5ba39fce8b242d9f77fff252e0da2b4a8d455929d247ffeb522d063eccc3253 +dist/2026-09-29/rust-std-beta-aarch64-unknown-uefi.tar.xz=16915d999525cd8e1488d6378ded9c11f58b0390e2a03f0dfb89d32b3d2339c6 +dist/2026-09-29/rust-std-beta-arm-linux-androideabi.tar.gz=e1d3999e6ba68470df99c7836ce8c51232fb62785ea7c96c6b2a456d468655f2 +dist/2026-09-29/rust-std-beta-arm-linux-androideabi.tar.xz=c23e03c5eea7fc437413e6d539ffdf43bfaf2f00d9f13db2e4d789e3edb31527 +dist/2026-09-29/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz=db082f4b98dd53be4f32b618d5494ec2fc2af75ac571d7aa1e93edba18ecf30c +dist/2026-09-29/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz=cdd51373266d628735f876c8acab3d658f1ec54fca4ef76511c45d7d555d598e +dist/2026-09-29/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz=62de66b7ac14a2d8b725be25b5f6af6246973ec5557ac631cff594177b1db726 +dist/2026-09-29/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz=fb86b6e2e3b556a63144c0cef982c200e2bca8d2d0a1db4b1374d0c7724107bd +dist/2026-09-29/rust-std-beta-arm-unknown-linux-musleabi.tar.gz=0f64d408c08361269324e8cea25053c5f542bda8b7afc5393a497808993254ea +dist/2026-09-29/rust-std-beta-arm-unknown-linux-musleabi.tar.xz=b05ec5e82bd8c93ceabbe2578dda16076e4b87daf315dc5baa54c8abd4e52ebf +dist/2026-09-29/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz=2effbb32422491890eb626dcf181cfb30499ba26c4f64d4a96c9e304c22dcde4 +dist/2026-09-29/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz=24bb014666bfadb3256c390b36c2369a324077036eb2cc1c6e2be825d6e8d278 +dist/2026-09-29/rust-std-beta-arm64ec-pc-windows-msvc.tar.gz=932df0b6c8289fb46a9f056b70921a4b1b9400c136dd45c0561f190802d10c1b +dist/2026-09-29/rust-std-beta-arm64ec-pc-windows-msvc.tar.xz=fcf67570a855d2e396f0019492767c8fabf00b20ba614c8e00ee9c987fb06b31 +dist/2026-09-29/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz=4805f236f0122d3a67e736031b2b530e9c1c4d7a1cf65039d7118d50985fe033 +dist/2026-09-29/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz=3ae97148bbabfee58ecd7db754eef720a084685d6df918713ee9103126f8b96b +dist/2026-09-29/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz=d5bc909741d6dabf577c4d26e9570e66a4fe0f7a253c61364f57bd3adf11c4d8 +dist/2026-09-29/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz=6bb41498459ce565e0c91228c41c23ba326cd4d964bb5554866cc350d30d2381 +dist/2026-09-29/rust-std-beta-armv7-linux-androideabi.tar.gz=04be64de9a88dc5fad743c7c9e3ece7ffa88fc8f6741bc7097011dafc3af867c +dist/2026-09-29/rust-std-beta-armv7-linux-androideabi.tar.xz=87450f7a9e275eb7dfd67e78eca210d867ce06e338b8a53eda0e5eebb2930b85 +dist/2026-09-29/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz=9d2c987cf665bd8490e43e66611cfae292c5bb74aacf7d36129cd71cadfe06dc +dist/2026-09-29/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz=0533fdb36d2e8ee0b1bf5274e000f7876ade0f6c6a289a4014b97bc41eb4b88d +dist/2026-09-29/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz=f6d92ec41459b974d5a1058b079560737261d263f218fa74982fb3d143117e02 +dist/2026-09-29/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz=b7ea4246a4a35d36596edba6f808b351abf99e0c861abfc5623d7a7cf2a125e9 +dist/2026-09-29/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz=004e933e4e1cf4c3bde0cb189b22c71bb58e84e9f9d0cab9288d46744c529b7d +dist/2026-09-29/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz=ceed6092180912b2e6bfa95cd7345d2a0da20aced84ef1626ee9945fa109b950 +dist/2026-09-29/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz=4acecafc1bec8a97b67206557e340d6de7fa3f6a39baef49e704e203798f12b0 +dist/2026-09-29/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz=b79c57d00374db2f33708752d0c0c1ded34c9282c1a14b7a155f5c8ef49b96fd +dist/2026-09-29/rust-std-beta-armv7-unknown-linux-ohos.tar.gz=2fd30887db6155ef25f02781322e79b6b69113f282e89b68e119677a3cf6c526 +dist/2026-09-29/rust-std-beta-armv7-unknown-linux-ohos.tar.xz=9d65af3b8227e2096f3f78cfa2207241d226ceacd6c5559d2ad682ab156c6842 +dist/2026-09-29/rust-std-beta-armv7a-none-eabi.tar.gz=141612e58714918c93ba2769723712e577c99e19b6d858e77d8677850e2dcf85 +dist/2026-09-29/rust-std-beta-armv7a-none-eabi.tar.xz=2bfe5040c6c62fec4e863428288baa735fc581ac2d7ff4a180b51e169db0f291 +dist/2026-09-29/rust-std-beta-armv7a-none-eabihf.tar.gz=5014575869a7ecf9c6a6c91cb69b97f6fb798d3836f0c1851a5a9128d380fc42 +dist/2026-09-29/rust-std-beta-armv7a-none-eabihf.tar.xz=84dd449ba61513a783e10ccdc4b40ebf02db42a435f306690f945b59a9b38274 +dist/2026-09-29/rust-std-beta-armv7r-none-eabi.tar.gz=bcb61efdf77fc8a3ac756a2989cb29e57519cbba6cd86eac5eed42799e6dab98 +dist/2026-09-29/rust-std-beta-armv7r-none-eabi.tar.xz=0cbdb7ea8c7c9505a3b8a9d055704d736ea9be344862ef3f335d4abc27bf9d21 +dist/2026-09-29/rust-std-beta-armv7r-none-eabihf.tar.gz=422ff4501a710a25cdc6569428320beff61c2840872af2d63dde98543cd795f6 +dist/2026-09-29/rust-std-beta-armv7r-none-eabihf.tar.xz=c26f216ca75b5ab3c9e837a55c6a0faa7ffc3a348125e6dc545def1ad92582b3 +dist/2026-09-29/rust-std-beta-armv8r-none-eabihf.tar.gz=44b08082e62a1469c30956d9b3c82053bf341cd15b36a9ad634d0b9061cb2702 +dist/2026-09-29/rust-std-beta-armv8r-none-eabihf.tar.xz=57eccc6e20abd4d9a95bbe0095401d8b64fca6829e11a0f4f0b4977bd5466b8e +dist/2026-09-29/rust-std-beta-i586-unknown-linux-gnu.tar.gz=c98675485760784593bfcc1c1239ace93b05a90649a37cf40c78ec76f54c9b6c +dist/2026-09-29/rust-std-beta-i586-unknown-linux-gnu.tar.xz=25c25743a6bcbdcf35b6a055b5bb4a7c3af73074bca1923d3b97dfb5b87d51a4 +dist/2026-09-29/rust-std-beta-i586-unknown-linux-musl.tar.gz=5f0a8c43f055eaffe5efe71b62f0ba276bed1afc158efd29f10b5ea064fcd881 +dist/2026-09-29/rust-std-beta-i586-unknown-linux-musl.tar.xz=5d73bfd5f7115ec5e67b07c0e978f4aab9e8f79adee879e917b29e01fb78ca76 +dist/2026-09-29/rust-std-beta-i686-linux-android.tar.gz=7cdb05e049ea8e32fad2cea069fa98181d5289dcb2d6322e5dc80d151ef31b66 +dist/2026-09-29/rust-std-beta-i686-linux-android.tar.xz=e127f07fd97a7fada184bdee50aa83002c7764d6a0b320aa10d6b2bd400b2786 +dist/2026-09-29/rust-std-beta-i686-pc-windows-gnu.tar.gz=3f9713c9e37aafd3ce7c825ccd9a145dd9c975841da6de6f0ce340ae2381ad24 +dist/2026-09-29/rust-std-beta-i686-pc-windows-gnu.tar.xz=ec6e784cd6d3dc3e76857654621a4072193945668287b9d7665eb5105878bf0b +dist/2026-09-29/rust-std-beta-i686-pc-windows-gnullvm.tar.gz=c174dc7ec2ef614eb6323f06e655eb86266c0b81e1d644e5e093d58af7806f5d +dist/2026-09-29/rust-std-beta-i686-pc-windows-gnullvm.tar.xz=ac9f8789b013c8ece9ba57a56bd05047b8b7134c7f3f13e616bca2b592ca5ab8 +dist/2026-09-29/rust-std-beta-i686-pc-windows-msvc.tar.gz=a4b5db143623d75352bab8f5154583b0ee8f3a4886062e9bd79675e956e528d7 +dist/2026-09-29/rust-std-beta-i686-pc-windows-msvc.tar.xz=cfde62235d503f2906d0aeb21a07b479d3765a4d6bff10c1e54fbd557937e089 +dist/2026-09-29/rust-std-beta-i686-unknown-freebsd.tar.gz=d7bbff8b33fd5d247a8bae9a3c83f879777c65651babd1cc7515c52668b0f43a +dist/2026-09-29/rust-std-beta-i686-unknown-freebsd.tar.xz=a22f726246f417641f5feda6ed2622749e375d03d11bb1be57072d74e286ff68 +dist/2026-09-29/rust-std-beta-i686-unknown-linux-gnu.tar.gz=0d90d3e128d0f378c4b8d948fa65e6d3f42c9eda9da023e9218b6d62cb3442d6 +dist/2026-09-29/rust-std-beta-i686-unknown-linux-gnu.tar.xz=5e78729655a70c60fe5c49a32a7f09418a720f7623d512dd260d04ad52645b8f +dist/2026-09-29/rust-std-beta-i686-unknown-linux-musl.tar.gz=1e10e1e648b38e1e5d102ba87c374937ee1eca7abf53d4abf4421c74e2583283 +dist/2026-09-29/rust-std-beta-i686-unknown-linux-musl.tar.xz=a716466a78991384a45d0f27c73a3268e4f1f709d27ea32474100a8cc9ac5736 +dist/2026-09-29/rust-std-beta-i686-unknown-uefi.tar.gz=f5168879aa790d8a823d98865b2abed8683ad998bc46cab8f92b3cb046c0b442 +dist/2026-09-29/rust-std-beta-i686-unknown-uefi.tar.xz=414e0fc0ff74ba42765954390114a14cdfb261dbcf82ffee03509421ac8b13f3 +dist/2026-09-29/rust-std-beta-loongarch32-unknown-none.tar.gz=f651fea2e64f49fb75f74e7b4a350fde8391808ddad903dea20f8af4e4f848b4 +dist/2026-09-29/rust-std-beta-loongarch32-unknown-none.tar.xz=cef214987ba5cff11f8a5c75b11a2bc4fd3be236e23ca98308addbdf83529232 +dist/2026-09-29/rust-std-beta-loongarch32-unknown-none-softfloat.tar.gz=1a2f03ff61944e81dfe485129f96084a8f0843da76ce62287bf6a70500f5644b +dist/2026-09-29/rust-std-beta-loongarch32-unknown-none-softfloat.tar.xz=39f6bd3be80e0ead123e2ac706d5da15e2c76a7fa1994de40817692d82bb7714 +dist/2026-09-29/rust-std-beta-loongarch64-unknown-linux-gnu.tar.gz=5012a90d4f9fcacbdf003f151cd6f3316defcd2283514526102834bbd74a8c8d +dist/2026-09-29/rust-std-beta-loongarch64-unknown-linux-gnu.tar.xz=b8d1bfd9c839ebc6b7c90b98b0be907f6246e69fc92eb48e85db4ed6c7dcbe0b +dist/2026-09-29/rust-std-beta-loongarch64-unknown-linux-musl.tar.gz=6569ec56c5f6fa26aff9883603cb9453504854fee82878b25b1b0d2cc9935e6b +dist/2026-09-29/rust-std-beta-loongarch64-unknown-linux-musl.tar.xz=2b087399ddcface9b4466a915bf99077adbd8849846498016d57a61c48236d5e +dist/2026-09-29/rust-std-beta-loongarch64-unknown-none.tar.gz=064352424fbc4bfb9637b2a5e45c7ac39d4c2acf937a48f5c8b71cc3b24cb615 +dist/2026-09-29/rust-std-beta-loongarch64-unknown-none.tar.xz=1f3bd23cb2fd4444efdbc15637a05ca8adeb18b2dfecdc5281ca92fdab40f6a1 +dist/2026-09-29/rust-std-beta-loongarch64-unknown-none-softfloat.tar.gz=cacb51b0256f0f30722f8b69c4cafaf79dc2e9d561ca4990e00ea450a49626d1 +dist/2026-09-29/rust-std-beta-loongarch64-unknown-none-softfloat.tar.xz=a6c1b71975918d172cf47017a2636a29814297ae79f80d223a83ac0cf17770d7 +dist/2026-09-29/rust-std-beta-nvptx64-nvidia-cuda.tar.gz=e8f0efcb638b971d12ff73cc32422ce944cb2816022dca9d875f1eae938319ca +dist/2026-09-29/rust-std-beta-nvptx64-nvidia-cuda.tar.xz=b40bcea95f0c915cf154ffdcbd1388156c2d3c1734b4b00e192869422dbe170c +dist/2026-09-29/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz=efffecff9289cb117cdf4e3fed6126b45a3a82040e0d5f44101ed52f0b38f274 +dist/2026-09-29/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz=e12f25ed25788772becf9f44bca4e88eda5a1f21cf17f08b09cdfa728f90a764 +dist/2026-09-29/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz=e032b20d3f989cc8c74478871e6dceed4ce3fe3d0a7d232c15b8976e679f870c +dist/2026-09-29/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz=82e13804fce4dc6a36832873db80d5a4a43d9d8aee44c40c33c30a88227d5d44 +dist/2026-09-29/rust-std-beta-powerpc64-unknown-linux-musl.tar.gz=20a1312d8c162e7cb26ffe9ba89fa50cc92f4ca4bfa862e3bf2a540d3718a3d6 +dist/2026-09-29/rust-std-beta-powerpc64-unknown-linux-musl.tar.xz=6c2b3f2b17804994388a18f443470049bb63a785852456b27eac2a3320c03b31 +dist/2026-09-29/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz=b02de7909bdcfb397a6714fee31031f7039c129fcbf831816d12957217fc9c7a +dist/2026-09-29/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz=a8d21a1e9f410be453757ca441d1872cec6a4c92aef1ea2111b4ee4a126a3db3 +dist/2026-09-29/rust-std-beta-powerpc64le-unknown-linux-musl.tar.gz=44be1a79c527831221deedfcfb0e0afd0d8c772102b90286dd9180c6f102dff9 +dist/2026-09-29/rust-std-beta-powerpc64le-unknown-linux-musl.tar.xz=d2305657826fd51a2c6480d8aa3d5fdf92d0f665770a83ee4c0f7f9ed0f0de65 +dist/2026-09-29/rust-std-beta-riscv32i-unknown-none-elf.tar.gz=f1c9e1c8dc22a6f1d784aec5b24922976972c1eeb56e7d587650e22d4eb0d601 +dist/2026-09-29/rust-std-beta-riscv32i-unknown-none-elf.tar.xz=7aaa2bde1cd84c38b13d24dafa47ab9083339795cac996437f41c7510426a562 +dist/2026-09-29/rust-std-beta-riscv32im-unknown-none-elf.tar.gz=f04d393d79727af08de12ca7d13fd41ec78120e3bcc30008f684508edff0f2af +dist/2026-09-29/rust-std-beta-riscv32im-unknown-none-elf.tar.xz=04c8d324b52253354cd7907693bce663b300c075f10a9a0668c29956a8cf8a24 +dist/2026-09-29/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz=2a8d5ffd86d7cf52d942933725653c6147ececd7f19a5d1ffee654a2cd1b8bdc +dist/2026-09-29/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz=5399870b58683f7d4d35b80c7079e959a092ced21e6b56969a9e9c67e82acfde +dist/2026-09-29/rust-std-beta-riscv32imafc-unknown-none-elf.tar.gz=6693b6d5032b92eb844e4c7dfbd7f251dae9a13c8af74aaff015c9258367c292 +dist/2026-09-29/rust-std-beta-riscv32imafc-unknown-none-elf.tar.xz=cd1f07d5a147eb17ff4b6d77a9a9fc567d46a47ec00a1ae15d8ca0d48f12e5bb +dist/2026-09-29/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz=e783e12d4436d0976053f3c559096b4baf6f5a02d222c45f3cb40f59fbb91f53 +dist/2026-09-29/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz=00564a08fd47392352f40d4b375a7ca565c8fee14f553bd526dbd88f049ad9a3 +dist/2026-09-29/rust-std-beta-riscv64a23-unknown-linux-gnu.tar.gz=731b85413ebe28cad585e028bc77b78869e80ee0714f01b5e6a913d452ab6cb9 +dist/2026-09-29/rust-std-beta-riscv64a23-unknown-linux-gnu.tar.xz=25902d5d514f10984a67301e6281b7e7fae38af2d95879c1ae730485068e3d67 +dist/2026-09-29/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz=a776dd47f9381e67c9fee75db18df84cc2faf51d8691d23c393ea1514f257bbf +dist/2026-09-29/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz=9acd29f728562348bc380cc112cf56709ffd62e499d52e5ac9c0446a8c1731d2 +dist/2026-09-29/rust-std-beta-riscv64gc-unknown-linux-musl.tar.gz=549702f03471fa57abd4b2d990b716fbc87274c8d0386afdc4ccadc7bc4dceff +dist/2026-09-29/rust-std-beta-riscv64gc-unknown-linux-musl.tar.xz=574bd2d37d0c13be91082d9633af161ee3eb7572fd72c8959cc744672723cd1b +dist/2026-09-29/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz=1c52932e910cada292e4561fb2d469d75e48369292475a67221a49e80b06e0f4 +dist/2026-09-29/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz=18c6df09267c26b5f5c3d55b0ca108aeb1c6cc4e286461baef7ad126f5ca208e +dist/2026-09-29/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz=3f42e1e761b319b3e348396c87c3b79f67783081b2ee0acbfcf72c29a5409fc1 +dist/2026-09-29/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz=f5fe44aeb22ad3c00eef4f4a1bac56436ba59bc9336b901d88c0f781032dc954 +dist/2026-09-29/rust-std-beta-s390x-unknown-linux-gnu.tar.gz=88b170bcd8d3e26d422bc8ae7a525d8969878434acdb60c4d8f75ad4a6f5fb10 +dist/2026-09-29/rust-std-beta-s390x-unknown-linux-gnu.tar.xz=5cbcf61ee88f021d7570f360971a4781a48ace118b6c2047a6d71b6928ec874d +dist/2026-09-29/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz=5772cb9750380f03ef47f1502e82ac790237967d7dd2e3de2eaf6fb0fb723df8 +dist/2026-09-29/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz=5f2fcaf8b81c370443b53989dd12f3885c299fd8009aaeefeafb63ffa8c2dc40 +dist/2026-09-29/rust-std-beta-sparcv9-sun-solaris.tar.gz=4bcae41b9149b8d7d3a968b7924fa60ca15ff014c997dffddf1073e3c756a48e +dist/2026-09-29/rust-std-beta-sparcv9-sun-solaris.tar.xz=1bcaae45605a3b9bc05265d48e4bf46bdae9d3a45c6e4cbab4de7df0ec76b73d +dist/2026-09-29/rust-std-beta-thumbv6m-none-eabi.tar.gz=c8668ef20b4a6bdf393082067ec753b9e7e7fdcb4215bfa159a8394e70ac62bf +dist/2026-09-29/rust-std-beta-thumbv6m-none-eabi.tar.xz=6aa054160722c4f887cd335f5e5549afe289f04e7abc5d6df95769a83be453d4 +dist/2026-09-29/rust-std-beta-thumbv7a-none-eabi.tar.gz=b28bd0f5ffebe62f944a3c5f17a9a73eab5c990201ea6961d530b769a69f8d3c +dist/2026-09-29/rust-std-beta-thumbv7a-none-eabi.tar.xz=ce529ebc58e8a757bdab3aee87d34a0c06dd59feca3b708aa97a97b79ca176b4 +dist/2026-09-29/rust-std-beta-thumbv7a-none-eabihf.tar.gz=bc94dbbfe23cf6adf1d34a8a6421480854eab76009a0d327d597f84c83c597d6 +dist/2026-09-29/rust-std-beta-thumbv7a-none-eabihf.tar.xz=96bb2b46a7b11d85ef347e2561730fabd1f56cbb334b5b4ebe8fd0a60fc980ec +dist/2026-09-29/rust-std-beta-thumbv7em-none-eabi.tar.gz=988b9a2c797874a96a155b152d2704624eea46e3ff76c9ee31081708661ee390 +dist/2026-09-29/rust-std-beta-thumbv7em-none-eabi.tar.xz=8c64f7dc6ac9d4b35a5683f6324cff0e1bf136b63dc7b0341f5b15570cd6ff37 +dist/2026-09-29/rust-std-beta-thumbv7em-none-eabihf.tar.gz=35fe10b86edb438efac8d9bb37992dc42758f84b82945fc5c115294c1645e997 +dist/2026-09-29/rust-std-beta-thumbv7em-none-eabihf.tar.xz=9ce5331218676dfe1cee5e975a7e9c56c30e051e4999cf4d7bcc89664aefbb60 +dist/2026-09-29/rust-std-beta-thumbv7m-none-eabi.tar.gz=d268977a2462bc3f8ee3a113965417313c8ce91de44700701bf89a1f485c1261 +dist/2026-09-29/rust-std-beta-thumbv7m-none-eabi.tar.xz=02cc4d5a04d190245b154769f6bf691ad82b2a37d5b6dcff98d25218f4da6508 +dist/2026-09-29/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz=6b5025117edb1c0f6e2a519d7a827c257371bcbfe8c1cd2cf194fd8e808fab18 +dist/2026-09-29/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz=f6ee4b5ee7439a71c1ac02c428ccafd739fb59e4d7743c73218472c99d54747e +dist/2026-09-29/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz=a1a6e10b069cc010dc0d25d33e39bf09d2e902ba824951571b1ff6272c406a30 +dist/2026-09-29/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz=1e5f603944d9a1565d7ec648298e126a8d039ca8112bc9f5f8c4e6b2bfbe6f40 +dist/2026-09-29/rust-std-beta-thumbv7r-none-eabi.tar.gz=36c4b0a0bfe975883763666d3cc6562ca9f98e1b8623dc6fb1d341390669b511 +dist/2026-09-29/rust-std-beta-thumbv7r-none-eabi.tar.xz=d087749ff3a16e4577ae93afac8635ce614cfd56c68d2f01e939a33ee07ee5c8 +dist/2026-09-29/rust-std-beta-thumbv7r-none-eabihf.tar.gz=fff03b17250ffc7c328b68b542d293da4d81ae16fda76b53ca87d2dbe03a9fd4 +dist/2026-09-29/rust-std-beta-thumbv7r-none-eabihf.tar.xz=e47e298a4f5583c447056566b2c559af073387024d74822d4f7d657396cbb842 +dist/2026-09-29/rust-std-beta-thumbv8m.base-none-eabi.tar.gz=6245b71146c9bf6892f87d36505f8f8b019f925a93e9e7382fb4c11ed7b92921 +dist/2026-09-29/rust-std-beta-thumbv8m.base-none-eabi.tar.xz=1190208ca7e613923db504b2f452c87f631934a9ff69c212824ec15e03a45a58 +dist/2026-09-29/rust-std-beta-thumbv8m.main-none-eabi.tar.gz=e2ae5641572df4bb08906d2b6e7f739eea79278333eb0c5547c49b0727c56dcb +dist/2026-09-29/rust-std-beta-thumbv8m.main-none-eabi.tar.xz=fe005ce3e6d13722a8a20e3ab668d44848ec97a3d7e275b74d60a84d8e421ee9 +dist/2026-09-29/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz=a53f0858a9105a0e1c675d7662838fa849266d19ce5e0f6a35f72e1d3c6d4294 +dist/2026-09-29/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz=f229db4fd00f6995049a9e6e3cec6763dee8ce10daac63bc522013b121bc4973 +dist/2026-09-29/rust-std-beta-thumbv8r-none-eabihf.tar.gz=341a7f6420319fb33970a0904144734539e605a2323dfbf2b91169e53aafdcc8 +dist/2026-09-29/rust-std-beta-thumbv8r-none-eabihf.tar.xz=2f3cebd916bff44147582514eeabe207c693cdd5e058febe7d86e8c5c87ec598 +dist/2026-09-29/rust-std-beta-wasm32-unknown-emscripten.tar.gz=e8b45a1e9bc3b54f1047a1cdaf9f6b8af3a6750413e4da81d69f0590a6137348 +dist/2026-09-29/rust-std-beta-wasm32-unknown-emscripten.tar.xz=5c20fdc9b9429f2cf72b11e31fc401d81a5deed37f7bd15627d81db5f834b57b +dist/2026-09-29/rust-std-beta-wasm32-unknown-unknown.tar.gz=0796808b38d7517a28cfe7ba3eba5ecb037242a9abcc26390e1f1825467c7977 +dist/2026-09-29/rust-std-beta-wasm32-unknown-unknown.tar.xz=44dbce1c52229c875f22d98de17a78260892e4759b996e989d96ca58f22ea89c +dist/2026-09-29/rust-std-beta-wasm32-wasip1.tar.gz=79a9003e337d684054d8c6859c2118da6992f4ac7eeb8bebb790b763294902c0 +dist/2026-09-29/rust-std-beta-wasm32-wasip1.tar.xz=7d0bdf81bdfea1bb40a17055aa3077b0b7afb214a6826a92a462ac9006b6aafa +dist/2026-09-29/rust-std-beta-wasm32-wasip1-threads.tar.gz=1721f8d930563486f9f90572797b910643b4b0d62f1d89f90114568401d3da68 +dist/2026-09-29/rust-std-beta-wasm32-wasip1-threads.tar.xz=dc0968859fba0cf6ac6e2b50cb53b7790529e23e008a8407aeeb72b9939fa2e6 +dist/2026-09-29/rust-std-beta-wasm32-wasip2.tar.gz=f57b56c42f272d0daa2a8dfe5e0dc16d2ec696a820037bffece1cacdb664232a +dist/2026-09-29/rust-std-beta-wasm32-wasip2.tar.xz=e82fa595d0ee89331a1f862efd487beac1d29895f8eb061ad6b6d04049ebdb9c +dist/2026-09-29/rust-std-beta-wasm32-wasip3.tar.gz=6abca32199fc1bd072ef19d69c9884efe3dcb07c81d0260faa0ebb17e94617cf +dist/2026-09-29/rust-std-beta-wasm32-wasip3.tar.xz=f019b8b140d81aa18a01dff20170f07a381908196828870d3b4dfcbe915f9d34 +dist/2026-09-29/rust-std-beta-wasm32v1-none.tar.gz=777b38934c1f0e70e39e2fb55f3424f8dd77c54ad49c72340ba20cd4307d49c4 +dist/2026-09-29/rust-std-beta-wasm32v1-none.tar.xz=4c78b7da12168bb42a4df10aa269b7a5218eb086ad203e093a87c84f64b2756f +dist/2026-09-29/rust-std-beta-x86_64-apple-darwin.tar.gz=232d6824004015f32bbbb285c872030bdaf8bbf36cd259494f24f2802cac399a +dist/2026-09-29/rust-std-beta-x86_64-apple-darwin.tar.xz=63bf0f85ed80e97cac13f2edc54826829867b62b1758565c580a0680bf370bff +dist/2026-09-29/rust-std-beta-x86_64-apple-ios.tar.gz=23e8e77b11c0870bb486fcd76cee15db11587d3f47df20f220d69a46640ddd8f +dist/2026-09-29/rust-std-beta-x86_64-apple-ios.tar.xz=9f556cc01cd4abf0949c23f1ddc4209d7b92cdc1d521cb22403a4258eac0fdb0 +dist/2026-09-29/rust-std-beta-x86_64-apple-ios-macabi.tar.gz=d5e6bd07a5aa5346c1294d851466579d567ce1d1db16f6b9a40c20619958c8d5 +dist/2026-09-29/rust-std-beta-x86_64-apple-ios-macabi.tar.xz=c3d8808f3b17c81d6de7a8ee2486b5a837f67069e51ad29b0a3c2fa4ebaf4f0d +dist/2026-09-29/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz=72d8b973a20da182acb65479a067be9b4e8fc4e7f7eed41deeac27f8e2401737 +dist/2026-09-29/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz=ec661a7156e6baab75839b55377d270a1151e6c432420683ad5a082571f37a77 +dist/2026-09-29/rust-std-beta-x86_64-linux-android.tar.gz=06c09a6a5e54e4da413cedcf0a178107f2d6cfc8082903b262a5db6c807b4a44 +dist/2026-09-29/rust-std-beta-x86_64-linux-android.tar.xz=d36c4d48fa72b2313ba29fc757c505e8a11c712cc779eda7ffa5062b2646907e +dist/2026-09-29/rust-std-beta-x86_64-pc-solaris.tar.gz=ca8ce15a7ae374cc46fde55808a54f94c197bcc9bb40448df06e09cef1257dae +dist/2026-09-29/rust-std-beta-x86_64-pc-solaris.tar.xz=f7c2208dec2c25805546258a81d361f250672ea51e0891254bee9f3c3f4e95ff +dist/2026-09-29/rust-std-beta-x86_64-pc-windows-gnu.tar.gz=b8632646f82535baf39460a3a6ed6a6a92e33f31787efaaf4e6d655225c4f5a8 +dist/2026-09-29/rust-std-beta-x86_64-pc-windows-gnu.tar.xz=ab6729530be4e5fbfa51e76f53f4dcf9c974e0ce6ed11556e8aeb831b1aecdc6 +dist/2026-09-29/rust-std-beta-x86_64-pc-windows-gnullvm.tar.gz=c76571599f894548491d95639bd75f551a3c614e8dfcf34dc6b38e1d3bf28474 +dist/2026-09-29/rust-std-beta-x86_64-pc-windows-gnullvm.tar.xz=52dcb526a8ddeaa6081809401ff94a733128a5e4adf9feb1671744ca1134d817 +dist/2026-09-29/rust-std-beta-x86_64-pc-windows-msvc.tar.gz=84c55c99e3a783136cd334db0fcd6cb6e6cc6d3fa997fedc87dd7620e094ca5d +dist/2026-09-29/rust-std-beta-x86_64-pc-windows-msvc.tar.xz=d7c46a39c354ca67f2197be455db7f3817cf35f476b8fac5c85e16c911cd291c +dist/2026-09-29/rust-std-beta-x86_64-unknown-freebsd.tar.gz=46331daca0d7bc83edd687190817d7b7b62f14fa037d9dd56c32314d10e1df29 +dist/2026-09-29/rust-std-beta-x86_64-unknown-freebsd.tar.xz=ac315cd71f957e6b555f98e7744bedf32ae06cecf8a688594d16dc11b0409e14 +dist/2026-09-29/rust-std-beta-x86_64-unknown-fuchsia.tar.gz=5ac6db26e1298837d5d660ca24be108c9fe569784ddb2061dd8b7afca06c4a1a +dist/2026-09-29/rust-std-beta-x86_64-unknown-fuchsia.tar.xz=0ccc6aa8a3746d143515882a33254fa67cead1e58fc3dbce191769f59fccb635 +dist/2026-09-29/rust-std-beta-x86_64-unknown-illumos.tar.gz=9547382b596806f8460bffd1fba4b21c87a809744b678d1379c821288256bfb7 +dist/2026-09-29/rust-std-beta-x86_64-unknown-illumos.tar.xz=a52fa0165572f08d7c0807b618b8841c9220065ff188095bdbf4bd4e9bb11771 +dist/2026-09-29/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz=b8eaac057b7b4197941904f539ef4e01eb93daeaedd8b158b5a0a2d8e370ea26 +dist/2026-09-29/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz=9095fa52e354f1441568af9e3a9839b16268f7ae7707fd4bc48b2a2bcd1d547f +dist/2026-09-29/rust-std-beta-x86_64-unknown-linux-gnuasan.tar.gz=956b82298d2f2c7e34e28d157ba41945d39865a23b9bbe20678d7ae31aa825b2 +dist/2026-09-29/rust-std-beta-x86_64-unknown-linux-gnuasan.tar.xz=3ecf190a1c53ee9aea39f643ac62923835379f08f6503f4134459ebc5f610a08 +dist/2026-09-29/rust-std-beta-x86_64-unknown-linux-gnumsan.tar.gz=10576f120a98f153c7a68e62a565a92424917a36fe3cb017f4a657e236b3ff88 +dist/2026-09-29/rust-std-beta-x86_64-unknown-linux-gnumsan.tar.xz=a009b119cc0d811e070dc329cf241b8b509d9a297faa68c5d2216b4e21f5ac50 +dist/2026-09-29/rust-std-beta-x86_64-unknown-linux-gnutsan.tar.gz=8d9aab19bea2b6b07f11e9a2bdc0058441e6f80f4d6e778c03cb5b52854c7aca +dist/2026-09-29/rust-std-beta-x86_64-unknown-linux-gnutsan.tar.xz=3507d9b8563db110eaae0253c5c041b62d42508696f1526b94b92fd28e200b14 +dist/2026-09-29/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz=7dc3b370335b15345dc9f508c4f132d00a69fdf0feacbaaaaa2f93bd7cbf9ce7 +dist/2026-09-29/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz=b8b24db68ff29f15c52fc70ee12cc6d10bcd65259c521876fb08892c8f67b980 +dist/2026-09-29/rust-std-beta-x86_64-unknown-linux-musl.tar.gz=13da2b408f85665b37c6ba519cc4fbf8eeacae1dd6835f22edd800fa229d6f56 +dist/2026-09-29/rust-std-beta-x86_64-unknown-linux-musl.tar.xz=61f986574cf89a091fc6563c0cef458e1c5618444364eb36751194b2da7f8da9 +dist/2026-09-29/rust-std-beta-x86_64-unknown-linux-ohos.tar.gz=48d5e6ffa8dc7319809a13ed109a8c5e43e9b17d5e6c9e88a57ec160068caae5 +dist/2026-09-29/rust-std-beta-x86_64-unknown-linux-ohos.tar.xz=962cc03687830996b02c8419b4f948b94b7f9b6c40a918212bfa9ba8a3186aea +dist/2026-09-29/rust-std-beta-x86_64-unknown-netbsd.tar.gz=93c518fd23ffc00d69f37e38b90033d76091b3946ee7ec82d906b78a4f7130e9 +dist/2026-09-29/rust-std-beta-x86_64-unknown-netbsd.tar.xz=a8922fe96c323e6ccf587a70841651143065e25d2ce465dea5e2d9ffb52fd42c +dist/2026-09-29/rust-std-beta-x86_64-unknown-none.tar.gz=4df5d869a75e754e1952b73fa78a4722fca53038325dd0e541887e8d0c8a7620 +dist/2026-09-29/rust-std-beta-x86_64-unknown-none.tar.xz=c37467acf1d25eaba261edff55a410a0104dd31b90f8158b10b6f2e369a6d9eb +dist/2026-09-29/rust-std-beta-x86_64-unknown-redox.tar.gz=8d245bade3f0fd036e16314190cf3a0ca535c75a4c97277e24135a9e76815156 +dist/2026-09-29/rust-std-beta-x86_64-unknown-redox.tar.xz=76392bc2afdd60a591b3114fb191deacf59388c21fb1d7ec1733f2f561778648 +dist/2026-09-29/rust-std-beta-x86_64-unknown-uefi.tar.gz=b17e48ee19a86057a7ebaeadd697a4dc86853cf9e66c4981e5043de9e3ce1c00 +dist/2026-09-29/rust-std-beta-x86_64-unknown-uefi.tar.xz=1cd2a4b32284ffb4d11bcd3fb561578f3d00c763c704a177e113af6899c22d0d +dist/2026-09-29/cargo-beta-aarch64-apple-darwin.tar.gz=f3066c859807c0ab69429ea69362654e18c21d26b2c482be436280bfad4b49e2 +dist/2026-09-29/cargo-beta-aarch64-apple-darwin.tar.xz=0bdc913f85f762ca2af5c9bd2d72a31865c102bc66bcc43250a7eae06208de49 +dist/2026-09-29/cargo-beta-aarch64-pc-windows-gnullvm.tar.gz=e2bde3311142eee8c4d822ff838778cbd2ef7d7f496f4dd4a0237756fd16964e +dist/2026-09-29/cargo-beta-aarch64-pc-windows-gnullvm.tar.xz=1c26e80d1529c17cf4ef6025ceb63490cd862ca9eb7c21fa043fa89d2ee1f0ff +dist/2026-09-29/cargo-beta-aarch64-pc-windows-msvc.tar.gz=2b71fb86fb57894c2fa83ae53522783fd9b5b654332ae7623eca3b153f4136e8 +dist/2026-09-29/cargo-beta-aarch64-pc-windows-msvc.tar.xz=5a8b4e3db041cafd58e59333f872cb1020f385d54269ea02ca7ca8a4ce45796a +dist/2026-09-29/cargo-beta-aarch64-unknown-freebsd.tar.gz=12d98333d9d48a97a6cf0bbb9b01cd4a215a51feaf81c5ddb4ac54ce3644814b +dist/2026-09-29/cargo-beta-aarch64-unknown-freebsd.tar.xz=89fc6ebdf93d553fc957a1adac94f9d83feabd5c84c364aa875f36a4d274e31d +dist/2026-09-29/cargo-beta-aarch64-unknown-linux-gnu.tar.gz=173043d8b96bbd8149de521da3271123895af497379a278a87039cef644f5245 +dist/2026-09-29/cargo-beta-aarch64-unknown-linux-gnu.tar.xz=69eae56c755c9d90a1e35d38e22d43b46e204913d237c5c2ea9001906a273db5 +dist/2026-09-29/cargo-beta-aarch64-unknown-linux-musl.tar.gz=2590e51b526f771cc486a2ca15f89c7ccdf878bce18c7e03cf32b6116ab5483c +dist/2026-09-29/cargo-beta-aarch64-unknown-linux-musl.tar.xz=8d1325965495214ac2894aa9062255e519397eded66a32e71b871eeea81dd930 +dist/2026-09-29/cargo-beta-aarch64-unknown-linux-ohos.tar.gz=e18bfa187c668f15f48aa36fcb29d71791577132e829334dd3ac05d5b7cdd7f4 +dist/2026-09-29/cargo-beta-aarch64-unknown-linux-ohos.tar.xz=fc3d7339b87792efe262f8f0685bc97c42bda4ee21541d4d0719e1ba1fdba4a5 +dist/2026-09-29/cargo-beta-arm-unknown-linux-gnueabi.tar.gz=25a23eaf8af21d465d2b0ca6461281702c10339d968ff226e0c63dc6bf9950ae +dist/2026-09-29/cargo-beta-arm-unknown-linux-gnueabi.tar.xz=7544dd758d44668e985e93eb777bccead5f63bf98326ed7e003d2a68a59d4c4f +dist/2026-09-29/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz=016d2a04723930c835f7564bbf460978ec2ba9f497c5b94c6665ece36989aefd +dist/2026-09-29/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz=4a40079a253cb349e41148b919b7e460e5eb6c659089dc4366c32cb343dab5c8 +dist/2026-09-29/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz=36b8677ec6fc857af1777e2f4a68c2cbe90a72b21f9afe2b4221593a76cd3748 +dist/2026-09-29/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz=816e1c6b3bdaf08b24514331b972d864a6b7bd8d63479fd154dd1608ac66f26e +dist/2026-09-29/cargo-beta-i686-unknown-linux-gnu.tar.gz=f374e9858d5d4294e9007986d4df7af0d15a57bef5862c289b818c3bf7bc8299 +dist/2026-09-29/cargo-beta-i686-unknown-linux-gnu.tar.xz=ebaa11b6203e20cb0a612c53aaa9a0ae5c275795c1737c4bebebb679cdabb485 +dist/2026-09-29/cargo-beta-loongarch64-unknown-linux-gnu.tar.gz=e6816ee73290cf376b402ad6470eb2b68004a883b1b4294393a8966d7bdf1aa0 +dist/2026-09-29/cargo-beta-loongarch64-unknown-linux-gnu.tar.xz=b476bcd327cd838606b8482381c6fabf0ce97f518e3ab11b35a50827f2b18b51 +dist/2026-09-29/cargo-beta-loongarch64-unknown-linux-musl.tar.gz=b83080fee58cb9e72db9c8ac994724ff3a5bd2b7039eabbfa0cd9bc77d2d4532 +dist/2026-09-29/cargo-beta-loongarch64-unknown-linux-musl.tar.xz=889717547ec4cbb80d7f2d2e35b1b2b512c3e450ffa56a1bc27289fe285898d6 +dist/2026-09-29/cargo-beta-powerpc-unknown-linux-gnu.tar.gz=340d7ff13760dbb32148c8616dd7c7246870a7f091bda102902589a20dc8c2f0 +dist/2026-09-29/cargo-beta-powerpc-unknown-linux-gnu.tar.xz=29c10897c335e4bd2dde7848bf67bcc93b96e5911888524d0abb8704fb88a629 +dist/2026-09-29/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz=62fe339183294cdd58c5c90a6a1dff3a388dc2b054f2ef46babc127cb1dcaf11 +dist/2026-09-29/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz=18ce8044103b232c245c8ad3ab92073a473740a897506c69f79c406b611263d1 +dist/2026-09-29/cargo-beta-powerpc64-unknown-linux-musl.tar.gz=fa3e4a74e3bcb12816049d51dcbd9103fd2136679c509b47b4a64f38686c46d9 +dist/2026-09-29/cargo-beta-powerpc64-unknown-linux-musl.tar.xz=d5efafc560f206af8e32d6a720c923e74443af1e5e30e8e9a5a06a38b2955534 +dist/2026-09-29/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz=07a1eb729d565b322c3f65d925cb98f125d8c031ea390e5537208fc831cdb627 +dist/2026-09-29/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz=fae744722b6f92d00dcf4325d1f5c7a59dbf3563f53318e772ac48ec330c7d2c +dist/2026-09-29/cargo-beta-powerpc64le-unknown-linux-musl.tar.gz=95e91adc5af521f58650ab88707c870952be0492e913c96df8dd3a816449f242 +dist/2026-09-29/cargo-beta-powerpc64le-unknown-linux-musl.tar.xz=d46a64931a0ff96ba7c80f3fb3edceee803d86458c72255b5037857e0ff86de5 +dist/2026-09-29/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz=05a6b30d13367ee0f13295ebad1ab19c41d6ef7e0658971b4857e2b423d04ac2 +dist/2026-09-29/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz=d64db7e154194aab5d689236a545e3a2f657a67339c0eff77581be6f1808e6f1 +dist/2026-09-29/cargo-beta-riscv64gc-unknown-linux-musl.tar.gz=88adb1c494bc36bc577bb13bae1efa5051481cfce96249c4d340ab7eca017c17 +dist/2026-09-29/cargo-beta-riscv64gc-unknown-linux-musl.tar.xz=8347b2a940db92e3af2c11208ad77c337f5dc856960656c179e19c2b17a161b5 +dist/2026-09-29/cargo-beta-s390x-unknown-linux-gnu.tar.gz=79070ab2fd6d93d37a1cdd11d24a1de15a887d5503086f1e64766a99796decb3 +dist/2026-09-29/cargo-beta-s390x-unknown-linux-gnu.tar.xz=b64e4cca016d3ffce2b1e6944cd249d4e93eab689c0d9cced01a798bc7e183e9 +dist/2026-09-29/cargo-beta-sparcv9-sun-solaris.tar.gz=fc1bfe106843c3b7f0affa77de936dfdb8b9f5a438176d9037aa0533e9b9c8ee +dist/2026-09-29/cargo-beta-sparcv9-sun-solaris.tar.xz=9e12971574167eff3830526f10db3e21cb817f4b28acdb10c42c7689cc2f9656 +dist/2026-09-29/cargo-beta-x86_64-apple-darwin.tar.gz=97072e5a8006d0a8932983a44b1dc0a7b56d46efc76fac6378315f43cfb2c556 +dist/2026-09-29/cargo-beta-x86_64-apple-darwin.tar.xz=d296336efa8cee66a03655cbd66e64709866fe98bc2aeabfdff88039b74eb800 +dist/2026-09-29/cargo-beta-x86_64-pc-solaris.tar.gz=96832f53757943d1c752dadb11fc122af8085317017d7263b73d413029ce9b1f +dist/2026-09-29/cargo-beta-x86_64-pc-solaris.tar.xz=091cd84168e3c9dd860bc0df58d07def0318e470c3459cd431fed713c0ab2616 +dist/2026-09-29/cargo-beta-x86_64-pc-windows-gnu.tar.gz=f99a145aa7938be5baae99302858b680e2c2ae3dd84e8434cc38de374967deaf +dist/2026-09-29/cargo-beta-x86_64-pc-windows-gnu.tar.xz=5bd7758064d9500c68fcfdd5a3a851d4dda40d3c870a83b9a70c93b46a8c8823 +dist/2026-09-29/cargo-beta-x86_64-pc-windows-gnullvm.tar.gz=06494aceb50c8bf0131639c52c62746b07ff2143b04d383be6f137277397a0ae +dist/2026-09-29/cargo-beta-x86_64-pc-windows-gnullvm.tar.xz=5923b97ac130c29f6f0ba96fdbf4b034a452a9d03ae78d8bd812e9d3eb304a80 +dist/2026-09-29/cargo-beta-x86_64-pc-windows-msvc.tar.gz=26736efdd0b4e09dd8e7ed421881fcd39e588b31c45129c011bd78d83e42c153 +dist/2026-09-29/cargo-beta-x86_64-pc-windows-msvc.tar.xz=9e72a3172d9638143bbb51c9cecfce7dfbe463644ca3e90cfbfb04a30561ed93 +dist/2026-09-29/cargo-beta-x86_64-unknown-freebsd.tar.gz=e9b9ecfda5fa944168f81c12ebbe2c127ab245f867d61d47197ae23ca8eb9c86 +dist/2026-09-29/cargo-beta-x86_64-unknown-freebsd.tar.xz=e7359e4d2a6e0ff4ac99fa49346f1e2ee4c64487bb2f35ef0b75fc497bb676bf +dist/2026-09-29/cargo-beta-x86_64-unknown-illumos.tar.gz=bc5382bc1853388dc1573f464bfc3393f41293b159826488b0096ce1dd87582a +dist/2026-09-29/cargo-beta-x86_64-unknown-illumos.tar.xz=46867f0df7b6b95f13598267c5e17a96aff1de929ea2b23fbedb30e7b24324da +dist/2026-09-29/cargo-beta-x86_64-unknown-linux-gnu.tar.gz=dc8dcdee80d7034b0ceadb64fb15608c7617022f4c0be595bffed3326f1bc7a8 +dist/2026-09-29/cargo-beta-x86_64-unknown-linux-gnu.tar.xz=6ed534d029be58d7c0fcebaba581166fee81167de58806540d9d6ffb0f68836d +dist/2026-09-29/cargo-beta-x86_64-unknown-linux-musl.tar.gz=ccace12c5ac38d428e107faf94c7945eeff7f72058910fe93f6514234feb0aef +dist/2026-09-29/cargo-beta-x86_64-unknown-linux-musl.tar.xz=03501cfc18b5dd937612ee76fbd57ded962da245f87aefe3bfa4f332afd9812f +dist/2026-09-29/cargo-beta-x86_64-unknown-netbsd.tar.gz=384f90d70932ba9babe4f9baae008cdf52ef7815a0052b7b6c73bece3f024aa6 +dist/2026-09-29/cargo-beta-x86_64-unknown-netbsd.tar.xz=3c4e43e90a741f1e10409b3cbe426ebe3d3506ad7196eb291c55c4ee060d9e19 +dist/2026-09-29/clippy-beta-aarch64-apple-darwin.tar.gz=aa278ec430f7939b2eb5da177a7e3229b92105ee9e7ad71d8ba7f04cf6f41a4a +dist/2026-09-29/clippy-beta-aarch64-apple-darwin.tar.xz=3929ed842657b6792f95b987e0deb630d96aeb98db9700493be148999390c5e7 +dist/2026-09-29/clippy-beta-aarch64-pc-windows-gnullvm.tar.gz=1272823dc9eeb737f03ec09313c09953d75fea3a848e695c8bc434c2ab53e062 +dist/2026-09-29/clippy-beta-aarch64-pc-windows-gnullvm.tar.xz=76155e48b475fc40bb31ad136725f00875385db2bf179fdb3979065c7a65282f +dist/2026-09-29/clippy-beta-aarch64-pc-windows-msvc.tar.gz=f79d2073573010aa0f1ee4a8579b17f3a1997dc96fc6652a8c8c19a566231eb0 +dist/2026-09-29/clippy-beta-aarch64-pc-windows-msvc.tar.xz=4b8303804318d731b61159959c93782f205a95d8a3aa14e1643b319b6fe9558e +dist/2026-09-29/clippy-beta-aarch64-unknown-freebsd.tar.gz=1028922c743c9170603eb76731be0d3d994873a2b5a47f6a46480eb339a14554 +dist/2026-09-29/clippy-beta-aarch64-unknown-freebsd.tar.xz=ac262e022b7b09669051a325e02eef2cb83087c25ffdee6f059e2069ef430e3f +dist/2026-09-29/clippy-beta-aarch64-unknown-linux-gnu.tar.gz=bb1376c0de000c1e0efdaf8ecb9fa11b14a64a6fe99000ac391f828376ce2c12 +dist/2026-09-29/clippy-beta-aarch64-unknown-linux-gnu.tar.xz=90d1d3895d777b2a473f8035d3264f374c9ca91fdeec32d78899ae0a5f33f0ad +dist/2026-09-29/clippy-beta-aarch64-unknown-linux-musl.tar.gz=5a4cd4fa995f9ba8ef4f87001a87595c4a144f1af62b6a61ff2c5d4e2d90f7aa +dist/2026-09-29/clippy-beta-aarch64-unknown-linux-musl.tar.xz=3775b95f01258fb108f323d031ba8920993d23746f62a6fe5b14add176833b05 +dist/2026-09-29/clippy-beta-aarch64-unknown-linux-ohos.tar.gz=7759e690dcf0984c274dadb14d5601f64cecd21c833b44c93b24c509234bd697 +dist/2026-09-29/clippy-beta-aarch64-unknown-linux-ohos.tar.xz=21598de0d56cb2c0a259cec74bf4b45b1ae350a9d62296289e85f6648fff0427 +dist/2026-09-29/clippy-beta-arm-unknown-linux-gnueabi.tar.gz=a8db1b3308fd0764012d3e76bdf92a076749a04af6f3f139288a1b5f7aea6d3a +dist/2026-09-29/clippy-beta-arm-unknown-linux-gnueabi.tar.xz=746e5b548d3c57a9c42da80a02a5c2df0c2e025b2a8becf17ca1aab937d0bf27 +dist/2026-09-29/clippy-beta-arm-unknown-linux-gnueabihf.tar.gz=6100df771233dbe2d31b9ca7ac202ecdefddb964687b9ea2b97799c58f2df8b1 +dist/2026-09-29/clippy-beta-arm-unknown-linux-gnueabihf.tar.xz=601693ede0315db0d92c6bc9c53f1f4d829d1a506001cd291fcb61c12c91ca09 +dist/2026-09-29/clippy-beta-armv7-unknown-linux-gnueabihf.tar.gz=a7cf7145fc885a7d0359e6f32f8d9a46bf2adbdf268823f1202647bc2a131fdc +dist/2026-09-29/clippy-beta-armv7-unknown-linux-gnueabihf.tar.xz=4b26189ee6eb3d78cce60be7a54d0d64f1a5bfb18dbabf30e829296ee09065a6 +dist/2026-09-29/clippy-beta-i686-unknown-linux-gnu.tar.gz=b73fe9b224653599b38a0a68cada53106276e749f5350871a1d549be5c147487 +dist/2026-09-29/clippy-beta-i686-unknown-linux-gnu.tar.xz=60fa35754a88f0f97e538b639c24237d651c11e1edb16c653536111e1a7f123b +dist/2026-09-29/clippy-beta-loongarch64-unknown-linux-gnu.tar.gz=8a59dd0ce10cde5e714d3f8989fb1ebde1ac51d746e96a72d6239feae81d1568 +dist/2026-09-29/clippy-beta-loongarch64-unknown-linux-gnu.tar.xz=3e7d0761021dc58612054a575e202224645665f90fec0bf805998694a71c8fb9 +dist/2026-09-29/clippy-beta-loongarch64-unknown-linux-musl.tar.gz=f220e05c95906dfe8247ab7a3fb732a5444cba27facad044f50db4c04b62bff4 +dist/2026-09-29/clippy-beta-loongarch64-unknown-linux-musl.tar.xz=d9a7343dd0ee670a086a565a1013f647b3fd8061d4594d42cd2724d3cd52bb78 +dist/2026-09-29/clippy-beta-powerpc-unknown-linux-gnu.tar.gz=e025c6f5c822b45ddf5387dc9a42dcdca9f69be2546a020b2d4de2c07590b8c4 +dist/2026-09-29/clippy-beta-powerpc-unknown-linux-gnu.tar.xz=90d7b50a45274903c43d4d1c57c9ed5b4213c07563feed70caec38723df02bbf +dist/2026-09-29/clippy-beta-powerpc64-unknown-linux-gnu.tar.gz=31aa57d681a3215023de663aafeea01e476be4e2a1f89345602e89eb3b1b5c03 +dist/2026-09-29/clippy-beta-powerpc64-unknown-linux-gnu.tar.xz=fc5dd5800afb155af741770c6111262e9a2b5817bbd8bc2ba06ba2daeb72c822 +dist/2026-09-29/clippy-beta-powerpc64-unknown-linux-musl.tar.gz=ab5edc689617c105a00941d3870b6490c62209d235d09b7f2d48b8ca460b62a5 +dist/2026-09-29/clippy-beta-powerpc64-unknown-linux-musl.tar.xz=77b9759b82d634222650a2ccf9160b654fc636be0b7fd4fac822728eaf1f7317 +dist/2026-09-29/clippy-beta-powerpc64le-unknown-linux-gnu.tar.gz=04f40458440c4222e49df829159f51fc667c10b0762181a535ff61cf120352be +dist/2026-09-29/clippy-beta-powerpc64le-unknown-linux-gnu.tar.xz=ebc8495543e025b2c6db795f665725ad62a2ddd17094f6725bf4e00cbbf25331 +dist/2026-09-29/clippy-beta-powerpc64le-unknown-linux-musl.tar.gz=ba6653802d14bd1586819ae5f9510b9a97e0ba6ebc062942264b52fc297fcff8 +dist/2026-09-29/clippy-beta-powerpc64le-unknown-linux-musl.tar.xz=7c3f65a1938dea882cfee18992a10cb23b58de801a191d0cbbf8c6cc6f9cd900 +dist/2026-09-29/clippy-beta-riscv64gc-unknown-linux-gnu.tar.gz=ad43d2a294308b1509151379c5a9bb40b064e13a841c2d34ed31537bd9eda3a0 +dist/2026-09-29/clippy-beta-riscv64gc-unknown-linux-gnu.tar.xz=d7f118d124d772ebe763780f8ab1cd0295562eccae944c5507500971eebc5352 +dist/2026-09-29/clippy-beta-riscv64gc-unknown-linux-musl.tar.gz=9bb458f695227868c03d21e15177210b067c7fea223c1ba877f3908cff85e673 +dist/2026-09-29/clippy-beta-riscv64gc-unknown-linux-musl.tar.xz=80de4f209ad908cb28921bedd07cbb1444cd7f25179a851c6a2285bfcb13e13e +dist/2026-09-29/clippy-beta-s390x-unknown-linux-gnu.tar.gz=1f3e61cac84317586d80f816465800299920d032548dc82ac6fdd554503d9f86 +dist/2026-09-29/clippy-beta-s390x-unknown-linux-gnu.tar.xz=98f16b6b0a8a6a4542a07d0ec06ae1a05cefdeacfe7398363c61af5b5ea0e3dd +dist/2026-09-29/clippy-beta-sparcv9-sun-solaris.tar.gz=d5876205c3b704883a464354e287ad485dd0677261d36c7adb3beb97509bf907 +dist/2026-09-29/clippy-beta-sparcv9-sun-solaris.tar.xz=b036fc21fa47b8bce0113a1ef74ff869a54ea06d8f3bc0058f4551c3daa1f089 +dist/2026-09-29/clippy-beta-x86_64-apple-darwin.tar.gz=d9f9860fa397ae78d9e6612240a05eedb917cf536ebdef03bfd4bee110feff58 +dist/2026-09-29/clippy-beta-x86_64-apple-darwin.tar.xz=b054e2bba8ea8b3994e48aa1ef9b121eb1760f1c9468da3dcd9ca8cbe05728b9 +dist/2026-09-29/clippy-beta-x86_64-pc-solaris.tar.gz=ab47ad303b93eae7fd933333a94e7b4f65f0119735b83b8eca83691e24f982bf +dist/2026-09-29/clippy-beta-x86_64-pc-solaris.tar.xz=2187367acea68f7bf09932f0cdb237e30e57a57aadcc388219f60aaf28ec4ea1 +dist/2026-09-29/clippy-beta-x86_64-pc-windows-gnu.tar.gz=97af8a6c3356b4d65d1f96f893e57100969ecfbb2e6c364a2380b07d154677ec +dist/2026-09-29/clippy-beta-x86_64-pc-windows-gnu.tar.xz=af7f0a59d0ae24d0dc8ab6424a781a65d5d0cb8b3253786daa908e1d43efa7eb +dist/2026-09-29/clippy-beta-x86_64-pc-windows-gnullvm.tar.gz=683647b7e096c4085162b0936c614e7151bd6e9e6c5da902a80e68b91d54f47a +dist/2026-09-29/clippy-beta-x86_64-pc-windows-gnullvm.tar.xz=9a3b7efc7dd8c84f8351f117bcad61423c2f2cae6c1782fd28361a6865d31abf +dist/2026-09-29/clippy-beta-x86_64-pc-windows-msvc.tar.gz=b487c66b5091b4a7446f70d9fe138b62a253aac4a8a7745151f8a2e0f0af2c77 +dist/2026-09-29/clippy-beta-x86_64-pc-windows-msvc.tar.xz=53c63e23c797c931401bfc23053b336ebb0e0e26d603cd817770daff8aa8fb8e +dist/2026-09-29/clippy-beta-x86_64-unknown-freebsd.tar.gz=1174a56bb2aab0c5f88b963a8d2d26fbb5d29ff3f3dab449c6848711641060c8 +dist/2026-09-29/clippy-beta-x86_64-unknown-freebsd.tar.xz=adbb076f2efe0532c7e6997ceab6c08e8e5c228774b0e9f844bc238c14663f90 +dist/2026-09-29/clippy-beta-x86_64-unknown-illumos.tar.gz=07047d9cf9e20e6cdf12f1a24706eaf6791d921df97430aea8a1fe37f6cfd6f1 +dist/2026-09-29/clippy-beta-x86_64-unknown-illumos.tar.xz=7720a8410c6c1814a60c9c1432190179aff2bcdba720cc1fe7863cfe9c4ef7e2 +dist/2026-09-29/clippy-beta-x86_64-unknown-linux-gnu.tar.gz=9a47312d7f06050e2a35f884a0fadb63994d9ce4c988db7055df9c3a18826db5 +dist/2026-09-29/clippy-beta-x86_64-unknown-linux-gnu.tar.xz=dc6cafdb56fa4c08d69fabb0fe55c2bfbd13f5452676b7ca7ea80b0feb0636b7 +dist/2026-09-29/clippy-beta-x86_64-unknown-linux-musl.tar.gz=681bc45997f340b2767e1fcdb98d77cd6331b435c0b9b94d3dae0d68d2afe628 +dist/2026-09-29/clippy-beta-x86_64-unknown-linux-musl.tar.xz=09933240e021562422512f9a01364a681fa5fbdc0518d53b2c84159cc160f64b +dist/2026-09-29/clippy-beta-x86_64-unknown-netbsd.tar.gz=7f09a6e7d2a3ca645d600f7b2836854240927b9b22ed5b90c2e19b18602f1bf9 +dist/2026-09-29/clippy-beta-x86_64-unknown-netbsd.tar.xz=6f55faa86ce658f28036f4828d00d9f3554a16bc4c08d338f90e46cc0b8290a8 +dist/2026-09-29/rust-beta-aarch64-pc-windows-gnullvm.msi=110b7787e72503c3d8f00bb83b4844ee5d86282006535978fabaeccd570b9446 +dist/2026-09-29/rust-beta-aarch64-pc-windows-msvc.msi=52095c3b2bb30c4444514adb315c441ae99a908fcd6dabf84df86e9fa547fdf2 +dist/2026-09-29/rust-beta-x86_64-pc-windows-gnu.msi=0dbeb0a4874799e29f61078c2ee7b52aa09f3892da449949562f7b834213d2af +dist/2026-09-29/rust-beta-x86_64-pc-windows-gnullvm.msi=67bb2a3bdf0efa223c1ec3961ca8bb3becd0dfec3dbba6c1664f3800c13f96c9 +dist/2026-09-29/rust-beta-x86_64-pc-windows-msvc.msi=f3832ded2d7dc6b8e344a8a05a58145753b7206409ab7989e4e175b000891bb5 +dist/2026-09-29/rust-beta-aarch64-apple-darwin.pkg=ba8205d70d4e7d9dac9fcbbb50e26a1c48d95f6d53115cd4f31b4894fb770f86 +dist/2026-09-29/rust-beta-x86_64-apple-darwin.pkg=821a0c24ed40c517f6b3cadc591a41e12f3dc17ee177d304d91adcc4d00d1557 +dist/2026-09-29/rustc-beta-src.tar.gz=1e970d860c77112674c08a4f6fe27c7f5f3eabdbaaabae7924872f37b0a6bc9f +dist/2026-09-29/rustc-beta-src.tar.xz=8c8dd7643b15e160e0ee62ce2ea2804c1c463954a983527335c9ad05e2d7a282 +dist/2026-09-29/rustfmt-nightly-aarch64-apple-darwin.tar.gz=3bd82067ee57ab38a80d21b212397e600b1cdfac4ffe67e064a0d4fc72696579 +dist/2026-09-29/rustfmt-nightly-aarch64-apple-darwin.tar.xz=105657de7a7e2989d0e1aa27655bad89689dfa702e6208072c04a5dc1f116dca +dist/2026-09-29/rustfmt-nightly-aarch64-pc-windows-gnullvm.tar.gz=e9ac24651a2dcd1b92c85ce3ca537ca4004d077d13d81a76c30543a2ff537ce8 +dist/2026-09-29/rustfmt-nightly-aarch64-pc-windows-gnullvm.tar.xz=12d0fed0b8caaea2fb0b63590a4dc2f96af71a54eb3ca113e09601a5e603fa7e +dist/2026-09-29/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz=2e4a7ba18c433714efdcce7b1b858a3128b10918f0b6c081225b5699ce095b4c +dist/2026-09-29/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz=da5ef1e3577b7c7817509115086de5f3b32d943b76ee3e5d3d555f88bc02b28e +dist/2026-09-29/rustfmt-nightly-aarch64-unknown-freebsd.tar.gz=44446b6fae2d871a1b6af2100f4c0783b3f000b348a1536c5c4f23b82c8107ec +dist/2026-09-29/rustfmt-nightly-aarch64-unknown-freebsd.tar.xz=7d37bb9b45eac9c1e2bbcc623da6dde1d653527d408292e913b4347f3622accb +dist/2026-09-29/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz=997a0e463191defc94ffa9405ba11a4a2bba1b220b40cef415a1a8d21e2e88f1 +dist/2026-09-29/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz=2050f09bd2dffd324c3b949626c35830d16f2e92a680a8c7001a19287d3d1b54 +dist/2026-09-29/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz=479090aac278503503b41acf936c774c645343a33193e5c149f9db28a301edc9 +dist/2026-09-29/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz=87b3f386c5372dca8676065403c0123935c7a47c5e69579beecc040a0b67ba15 +dist/2026-09-29/rustfmt-nightly-aarch64-unknown-linux-ohos.tar.gz=10da9a954bd0b76aab56c09ba01262eb87069ba18d67c36b982f3a5ca0ca3555 +dist/2026-09-29/rustfmt-nightly-aarch64-unknown-linux-ohos.tar.xz=f3db934402e31236c41188b5f85eab229e4cc8d108c9c33375e03bdd3b72bde3 +dist/2026-09-29/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz=9891c54c4cc027cb0e0edbf6c7b2d85a80287ff486074d8334cc0eea662fce48 +dist/2026-09-29/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz=8ef63b3fbace26c9ae4045bf08d8bc2c60fc6fcac095bab34f0743f82729a15b +dist/2026-09-29/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz=fabf81e854b1e1220756f2cc04cb911bff886af1a84f306bd38eaf3d7bf6e0bf +dist/2026-09-29/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz=f47aac3590c165cd800be1526278bc06538fddfbadc2f2ea0288a16274a3b04f +dist/2026-09-29/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz=fe5a0792b5f3f34e4854e257f3edf27e30c90938568e4ff576d374e5a4800cfd +dist/2026-09-29/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz=3ed3e957bbb5aeb4895cb5ddfcb93abd32a5c10982adb0724a3b91a578c9d5a5 +dist/2026-09-29/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz=d1e17fb001c32efa0455ce814ba35276e02de4fa41f858a1573f407ebfb79b21 +dist/2026-09-29/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz=d673931f677d95884fcbdb9dd1d971588ad8209b12b9ec10338f2c9e1516479e +dist/2026-09-29/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.gz=96cf73ca815b8b5c912cb82e2ee2d75d1d60a11bda9fe242173b8a74335441b1 +dist/2026-09-29/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.xz=a1af935bc9d319a962f388cb9d52981ccc1adae9e79ad561c520f92994b5b4de +dist/2026-09-29/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.gz=a614c7570d2f7370761938d62a93cf231d6673f4cf51be1ec13a86d7857d8a26 +dist/2026-09-29/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.xz=c7033f3846f19329e2d0c177cdc328387e9fd1d6738f44f3134fd73993e810e7 +dist/2026-09-29/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz=5a5c754617ff1d20b76c4f6a651bfbeb3446d133e2d7d1d151195d9c9709ba37 +dist/2026-09-29/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz=efba88f943670d427859c411c71090e3c9c503400c527432bf0f4e45aa9a640d +dist/2026-09-29/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz=bdfd3bd37453fd6c8cf965b2ccc56d0db4131dcb232843ff8ccb744126d07581 +dist/2026-09-29/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz=36fb9e8d537d9439391b2eac5357bc93d422be396fe226c4e7ada987ae1cd0fd +dist/2026-09-29/rustfmt-nightly-powerpc64-unknown-linux-musl.tar.gz=613aaa4c896802ad5065a846a0412e708ba4ec8d4cbb8c4cc268924edf50866d +dist/2026-09-29/rustfmt-nightly-powerpc64-unknown-linux-musl.tar.xz=d11268b84e0ad465d055208171268a63340724909916c9c4c8f2db7f77930406 +dist/2026-09-29/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz=3635e8a286ebf903e86b13e71a0c9da91b1cf08acd15fb529990e1bb01938994 +dist/2026-09-29/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz=7ba06366c9cb6641d177d1fea1efc74b4a70145f4947aea57dc3161d21e3b53b +dist/2026-09-29/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.gz=64fa9295cd882e817a913046856e11e3980740efedbc9dce3c9154e38c9a1088 +dist/2026-09-29/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.xz=190e0db21d5bfb1e47a6448566b42429a386e8ced68eccf29c575e8a17774aa2 +dist/2026-09-29/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz=780db7ef6ffc4d4e3bf37588d111b2733b2f30b22d1247111a7b6d290ed7882a +dist/2026-09-29/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz=0c55a5f391d9e032ec946bf2ea977b80514239c54109438e4ef17257bbae05e6 +dist/2026-09-29/rustfmt-nightly-riscv64gc-unknown-linux-musl.tar.gz=e9f3036825eae095bc415b485ad9397c96edc3481a258865db9d4b54e7cc9134 +dist/2026-09-29/rustfmt-nightly-riscv64gc-unknown-linux-musl.tar.xz=e4b3cac7c35db29013a22fa6e374011dea824909e3411ce4dabdb197344e0a9d +dist/2026-09-29/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz=c989d87883a750ae2e3d92366f22c0a55858dab9402260af5b2272b8b3eb10c2 +dist/2026-09-29/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz=e9cbc14a8c2a85229bd523a46982aaf50e242a810708f3ebb3eff2b89c8d1ddd +dist/2026-09-29/rustfmt-nightly-sparcv9-sun-solaris.tar.gz=9bcbe9b3c071008b490ecfc47e0b6007ef0577a488b71ffe829cac10c3f7b467 +dist/2026-09-29/rustfmt-nightly-sparcv9-sun-solaris.tar.xz=319876228cd9ba5319749c819fd127ff7fd9fb87e2886aa4eb97f14e13e95cf3 +dist/2026-09-29/rustfmt-nightly-x86_64-apple-darwin.tar.gz=a71ddea3e092dcd35a2b18c2538a7fc7df24b7fbde464e5b2a8014f86b24b843 +dist/2026-09-29/rustfmt-nightly-x86_64-apple-darwin.tar.xz=5b0ccd75907f3dc4a603cd60bbe87a11c3d75dc793adbdeb453fd385ef91df1f +dist/2026-09-29/rustfmt-nightly-x86_64-pc-solaris.tar.gz=26149456d7e5823901a3df9e5b4e49a93a04e02a536b324bfe2aa16dc5a5f4b6 +dist/2026-09-29/rustfmt-nightly-x86_64-pc-solaris.tar.xz=874f59be83371e0371bd30ccd4abcfe038377c2544ee2c3c577b8a8882a28ee5 +dist/2026-09-29/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz=bc002bd19c2d5c13692d8571c6a82cca5213dd5454e8f1d17215452683b612c3 +dist/2026-09-29/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz=09c04f5a4a7bf944b58b1b3a0a3142ac8450d7db5277028b881d7ab96242a0b9 +dist/2026-09-29/rustfmt-nightly-x86_64-pc-windows-gnullvm.tar.gz=4af726ea96c30759085ad6886d3d0b0c429abb6ca68990c382f2490ec44cb022 +dist/2026-09-29/rustfmt-nightly-x86_64-pc-windows-gnullvm.tar.xz=77e0d096d1d241c238dd84266a0d32500f3dc0acf142a644012a99eb50fb00f7 +dist/2026-09-29/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz=294cf327a6af4a8a88ddd6c3bd74a9877afe0940b6a451675f67b10c20588c1d +dist/2026-09-29/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz=8d2be13b650f7ce0eea90ad26f7b8f5b02771d140ef4b13d3b50ca0a7fe53e21 +dist/2026-09-29/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz=bb8a080ab727bf0503740763ce1eaadcedfd5f592b5462140d1038d397a973b7 +dist/2026-09-29/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz=35dc1d5b43934cb35f390491d62c5ebb570005f64edc0baab6456bf2421a71d2 +dist/2026-09-29/rustfmt-nightly-x86_64-unknown-illumos.tar.gz=7af5c6a369e6bee9a0239a5c2ebb901dbec8a5b4ed7dbefcd54b84d72cdcf2e1 +dist/2026-09-29/rustfmt-nightly-x86_64-unknown-illumos.tar.xz=633ea7cccf1ff5d0999efcb16a35da4e47911ec276b03bf62db5e87b38e82e88 +dist/2026-09-29/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz=cb4dc1c222f7cc67bb2a97418347fce26bdcec945567afc4867549901c9248ad +dist/2026-09-29/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz=243f7fd02c5e75bdbd2e35b0ff0ad916de1fe812bf05ae92e51e73f085ebf3bf +dist/2026-09-29/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz=864f2b4694d45c087f28a54096c5419970a8b7f21e8d60bcd34eef10c6505ab3 +dist/2026-09-29/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz=a461bbc1b5162ae9c7f0c6d96025b6eecf35f4f0bd472715a52937934049bd52 +dist/2026-09-29/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz=11653a0860011db783741465f11dd6833325d1396a82518acb4024d9f65cc545 +dist/2026-09-29/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz=ba8ac6446ce72fc0bad895759fe9fa281acabbf06ed676908b07a303849f1139 +dist/2026-09-29/rustc-nightly-aarch64-apple-darwin.tar.gz=f8bd07a928a00f5a58ecb90eaa3f5383deeca284ecd269c259dce96229c8dd9f +dist/2026-09-29/rustc-nightly-aarch64-apple-darwin.tar.xz=e76cb7ea5f41feb883ee7ed89bd71f48ec029eca9605d3aa757d952bd6a153be +dist/2026-09-29/rustc-nightly-aarch64-pc-windows-gnullvm.tar.gz=0c6136923cf94fa3910caa5440e2e68a4c517366154a57824a1e96f30da4fc81 +dist/2026-09-29/rustc-nightly-aarch64-pc-windows-gnullvm.tar.xz=bc94e0fe4523abe8ae134c068651b571e24ddb44a2ed7bbf40feff344d1668f0 +dist/2026-09-29/rustc-nightly-aarch64-pc-windows-msvc.tar.gz=1d0196716ec66f3a08092736407bcff0b5eeeb0ed9e700467f16959ca8167594 +dist/2026-09-29/rustc-nightly-aarch64-pc-windows-msvc.tar.xz=c08b4c0f3f5df85fb2a39a9e7748be6ce73bb4c17aa00549d3098a4e30738336 +dist/2026-09-29/rustc-nightly-aarch64-unknown-freebsd.tar.gz=3a513dd49f1fd8c80c8a9b51d68bf40a01a4fe6cf0c98a12420dc130e28c3a0d +dist/2026-09-29/rustc-nightly-aarch64-unknown-freebsd.tar.xz=3553abe463701c7b10bc853849e4273eb1f3a0d1d3c6e3d5dda106a41729e2ad +dist/2026-09-29/rustc-nightly-aarch64-unknown-linux-gnu.tar.gz=23732a3568613f3a0b2b06ab28e1732282297399ddfa07baccaa67c12f50ebdf +dist/2026-09-29/rustc-nightly-aarch64-unknown-linux-gnu.tar.xz=651734f660be3f83022f4a627c920753e19e79a7e6c0ee1e6c41f4f78c59767d +dist/2026-09-29/rustc-nightly-aarch64-unknown-linux-musl.tar.gz=fa01fe14d03160af899bf60866613826f83a180a6b9eafacb0cf97380059ebf8 +dist/2026-09-29/rustc-nightly-aarch64-unknown-linux-musl.tar.xz=3a67ebd7e6083ba65598070c2097e322eab032d4a5084bb6a829a51d4923e6b8 +dist/2026-09-29/rustc-nightly-aarch64-unknown-linux-ohos.tar.gz=444251d96d77b80d3b8252ab13108ef053c9482340fbed582b1bef245c258b32 +dist/2026-09-29/rustc-nightly-aarch64-unknown-linux-ohos.tar.xz=1df09853d89ca89950dcf058933dd4d760d1aad172a67b9f60c5b25522f0fe40 +dist/2026-09-29/rustc-nightly-arm-unknown-linux-gnueabi.tar.gz=61f9a4ade7f6daf6b5dfab631321b99625533bab506020594fbc9b6b7c1065c5 +dist/2026-09-29/rustc-nightly-arm-unknown-linux-gnueabi.tar.xz=21ccf42a6618c0b7c1db874566d1e1abe48556c6d8b45139f7c858583debdafa +dist/2026-09-29/rustc-nightly-arm-unknown-linux-gnueabihf.tar.gz=296d9251257c6b8a1225cc6cf5c422a790df5b9ccd2fb84d269bff701ee71928 +dist/2026-09-29/rustc-nightly-arm-unknown-linux-gnueabihf.tar.xz=d732c98eeefb447784ef6cb61c59fc919c3e48d4c291efff524cd9492a44e8ae +dist/2026-09-29/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.gz=5f50621c4463f2cd439922c2bb3b9d2e6af145bfe63aae0a2edec7073af7c758 +dist/2026-09-29/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.xz=52af25e8af238a3fe660f984c1c1cd28f90355cb78d5ba6a967bba94377572ef +dist/2026-09-29/rustc-nightly-i686-unknown-linux-gnu.tar.gz=55f65a08943922d590639588d8c29a6fba4049b33f20332195d72a71a7af7d2e +dist/2026-09-29/rustc-nightly-i686-unknown-linux-gnu.tar.xz=e234eb2638f14654677955ecdf87fe369d439da77cd466e0154f673898bee09b +dist/2026-09-29/rustc-nightly-loongarch64-unknown-linux-gnu.tar.gz=70772d9f7de70d4f0ac082e266bfe582a9c20969be33482f3f3244aeca11ee57 +dist/2026-09-29/rustc-nightly-loongarch64-unknown-linux-gnu.tar.xz=4f021e104f149d5c128b646bfd7a889c3bef37322ace9a5759075b42cde2c193 +dist/2026-09-29/rustc-nightly-loongarch64-unknown-linux-musl.tar.gz=b771b86f7af7055f876cd842f70288d26f34c9fb594b7930a43d94d22e07e6f5 +dist/2026-09-29/rustc-nightly-loongarch64-unknown-linux-musl.tar.xz=bfa7b382ac38053d6cd2a5fbda87494168ce54bfad95b161c0bc3790e94d99bf +dist/2026-09-29/rustc-nightly-powerpc-unknown-linux-gnu.tar.gz=576a6faa602ebc4eec32cc06209fe958f98ae584db2c8ba52c7d55bca258c16a +dist/2026-09-29/rustc-nightly-powerpc-unknown-linux-gnu.tar.xz=7a9563061646816f62ed10d8470ddc931d1df850342bca8a5e109dca445a487e +dist/2026-09-29/rustc-nightly-powerpc64-unknown-linux-gnu.tar.gz=d94deb2c8476421c2a223af821d7bda039ab853954e6e7eec3dc4235a4bd1de2 +dist/2026-09-29/rustc-nightly-powerpc64-unknown-linux-gnu.tar.xz=9d21d197013585e2cba456b76885c41e3b43a30c4004845fdc313eb33a38afcd +dist/2026-09-29/rustc-nightly-powerpc64-unknown-linux-musl.tar.gz=bab32e4293dd8052aa081aaa6643303d7b0aa4fbe3acbfd91c717a0bea776a5f +dist/2026-09-29/rustc-nightly-powerpc64-unknown-linux-musl.tar.xz=445b9ff411c8dc74cbc337b1b353c72dc75bc431eb997a36436e6b80987059ec +dist/2026-09-29/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.gz=c8ef38bc477c70fa0e7131c4971b52ae728524e80e29a2570dd5354d5dbe48f2 +dist/2026-09-29/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.xz=afb3c35107b8e4cb6edb48cfa71113084d6fa5360b28127a0e8e1948fa2abc7e +dist/2026-09-29/rustc-nightly-powerpc64le-unknown-linux-musl.tar.gz=9ce50d28b324c020b5fdb37db1a5522ed5a59815a14cfc117177a6c0d98a0d73 +dist/2026-09-29/rustc-nightly-powerpc64le-unknown-linux-musl.tar.xz=9bed9c6990d3ae6b33e60ffa7b60d0e9f6020b24c2c8b5cf7fff61474c87c987 +dist/2026-09-29/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.gz=5ae5f61985ed5dae8cd9f6d7638afe4957ef1d860a8e819249fdac1d58750e93 +dist/2026-09-29/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.xz=445bef041d5e64fdbd50ad4e883c6b831c4df8bc170f04f779fdccd5db553f02 +dist/2026-09-29/rustc-nightly-riscv64gc-unknown-linux-musl.tar.gz=57f862b615abeef4aa0988e9b023468230757454c30ff8af847a87f884e43f41 +dist/2026-09-29/rustc-nightly-riscv64gc-unknown-linux-musl.tar.xz=eaa6fd5bbb2a8e256e8f0a335f9b7ef33c60a40f0d4bae7a72ad1341809dc8aa +dist/2026-09-29/rustc-nightly-s390x-unknown-linux-gnu.tar.gz=1d2eb7ede0ef321f5a5eed1f19ff30c645048cad2dce1ee32a01fd473d07d529 +dist/2026-09-29/rustc-nightly-s390x-unknown-linux-gnu.tar.xz=892a029d3c60c9b037c073a02cad2c22247bfdf6c21ef94891eb5709ac85b3aa +dist/2026-09-29/rustc-nightly-sparcv9-sun-solaris.tar.gz=75ebd4fd6a89b3fa3529fecc6ab418a88aaea6f2085d2041bf7aa65cab57d1e7 +dist/2026-09-29/rustc-nightly-sparcv9-sun-solaris.tar.xz=56b8da62b8a49658c8a9a575bee49c28eaf955d21442e77c1740935f94d4b0bc +dist/2026-09-29/rustc-nightly-x86_64-apple-darwin.tar.gz=a1063ef2cf262f973c63358955bb1381fb37e73f154b44828b77091d3156a450 +dist/2026-09-29/rustc-nightly-x86_64-apple-darwin.tar.xz=99773bfc304755bc642fbc3617989bc62ee79f2265008c64d6ac15c514673f10 +dist/2026-09-29/rustc-nightly-x86_64-pc-solaris.tar.gz=b9c97ef49440e32d0f62012b1614b8e5cb9773efafeeec5517f82f6ef4f21623 +dist/2026-09-29/rustc-nightly-x86_64-pc-solaris.tar.xz=a286922465f006f15c802a8c1dce3c12ea73846a89006a917c86ebe20e605d7b +dist/2026-09-29/rustc-nightly-x86_64-pc-windows-gnu.tar.gz=ad9a406b01931b98be6021e991899cc1fb028a121b35ffd0a30d8b9359e3b940 +dist/2026-09-29/rustc-nightly-x86_64-pc-windows-gnu.tar.xz=8de44d3694475f3d7579f24d9d8c0dc66f927cd816db4cb572f81eb1e3eb5656 +dist/2026-09-29/rustc-nightly-x86_64-pc-windows-gnullvm.tar.gz=404c667f59d56ac591e9d043ec0427ac2a676fd2a1825442efc3465fe48719f6 +dist/2026-09-29/rustc-nightly-x86_64-pc-windows-gnullvm.tar.xz=f581783eb350436723bb5b4f12257dec703651b055998e3490f7af616e987479 +dist/2026-09-29/rustc-nightly-x86_64-pc-windows-msvc.tar.gz=530917988010ccbe9a6449f5136ba01958d5f94e282a41834b10e7017da11b20 +dist/2026-09-29/rustc-nightly-x86_64-pc-windows-msvc.tar.xz=8ad2b5afbc056d7515626ac2aef4b81d992469f70ebf41701222cca1bc2bcd3b +dist/2026-09-29/rustc-nightly-x86_64-unknown-freebsd.tar.gz=b693055d8cd4da607549da8ed0a186876a3db81f3a27deba1872439d68748014 +dist/2026-09-29/rustc-nightly-x86_64-unknown-freebsd.tar.xz=b55c0de331ce5601970e8227a34ada46ec394a18a86b89700b50d369b6dd59d7 +dist/2026-09-29/rustc-nightly-x86_64-unknown-illumos.tar.gz=54a37b89c59f32e12225f183cb4b0cf2c79721fe5dc15f5801c9527d426b2df7 +dist/2026-09-29/rustc-nightly-x86_64-unknown-illumos.tar.xz=0cff97495e37bdb45230d2ce7d57ccf06b767f6fbbb7cf69df57ae1595b8511e +dist/2026-09-29/rustc-nightly-x86_64-unknown-linux-gnu.tar.gz=ee5cf66faf459f360fb13c9898c2f588f5e04525ce29539c81147b3d0daaa3e8 +dist/2026-09-29/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz=dba6569dd43bf3566bea07621fd270baac5ee75ca70a729c45bcef46f2725b9f +dist/2026-09-29/rustc-nightly-x86_64-unknown-linux-musl.tar.gz=ce2f90940e5911e475f10dc25f7f49a32a971b375bc5d990de4c729f3efde5e0 +dist/2026-09-29/rustc-nightly-x86_64-unknown-linux-musl.tar.xz=7a3b06231fb1604c465c241951cb6c2286d841bb175da13d933c24bc5822a2f5 +dist/2026-09-29/rustc-nightly-x86_64-unknown-netbsd.tar.gz=ce146f46f22b2cb210fbd5a2ce33511b309101f2e1e290db4319e8ed14894bf9 +dist/2026-09-29/rustc-nightly-x86_64-unknown-netbsd.tar.xz=9c787a33d96d02b05e1ad8eb0ce6da8de1f531a05e05199ac4ed8dff051314cb +dist/2026-09-29/rust-nightly-aarch64-pc-windows-gnullvm.msi=a4c6d25c32b5a0a751b53568185e1bb29b6701e9c78a3c964c99de435d5df5d2 +dist/2026-09-29/rust-nightly-aarch64-pc-windows-msvc.msi=1bcc25f2c3c3d154578d314d51488de9a5db11759a38703a6ecf8bb5a5d6d6a3 +dist/2026-09-29/rust-nightly-x86_64-pc-windows-gnu.msi=3f8abd777867a484177ebce45009325559fd89f56065c257cc12e05b679b1658 +dist/2026-09-29/rust-nightly-x86_64-pc-windows-gnullvm.msi=be77343c9728e594b2f355f0d1c03a42ecde8757b2ff4ce4cd6da95a4dbd3e34 +dist/2026-09-29/rust-nightly-x86_64-pc-windows-msvc.msi=a080cc47533ac72c19cbd2b309fab14f33ae11f7aca18feb7a1c955883df37e6 +dist/2026-09-29/rust-nightly-aarch64-apple-darwin.pkg=1485843b8c0f53e3b50dd277aef08d2669a01ce6485bacf202c8f24fbebaf623 +dist/2026-09-29/rust-nightly-x86_64-apple-darwin.pkg=62ef2681dc83414c208c981949efbfc007b9b9ac8ab1bee776b3ed42130f2fe5 +dist/2026-09-29/rustc-nightly-src.tar.gz=54f78062b491bb27ba1eeedcf59fe8f0ce7c4e30189613af34b711feb4839ff8 +dist/2026-09-29/rustc-nightly-src.tar.xz=5d38310ee84c42e638094781bf1ec5456977fdc64021a83c8e0779d04371159a diff --git a/src/tools/build-manifest/Cargo.toml b/src/tools/build-manifest/Cargo.toml index 0b766a3e557c5..0900afd6594fe 100644 --- a/src/tools/build-manifest/Cargo.toml +++ b/src/tools/build-manifest/Cargo.toml @@ -16,5 +16,4 @@ rayon = "1.5.1" hex = "0.4.2" [build-dependencies] -serde = "1" serde_json = "1" diff --git a/src/tools/cargo b/src/tools/cargo index 495c385d0875c..f3865b2a4d1ac 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 495c385d0875c4ba51eb72ea0448a2d4c018b8d4 +Subproject commit f3865b2a4d1acc5276f6b3c67d0e057f4dab3928 diff --git a/src/tools/clippy/.remarkrc b/src/tools/clippy/.remarkrc index 04b82b8cc581f..43d1b4f22734c 100644 --- a/src/tools/clippy/.remarkrc +++ b/src/tools/clippy/.remarkrc @@ -5,6 +5,7 @@ ["remark-lint-list-item-indent", false], ["remark-lint-no-literal-urls", false], ["remark-lint-no-shortcut-reference-link", false], + ["remark-lint-no-undefined-references", {"allow": ["!NOTE", "!TIP", "!IMPORTANT", "!WARNING", "!CAUTION"]}], ["remark-lint-maximum-line-length", 120] ], "settings": { diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 37f5eb09e5247..dd35c0a1136c1 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -6,7 +6,95 @@ document. ## Unreleased / Beta / In Rust Nightly -[64c7431...master](https://github.com/rust-lang/rust-clippy/compare/64c7431...master) +[49e2f89...master](https://github.com/rust-lang/rust-clippy/compare/49e2f89...master) + +## Rust 1.99 + +Current stable, released 2026-10-01 + +[View all 123 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2026-06-27T20%3A01%3A42Z..2026-08-07T11%3A58%3A08Z+base%3Amaster) + +### New Lints + +* Added [`assert_is_empty`] to `pedantic` + [#17149](https://github.com/rust-lang/rust-clippy/pull/17149) +* Added [`nonnull_unchecked_on_box_ptr`] to `complexity` + [#17336](https://github.com/rust-lang/rust-clippy/pull/17336) +* Added [`block_scrutinee`] to `suspicious` + [#16855](https://github.com/rust-lang/rust-clippy/pull/16855) +* Added [`definition_in_module_root`] to `restriction` + [#16965](https://github.com/rust-lang/rust-clippy/pull/16965) +* Added [`rest_pattern_accessible_field`] to `restriction` + [#15000](https://github.com/rust-lang/rust-clippy/pull/15000) +* Added [`unnecessary_rest_pattern`] to `restriction` + [#15000](https://github.com/rust-lang/rust-clippy/pull/15000) +* Added [`manual_bit_width`] to `pedantic` + [#16902](https://github.com/rust-lang/rust-clippy/pull/16902) +* Added [`mismatched_bit_width_type`] to `suspicious` + [#16902](https://github.com/rust-lang/rust-clippy/pull/16902) + +### Enhancements + +* [`redundant_pattern_matching`] parenthesize guarded `matches!` suggestions so the autofix keeps + the original operator precedence + [#17287](https://github.com/rust-lang/rust-clippy/pull/17287) +* [`unwrap_or_default`] don't suggest `unwrap_or_default()` for a raw-pointer value below the 1.88 + MSRV that stabilized `Default` for raw pointers + [#17452](https://github.com/rust-lang/rust-clippy/pull/17452) +* [`clone_on_copy`] lint UFCS calls like `Clone::clone()` and `i32::clone()` + [#16972](https://github.com/rust-lang/rust-clippy/pull/16972) +* [`manual_div_ceil`] avoid suggestions that change the evaluation count of side-effectful divisors + [#17468](https://github.com/rust-lang/rust-clippy/pull/17468) +* [`float_cmp_const`] fix FN on `assert_eq!` + [#17024](https://github.com/rust-lang/rust-clippy/pull/17024) +* [`duration_suboptimal_units`] print the complete method name in the suggestion + [#17002](https://github.com/rust-lang/rust-clippy/pull/17002) +* [`min_ident_chars`] add an option to enforce the lint even when following the naming from the + trait + [#16741](https://github.com/rust-lang/rust-clippy/pull/16741) +* [`unnecessary_safety_comment`] handle compound assignments + [#17044](https://github.com/rust-lang/rust-clippy/pull/17044) +* [`big_endian_bytes`], [`host_endian_bytes`], [`little_endian_bytes`] lint method names (including + UFCS) rather than only calls + [#17363](https://github.com/rust-lang/rust-clippy/pull/17363) +* [`branches_sharing_code`] also lint `match` expressions whose arms end with the same expression + [#17313](https://github.com/rust-lang/rust-clippy/pull/17313) +* [`approx_constant`] add `EULER_GAMMA` and `GOLDEN_RATIO` constants + [#17441](https://github.com/rust-lang/rust-clippy/pull/17441) +* [`double_must_use`], [`let_underscore_must_use`], [`must_use_candidate`] determine `#[must_use]` + needs using the compiler algorithm + [#16633](https://github.com/rust-lang/rust-clippy/pull/16633) + +### False Positive Fixes + +* [`doc_paragraphs_missing_punctuation`] fix FP on images with an embedded link + [#16773](https://github.com/rust-lang/rust-clippy/pull/16773) +* [`multiple_unsafe_ops_per_block`] fix FP when taking a pointer to a mutable static without + reading, writing, or field-projecting into it + [#17461](https://github.com/rust-lang/rust-clippy/pull/17461) +* [`non_zero_suggestions`] fix FP when suggesting `NonZero` for signed integer div/rem + [#17385](https://github.com/rust-lang/rust-clippy/pull/17385) +* [`infinite_loop`] fix FP on `yield` inside gen blocks + [#17311](https://github.com/rust-lang/rust-clippy/pull/17311) + +### ICE Fixes + +* [`unnecessary_unwrap_unchecked`] fix ICE when resolving a path to a local variable + [#17353](https://github.com/rust-lang/rust-clippy/pull/17353) + +### Documentation Improvements + +* [`cargo_common_metadata`] update the link in the documentation + [#17435](https://github.com/rust-lang/rust-clippy/pull/17435) +* [`manual_c_str_literals`] add a clarifying note for the `as_ptr` suggestion + [#17282](https://github.com/rust-lang/rust-clippy/pull/17282) +* Fix accessible labels for the theme and version filters on the lint page + [#17434](https://github.com/rust-lang/rust-clippy/pull/17434) + +### Performance Improvements + +* [`nonstandard_macro_braces`] optimize by 99.97% + [#16808](https://github.com/rust-lang/rust-clippy/pull/16808) ## Rust 1.98 @@ -318,7 +406,7 @@ Current stable, released 2026-05-28 [#16685](https://github.com/rust-lang/rust-clippy/pull/16685) * [`nonminimal_bool`] fix ICE in `swap_binop()` by using the proper `TypeckResults` [#16659](https://github.com/rust-lang/rust-clippy/pull/16659) -* Fix ICE when using the `min_generic_const_args` incomplete feature +* Fix ICE when using the `gca_min_const_items` incomplete feature [#16692](https://github.com/rust-lang/rust-clippy/pull/16692) ### Documentation Improvements @@ -7341,6 +7429,7 @@ Released 2018-09-13 [`multiple_unsafe_ops_per_block`]: https://rust-lang.github.io/rust-clippy/main/index.html#multiple_unsafe_ops_per_block [`must_use_candidate`]: https://rust-lang.github.io/rust-clippy/main/index.html#must_use_candidate [`must_use_unit`]: https://rust-lang.github.io/rust-clippy/main/index.html#must_use_unit +[`must_use_without_reason`]: https://rust-lang.github.io/rust-clippy/main/index.html#must_use_without_reason [`mut_from_ref`]: https://rust-lang.github.io/rust-clippy/main/index.html#mut_from_ref [`mut_mut`]: https://rust-lang.github.io/rust-clippy/main/index.html#mut_mut [`mut_mutex_lock`]: https://rust-lang.github.io/rust-clippy/main/index.html#mut_mutex_lock @@ -7647,6 +7736,7 @@ Released 2018-09-13 [`trivial_regex`]: https://rust-lang.github.io/rust-clippy/main/index.html#trivial_regex [`trivially_copy_pass_by_ref`]: https://rust-lang.github.io/rust-clippy/main/index.html#trivially_copy_pass_by_ref [`try_err`]: https://rust-lang.github.io/rust-clippy/main/index.html#try_err +[`try_from_instead_of_from_str`]: https://rust-lang.github.io/rust-clippy/main/index.html#try_from_instead_of_from_str [`tuple_array_conversions`]: https://rust-lang.github.io/rust-clippy/main/index.html#tuple_array_conversions [`type_complexity`]: https://rust-lang.github.io/rust-clippy/main/index.html#type_complexity [`type_id_on_box`]: https://rust-lang.github.io/rust-clippy/main/index.html#type_id_on_box @@ -7668,6 +7758,7 @@ Released 2018-09-13 [`unit_hash`]: https://rust-lang.github.io/rust-clippy/main/index.html#unit_hash [`unit_return_expecting_ord`]: https://rust-lang.github.io/rust-clippy/main/index.html#unit_return_expecting_ord [`unknown_clippy_lints`]: https://rust-lang.github.io/rust-clippy/main/index.html#unknown_clippy_lints +[`unnecessary_as_slice`]: https://rust-lang.github.io/rust-clippy/main/index.html#unnecessary_as_slice [`unnecessary_box_returns`]: https://rust-lang.github.io/rust-clippy/main/index.html#unnecessary_box_returns [`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/main/index.html#unnecessary_cast [`unnecessary_clippy_cfg`]: https://rust-lang.github.io/rust-clippy/main/index.html#unnecessary_clippy_cfg diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index bcf2174ee35ab..653d7fd7fd65d 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.100" +version = "0.1.101" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" license = "MIT OR Apache-2.0" diff --git a/src/tools/clippy/book/src/continuous_integration/README.md b/src/tools/clippy/book/src/continuous_integration/README.md index e5c3673bde451..2a28c4f30f130 100644 --- a/src/tools/clippy/book/src/continuous_integration/README.md +++ b/src/tools/clippy/book/src/continuous_integration/README.md @@ -8,7 +8,8 @@ We recommend to use Clippy from the same toolchain, that you use for compiling your crate for maximum compatibility. E.g. if your crate is compiled with the `stable` toolchain, you should also use `stable` Clippy. -> _Note:_ New Clippy lints are first added to the `nightly` toolchain. If you +> [!NOTE] +> New Clippy lints are first added to the `nightly` toolchain. If you > want to help with improving Clippy and have CI resources left, please consider > adding a `nightly` Clippy check to your CI and report problems like false > positives back to us. With that we can fix bugs early, before they can get to diff --git a/src/tools/clippy/book/src/development/README.md b/src/tools/clippy/book/src/development/README.md index b33cdc00ead67..bc490426a88ce 100644 --- a/src/tools/clippy/book/src/development/README.md +++ b/src/tools/clippy/book/src/development/README.md @@ -4,7 +4,8 @@ Hello fellow Rustacean! If you made it here, you're probably interested in making Clippy better by contributing to it. In that case, welcome to the project! -> _Note:_ If you're just interested in using Clippy, there's nothing to see from +> [!NOTE] +> If you're just interested in using Clippy, there's nothing to see from > this point onward, and you should return to one of the earlier chapters. ## Getting started @@ -46,7 +47,8 @@ also a good idea to give the [stability guarantees][rfc_stability] and quick read. The lint categories are also described [earlier in this book](../lints.md). -> _Note:_ Some higher level things about contributing to Clippy are still +> [!NOTE] +> Some higher level things about contributing to Clippy are still > covered in the [`CONTRIBUTING.md`] document. Some of those will be moved to > the book over time, like: > - Finding something to fix diff --git a/src/tools/clippy/book/src/development/adding_lints.md b/src/tools/clippy/book/src/development/adding_lints.md index f1d1bbf93b59f..9771e8cf58540 100644 --- a/src/tools/clippy/book/src/development/adding_lints.md +++ b/src/tools/clippy/book/src/development/adding_lints.md @@ -144,7 +144,8 @@ should pass on its own. When we commit our lint, we need to commit the generated `.stderr` and if applicable `.fixed` files, too. In general, you should only commit files changed by `cargo bless` for the specific lint you are creating/editing. -> _Note:_ you can run multiple test files by specifying a comma separated list: +> [!NOTE] +> you can run multiple test files by specifying a comma separated list: > `TESTNAME=foo_functions,test2,test3`. ### Cargo lints @@ -530,6 +531,16 @@ define_Conf! { Afterwards update the documentation for the book as described in [Adding configuration to a lint](#adding-configuration-to-a-lint). +> [!TIP] +> Please be aware that items in the standard library usually have two stability dates. +> One for regular and one for const contexts. +> For example `str::split_at` became stable in Rust 1.4 and const stable in 1.86. +> +> To fix this, use `clippy_utils::is_in_const_context(cx)` together with MSRV checks. +> `clippy_utils::std_or_core(cx)` can also be necessary +> if the suggestions MSRV differs between `std` and `core`. +> To test the suggestions, annotate a `const` function with `#[clippy::msrv]` before and after the MSRV. + ## Author lint If you have trouble implementing your lint, there is also the internal `author` diff --git a/src/tools/clippy/book/src/development/basics.md b/src/tools/clippy/book/src/development/basics.md index 9f9ec09969578..0261fcdf6fae8 100644 --- a/src/tools/clippy/book/src/development/basics.md +++ b/src/tools/clippy/book/src/development/basics.md @@ -72,7 +72,8 @@ cargo bless For example, this is necessary if you fix a typo in an error message of a lint, or if you modify a test file to add a test case. -> _Note:_ This command may update more files than you intended. In that case +> [!NOTE] +> This command may update more files than you intended. In that case > only commit the files you wanted to update. [UI test]: https://rustc-dev-guide.rust-lang.org/tests/adding.html#ui-test-walkthrough diff --git a/src/tools/clippy/book/src/development/common_tools_writing_lints.md b/src/tools/clippy/book/src/development/common_tools_writing_lints.md index 1b02e3ced6b76..f588601ec9d0b 100644 --- a/src/tools/clippy/book/src/development/common_tools_writing_lints.md +++ b/src/tools/clippy/book/src/development/common_tools_writing_lints.md @@ -216,7 +216,9 @@ functions to deal with macros: return; } ``` - > Note: Code that is not from expansion is in the "root" context. So any spans + + > [!NOTE] + > Code that is not from expansion is in the "root" context. So any spans > where `from_expansion` returns `true` can be assumed to have the same > context. And so just using `span.from_expansion()` is often good enough. diff --git a/src/tools/clippy/book/src/development/emitting_lints.md b/src/tools/clippy/book/src/development/emitting_lints.md index 9ce56f0fc9dcf..4a03b004c5454 100644 --- a/src/tools/clippy/book/src/development/emitting_lints.md +++ b/src/tools/clippy/book/src/development/emitting_lints.md @@ -24,7 +24,8 @@ By far the most common method used for Clippy lints is [`check_expr` method][late_check_expr], this is because Rust is an expression language and, more often than not, the lint we want to work on must examine expressions. -> _Note:_ If you don't fully understand what expressions are in Rust, take a +> [!NOTE] +> If you don't fully understand what expressions are in Rust, take a > look at the official documentation on [expressions][rust_expressions] Other common ones include the [`check_fn` method][late_check_fn] and the @@ -65,7 +66,8 @@ impl<'tcx> LateLintPass<'tcx> for LintName { } ``` -> Note: The message should be matter of fact and avoid capitalization and +> [!NOTE] +> The message should be matter of fact and avoid capitalization and > punctuation. If multiple sentences are needed, the messages should probably be > split up into an error + a help / note / suggestion message. diff --git a/src/tools/clippy/book/src/development/infrastructure/README.md b/src/tools/clippy/book/src/development/infrastructure/README.md index 3b2a253999629..a8c82c17e42a4 100644 --- a/src/tools/clippy/book/src/development/infrastructure/README.md +++ b/src/tools/clippy/book/src/development/infrastructure/README.md @@ -15,5 +15,6 @@ Version](release.md) section. During a release cycle a changelog entry for the next release has to be written. The format of that and how to do that is documented in the [Changelog Update](changelog_update.md) section. -> _Note:_ The Clippy CI should also be described in this chapter, but for now is +> [!NOTE] +> The Clippy CI should also be described in this chapter, but for now is > left as a TODO. diff --git a/src/tools/clippy/book/src/development/infrastructure/backport.md b/src/tools/clippy/book/src/development/infrastructure/backport.md index 47ea6a412c503..3999525311c3f 100644 --- a/src/tools/clippy/book/src/development/infrastructure/backport.md +++ b/src/tools/clippy/book/src/development/infrastructure/backport.md @@ -5,7 +5,8 @@ Backports in Clippy are rare and should be approved by the Clippy team. For example, a backport is done, if a crucial ICE was fixed or a lint is broken to a point, that it has to be disabled, before landing on stable. -> Note: If you think a PR should be backported you can label it with +> [!NOTE] +> If you think a PR should be backported you can label it with > `beta-nominated`. This has to be done before the Thursday the week before the > release. @@ -45,7 +46,8 @@ some explanation and are quite subjective. Good judgement is required. ## Preparation -> Note: All commands in this chapter will be run in the Rust clone. +> [!NOTE] +> All commands in this chapter will be run in the Rust clone. Follow the instructions in [defining remotes] to define the `clippy-upstream` remote in the Rust repository. diff --git a/src/tools/clippy/book/src/development/infrastructure/changelog_update.md b/src/tools/clippy/book/src/development/infrastructure/changelog_update.md index 4652d71a9ea33..58d681176d1a8 100644 --- a/src/tools/clippy/book/src/development/infrastructure/changelog_update.md +++ b/src/tools/clippy/book/src/development/infrastructure/changelog_update.md @@ -93,7 +93,8 @@ Look for the [`beta-accepted`] label and make sure to also include the PRs with that label in the changelog. If you can, remove the `beta-accepted` labels **after** the changelog PR was merged. -> _Note:_ Some of those PRs might even get backported to the previous `beta`. +> [!NOTE] +> Some of those PRs might even get backported to the previous `beta`. > Those have to be included in the changelog of the _previous_ release. ### 5. Update `clippy::version` attributes diff --git a/src/tools/clippy/book/src/development/infrastructure/release.md b/src/tools/clippy/book/src/development/infrastructure/release.md index a429e0d953c46..3091e994fd0fb 100644 --- a/src/tools/clippy/book/src/development/infrastructure/release.md +++ b/src/tools/clippy/book/src/development/infrastructure/release.md @@ -1,6 +1,7 @@ # Release a new Clippy Version -> _NOTE:_ This document is probably only relevant to you, if you're a member of +> [!NOTE] +> This document is probably only relevant to you, if you're a member of > the Clippy team. Clippy is released together with stable Rust releases. The dates for these @@ -103,7 +104,8 @@ The `clippy_utils` crate is published to `crates.io` without any stability guarantees. To do this, after the [sync] and the release is done, switch back to the `upstream/master` branch and publish `clippy_utils`: -> Note: The Rustup PR bumping the nightly and Clippy version **must** be merged +> [!NOTE] +> The Rustup PR bumping the nightly and Clippy version **must** be merged > before doing this. ```bash diff --git a/src/tools/clippy/book/src/development/infrastructure/sync.md b/src/tools/clippy/book/src/development/infrastructure/sync.md index c2b62b092dd71..f938306ab2b12 100644 --- a/src/tools/clippy/book/src/development/infrastructure/sync.md +++ b/src/tools/clippy/book/src/development/infrastructure/sync.md @@ -40,19 +40,22 @@ sudo chmod --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subt sudo chown --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree ``` -> _Note:_ The first time running `git subtree push` a cache has to be built. +> [!NOTE] +> The first time running `git subtree push` a cache has to be built. > This involves going through the complete Clippy history once. For this you > have to increase the stack limit though, which you can do with `ulimit -s > 60000`. Make sure to run the `ulimit` command from the same session you call > git subtree. -> _Note:_ If you are a Debian user, `dash` is the shell used by default for +> [!NOTE] +> If you are a Debian user, `dash` is the shell used by default for > scripts instead of `sh`. This shell has a hardcoded recursion limit set to > 1,000. In order to make this process work, you need to force the script to run > `bash` instead. You can do this by editing the first line of the `git-subtree` > script and changing `sh` to `bash`. -> Note: The following sections assume that you have set up remotes following the +> [!NOTE] +> The following sections assume that you have set up remotes following the > instructions in [defining remotes]. [gitgitgadget-pr]: https://github.com/gitgitgadget/git/pull/493 @@ -67,22 +70,25 @@ to be run inside the `rust` directory): 2. Checkout the commit from the latest available nightly. You can get it using `rustup check`. 3. Sync the changes to the rust-copy of Clippy to your Clippy fork: - ```bash - # Be sure to either use a net-new branch, e.g. `rustup`, or delete the branch beforehand - # because changes cannot be fast forwarded and you have to run this command again. - git subtree push -P src/tools/clippy clippy-local rustup - ``` + ```bash + # Be sure to either use a net-new branch, e.g. `rustup`, or delete the branch beforehand + # because changes cannot be fast forwarded and you have to run this command again. + git subtree push -P src/tools/clippy clippy-local rustup + ``` + +> [!NOTE] +> Most of the time you have to create a merge commit in the +> `rust-clippy` repo (this has to be done in the Clippy repo, not in the +> rust-copy of Clippy): +> ```bash +> git fetch upstream # assuming upstream is the rust-lang/rust remote +> git switch rustup +> git merge upstream/main --no-ff +> ``` +> +> This is one of the few instances where a merge commit is allowed in +> a PR. - > _Note:_ Most of the time you have to create a merge commit in the - > `rust-clippy` repo (this has to be done in the Clippy repo, not in the - > rust-copy of Clippy): - ```bash - git fetch upstream # assuming upstream is the rust-lang/rust remote - git switch rustup - git merge upstream/main --no-ff - ``` - > Note: This is one of the few instances where a merge commit is allowed in - > a PR. 4. Bump the nightly version in the Clippy repository by running these commands: ```bash cargo dev sync update_nightly diff --git a/src/tools/clippy/book/src/development/macro_expansions.md b/src/tools/clippy/book/src/development/macro_expansions.md index 63a96dc373f31..655c38ff3305f 100644 --- a/src/tools/clippy/book/src/development/macro_expansions.md +++ b/src/tools/clippy/book/src/development/macro_expansions.md @@ -172,7 +172,8 @@ point the user to the relevant code in case of a compile error. However, for Clippy this is inconvenient, because most of the time *we don't* want to lint proc macro-generated code and this makes it impossible to tell what is and isn't proc macro code. -> NOTE: this is specifically only an issue when a proc macro explicitly sets the span to that of an **input span**. +> [!NOTE] +> this is specifically only an issue when a proc macro explicitly sets the span to that of an **input span**. > > For example, other common ways of creating `TokenStream`s, such as `"fn foo() {...}".parse::()`, > sets each token's span to `Span::call_site()`, which already marks the span as coming from a proc macro diff --git a/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md b/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md index 92fbf733a8faf..83d25e42cdac6 100644 --- a/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md +++ b/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md @@ -133,7 +133,8 @@ The `pattern!` macro call expands to a function `my_pattern` that expects a syntax tree expression as its argument and returns an `Option` that indicates whether the pattern matched. -> Note: The result type is explained in more detail in [a later +> [!NOTE] +> The result type is explained in more detail in [a later > section](#the-result-type). For now, it's enough to know that the result is > `Some` if the pattern matched and `None` otherwise. @@ -482,7 +483,8 @@ differences: The code below shows a simplified version of the current PatternTree: -> Note: The current implementation can be found +> [!NOTE] +> The current implementation can be found > [here](https://github.com/fkohlgrueber/pattern-matching/blob/dfb3bc9fbab69cec7c91e72564a63ebaa2ede638/pattern-match/src/pattern_tree.rs#L50-L96). @@ -516,7 +518,8 @@ pub enum BlockType { The `Alt`, `Seq` and `Opt` structs look like these: -> Note: The current implementation can be found +> [!NOTE] +> The current implementation can be found > [here](https://github.com/fkohlgrueber/pattern-matching/blob/dfb3bc9fbab69cec7c91e72564a63ebaa2ede638/pattern-match/src/matchers.rs#L35-L60). ```rust,ignore @@ -562,7 +565,8 @@ the `Expr` enum is `Any` and therefore doesn't support repetition (`*`). As another example, `Array( Lit(_)* )` is a valid pattern because the parameter of `Array` is of type `Seq` which allows sequences and repetitions. -> Note: names in the pattern syntax correspond to *PatternTree* enum +> [!NOTE] +> names in the pattern syntax correspond to *PatternTree* enum > **variants**. For example, the `Lit` in the pattern above refers to the `Lit` > variant of the `Expr` enum (`Expr::Lit`), not the `Lit` enum. diff --git a/src/tools/clippy/book/src/development/type_checking.md b/src/tools/clippy/book/src/development/type_checking.md index e92e766988858..655480e2eed25 100644 --- a/src/tools/clippy/book/src/development/type_checking.md +++ b/src/tools/clippy/book/src/development/type_checking.md @@ -12,7 +12,8 @@ The lint context [`LateContext`][LateContext] and [`TypeckResults`][TypeckResult in `LateLintPass`. They allow us to jump to type definitions and other compilation stages such as HIR. -> Note: `LateContext.typeck_results`'s return value is [`TypeckResults`][TypeckResults] +> [!NOTE] +> `LateContext.typeck_results`'s return value is [`TypeckResults`][TypeckResults] > and is created in the type checking step, it includes useful information such as types of > expressions, ways to resolve methods and so on. diff --git a/src/tools/clippy/book/src/development/writing_tests.md b/src/tools/clippy/book/src/development/writing_tests.md index 1da54322fcf72..6f8ca138d65ee 100644 --- a/src/tools/clippy/book/src/development/writing_tests.md +++ b/src/tools/clippy/book/src/development/writing_tests.md @@ -141,7 +141,8 @@ error: aborting due to 3 previous errors Note the *failures* label at the top of the fragment, we'll get rid of it (saving this output) in the next section. -> _Note:_ You can run multiple test files by specifying a comma separated list: +> [!NOTE] +> You can run multiple test files by specifying a comma separated list: > `TESTNAME=foo_functions,bar_methods,baz_structs`. ### `cargo bless` @@ -162,7 +163,8 @@ our lint, we need to commit the generated `.stderr` files, too. In general, you should only commit files changed by `cargo bless` for the specific lint you are creating/editing. -> _Note:_ If the generated `.stderr`, and `.fixed` files are empty, +> [!NOTE] +> If the generated `.stderr`, and `.fixed` files are empty, > they should be removed. ## `toml` Tests diff --git a/src/tools/clippy/book/src/installation.md b/src/tools/clippy/book/src/installation.md index d54fff9deba18..2445d09b35af3 100644 --- a/src/tools/clippy/book/src/installation.md +++ b/src/tools/clippy/book/src/installation.md @@ -4,7 +4,8 @@ If you're using `rustup` to install and manage your Rust toolchains, Clippy is usually **already installed**. In that case you can skip this chapter and go to the [Usage] chapter. -> Note: If you used the `minimal` profile when installing a Rust toolchain, +> [!NOTE] +> If you used the `minimal` profile when installing a Rust toolchain, > Clippy is not automatically installed. ## Using Rustup diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index f323d7040d260..dd754a5c01661 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -544,7 +544,7 @@ The maximum cognitive complexity a function can have ## `const-literal-digits-threshold` -The minimum digits a const float literal must have to supress the `excessive_precicion` lint +The minimum digits a const float literal must have to suppress the `excessive_precision` lint **Default Value:** `30` @@ -640,7 +640,7 @@ default configuration of Clippy. By default, any configuration will replace the * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`. * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list. -**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "MHz", "GHz", "THz", "AccessKit", "CoAP", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "InfiniBand", "RoCE", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "PowerPC", "PowerShell", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "SQLite", "MySQL", "PostgreSQL", "MariaDB", "MongoDB", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", "WebAuthn", "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "NixOS", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` +**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "MHz", "GHz", "THz", "AccessKit", "CoAP", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "InfiniBand", "RoCE", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "PowerPC", "PowerShell", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "SQLite", "MySQL", "PostgreSQL", "MariaDB", "MongoDB", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", "WebAuthn", "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "NixOS", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase", "x86_64", "CommonMark", "IntelliJ"]` --- **Affected lints:** @@ -1006,6 +1006,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`repeat_vec_with_capacity`](https://rust-lang.github.io/rust-clippy/main/index.html#repeat_vec_with_capacity) * [`same_item_push`](https://rust-lang.github.io/rust-clippy/main/index.html#same_item_push) * [`seek_from_current`](https://rust-lang.github.io/rust-clippy/main/index.html#seek_from_current) +* [`swap_ptr_to_ref`](https://rust-lang.github.io/rust-clippy/main/index.html#swap_ptr_to_ref) * [`to_digit_is_some`](https://rust-lang.github.io/rust-clippy/main/index.html#to_digit_is_some) * [`transmute_ptr_to_ref`](https://rust-lang.github.io/rust-clippy/main/index.html#transmute_ptr_to_ref) * [`tuple_array_conversions`](https://rust-lang.github.io/rust-clippy/main/index.html#tuple_array_conversions) diff --git a/src/tools/clippy/book/src/lints.md b/src/tools/clippy/book/src/lints.md index 8943274a38e81..ce04872f2be43 100644 --- a/src/tools/clippy/book/src/lints.md +++ b/src/tools/clippy/book/src/lints.md @@ -73,7 +73,8 @@ the whole group with `#![warn(clippy::pedantic)]` in the `lib.rs`/`main.rs` of your crate. This lint group is for Clippy power users that want an in depth check of their code. -> _Note:_ Instead of enabling the whole group (like Clippy itself does), you may +> [!NOTE] +> Instead of enabling the whole group (like Clippy itself does), you may > want to cherry-pick lints out of the pedantic group. If you enable this group, expect to also use `#[allow]` attributes generously @@ -87,7 +88,8 @@ using certain parts of the Rust language. It is **not** recommended to enable the whole group, but rather cherry-pick lints that are useful for your code base and your use case. -> _Note:_ Clippy will produce a warning if it finds a +> [!NOTE] +> Clippy will produce a warning if it finds a > `#![warn(clippy::restriction)]` attribute in your code! Lints from this group will restrict you in some way. If you enable a restriction diff --git a/src/tools/clippy/book/src/usage.md b/src/tools/clippy/book/src/usage.md index a7e07e9e92d68..1ab1782525a12 100644 --- a/src/tools/clippy/book/src/usage.md +++ b/src/tools/clippy/book/src/usage.md @@ -4,7 +4,8 @@ This chapter describes how to use Clippy to get the most out of it. Clippy can be used as a `cargo` subcommand or, like `rustc`, directly with the `clippy-driver` binary. -> _Note:_ This chapter assumes that you have Clippy installed already. If you're +> [!NOTE] +> This chapter assumes that you have Clippy installed already. If you're > not sure, take a look at the [Installation] chapter. ## Cargo subcommand @@ -23,7 +24,8 @@ lint group `clippy::all`. You might want to use even more lints, or you may not agree with every Clippy lint, and for that there are ways to configure lint levels. -> _Note:_ Clippy is meant to be used with a generous sprinkling of +> [!NOTE] +> Clippy is meant to be used with a generous sprinkling of > `#[allow(..)]`s through your code. So if you disagree with a lint, don't feel > bad disabling them for parts of your code or the whole project. @@ -57,7 +59,8 @@ CARGO_BUILD_WARNINGS=deny cargo clippy warnings = "deny" ``` -> _Note:_ Using `CARGO_BUILD_WARNINGS=deny` (or `-D warnings`) will cause your build to fail if **any** warnings +> [!NOTE] +> Using `CARGO_BUILD_WARNINGS=deny` (or `-D warnings`) will cause your build to fail if **any** warnings > are found in your code. That includes warnings found by rustc (e.g. > `dead_code`, etc.) @@ -103,7 +106,8 @@ The second group is the `restriction` group. This group contains lints that this group won't allow you to use `.unwrap()` in your code. You may want to look through the lints in this group and enable the ones that fit your need. -> _Note:_ You shouldn't enable the whole lint group, but cherry-pick lints from +> [!NOTE] +> You shouldn't enable the whole lint group, but cherry-pick lints from > this group. Some lints in this group will even contradict other Clippy lints! #### Too many lints @@ -113,7 +117,8 @@ group. Some people prefer to disable this group completely and then cherry-pick some lints they like from this group. The same is of course possible with every other of Clippy's lint groups. -> _Note:_ We try to keep the warn-by-default groups free from false positives +> [!NOTE] +> We try to keep the warn-by-default groups free from false positives > (FP). If you find that a lint wrongly triggers, please report it in an issue > (if there isn't an issue for that FP already) @@ -167,7 +172,8 @@ Clippy can also be used in projects that do not use cargo. To do so, run clippy-driver --edition 2018 -Cpanic=abort foo.rs ``` -> _Note:_ `clippy-driver` is designed for running Clippy and should not be used +> [!NOTE] +> `clippy-driver` is designed for running Clippy and should not be used > as a general replacement for `rustc`. `clippy-driver` may produce artifacts > that are not optimized as expected, for example. diff --git a/src/tools/clippy/clippy_config/Cargo.toml b/src/tools/clippy/clippy_config/Cargo.toml index e230f61d558a6..1ef5e94a8fa8a 100644 --- a/src/tools/clippy/clippy_config/Cargo.toml +++ b/src/tools/clippy/clippy_config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_config" -version = "0.1.100" +version = "0.1.101" edition = "2024" publish = false diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index d547eb294f6aa..5bbde19229372 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -45,6 +45,8 @@ static DEFAULT_DOC_VALID_IDENTS: &[&str] = &[ "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase", + "x86_64", + "CommonMark", "IntelliJ", ]; static DEFAULT_DISALLOWED_NAMES: &[&str] = &["foo", "baz", "quux"]; static DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS: &[&str] = &["i", "j", "x", "y", "z", "w", "n"]; @@ -477,7 +479,7 @@ define_Conf! { /// The maximum cognitive complexity a function can have #[lints(cognitive_complexity)] cognitive_complexity_threshold("cognitive-complexity-threshold"): u64 = 25, - /// The minimum digits a const float literal must have to supress the `excessive_precicion` lint + /// The minimum digits a const float literal must have to suppress the `excessive_precision` lint #[lints(excessive_precision)] const_literal_digits_threshold("const-literal-digits-threshold"): u32 = 30, #[rename = cognitive_complexity_threshold] @@ -712,6 +714,7 @@ define_Conf! { repeat_vec_with_capacity, same_item_push, seek_from_current, + swap_ptr_to_ref, to_digit_is_some, transmute_ptr_to_ref, tuple_array_conversions, diff --git a/src/tools/clippy/clippy_config/src/types.rs b/src/tools/clippy/clippy_config/src/types.rs index 53df7f4e2581b..a9a04e8a5c0a9 100644 --- a/src/tools/clippy/clippy_config/src/types.rs +++ b/src/tools/clippy/clippy_config/src/types.rs @@ -141,7 +141,7 @@ impl DisallowedPath { &self.path.node } - pub fn diag_amendment(&self, span: Span) -> impl FnOnce(&mut Diag<'_, ()>) { + pub fn diag_amendment(&self, span: Span) -> impl FnOnce(&mut Diag<'_>) { move |diag| { if let Some(replacement) = &self.replacement { diag.span_suggestion( diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index 568c15f8d5f53..a8c8e02d8c9bf 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.100" +version = "0.1.101" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" license = "MIT OR Apache-2.0" diff --git a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs index 8984184269421..db76caa3473ca 100644 --- a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs +++ b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs @@ -6,13 +6,13 @@ use clippy_config::types::{ }; use clippy_utils::diagnostics::span_lint_and_note; use clippy_utils::is_cfg_test; -use rustc_attr_ir::AttributeKind; +use rustc_attr_ir::{Attribute, AttributeKind}; use rustc_hir::{ - Attribute, FieldDef, HirId, ImplItemId, IsAuto, Item, ItemKind, Mod, OwnerId, QPath, TraitItemId, TyKind, Variant, - VariantData, + FieldDef, ImplItemId, IsAuto, Item, ItemKind, Mod, OwnerId, QPath, TraitItemId, TyKind, Variant, VariantData, }; use rustc_lint::{LateContext, LateLintPass, LintContext, impl_lint_pass}; use rustc_middle::ty::{AssocKind, TyCtxt}; +use rustc_span::def_id::LocalModId; use rustc_span::{Ident, Symbol, bug}; declare_clippy_lint! { @@ -58,13 +58,13 @@ declare_clippy_lint! { /// /// | Group | Item Kinds | /// |--------------------|----------------------| - /// | `modules` | "mod", "foreign_mod" | - /// | `use` | "use" | - /// | `macros` | "macro" | - /// | `global_asm` | "global_asm" | - /// | `UPPER_SNAKE_CASE` | "static", "const" | - /// | `PascalCase` | "ty_alias", "opaque_ty", "enum", "struct", "union", "trait", "trait_alias", "impl" | - /// | `lower_snake_case` | "fn" | + /// | `modules` | `mod`, `foreign_mod` | + /// | `use` | `use` | + /// | `macros` | `macro` | + /// | `global_asm` | `global_asm` | + /// | `UPPER_SNAKE_CASE` | `static`, `const` | + /// | `PascalCase` | `ty_alias`, `opaque_ty`, `enum`, `struct`, `union`, `trait`, `trait_alias`, `impl` | + /// | `lower_snake_case` | `fn` | /// /// The groups' names are arbitrary and can be changed to suit the /// conventions that should be enforced for a specific project. @@ -486,7 +486,7 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering { } } - fn check_mod(&mut self, cx: &LateContext<'tcx>, module: &'tcx Mod<'tcx>, _: HirId) { + fn check_mod(&mut self, cx: &LateContext<'tcx>, module: &'tcx Mod<'tcx>, _: LocalModId) { struct CurItem<'a> { item: &'a Item<'a>, order: usize, diff --git a/src/tools/clippy/clippy_lints/src/assert_is_empty.rs b/src/tools/clippy/clippy_lints/src/assert_is_empty.rs index 677a7d08c562d..6d9892af61854 100644 --- a/src/tools/clippy/clippy_lints/src/assert_is_empty.rs +++ b/src/tools/clippy/clippy_lints/src/assert_is_empty.rs @@ -65,7 +65,7 @@ declare_clippy_lint! { /// assert_eq!(items, [] as [i32; 0]); /// assert_ne!(items, [] as [i32; 0]); /// ``` - #[clippy::version = "1.98.0"] + #[clippy::version = "1.99.0"] pub ASSERT_IS_EMPTY, pedantic, "asserting on emptiness without showing the asserted value on failure" diff --git a/src/tools/clippy/clippy_lints/src/attrs/deprecated_semver.rs b/src/tools/clippy/clippy_lints/src/attrs/deprecated_semver.rs index e3b1a05bda7dd..ff929df0acded 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/deprecated_semver.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/deprecated_semver.rs @@ -2,7 +2,7 @@ use super::DEPRECATED_SEMVER; use clippy_utils::diagnostics::span_lint; use clippy_utils::sym; use rustc_ast::{LitKind, MetaItemLit}; -use rustc_hir::VERSION_PLACEHOLDER; +use rustc_attr_ir::VERSION_PLACEHOLDER; use rustc_lint::EarlyContext; use rustc_span::Span; use semver::Version; diff --git a/src/tools/clippy/clippy_lints/src/attrs/mod.rs b/src/tools/clippy/clippy_lints/src/attrs/mod.rs index 23d5adfb201a4..071b3b1df0786 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mod.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mod.rs @@ -83,7 +83,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category. + /// Checks for `warn`/`deny`/`forbid` attributes targeting the whole `clippy::restriction` category. /// /// ### Why is this bad? /// Restriction lints sometimes are in contrast with other lints or even go against idiomatic rust. @@ -110,12 +110,12 @@ declare_clippy_lint! { /// with `#[rustfmt::skip]`. /// /// ### Why is this bad? - /// Since tool_attributes ([rust-lang/rust#44690](https://github.com/rust-lang/rust/issues/44690)) + /// Since `tool_attributes` ([rust-lang/rust#44690](https://github.com/rust-lang/rust/issues/44690)) /// are stable now, they should be used instead of the old `cfg_attr(rustfmt)` attributes. /// /// ### Known problems /// This lint doesn't detect crate level inner attributes, because they get - /// processed before the PreExpansionPass lints get executed. See + /// processed before the pre-expansion pass lints get executed. See /// [#3123](https://github.com/rust-lang/rust-clippy/pull/3123#issuecomment-422321765) /// /// ### Example @@ -432,22 +432,22 @@ declare_clippy_lint! { /// /// This lint permits lint attributes for lints emitted on the items themself. /// For `use` items these lints are: - /// * ambiguous_glob_reexports - /// * dead_code - /// * deprecated - /// * hidden_glob_reexports - /// * unreachable_pub - /// * unused - /// * unused_braces - /// * unused_import_braces - /// * clippy::disallowed_types - /// * clippy::enum_glob_use - /// * clippy::macro_use_imports - /// * clippy::module_name_repetitions - /// * clippy::redundant_pub_crate - /// * clippy::single_component_path_imports - /// * clippy::unsafe_removed_from_name - /// * clippy::wildcard_imports + /// * `ambiguous_glob_reexports` + /// * `dead_code` + /// * `deprecated` + /// * `hidden_glob_reexports` + /// * `unreachable_pub` + /// * `unused` + /// * `unused_braces` + /// * `unused_import_braces` + /// * `clippy::disallowed_types` + /// * `clippy::enum_glob_use` + /// * `clippy::macro_use_imports` + /// * `clippy::module_name_repetitions` + /// * `clippy::redundant_pub_crate` + /// * `clippy::single_component_path_imports` + /// * `clippy::unsafe_removed_from_name` + /// * `clippy::wildcard_imports` /// /// For `extern crate` items these lints are: /// * `unused_imports` on items with `#[macro_use]` diff --git a/src/tools/clippy/clippy_lints/src/bit_width.rs b/src/tools/clippy/clippy_lints/src/bit_width.rs index cd08188866ead..2b7f41ff32591 100644 --- a/src/tools/clippy/clippy_lints/src/bit_width.rs +++ b/src/tools/clippy/clippy_lints/src/bit_width.rs @@ -25,7 +25,7 @@ declare_clippy_lint! { /// let x: u32 = 5; /// let bit_width = x.bit_width(); /// ``` - #[clippy::version = "1.98.0"] + #[clippy::version = "1.99.0"] pub MANUAL_BIT_WIDTH, pedantic, "manually reimplementing `bit_width`" @@ -49,7 +49,7 @@ declare_clippy_lint! { /// let x: u64 = 5; /// let bit_width = x.bit_width(); /// ``` - #[clippy::version = "1.98.0"] + #[clippy::version = "1.99.0"] pub MISMATCHED_BIT_WIDTH_TYPE, suspicious, "type mismatch in bit width calculation" diff --git a/src/tools/clippy/clippy_lints/src/block_scrutinee.rs b/src/tools/clippy/clippy_lints/src/block_scrutinee.rs index 2762f85b91029..03a8b7c318144 100644 --- a/src/tools/clippy/clippy_lints/src/block_scrutinee.rs +++ b/src/tools/clippy/clippy_lints/src/block_scrutinee.rs @@ -22,7 +22,7 @@ declare_clippy_lint! { /// ```rust,ignore /// if let Some(x) = { my_function() } { .. } /// ``` - #[clippy::version = "1.98.0"] + #[clippy::version = "1.99.0"] pub BLOCK_SCRUTINEE, suspicious, "warns when the scrutinee is wrapped in a block in older editions" diff --git a/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs b/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs index ecddefe25a264..c1ce1211ddfcd 100644 --- a/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs +++ b/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs @@ -15,7 +15,7 @@ declare_clippy_lint! { /// Coercion or `from()` is another way to convert bool to a number. /// Both methods are guaranteed to return 1 for true, and 0 for false. /// - /// See https://doc.rust-lang.org/std/primitive.bool.html#impl-From%3Cbool%3E + /// See /// /// ### Example /// ```no_run diff --git a/src/tools/clippy/clippy_lints/src/casts/as_pointer_underscore.rs b/src/tools/clippy/clippy_lints/src/casts/as_pointer_underscore.rs index 3ab6693756f5a..63df64f4edb57 100644 --- a/src/tools/clippy/clippy_lints/src/casts/as_pointer_underscore.rs +++ b/src/tools/clippy/clippy_lints/src/casts/as_pointer_underscore.rs @@ -3,7 +3,7 @@ use rustc_lint::LateContext; use rustc_middle::ty::Ty; pub fn check<'tcx>(cx: &LateContext<'tcx>, ty_into: Ty<'_>, cast_to_hir: &'tcx rustc_hir::Ty<'tcx>) { - if let rustc_hir::TyKind::Ptr(rustc_hir::MutTy { ty, .. }) = cast_to_hir.kind + if let rustc_hir::TyKind::Ptr(ty, _) = cast_to_hir.kind && matches!(ty.kind, rustc_hir::TyKind::Infer(())) { clippy_utils::diagnostics::span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs index cb9f7f618d7f9..3db972278a8ad 100644 --- a/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs @@ -18,8 +18,8 @@ pub(super) fn check<'tcx>( cast_to: &'tcx Ty<'_>, msrv: Msrv, ) -> bool { - if let TyKind::Ptr(target) = cast_to.kind - && !matches!(target.ty.kind, TyKind::TraitObject(..)) + if let TyKind::Ptr(target, _) = cast_to.kind + && !matches!(target.kind, TyKind::TraitObject(..)) && let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = cast_expr.kind && !is_lint_allowed(cx, BORROW_AS_PTR, expr.hir_id) // Fix #9884 diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs index f758fb0042301..ce03afac4c102 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs @@ -181,7 +181,7 @@ fn offer_suggestion( expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to_span: Span, - diag: &mut Diag<'_, ()>, + diag: &mut Diag<'_>, ) { let cast_to_snip = snippet(cx, cast_to_span, ".."); let mut applicability = Applicability::Unspecified; diff --git a/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs index 2b1b3c1b96fd3..4c78f2639c0ba 100644 --- a/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs @@ -11,21 +11,21 @@ use rustc_span::Spanned; use super::MANUAL_DANGLING_PTR; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>) { - if let TyKind::Ptr(ref ptr_ty) = to.kind { + if let TyKind::Ptr(ptr_ty, mutbl) = to.kind { let init_expr = expr_or_init(cx, from); - if is_expr_const_aligned(cx, init_expr, ptr_ty.ty) + if is_expr_const_aligned(cx, init_expr, ptr_ty) && let Some(std_or_core) = std_or_core(cx) - && let pointee_ty = cx.typeck_results().node_type(ptr_ty.ty.hir_id) + && let pointee_ty = cx.typeck_results().node_type(ptr_ty.hir_id) && pointee_ty.is_sized(cx.tcx, cx.typing_env()) { - let sugg_fn = match ptr_ty.mutbl { + let sugg_fn = match mutbl { Mutability::Not => "ptr::dangling", Mutability::Mut => "ptr::dangling_mut", }; - let sugg = if let TyKind::Infer(()) = ptr_ty.ty.kind { + let sugg = if let TyKind::Infer(()) = ptr_ty.kind { format!("{std_or_core}::{sugg_fn}()") - } else if let Some(mut_ty_snip) = ptr_ty.ty.span.get_text(cx) { + } else if let Some(mut_ty_snip) = ptr_ty.span.get_text(cx) { format!("{std_or_core}::{sugg_fn}::<{mut_ty_snip}>()") } else { return; diff --git a/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs index 61fe563512b60..bee8478a7bcc8 100644 --- a/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs @@ -47,13 +47,13 @@ pub(super) fn check<'tcx>( let mut app = Applicability::MachineApplicable; let turbofish = match &cast_to_hir.kind { TyKind::Infer(()) => String::new(), - TyKind::Ptr(mut_ty) => { - if matches!(mut_ty.ty.kind, TyKind::Infer(())) { + TyKind::Ptr(inner_ty, _) => { + if matches!(inner_ty.kind, TyKind::Infer(())) { String::new() } else { format!( "::<{}>", - snippet_with_applicability(cx, mut_ty.ty.span, "/* type */", &mut app) + snippet_with_applicability(cx, inner_ty.span, "/* type */", &mut app) ) } }, diff --git a/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs b/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs index 5f20efe2e3646..6107a0cb363d0 100644 --- a/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs +++ b/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs @@ -52,8 +52,8 @@ pub(super) fn check<'tcx>( if msrv.meets(cx, msrvs::POINTER_CAST_CONSTNESS) { let mut app = Applicability::MachineApplicable; let sugg = if let ExprKind::Cast(nested_from, nested_hir_ty) = cast_from_expr.kind - && let hir::TyKind::Ptr(ptr_ty) = nested_hir_ty.kind - && let hir::TyKind::Infer(()) = ptr_ty.ty.kind + && let hir::TyKind::Ptr(inner_ty, _) = nested_hir_ty.kind + && let hir::TyKind::Infer(()) = inner_ty.kind { // `(foo as *const _).cast_mut()` fails method name resolution // avoid this by `as`-ing the full type diff --git a/src/tools/clippy/clippy_lints/src/casts/ref_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/ref_as_ptr.rs index b850f7f41ed33..0e69445caf5f4 100644 --- a/src/tools/clippy/clippy_lints/src/casts/ref_as_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/ref_as_ptr.rs @@ -44,13 +44,13 @@ pub(super) fn check<'tcx>( let mut app = Applicability::MachineApplicable; let turbofish = match &cast_to_hir_ty.kind { TyKind::Infer(()) => String::new(), - TyKind::Ptr(mut_ty) => { - if matches!(mut_ty.ty.kind, TyKind::Infer(())) { + TyKind::Ptr(inner_ty, _) => { + if matches!(inner_ty.kind, TyKind::Infer(())) { String::new() } else { format!( "::<{}>", - snippet_with_applicability(cx, mut_ty.ty.span, "/* type */", &mut app) + snippet_with_applicability(cx, inner_ty.span, "/* type */", &mut app) ) } }, diff --git a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs index c98fa1a5cba74..0b2ba1428b53a 100644 --- a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs +++ b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs @@ -35,13 +35,13 @@ pub(super) fn check<'tcx>( // Ignore casts to e.g. type aliases and infer types // - p as pointer_alias // - p as _ - && let TyKind::Ptr(to_pointee) = cast_to_hir.kind + && let TyKind::Ptr(to_pointee, _) = cast_to_hir.kind { - match to_pointee.ty.kind { + match to_pointee.kind { // Ignore casts to pointers that are aliases or cfg dependant, e.g. // - p as *const std::ffi::c_char (alias) // - p as *const std::os::raw::c_char (cfg dependant) - TyKind::Path(qpath) if is_ty_alias(&qpath) || is_hir_ty_cfg_dependant(cx, to_pointee.ty) => { + TyKind::Path(qpath) if is_ty_alias(&qpath) || is_hir_ty_cfg_dependant(cx, to_pointee) => { return false; }, // Ignore `p as *const _` diff --git a/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs index 7f63a7336b1b0..d5c93de2ba773 100644 --- a/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs @@ -9,19 +9,19 @@ use rustc_lint::LateContext; use super::ZERO_PTR; pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>, msrv: Msrv) { - if let TyKind::Ptr(ref mut_ty) = to.kind + if let TyKind::Ptr(ty, mutbl) = to.kind && is_integer_literal(from, 0) && (!is_in_const_context(cx) || msrv.meets(cx, msrvs::PTR_NULL)) && let Some(std_or_core) = std_or_core(cx) { - let (msg, sugg_fn) = match mut_ty.mutbl { + let (msg, sugg_fn) = match mutbl { Mutability::Mut => ("`0 as *mut _` detected", "ptr::null_mut"), Mutability::Not => ("`0 as *const _` detected", "ptr::null"), }; - let sugg = if let TyKind::Infer(()) = mut_ty.ty.kind { + let sugg = if let TyKind::Infer(()) = ty.kind { format!("{std_or_core}::{sugg_fn}()") - } else if let Some(mut_ty_snip) = mut_ty.ty.span.get_text(cx) { + } else if let Some(mut_ty_snip) = ty.span.get_text(cx) { format!("{std_or_core}::{sugg_fn}::<{mut_ty_snip}>()") } else { return; diff --git a/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs b/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs index ddd666477612a..0a9bb452a0a7c 100644 --- a/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs +++ b/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs @@ -14,7 +14,7 @@ declare_clippy_lint! { /// ### Why is this bad? /// `crate` refers to the macro call's crate, whereas `$crate` refers to the macro definition's /// crate. Rarely is the former intended. See: - /// https://doc.rust-lang.org/reference/macros-by-example.html#hygiene + /// /// /// ### Example /// ```no_run diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 5a143cca874b5..9b4ca2c979b8e 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -164,7 +164,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::excessive_nesting::EXCESSIVE_NESTING_INFO, crate::exhaustive_items::EXHAUSTIVE_ENUMS_INFO, crate::exhaustive_items::EXHAUSTIVE_STRUCTS_INFO, - crate::exit::EXIT_INFO, crate::explicit_write::EXPLICIT_WRITE_INFO, crate::extra_unused_type_parameters::EXTRA_UNUSED_TYPE_PARAMETERS_INFO, crate::fallible_impl_from::FALLIBLE_IMPL_FROM_INFO, @@ -200,6 +199,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::functions::MISNAMED_GETTERS_INFO, crate::functions::MUST_USE_CANDIDATE_INFO, crate::functions::MUST_USE_UNIT_INFO, + crate::functions::MUST_USE_WITHOUT_REASON_INFO, crate::functions::NOT_UNSAFE_PTR_ARG_DEREF_INFO, crate::functions::REF_OPTION_INFO, crate::functions::RENAMED_FUNCTION_PARAMS_INFO, @@ -382,6 +382,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::DOUBLE_ENDED_ITERATOR_LAST_INFO, crate::methods::DRAIN_COLLECT_INFO, crate::methods::ERR_EXPECT_INFO, + crate::methods::EXIT_INFO, crate::methods::EXPECT_FUN_CALL_INFO, crate::methods::EXPECT_USED_INFO, crate::methods::EXTEND_WITH_DRAIN_INFO, @@ -502,6 +503,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::UNBUFFERED_BYTES_INFO, crate::methods::UNINIT_ASSUMED_INIT_INFO, crate::methods::UNIT_HASH_INFO, + crate::methods::UNNECESSARY_AS_SLICE_INFO, crate::methods::UNNECESSARY_FALLIBLE_CONVERSIONS_INFO, crate::methods::UNNECESSARY_FILTER_MAP_INFO, crate::methods::UNNECESSARY_FIND_MAP_INFO, @@ -762,6 +764,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::transmute::UNSOUND_COLLECTION_TRANSMUTE_INFO, crate::transmute::USELESS_TRANSMUTE_INFO, crate::transmute::WRONG_TRANSMUTE_INFO, + crate::try_from_instead_of_from_str::TRY_FROM_INSTEAD_OF_FROM_STR_INFO, crate::tuple_array_conversions::TUPLE_ARRAY_CONVERSIONS_INFO, crate::types::BORROWED_BOX_INFO, crate::types::BOX_COLLECTION_INFO, diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs index d6812cc29173a..d6db2c570e616 100644 --- a/src/tools/clippy/clippy_lints/src/default.rs +++ b/src/tools/clippy/clippy_lints/src/default.rs @@ -37,7 +37,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does /// Checks for immediate reassignment of fields initialized - /// with Default::default(). + /// with `Default::default()`. /// /// ### Why is this bad? ///It's more idiomatic to use the [functional update syntax](https://doc.rust-lang.org/reference/expressions/struct-expr.html#functional-update-syntax). diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 50017eec7cf65..6257ec51eb8da 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -853,14 +853,14 @@ impl TyCoercionStability { // Here `y1` and `y2` would resolve to different types, so the type `&Box<_>` is not stable when // switching to auto-dereferencing. fn for_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Self { - let TyKind::Ref(_, ty) = &ty.kind else { + let TyKind::Ref(_, ty, _) = ty.kind else { return Self::None; }; let mut ty = ty; loop { - break match ty.ty.kind { - TyKind::Ref(_, ref ref_ty) => { + break match ty.kind { + TyKind::Ref(_, ref_ty, _) => { ty = ref_ty; continue; }, @@ -887,7 +887,7 @@ impl TyCoercionStability { }, TyKind::Slice(_) | TyKind::Array(..) - | TyKind::Ptr(_) + | TyKind::Ptr(..) | TyKind::FnPtr(_) | TyKind::Pat(..) | TyKind::FieldOf(..) diff --git a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs index 93f079f7351db..fe0e8c98b46e0 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs @@ -77,7 +77,7 @@ impl EarlyLintPass for DisallowedScriptIdents { // Sort by `Span` so that error messages make sense with respect to the // order of identifier locations in the code. let mut symbols: Vec<_> = symbols.iter().collect(); - symbols.sort_unstable_by_key(|k| k.1); + symbols.sort_unstable_by_key(|k| k.1.lo_hi()); for &(symbol, &span) in &symbols { // Note: `symbol.as_str()` is an expensive operation, thus should not be called diff --git a/src/tools/clippy/clippy_lints/src/disallowed_types.rs b/src/tools/clippy/clippy_lints/src/disallowed_types.rs index 2236e613b1472..fb2e70844865d 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_types.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_types.rs @@ -5,7 +5,7 @@ use clippy_utils::paths::PathNS; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefIdMap; -use rustc_hir::{AmbigArg, Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind}; +use rustc_hir::{AmbigArg, Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind, UseTree}; use rustc_lint::{LateContext, LateLintPass, impl_lint_pass}; use rustc_middle::ty::TyCtxt; use rustc_span::Span; @@ -107,8 +107,11 @@ pub fn def_kind_predicate(def_kind: DefKind) -> bool { impl<'tcx> LateLintPass<'tcx> for DisallowedTypes { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if let ItemKind::Use(path, UseKind::Single(_)) = &item.kind - && let Some(res) = path.res.type_ns + if let ItemKind::Use(UseTree { + prefix, + kind: UseKind::Single(_), + }) = &item.kind + && let Some(res) = prefix.res.type_ns { self.check_res_emit(cx, &res, item.span); } diff --git a/src/tools/clippy/clippy_lints/src/doc/doc_suspicious_footnotes.rs b/src/tools/clippy/clippy_lints/src/doc/doc_suspicious_footnotes.rs index 6c117ad2a4df8..b2b6bbe3e4b20 100644 --- a/src/tools/clippy/clippy_lints/src/doc/doc_suspicious_footnotes.rs +++ b/src/tools/clippy/clippy_lints/src/doc/doc_suspicious_footnotes.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::attr::AttributeExt as _; use rustc_ast::token::{CommentKind, DocFragmentKind}; -use rustc_attr_ir::AttributeKind; +use rustc_attr_ir::{Attribute, AttributeKind}; use rustc_errors::Applicability; -use rustc_hir::{AttrStyle, Attribute}; +use rustc_hir::AttrStyle; use rustc_lint::{LateContext, LintContext as _}; use std::ops::Range; diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index 8d2eb01e8743f..7fde6fcf4dd35 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -1,7 +1,9 @@ use clippy_config::Conf; use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_then}; -use clippy_utils::{is_entrypoint_fn, is_trait_impl_item}; +use clippy_utils::macros::root_macro_call; +use clippy_utils::{is_entrypoint_fn, is_trait_impl_item, sym}; +use rustc_ast::token::DocFragmentKind; use rustc_attr_ir::Attribute; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -263,7 +265,7 @@ declare_clippy_lint! { /// /// Consume the array and return the inner /// /// [`SmallVec<[T; INLINE_CAPACITY]>`][SmallVec]. /// /// [SmallVec]: SmallVec - /// fn main() {} + /// fn foo() {} /// ``` #[clippy::version = "pre 1.29.0"] pub DOC_MARKDOWN, @@ -878,11 +880,15 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ let (fragments, _) = attrs_to_doc_fragments( attrs.iter().filter_map(|attr| { - if attr.doc_str_and_fragment_kind().is_none() || attr.span().in_external_macro(cx.sess().source_map()) { - None - } else { - Some((attr, None)) - } + let (_, kind) = attr.doc_str_and_fragment_kind()?; + let (is_raw, doc_str_span) = match kind { + DocFragmentKind::Sugared(_) => (false, attr.span()), + DocFragmentKind::Raw(span) => (true, span), + }; + let from_include_str_macro = is_raw + && !attr.span().in_external_macro(cx.sess().source_map()) + && originated_from_include_str_macro(cx, doc_str_span); + (from_include_str_macro || !doc_str_span.in_external_macro(cx.sess().source_map())).then_some((attr, None)) }), true, ); @@ -963,6 +969,10 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ )) } +fn originated_from_include_str_macro(cx: &LateContext<'_>, span: Span) -> bool { + root_macro_call(span).is_some_and(|macro_call| cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id)) +} + enum Container { Blockquote, List(usize), diff --git a/src/tools/clippy/clippy_lints/src/empty_line_after.rs b/src/tools/clippy/clippy_lints/src/empty_line_after.rs index aa5b8ec2f727a..805f5dca1bf5d 100644 --- a/src/tools/clippy/clippy_lints/src/empty_line_after.rs +++ b/src/tools/clippy/clippy_lints/src/empty_line_after.rs @@ -431,7 +431,7 @@ impl EmptyLineAfter { /// If the node the attributes/docs apply to is the first in the module/crate suggest converting /// them to inner attributes/docs - fn suggest_inner(&self, diag: &mut Diag<'_, ()>, kind: StopKind, gaps: &[Gap<'_>], id: NodeId) { + fn suggest_inner(&self, diag: &mut Diag<'_>, kind: StopKind, gaps: &[Gap<'_>], id: NodeId) { if let Some(parent) = self.items.iter().rev().nth(1) && matches!(parent.kind, ItemKindDescr::Module | ItemKindDescr::Crate) && parent.mod_items == Some(id) diff --git a/src/tools/clippy/clippy_lints/src/exit.rs b/src/tools/clippy/clippy_lints/src/exit.rs deleted file mode 100644 index 245d3bd8ec257..0000000000000 --- a/src/tools/clippy/clippy_lints/src/exit.rs +++ /dev/null @@ -1,76 +0,0 @@ -use clippy_utils::diagnostics::span_lint; -use clippy_utils::sym; -use rustc_hir::{Expr, ExprKind, Item, ItemKind, OwnerNode}; -use rustc_lint::{LateContext, LateLintPass, declare_lint_pass}; - -declare_clippy_lint! { - /// ### What it does - /// Detects calls to the `exit()` function that are not in the `main` function. Calls to `exit()` - /// immediately terminate the program. - /// - /// ### Why restrict this? - /// `exit()` immediately terminates the program with no information other than an exit code. - /// This provides no means to troubleshoot a problem, and may be an unexpected side effect. - /// - /// Codebases may use this lint to require that all exits are performed either by panicking - /// (which produces a message, a code location, and optionally a backtrace) - /// or by calling `exit()` from `main()` (which is a single place to look). - /// - /// ### Good example - /// ```no_run - /// fn main() { - /// std::process::exit(0); - /// } - /// ``` - /// - /// ### Bad example - /// ```no_run - /// fn main() { - /// other_function(); - /// } - /// - /// fn other_function() { - /// std::process::exit(0); - /// } - /// ``` - /// - /// Use instead: - /// - /// ```ignore - /// // To provide a stacktrace and additional information - /// panic!("message"); - /// - /// // or a main method with a return - /// fn main() -> Result<(), i32> { - /// Ok(()) - /// } - /// ``` - #[clippy::version = "1.41.0"] - pub EXIT, - restriction, - "detects `std::process::exit` calls outside of `main`" -} - -declare_lint_pass!(Exit => [EXIT]); - -impl<'tcx> LateLintPass<'tcx> for Exit { - fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if let ExprKind::Call(path_expr, [_]) = e.kind - && let ExprKind::Path(ref path) = path_expr.kind - && let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id() - && cx.tcx.is_diagnostic_item(sym::process_exit, def_id) - && let parent = cx.tcx.hir_get_parent_item(e.hir_id) - && let OwnerNode::Item(Item{kind: ItemKind::Fn{ ident, .. }, ..}) = cx.tcx.hir_owner_node(parent) - // If the next item up is a function we check if it isn't named "main" - // and only then emit a linter warning - - // if you instead check for the parent of the `exit()` call being the entrypoint function, as this worked before, - // in compilation contexts like --all-targets (which include --tests), you get false positives - // because in a test context, main is not the entrypoint function - && ident.name != sym::main - && !e.span.in_external_macro(cx.tcx.sess.source_map()) - { - span_lint(cx, EXIT, e.span, "usage of `process::exit`"); - } - } -} diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs index 22be9d8367daa..55824af734891 100644 --- a/src/tools/clippy/clippy_lints/src/format_args.rs +++ b/src/tools/clippy/clippy_lints/src/format_args.rs @@ -341,7 +341,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs<'tcx> { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let Some(macro_call) = root_macro_call_first_node(cx, expr) && is_format_macro(cx, macro_call.def_id) - && let Some(format_args) = self.format_args.get(cx, expr, macro_call.expn) + && let Some(mut format_args) = self.format_args.get(cx, expr, macro_call.expn) { let mut linter = FormatArgsExpr { cx, @@ -358,8 +358,34 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs<'tcx> { linter.check_trailing_comma(); linter.check_templates(); - if self.msrv.meets(cx, msrvs::FORMAT_ARGS_CAPTURE) { - linter.check_uninlined_args(); + if !self.msrv.meets(cx, msrvs::FORMAT_ARGS_CAPTURE) { + return; + } + + let mut uninlined_queue = vec![]; + loop { + if linter.check_uninlined_args() { + break; + } + + uninlined_queue.extend(self.format_args.get_nested(format_args)); + if let Some(inner_format_args) = uninlined_queue.pop() { + format_args = inner_format_args; + + linter = FormatArgsExpr { + cx, + expr, + macro_call: ¯o_call, + format_args, + ignore_mixed: self.ignore_mixed, + msrv: &self.msrv, + ty_msrv_map: &self.ty_msrv_map, + has_derived_debug: &mut self.has_derived_debug, + has_pointer_format: &mut self.has_pointer_format, + }; + } else { + break; + } } } } @@ -575,16 +601,16 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> { } } - fn check_uninlined_args(&self) { + fn check_uninlined_args(&self) -> bool { if self.format_args.span.from_expansion() { - return; + return false; } if self.macro_call.span.edition() < Edition2021 && (is_panic(self.cx, self.macro_call.def_id) || is_assert_macro(self.cx, self.macro_call.def_id)) { // panic!, assert!, and debug_assert! before 2021 edition considers a single string argument as // non-format - return; + return false; } let mut fixes = Vec::new(); @@ -595,12 +621,12 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> { // Example of an un-inlinable format: print!("{}{1}", foo, 2) for (pos, usage) in self.format_arg_positions() { if !self.check_one_arg(pos, usage, &mut fixes) { - return; + return !fixes.is_empty(); } } if fixes.is_empty() { - return; + return false; } // multiline span display suggestion is sometimes broken: https://github.com/rust-lang/rust/pull/102729#discussion_r988704308 @@ -610,8 +636,8 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> { .any(|(span, _)| self.cx.sess().source_map().is_multiline(*span)); // Suggest removing each argument only once, for example in `format!("{0} {0}", arg)`. - fixes.sort_unstable_by_key(|(span, _)| *span); - fixes.dedup_by_key(|(span, _)| *span); + fixes.sort_unstable_by_key(|(span, _)| span.lo_hi()); + fixes.dedup_by_key(|(span, _)| span.lo_hi()); span_lint_and_then( self.cx, @@ -627,6 +653,8 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> { ); }, ); + + true } fn check_one_arg(&self, pos: &FormatArgPosition, usage: FormatParamUsage, fixes: &mut Vec<(Span, String)>) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/functions/mod.rs b/src/tools/clippy/clippy_lints/src/functions/mod.rs index 4c23f21a7318a..2c1319ba8f2f3 100644 --- a/src/tools/clippy/clippy_lints/src/functions/mod.rs +++ b/src/tools/clippy/clippy_lints/src/functions/mod.rs @@ -193,6 +193,30 @@ declare_clippy_lint! { "`#[must_use]` attribute on a unit-returning function / method" } +declare_clippy_lint! { + /// ### What it does + /// Checks for `#[must_use]` attributes without a reason. + /// + /// ### Why restrict this? + /// A reason explains why the return value must be used. Without it, + /// users only see a generic "unused must-use value" message which is less helpful. + /// + /// ### Example + /// ```no_run + /// #[must_use] + /// fn compute() -> i32 { 42 } + /// ``` + /// Use instead: + /// ```no_run + /// #[must_use = "computation is expensive"] + /// fn compute() -> i32 { 42 } + /// ``` + #[clippy::version = "1.99.0"] + pub MUST_USE_WITHOUT_REASON, + restriction, + "`#[must_use]` attribute without a reason" +} + declare_clippy_lint! { /// ### What it does /// Checks for public functions that dereference raw pointer @@ -486,6 +510,7 @@ impl_lint_pass!(Functions => [ MISNAMED_GETTERS, MUST_USE_CANDIDATE, MUST_USE_UNIT, + MUST_USE_WITHOUT_REASON, NOT_UNSAFE_PTR_ARG_DEREF, REF_OPTION, RENAMED_FUNCTION_PARAMS, diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs index bca9705f6ed59..4cb798c67a7d2 100644 --- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs +++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs @@ -1,25 +1,24 @@ use hir::FnSig; -use rustc_attr_ir::find_attr; +use rustc_attr_ir::{Attribute, find_attr}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::def_id::DefIdSet; -use rustc_hir::{self as hir, Attribute, QPath}; +use rustc_hir::{self as hir, QPath}; use rustc_lint::unused::must_use::MustUsePath; use rustc_lint::{LateContext, LintContext as _}; use rustc_middle::ty::{self, Ty}; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol, sym}; use clippy_utils::attrs::is_proc_macro; -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_indent; use clippy_utils::ty::{describe_must_use_type, opt_must_use_path}; use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{is_entrypoint_fn, return_ty, trait_ref_of_method}; -use rustc_span::Symbol; +use clippy_utils::{is_entrypoint_fn, is_lint_allowed, return_ty, trait_ref_of_method}; use core::ops::ControlFlow; -use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT}; +use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT, MUST_USE_WITHOUT_REASON}; pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { let attrs = cx.tcx.hir_attrs(item.hir_id()); @@ -30,21 +29,24 @@ pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_> ident, .. } = item.kind + && !item.span.in_external_macro(cx.sess().source_map()) { let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); if let Some((attr_span, reason)) = attr { - check_needless_must_use( + let is_needless_must_use = check_needless_must_use( cx, sig.decl, item.owner_id, - item.span, fn_header_span, *attr_span, *reason, attrs, sig, ); + if !is_needless_must_use { + check_must_use_without_reason(cx, *attr_span, *reason); + } } else if is_public && !is_proc_macro(attrs) && !find_attr!(attrs, NoMangle(..)) { check_must_use_candidate( cx, @@ -60,23 +62,27 @@ pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_> } pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { - if let hir::ImplItemKind::Fn(ref sig, ref body_id) = item.kind { + if let hir::ImplItemKind::Fn(ref sig, ref body_id) = item.kind + && !item.span.in_external_macro(cx.sess().source_map()) + { let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); let attrs = cx.tcx.hir_attrs(item.hir_id()); let attr = find_attr!(cx.tcx, item.hir_id(), MustUse { span, reason } => (span, reason)); if let Some((attr_span, reason)) = attr { - check_needless_must_use( + let is_needless_must_use = check_needless_must_use( cx, sig.decl, item.owner_id, - item.span, fn_header_span, *attr_span, *reason, attrs, sig, ); + if !is_needless_must_use { + check_must_use_without_reason(cx, *attr_span, *reason); + } } else if is_public && !is_proc_macro(attrs) && trait_ref_of_method(cx, item.owner_id).is_none() { check_must_use_candidate( cx, @@ -92,24 +98,28 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Imp } pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { - if let hir::TraitItemKind::Fn(ref sig, ref eid) = item.kind { + if let hir::TraitItemKind::Fn(ref sig, ref eid) = item.kind + && !item.span.in_external_macro(cx.sess().source_map()) + { let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); let attrs = cx.tcx.hir_attrs(item.hir_id()); let attr = find_attr!(cx.tcx, item.hir_id(), MustUse { span, reason } => (span, reason)); if let Some((attr_span, reason)) = attr { - check_needless_must_use( + let is_needless_must_use = check_needless_must_use( cx, sig.decl, item.owner_id, - item.span, fn_header_span, *attr_span, *reason, attrs, sig, ); + if !is_needless_must_use { + check_must_use_without_reason(cx, *attr_span, *reason); + } } else if let hir::TraitFn::Provided(eid) = *eid { let body = cx.tcx.hir_body(eid); if attr.is_none() && is_public && !is_proc_macro(attrs) { @@ -133,15 +143,14 @@ fn check_needless_must_use( cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_id: hir::OwnerId, - item_span: Span, fn_header_span: Span, attr_span: Span, reason: Option, attrs: &[Attribute], sig: &FnSig<'_>, -) { - if item_span.in_external_macro(cx.sess().source_map()) || attr_span.from_expansion() { - return; +) -> bool { + if attr_span.from_expansion() { + return false; } if returns_unit(decl) { span_lint_and_then( @@ -162,6 +171,7 @@ fn check_needless_must_use( } }, ); + return true; } else if reason.is_none() && let Some(return_must_use_path) = opt_must_use_path(cx, return_ty(cx, item_id)) { @@ -170,7 +180,7 @@ fn check_needless_must_use( && let Some(future_ty) = cx.tcx.get_impl_future_output_ty(return_ty(cx, item_id)) && opt_must_use_path(cx, future_ty).is_none() { - return; + return false; } span_lint_and_then( @@ -202,7 +212,27 @@ fn check_needless_must_use( diag.note("alternatively, you may add an explicit reason to the `must_use` attribute"); }, ); + return true; } + + false +} + +/// Checks for `must_use` usage without a reason +fn check_must_use_without_reason(cx: &LateContext<'_>, attr_span: Span, reason: Option) { + if reason.is_some() || attr_span.from_expansion() { + return; + } + + span_lint_and_sugg( + cx, + MUST_USE_WITHOUT_REASON, + attr_span, + "`#[must_use]` attribute without a reason", + "add a reason why the value must be used", + "#[must_use = \"\"]".to_string(), + Applicability::HasPlaceholders, + ); } fn check_must_use_candidate<'tcx>( @@ -216,7 +246,6 @@ fn check_must_use_candidate<'tcx>( ) { if has_mutable_arg(cx, body) || mutates_static(cx, body) - || item_span.in_external_macro(cx.sess().source_map()) || returns_unit(decl) || !cx.effective_visibilities.is_exported(item_id.def_id) || opt_must_use_path(cx, return_ty(cx, item_id)).is_some() @@ -225,14 +254,19 @@ fn check_must_use_candidate<'tcx>( { return; } + let hir_id = cx.tcx.local_def_id_to_hir_id(item_id.def_id); span_lint_and_then(cx, MUST_USE_CANDIDATE, ident_span, msg, |diag| { let indent = snippet_indent(cx, item_span).unwrap_or_default(); - diag.span_suggestion( - item_span.shrink_to_lo(), - "add the attribute", - format!("#[must_use]\n{indent}"), - Applicability::MachineApplicable, - ); + // Suggest a reason as well, otherwise `must_use_without_reason` fires on the result. + let (sugg, applicability) = if is_lint_allowed(cx, MUST_USE_WITHOUT_REASON, hir_id) { + (format!("#[must_use]\n{indent}"), Applicability::MachineApplicable) + } else { + ( + format!("#[must_use = \"\"]\n{indent}"), + Applicability::HasPlaceholders, + ) + }; + diag.span_suggestion(item_span.shrink_to_lo(), "add the attribute", sugg, applicability); }); } diff --git a/src/tools/clippy/clippy_lints/src/functions/ref_option.rs b/src/tools/clippy/clippy_lints/src/functions/ref_option.rs index 1afec00f94792..733f10b3506f8 100644 --- a/src/tools/clippy/clippy_lints/src/functions/ref_option.rs +++ b/src/tools/clippy/clippy_lints/src/functions/ref_option.rs @@ -16,7 +16,7 @@ fn check_ty<'a>(cx: &LateContext<'a>, param: &hir::Ty<'a>, param_ty: Ty<'a>, fix && let Some(gen_ty) = option_arg_ty(cx, *opt_ty) && !gen_ty.is_ref() // Need to gen the original spans, so first parsing mid, and hir parsing afterward - && let hir::TyKind::Ref(lifetime, hir::MutTy { ty, .. }) = param.kind + && let hir::TyKind::Ref(lifetime, ty, ..) = param.kind && let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind && let (Some(first), Some(last)) = (path.segments.first(), path.segments.last()) && let Some(hir::GenericArgs { diff --git a/src/tools/clippy/clippy_lints/src/functions/result.rs b/src/tools/clippy/clippy_lints/src/functions/result.rs index 6a7013e0f1737..a2121bb431b4e 100644 --- a/src/tools/clippy/clippy_lints/src/functions/result.rs +++ b/src/tools/clippy/clippy_lints/src/functions/result.rs @@ -174,7 +174,7 @@ fn check_result_large_err<'tcx>( RESULT_LARGE_ERR, hir_ty_span, format!("the `Err`-variant returned from this {subject} is very large"), - |diag: &mut Diag<'_, ()>| { + |diag: &mut Diag<'_>| { diag.span_label(hir_ty_span, format!("the `Err`-variant is at least {ty_size} bytes")); diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`")); }, diff --git a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs index 310b45ca408cf..4e9cf7925f286 100644 --- a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs +++ b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs @@ -66,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex { && let Some(arm_mutex) = for_each_expr_without_closures((if_then, if_else), |e| mutex_lock_call(cx, ctxt, e, Some(op_mutex))) { - let diag = |diag: &mut Diag<'_, ()>| { + let diag = |diag: &mut Diag<'_>| { diag.span_label( op_mutex.span, "this Mutex will remain locked for the entire `if let`-block...", diff --git a/src/tools/clippy/clippy_lints/src/ifs/branches_sharing_code.rs b/src/tools/clippy/clippy_lints/src/ifs/branches_sharing_code.rs index ff4683349ad88..de361e0d88d3c 100644 --- a/src/tools/clippy/clippy_lints/src/ifs/branches_sharing_code.rs +++ b/src/tools/clippy/clippy_lints/src/ifs/branches_sharing_code.rs @@ -11,7 +11,7 @@ use core::iter; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{ - Arm, Block, Expr, ExprKind, HirId, HirIdSet, ItemKind, LetStmt, Node, Stmt, StmtKind, UseKind, intravisit, + Arm, Block, Expr, ExprKind, HirId, HirIdSet, ItemKind, LetStmt, Node, Stmt, StmtKind, UseKind, UseTree, intravisit, }; use rustc_lint::LateContext; use rustc_span::hygiene::walk_chain; @@ -259,7 +259,10 @@ fn eq_binding_names(cx: &LateContext<'_>, s: &Stmt<'_>, names: &[(HirId, Symbol) | ItemKind::Const(ident, ..) | ItemKind::Fn { ident, .. } | ItemKind::TyAlias(ident, ..) - | ItemKind::Use(_, UseKind::Single(ident)) + | ItemKind::Use(UseTree { + kind: UseKind::Single(ident), + .. + }) | ItemKind::Mod(ident, _) = item.kind => { *name == ident.name diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs index e39ff754a5b3b..ea443487df9ba 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs @@ -9,7 +9,7 @@ use rustc_hir_analysis::lower_ty; use rustc_lint::{LateContext, LateLintPass, declare_lint_pass}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{Ty, TypeckResults}; -use rustc_span::Span; +use rustc_span::{OrdSpan, Span}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{IntoSpan as _, SpanExt as _, snippet, snippet_with_context}; @@ -61,7 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { fn suggestion( cx: &LateContext<'_>, - diag: &mut Diag<'_, ()>, + diag: &mut Diag<'_>, generics_span: Span, generics_suggestion_span: Span, target: &ImplicitHasherType<'_>, @@ -94,7 +94,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { format!("{}<{}, S>", target.type_name(), target.type_arguments()), ), ]; - suggestions.extend(vis.suggestions); + suggestions.extend(vis.suggestions.into_iter().map(|(span, s)| (span.0, s))); diag.multipart_suggestion( "add a type parameter for `BuildHasher`", @@ -295,7 +295,7 @@ struct ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> { cx: &'a LateContext<'tcx>, maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>, target: &'b ImplicitHasherType<'tcx>, - suggestions: BTreeMap, + suggestions: BTreeMap, } impl<'a, 'b, 'tcx> ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> { @@ -337,7 +337,8 @@ impl<'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'_, '_, 'tcx> { match method.ident.name { sym::new => { - self.suggestions.insert(e.span, format!("{container_name}::default()")); + self.suggestions + .insert(OrdSpan(e.span), format!("{container_name}::default()")); }, sym::with_capacity => { let (arg_snippet, _) = snippet_with_context( @@ -350,7 +351,7 @@ impl<'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'_, '_, 'tcx> { &mut Applicability::MaybeIncorrect, ); self.suggestions.insert( - e.span, + OrdSpan(e.span), format!("{container_name}::with_capacity_and_hasher({arg_snippet}, Default::default())"), ); }, diff --git a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs index 73e430fa08d6b..60b69fd53311e 100644 --- a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs +++ b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs @@ -1,3 +1,5 @@ +use std::cmp::max_by_key; + use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use rustc_errors::{Applicability, SuggestionStyle}; @@ -96,7 +98,9 @@ fn emit_lint( // `<>` needs to be added if there aren't yet any generic arguments or constraints let needs_angle_brackets = bound.args.is_empty() && bound.constraints.is_empty(); let insert_span = match (bound.args, bound.constraints) { - ([.., arg], [.., constraint]) => arg.span().max(constraint.span).shrink_to_hi(), + ([.., arg], [.., constraint]) => { + max_by_key(arg.span(), constraint.span, |span| span.lo_hi()).shrink_to_hi() + }, ([.., arg], []) => arg.span().shrink_to_hi(), ([], [.., constraint]) => constraint.span.shrink_to_hi(), ([], []) => bound.span.shrink_to_hi(), diff --git a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs index a09b3aa0135f8..600f627c03d37 100644 --- a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs +++ b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs @@ -2,9 +2,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::Msrv; use clippy_utils::{is_in_const_context, is_in_test, sym}; -use rustc_attr_ir::{RustcVersion, find_attr}; +use rustc_attr_ir::{RustcVersion, StabilityLevel, StableSince, find_attr}; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, HirId, StabilityLevel, StableSince}; +use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, HirId}; use rustc_lint::{LateContext, LateLintPass, impl_lint_pass}; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::def_id::{CrateNum, DefId}; diff --git a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs index 107ab36741b2b..4b5b80ed3036c 100644 --- a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs @@ -6,6 +6,7 @@ use clippy_utils::{higher, is_from_proc_macro, is_in_test, sym}; use rustc_ast::ast::RangeLimits; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, impl_lint_pass}; +use rustc_middle::ty::consts::ConstExt as _; use rustc_middle::ty::{self, Ty}; declare_clippy_lint! { diff --git a/src/tools/clippy/clippy_lints/src/inline_trait_bounds.rs b/src/tools/clippy/clippy_lints/src/inline_trait_bounds.rs index 9b77d3dc138b3..e9330fbf69267 100644 --- a/src/tools/clippy/clippy_lints/src/inline_trait_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/inline_trait_bounds.rs @@ -19,7 +19,7 @@ declare_clippy_lint! { /// Avoid complex inline bounds, which could make a function declaration more difficult to read. /// /// ### Known limitations - /// Only lints functions and method declararions. Bounds on structs, enums, + /// Only lints functions and method declarations. Bounds on structs, enums, /// and impl blocks are not yet covered. /// /// ### Example diff --git a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs index 8c604aadc811e..d657e384d0643 100644 --- a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs +++ b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs @@ -3,9 +3,9 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_hir}; use clippy_utils::str_utils::{camel_case_split, count_match_end, count_match_start, to_camel_case, to_snake_case}; use clippy_utils::{is_bool, is_from_proc_macro}; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::{Body, EnumDef, FieldDef, Item, ItemKind, QPath, TyKind, UseKind, Variant, VariantData}; +use rustc_hir::{Body, EnumDef, FieldDef, Item, ItemKind, QPath, TyKind, UseKind, UseTree, Variant, VariantData}; use rustc_lint::{LateContext, LateLintPass, impl_lint_pass}; -use rustc_span::symbol::Symbol; +use rustc_span::symbol::{Ident, Symbol}; declare_clippy_lint! { /// ### What it does @@ -200,6 +200,63 @@ impl ItemNameRepetitions { fn is_allowed_prefix(&self, prefix: &str) -> bool { self.allowed_prefixes.contains(prefix) } + + fn check_item_kind(&self, cx: &LateContext<'_>, item: &Item<'_>) -> Option { + match item.kind { + ItemKind::Mod(ident, _) => { + if let [.., prev] = &*self.modules + && prev.name == ident.name + && prev.in_body_count == 0 + && (!self.allow_private_module_inception || prev.is_public) + && !item.span.from_expansion() + && !is_from_proc_macro(cx, item) + { + span_lint( + cx, + MODULE_INCEPTION, + item.span, + "module has the same name as its containing module", + ); + } + Some(ident) + }, + + ItemKind::Enum(ident, _, def) => { + if !ident.span.in_external_macro(cx.tcx.sess.source_map()) { + self.check_variants(cx, item, &def); + } + Some(ident) + }, + ItemKind::Struct(ident, _, data) => { + if let VariantData::Struct { fields, .. } = data + && !ident.span.in_external_macro(cx.tcx.sess.source_map()) + { + self.check_fields(cx, item, fields); + } + Some(ident) + }, + + ItemKind::Const(ident, ..) + | ItemKind::ExternCrate(_, ident) + | ItemKind::Fn { ident, .. } + | ItemKind::Macro(ident, ..) + | ItemKind::Static(_, ident, ..) + | ItemKind::Trait { ident, .. } + | ItemKind::TraitAlias(_, ident, ..) + | ItemKind::TyAlias(ident, ..) + | ItemKind::Union(ident, ..) + | ItemKind::Use(UseTree { + kind: UseKind::Single(ident), + .. + }) => Some(ident), + + ItemKind::ForeignMod { .. } + | ItemKind::GlobalAsm { .. } + | ItemKind::Impl(_) + | ItemKind::Use(..) + | ItemKind::TestBinderConstraints { .. } => None, + } + } } #[must_use] @@ -488,56 +545,8 @@ impl LateLintPass<'_> for ItemNameRepetitions { } fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - let ident = match item.kind { - ItemKind::Mod(ident, _) => { - if let [.., prev] = &*self.modules - && prev.name == ident.name - && prev.in_body_count == 0 - && (!self.allow_private_module_inception || prev.is_public) - && !item.span.from_expansion() - && !is_from_proc_macro(cx, item) - { - span_lint( - cx, - MODULE_INCEPTION, - item.span, - "module has the same name as its containing module", - ); - } - ident - }, - - ItemKind::Enum(ident, _, def) => { - if !ident.span.in_external_macro(cx.tcx.sess.source_map()) { - self.check_variants(cx, item, &def); - } - ident - }, - ItemKind::Struct(ident, _, data) => { - if let VariantData::Struct { fields, .. } = data - && !ident.span.in_external_macro(cx.tcx.sess.source_map()) - { - self.check_fields(cx, item, fields); - } - ident - }, - - ItemKind::Const(ident, ..) - | ItemKind::ExternCrate(_, ident) - | ItemKind::Fn { ident, .. } - | ItemKind::Macro(ident, ..) - | ItemKind::Static(_, ident, ..) - | ItemKind::Trait { ident, .. } - | ItemKind::TraitAlias(_, ident, ..) - | ItemKind::TyAlias(ident, ..) - | ItemKind::Union(ident, ..) - | ItemKind::Use(_, UseKind::Single(ident)) => ident, - - ItemKind::ForeignMod { .. } - | ItemKind::GlobalAsm { .. } - | ItemKind::Impl(_) - | ItemKind::Use(..) - | ItemKind::TestBinderConstraints { .. } => return, + let Some(ident) = self.check_item_kind(cx, item) else { + return; }; let item_name = ident.name.as_str(); diff --git a/src/tools/clippy/clippy_lints/src/items_after_test_module.rs b/src/tools/clippy/clippy_lints/src/items_after_test_module.rs index dac7a24bf2a8d..8260327668796 100644 --- a/src/tools/clippy/clippy_lints/src/items_after_test_module.rs +++ b/src/tools/clippy/clippy_lints/src/items_after_test_module.rs @@ -2,8 +2,9 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::SpanExt as _; use clippy_utils::{fulfill_or_allowed, is_cfg_test, is_from_proc_macro}; use rustc_errors::{Applicability, SuggestionStyle}; -use rustc_hir::{HirId, Item, ItemKind, Mod}; +use rustc_hir::{Item, ItemKind, Mod}; use rustc_lint::{LateContext, LateLintPass, declare_lint_pass}; +use rustc_span::def_id::LocalModId; use rustc_span::hygiene::AstPass; use rustc_span::{ExpnKind, sym}; @@ -56,7 +57,7 @@ fn cfg_test_module<'tcx>(cx: &LateContext<'tcx>, item: &Item<'tcx>) -> bool { } impl LateLintPass<'_> for ItemsAfterTestModule { - fn check_mod(&mut self, cx: &LateContext<'_>, module: &Mod<'_>, _: HirId) { + fn check_mod(&mut self, cx: &LateContext<'_>, module: &Mod<'_>, _: LocalModId) { let mut items = module.item_ids.iter().map(|&id| cx.tcx.hir_item(id)); let Some((mod_pos, test_mod)) = items.by_ref().enumerate().find(|(_, item)| cfg_test_module(cx, item)) else { @@ -66,15 +67,12 @@ impl LateLintPass<'_> for ItemsAfterTestModule { let after: Vec<_> = items .filter(|item| { // Ignore the generated test main function and `extern crate test` - if (matches!(item.kind, ItemKind::Fn { ident, .. } if ident.name == sym::main) + !((matches!(item.kind, ItemKind::Fn { ident, .. } if ident.name == sym::main) || matches!(item.kind, ItemKind::ExternCrate(None, ident) if ident.name == sym::test)) - && item.span.ctxt().outer_expn_data().kind == ExpnKind::AstPass(AstPass::TestHarness) - { - false - } else { - true - } + && item.span.ctxt().outer_expn_data().kind == ExpnKind::AstPass(AstPass::TestHarness)) }) + // Ignore items generated by derive macros, we only check items present in the source code + .filter(|item| !item.span.in_derive_expansion()) .collect(); if let Some(last) = after.last() diff --git a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs index 3c49595f83216..771cdc1e47136 100644 --- a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs +++ b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs @@ -126,7 +126,7 @@ fn is_ty_exported(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { impl LateLintPass<'_> for IterWithoutIntoIter { fn check_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::Item<'_>) { if let ItemKind::Impl(imp) = item.kind - && let TyKind::Ref(_, self_ty_without_ref) = &imp.self_ty.kind + && let TyKind::Ref(_, self_ty_without_ref, _) = &imp.self_ty.kind && let Some(of_trait) = imp.of_trait && of_trait .trait_ref @@ -178,7 +178,7 @@ impl {self_ty_without_ref} {{ }} }} ", - self_ty_without_ref = snippet(cx, self_ty_without_ref.ty.span, ".."), + self_ty_without_ref = snippet(cx, self_ty_without_ref.span, ".."), ref_self = mtbl.ref_prefix_str(), iter_ty = snippet(cx, iter_assoc_span, ".."), ); diff --git a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs index 4d8ef09d17da3..e39fe76715e3a 100644 --- a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs +++ b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs @@ -3,6 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass, impl_lint_pass}; +use rustc_middle::ty::consts::ConstExt as _; use rustc_middle::ty::layout::LayoutOf as _; use rustc_middle::ty::{self, Ty, Unnormalized}; use rustc_span::{BytePos, Pos as _, Span}; diff --git a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs index 49d4fe478cdf4..3c58425e2621b 100644 --- a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs +++ b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs @@ -8,6 +8,7 @@ use clippy_utils::{is_from_proc_macro, sym}; use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass, impl_lint_pass}; use rustc_middle::ty; +use rustc_middle::ty::consts::ConstExt as _; use rustc_middle::ty::layout::LayoutOf as _; use rustc_span::Span; diff --git a/src/tools/clippy/clippy_lints/src/large_stack_frames.rs b/src/tools/clippy/clippy_lints/src/large_stack_frames.rs index 3d3539bc70458..77357538461f5 100644 --- a/src/tools/clippy/clippy_lints/src/large_stack_frames.rs +++ b/src/tools/clippy/clippy_lints/src/large_stack_frames.rs @@ -183,7 +183,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackFrames { return; } - let explain_lint = |diag: &mut Diag<'_, ()>, ctxt: SyntaxContext| { + let explain_lint = |diag: &mut Diag<'_>, ctxt: SyntaxContext| { // Point out the largest individual contribution to this size, because // it is the most likely to be unintentionally large. if let Some((local, size)) = sizes_of_locals.iter().max_by_key(|&(_, size)| size) diff --git a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs index 4e982e8fb86f3..d19110ee8d4aa 100644 --- a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs +++ b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs @@ -5,8 +5,7 @@ use clippy_utils::source::SpanExt as _; use clippy_utils::{is_from_proc_macro, sym}; use hir::def_id::DefId; use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_hir::{ExprKind, Item, ItemKind, QPath, UseKind}; +use rustc_hir::{self as hir, ExprKind, Item, ItemKind, QPath, UseKind, UseTree}; use rustc_lint::{LateContext, LateLintPass, LintContext as _, impl_lint_pass}; use rustc_span::Symbol; use rustc_span::symbol::kw; @@ -44,18 +43,26 @@ impl LegacyNumericConstants { pub fn new(conf: &'static Conf) -> Self { Self { msrv: conf.msrv.into() } } -} -impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + fn check_use_tree(&mut self, cx: &LateContext<'_>, tree: &UseTree<'_>) { + match tree.kind { + UseKind::Single(_) | UseKind::Glob => {}, + UseKind::Nested { items } => { + for (tree, ..) in items { + self.check_use_tree(cx, tree); + } + return; + }, + } + + let prefix = tree.prefix; // Integer modules are "TBD" deprecated, and the contents are too, // so lint on the `use` statement directly. - if let ItemKind::Use(path, kind @ (UseKind::Single(_) | UseKind::Glob)) = item.kind - && !item.span.in_external_macro(cx.sess().source_map()) - // use `present_items` because it could be in either type_ns or value_ns - && let Some(res) = path.res.present_items().next() - && let Some(def_id) = res.opt_def_id() - && self.msrv.meets(cx, msrvs::NUMERIC_ASSOCIATED_CONSTANTS) + if !tree.prefix.span.in_external_macro(cx.sess().source_map()) + // use `present_items` because it could be in either type_ns or value_ns + && let Some(res) = prefix.res.present_items().next() + && let Some(def_id) = res.opt_def_id() + && self.msrv.meets(cx, msrvs::NUMERIC_ASSOCIATED_CONSTANTS) { let module = if is_integer_module(cx, def_id) { true @@ -68,14 +75,14 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { span_lint_and_then( cx, LEGACY_NUMERIC_CONSTANTS, - path.span, + prefix.span, if module { "importing legacy numeric constants" } else { "importing a legacy numeric constant" }, |diag| { - if let UseKind::Single(ident) = kind + if let UseKind::Single(ident) = tree.kind && ident.name == kw::Underscore { diag.help("remove this import"); @@ -85,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { let def_path = cx.get_def_path(def_id); if module && let [.., module_name] = &*def_path { - if kind == UseKind::Glob { + if matches!(tree.kind, UseKind::Glob) { diag.help(format!("remove this import and use associated constants `{module_name}::` from the primitive type instead")); } else { diag.help("remove this import").note(format!( @@ -94,13 +101,21 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { } } else if let [.., module_name, name] = &*def_path { diag.help( - format!("remove this import and use the associated constant `{module_name}::{name}` from the primitive type instead") - ); + format!("remove this import and use the associated constant `{module_name}::{name}` from the primitive type instead") + ); } }, ); } } +} + +impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if let ItemKind::Use(tree) = &item.kind { + self.check_use_tree(cx, tree); + } + } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { // `std::::` check diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 435f787f7012f..a4c9fc7480f02 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -7,10 +7,9 @@ #![feature(iter_partition_in_place)] #![feature(macro_metavar_expr)] #![feature(macro_metavar_expr_concat)] -#![cfg_attr(bootstrap, feature(never_type))] +#![cfg_attr(bootstrap, feature(unwrap_infallible))] #![feature(rustc_private)] #![feature(stmt_expr_attributes)] -#![feature(unwrap_infallible)] #![recursion_limit = "512"] #![expect(clippy::literal_string_with_formatting_args, clippy::must_use_candidate)] #![warn( @@ -133,7 +132,6 @@ mod eta_reduction; mod excessive_bools; mod excessive_nesting; mod exhaustive_items; -mod exit; mod explicit_write; mod extra_unused_type_parameters; mod fallible_impl_from; @@ -368,6 +366,7 @@ mod toplevel_ref_arg; mod trailing_empty_array; mod trait_bounds; mod transmute; +mod try_from_instead_of_from_str; mod tuple_array_conversions; mod types; mod unconditional_recursion; @@ -689,7 +688,6 @@ rustc_lint::late_lint_methods!( Default: default::Default = ::default(), UnusedSelf: unused_self::UnusedSelf = unused_self::UnusedSelf::new(conf), DebugAssertWithMutCall: mutable_debug_assertion::DebugAssertWithMutCall = mutable_debug_assertion::DebugAssertWithMutCall, - Exit: exit::Exit = exit::Exit, ToDigitIsSome: to_digit_is_some::ToDigitIsSome = to_digit_is_some::ToDigitIsSome::new(conf), LargeStackArrays: large_stack_arrays::LargeStackArrays = large_stack_arrays::LargeStackArrays::new(conf), LargeConstArrays: large_const_arrays::LargeConstArrays = large_const_arrays::LargeConstArrays::new(conf), @@ -749,7 +747,7 @@ rustc_lint::late_lint_methods!( FormatPushString: format_push_string::FormatPushString = format_push_string::FormatPushString::new(format_args.clone()), LargeIncludeFile: large_include_file::LargeIncludeFile = large_include_file::LargeIncludeFile::new(conf), RcCloneInVecInit: rc_clone_in_vec_init::RcCloneInVecInit = rc_clone_in_vec_init::RcCloneInVecInit, - SwapPtrToRef: swap_ptr_to_ref::SwapPtrToRef = swap_ptr_to_ref::SwapPtrToRef, + SwapPtrToRef: swap_ptr_to_ref::SwapPtrToRef = swap_ptr_to_ref::SwapPtrToRef::new(conf), TypeParamMismatch: mismatching_type_param_order::TypeParamMismatch = mismatching_type_param_order::TypeParamMismatch, ReadZeroByteVec: read_zero_byte_vec::ReadZeroByteVec = read_zero_byte_vec::ReadZeroByteVec, DefaultIterEmpty: default_instead_of_iter_empty::DefaultIterEmpty = default_instead_of_iter_empty::DefaultIterEmpty, @@ -862,6 +860,7 @@ rustc_lint::late_lint_methods!( ManualPopIf: manual_pop_if::ManualPopIf = manual_pop_if::ManualPopIf::new(tcx, conf), ManualNoopWaker: manual_noop_waker::ManualNoopWaker = manual_noop_waker::ManualNoopWaker::new(conf), ByteCharSlice: byte_char_slices::ByteCharSlice = byte_char_slices::ByteCharSlice, + TryFromInsteadOfFromStr: try_from_instead_of_from_str::TryFromInsteadOfFromStr = try_from_instead_of_from_str::TryFromInsteadOfFromStr::new(conf), ManualAssertEq: manual_assert_eq::ManualAssertEq = manual_assert_eq::ManualAssertEq, WithCapacityZero: with_capacity_zero::WithCapacityZero = with_capacity_zero::WithCapacityZero, RefPatterns: ref_patterns::RefPatterns = ref_patterns::RefPatterns, diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs index ba2a418104a47..ae56293a77b05 100644 --- a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs @@ -13,6 +13,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; use rustc_lint::LateContext; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; +use rustc_middle::ty::consts::ConstExt as _; use rustc_middle::ty::{self, EarlyBinder, Ty}; pub(super) fn check( diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs index ec0c4fb23390b..19085c5ffa687 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs @@ -11,6 +11,7 @@ use rustc_errors::Applicability; use rustc_hir::intravisit::walk_block; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Pat, PatKind, StmtKind}; use rustc_lint::LateContext; +use rustc_middle::ty::consts::ConstExt as _; use rustc_middle::ty::{self, Ty}; use rustc_span::symbol::sym; use std::fmt::Display; diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs index a4d3fe9e2296d..5ce0e919fbd3a 100644 --- a/src/tools/clippy/clippy_lints/src/loops/mod.rs +++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs @@ -333,7 +333,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for manual implementations of Iterator::find + /// Checks for manual implementations of `Iterator::find` /// /// ### Why is this bad? /// It doesn't affect performance, but using `find` is shorter and easier to read. diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs index e72842d72f5cb..fb648050439b9 100644 --- a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs @@ -15,6 +15,7 @@ use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BinOpKind, BorrowKind, Closure, Expr, ExprKind, HirId, Mutability, Pat, PatKind, QPath}; use rustc_lint::LateContext; use rustc_middle::middle::region; +use rustc_middle::ty::consts::ConstExt as _; use rustc_middle::ty::{self, Ty}; use rustc_span::symbol::{Symbol, sym}; use rustc_span::{Span, SyntaxContext}; diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs index 53df9a358b0e2..bdab6133316c9 100644 --- a/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs @@ -4,9 +4,9 @@ use clippy_utils::source::{snippet, snippet_indent, snippet_opt}; use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::any_temporaries_need_ordered_drop; use clippy_utils::{higher, peel_blocks}; -use rustc_ast::BindingMode; +use rustc_ast::{BindingMode, Label}; use rustc_errors::Applicability; -use rustc_hir::{Block, Expr, ExprKind, LetStmt, MatchSource, Pat, PatKind, Path, QPath, StmtKind, Ty}; +use rustc_hir::{Block, Expr, ExprKind, HirId, LetStmt, MatchSource, Pat, PatKind, Path, QPath, StmtKind, Ty}; use rustc_lint::LateContext; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) { @@ -26,10 +26,14 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_blo _ => return, }; let has_trailing_exprs = loop_block.stmts.len() + usize::from(loop_block.expr.is_some()) > 1; - + let loop_label = if let ExprKind::Loop(_, label, ..) = expr.kind { + label + } else { + None + }; if let Some(if_let) = higher::IfLet::hir(cx, init) && let Some(else_expr) = if_let.if_else - && is_simple_break_expr(else_expr) + && is_simple_break_expr(else_expr, loop_label, expr.hir_id) { could_be_while_let( cx, @@ -39,15 +43,16 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_blo has_trailing_exprs, let_info, Some(if_let.if_then), + loop_label, ); - } else if els.is_some_and(is_simple_break_block) + } else if els.is_some_and(|b| is_simple_break_block(b, loop_label, expr.hir_id)) && let Some((pat, _)) = let_info { - could_be_while_let(cx, expr, pat, init, has_trailing_exprs, let_info, None); + could_be_while_let(cx, expr, pat, init, has_trailing_exprs, let_info, None, loop_label); } else if let ExprKind::Match(scrutinee, [arm1, arm2], MatchSource::Normal) = init.kind && arm1.guard.is_none() && arm2.guard.is_none() - && is_simple_break_expr(arm2.body) + && is_simple_break_expr(arm2.body, loop_label, expr.hir_id) { could_be_while_let( cx, @@ -57,30 +62,34 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_blo has_trailing_exprs, let_info, Some(arm1.body), + loop_label, ); } } -/// Checks if `block` contains a single unlabeled `break` expression or statement, possibly embedded -/// inside other blocks. -fn is_simple_break_block(block: &Block<'_>) -> bool { +/// Checks if `block` contains a single (labeled or unlabeled) `break` +/// expression or statement, possibly embedded inside other blocks. +fn is_simple_break_block(block: &Block<'_>, loop_label: Option

    { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let mut diag = Diag::new( dcx, level, @@ -218,7 +218,7 @@ pub(crate) struct UnusedAssignOverwrite { } impl Subdiagnostic for UnusedAssignOverwrite { - fn add_to_diag(self, diag: &mut Diag<'_, G>) { + fn add_to_diag(self, diag: &mut Diag<'_>) { diag.span_label(self.assigned_span, "this value is reassigned later and never used"); diag.span_label( self.overwrite_span, @@ -297,7 +297,7 @@ pub(crate) struct UnusedVariableStringInterp { } impl Subdiagnostic for UnusedVariableStringInterp { - fn add_to_diag(self, diag: &mut Diag<'_, G>) { + fn add_to_diag(self, diag: &mut Diag<'_>) { diag.span_label( self.lit, msg!("you might have meant to use string interpolation in this string literal"), @@ -338,8 +338,8 @@ pub(crate) struct MustNotSupend<'a, 'tcx> { } // Needed for def_path_str -impl<'a> Diagnostic<'a, ()> for MustNotSupend<'_, '_> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { +impl<'a> Diagnostic<'a> for MustNotSupend<'_, '_> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let mut diag = Diag::new( dcx, level, diff --git a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs index b13a4fa16e21d..b1ac4d33445b0 100644 --- a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs +++ b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs @@ -175,7 +175,7 @@ impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch { discr: parent_op, targets: eq_targets, }, - attributes: ThinVec::new(), + loop_hint_attrs: ThinVec::new(), }), bbs[parent].is_cleanup, ); @@ -230,7 +230,7 @@ fn evaluate_candidate<'tcx>( let Terminator { kind: TerminatorKind::SwitchInt { targets: child_targets, discr: child_discr }, source_info, - attributes: _, + loop_hint_attrs: _, } = bbs[child].terminator() else { return None; diff --git a/compiler/rustc_mir_transform/src/elaborate_drop.rs b/compiler/rustc_mir_transform/src/elaborate_drop.rs index d9d9914d02bd4..9b81f55ab1145 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drop.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drop.rs @@ -2,12 +2,13 @@ use std::{fmt, iter, mem}; use itertools::Itertools; use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx}; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::thin_vec::ThinVec; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::{CoroutineDesugaring, CoroutineKind}; use rustc_index::Idx; use rustc_middle::mir::*; use rustc_middle::ty::adjustment::PointerCoercion; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::util::{Discr, IntTypeExt}; use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt}; use rustc_mir_dataflow::DropFlagState; @@ -1640,7 +1641,11 @@ where #[instrument(level = "trace", skip(self), ret)] fn new_block(&mut self, unwind: Unwind, k: TerminatorKind<'tcx>) -> BasicBlock { self.elaborator.patch().new_block(BasicBlockData::new( - Some(Terminator { source_info: self.source_info, kind: k, attributes: ThinVec::new() }), + Some(Terminator { + source_info: self.source_info, + kind: k, + loop_hint_attrs: ThinVec::new(), + }), unwind.is_cleanup(), )) } @@ -1654,7 +1659,11 @@ where ) -> BasicBlock { self.elaborator.patch().new_block(BasicBlockData::new_stmts( statements, - Some(Terminator { source_info: self.source_info, kind: k, attributes: ThinVec::new() }), + Some(Terminator { + source_info: self.source_info, + kind: k, + loop_hint_attrs: ThinVec::new(), + }), unwind.is_cleanup(), )) } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 78881d37a093b..b5fa022a527e5 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -114,6 +114,7 @@ use rustc_index::{IndexVec, newtype_index}; use rustc_middle::mir::interpret::{AllocRange, GlobalAlloc}; use rustc_middle::mir::visit::*; use rustc_middle::mir::*; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::layout::HasTypingEnv; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, Unnormalized}; use rustc_mir_dataflow::{Analysis, ResultsCursor}; @@ -724,6 +725,9 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { } CastKind::Transmute | CastKind::Subtype => { let value = self.eval_to_const(value)?; + if value.is_immediate_uninit() { + return Some(ImmTy::uninit(ty).into()); + } // `offset` for immediates generally only supports projections that match the // type of the immediate. However, as a HACK, we exploit that it can also do // limited transmutes: it only works between types with the same layout, and diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index c70b957da130e..38021f4b76a4d 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -4,9 +4,9 @@ use std::ops::{Range, RangeFrom}; use std::{debug_assert_matches, iter}; use rustc_abi::{ExternAbi, FieldIdx}; +use rustc_attr_ir::lang_items::LangItem; +use rustc_attr_ir::{InlineAttr, OptimizeAttr}; use rustc_data_structures::thin_vec::ThinVec; -use rustc_hir::attrs::lang_items::LangItem; -use rustc_hir::attrs::{InlineAttr, OptimizeAttr}; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_index::Idx; @@ -872,7 +872,7 @@ fn inline_call<'tcx, I: Inliner<'tcx>>( Some(Terminator { source_info: terminator.source_info, kind: TerminatorKind::Goto { target: block }, - attributes: ThinVec::new(), + loop_hint_attrs: ThinVec::new(), }), caller_body[block].is_cleanup, ); @@ -886,24 +886,13 @@ fn inline_call<'tcx, I: Inliner<'tcx>>( // Place could result in two different locations if `f` // writes to `i`. To prevent this we need to create a temporary // borrow of the place and pass the destination as `*temp` instead. - fn dest_needs_borrow(place: Place<'_>) -> bool { - for elem in place.projection.iter() { - match elem { - ProjectionElem::Deref | ProjectionElem::Index(_) => return true, - _ => {} - } - } - - false - } - - let dest = if dest_needs_borrow(destination) { + // + // This must be a raw pointer: a mutable reference could be invalidated by + // reading the arguments later, and would be invalid if the destination type + // is uninhabited. + let dest = if !destination.is_stable_offset() { trace!("creating temp for return destination"); - let dest = Rvalue::Ref( - tcx.lifetimes.re_erased, - BorrowKind::Mut { kind: MutBorrowKind::Default }, - destination, - ); + let dest = Rvalue::RawPtr(RawPtrKind::Mut, destination); let dest_ty = dest.ty(caller_body, tcx); let temp = Place::from(new_call_temp(caller_body, callsite, dest_ty, return_block)); caller_body[callsite.block].statements.push(Statement::new( @@ -1011,7 +1000,7 @@ fn inline_call<'tcx, I: Inliner<'tcx>>( caller_body[callsite.block].terminator = Some(Terminator { source_info: callsite.source_info, kind: TerminatorKind::Goto { target: integrator.map_block(START_BLOCK) }, - attributes: ThinVec::new(), + loop_hint_attrs: ThinVec::new(), }); // Copy required constants from the callee_body into the caller_body. Although we are only @@ -1172,7 +1161,7 @@ fn new_call_temp<'tcx>( * Integrates blocks from the callee function into the calling function. * Updates block indices, references to locals and other control flow * stuff. -*/ + */ struct Integrator<'a, 'tcx> { args: &'a [Local], new_locals: RangeFrom, diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs index b3b3dd1241a71..e02e29a2e23e0 100644 --- a/compiler/rustc_mir_transform/src/instsimplify.rs +++ b/compiler/rustc_mir_transform/src/instsimplify.rs @@ -1,11 +1,12 @@ //! Performs various peephole optimizations. use rustc_abi::{ExternAbi, Integer}; -use rustc_hir::attrs::lang_items::LangItem; -use rustc_hir::find_attr; +use rustc_attr_ir::find_attr; +use rustc_attr_ir::lang_items::LangItem; use rustc_index::IndexVec; use rustc_middle::mir::visit::MutVisitor; use rustc_middle::mir::*; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::layout::{IntegerExt, ValidityRequirement}; use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, layout}; use rustc_span::{Symbol, bug, sym}; diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index fc210ecebfa48..e25ed11d7a265 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -5,16 +5,18 @@ use std::fmt::Debug; use rustc_abi::{BackendRepr, FieldIdx, HasDataLayout, Size, TargetDataLayout, VariantIdx}; +use rustc_attr_ir::find_attr; use rustc_const_eval::const_eval::DummyMachine; use rustc_const_eval::interpret::{ImmTy, InterpCx, InterpResult, Projectable, Scalar, interp_ok}; use rustc_data_structures::fx::FxHashSet; +use rustc_hir::HirId; use rustc_hir::def::DefKind; -use rustc_hir::{HirId, find_attr}; use rustc_index::IndexVec; use rustc_index::bit_set::DenseBitSet; use rustc_lint_defs::builtin::UNCONDITIONAL_PANIC; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout}; use rustc_middle::ty::{ self, ConstInt, GenericArgKind, GenericParamDefKind, ScalarInt, Ty, TyCtxt, TypeVisitableExt, diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 95a708f125660..d0b9c686c3037 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -205,6 +205,7 @@ declare_passes! { mod sroa : ScalarReplacementOfAggregates; mod strip_debuginfo : StripDebugInfo; mod ssa_range_prop: SsaRangePropagation; + mod tail_copy_to_move : TailCopyToMove; mod unreachable_enum_branching : UnreachableEnumBranching; mod unreachable_prop : UnreachablePropagation; mod validate : Validator; @@ -761,6 +762,7 @@ pub(crate) fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<' ©_prop::CopyProp, &dead_store_elimination::DeadStoreElimination::Final, &dest_prop::DestinationPropagation, + &tail_copy_to_move::TailCopyToMove, &simplify::SimplifyLocals::Final, &multiple_return_terminators::MultipleReturnTerminators, // Some cleanup necessary at least for LLVM and potentially other codegen backends. diff --git a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs index a31a3cfcd6af9..7e91890a08cc2 100644 --- a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs +++ b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs @@ -523,7 +523,7 @@ struct LocalLabel<'a> { /// A custom `Subdiagnostic` implementation so that the notes are delivered in a specific order impl Subdiagnostic for LocalLabel<'_> { - fn add_to_diag(self, diag: &mut rustc_errors::Diag<'_, G>) { + fn add_to_diag(self, diag: &mut rustc_errors::Diag<'_>) { diag.span_label( self.span, msg!( diff --git a/compiler/rustc_mir_transform/src/liveness.rs b/compiler/rustc_mir_transform/src/liveness.rs index dc716c1886102..7725daa276cc6 100644 --- a/compiler/rustc_mir_transform/src/liveness.rs +++ b/compiler/rustc_mir_transform/src/liveness.rs @@ -1,8 +1,8 @@ use rustc_abi::FieldIdx; +use rustc_attr_ir::find_attr; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, IndexEntry}; use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::find_attr; use rustc_index::IndexVec; use rustc_index::bit_set::DenseBitSet; use rustc_lint_defs::builtin::{UNUSED_ASSIGNMENTS, UNUSED_VARIABLES}; @@ -293,7 +293,7 @@ fn annotate_mut_binding_to_immutable_binding<'tcx>( local.as_usize() - if tcx.is_closure_like(body_def_id.to_def_id()) { 2 } else { 1 }; let fn_decl = tcx.hir_node_by_def_id(body_def_id).fn_decl()?; let ty = fn_decl.inputs[hir_param_index]; - let hir::TyKind::Ref(lt, mut_ty) = ty.kind else { return None }; + let hir::TyKind::Ref(lt, inner_ty, mutbl) = ty.kind else { return None }; // ... as a binding pattern. let hir_body = tcx.hir_maybe_body_owned_by(body_def_id)?; @@ -312,12 +312,12 @@ fn annotate_mut_binding_to_immutable_binding<'tcx>( // Changes to the parameter's type. let pre = if lt.ident.span.is_empty() { "" } else { " " }; - let ty_span = if mut_ty.mutbl.is_mut() { + let ty_span = if mutbl.is_mut() { // Leave `&'name mut Ty` and `&mut Ty` as they are (#136028). None } else { // `&'name Ty` -> `&'name mut Ty` or `&Ty` -> `&mut Ty` - Some(mut_ty.ty.span.shrink_to_lo()) + Some(inner_ty.span.shrink_to_lo()) }; return Some(diagnostics::UnusedAssignSuggestion { @@ -415,7 +415,7 @@ fn find_self_assignments<'tcx>( // We ignore indirect self-assignment, because both occurrences of `dest` are uses. let is_indirect = checked_places .get(dest.as_ref()) - .map_or(false, |(_, projections)| is_indirect(projections)); + .is_some_and(|(_, projections)| is_indirect(projections)); if is_indirect { continue; } @@ -1100,7 +1100,7 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> { let outer_initializer_span = initializer_span.find_ancestor_in_same_ctxt(source_info.span); within.is_none() - && outer_initializer_span.map_or(true, |s| !s.contains(source_info.span)) + && outer_initializer_span.is_none_or(|s| !s.contains(source_info.span)) }); } diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs index 74910ce3acde4..f3f65a070e500 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -2,8 +2,8 @@ use std::cell::RefCell; use std::collections::hash_map::Entry; use std::sync::atomic::Ordering; +use rustc_attr_ir::OptimizeAttr; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; -use rustc_hir::attrs::OptimizeAttr; use rustc_hir::def_id::DefId; use rustc_middle::mir::{Body, MirDumper, MirPhase, RuntimePhase}; use rustc_middle::ty::TyCtxt; @@ -314,7 +314,7 @@ fn run_passes_inner<'tcx>( for name in rest { err.note(format!("pass `{name}` is also not declared in `PASS_NAMES`")); } - err.emit(); + err.emit_bug(); } } diff --git a/compiler/rustc_mir_transform/src/patch.rs b/compiler/rustc_mir_transform/src/patch.rs index bd4cbcd89163c..6a2d5101067e7 100644 --- a/compiler/rustc_mir_transform/src/patch.rs +++ b/compiler/rustc_mir_transform/src/patch.rs @@ -93,7 +93,7 @@ impl<'tcx> MirPatch<'tcx> { Some(Terminator { source_info: SourceInfo::outermost(self.body_span), kind: TerminatorKind::UnwindResume, - attributes: ThinVec::new(), + loop_hint_attrs: ThinVec::new(), }), true, )); @@ -110,7 +110,7 @@ impl<'tcx> MirPatch<'tcx> { Some(Terminator { source_info: SourceInfo::outermost(self.body_span), kind: TerminatorKind::Unreachable, - attributes: ThinVec::new(), + loop_hint_attrs: ThinVec::new(), }), true, )); @@ -127,7 +127,7 @@ impl<'tcx> MirPatch<'tcx> { Some(Terminator { source_info: SourceInfo::outermost(self.body_span), kind: TerminatorKind::Unreachable, - attributes: ThinVec::new(), + loop_hint_attrs: ThinVec::new(), }), false, )); @@ -146,7 +146,7 @@ impl<'tcx> MirPatch<'tcx> { Some(Terminator { source_info: SourceInfo::outermost(self.body_span), kind: TerminatorKind::UnwindTerminate(reason), - attributes: ThinVec::new(), + loop_hint_attrs: ThinVec::new(), }), true, )); diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index b749d6d36cf84..92c2f9a1c20e1 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -23,6 +23,7 @@ use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir; use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::{self, GenericArgs, List, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::{Span, Spanned, bug, span_bug}; use tracing::{debug, instrument}; @@ -756,7 +757,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { Some(Terminator { source_info: SourceInfo::outermost(span), kind: TerminatorKind::Return, - attributes: ThinVec::new(), + loop_hint_attrs: ThinVec::new(), }), false, )) @@ -845,7 +846,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { Terminator { source_info: terminator.source_info, kind: mem::replace(&mut terminator.kind, TerminatorKind::Goto { target }), - attributes: ThinVec::new(), + loop_hint_attrs: ThinVec::new(), } }; diff --git a/compiler/rustc_mir_transform/src/remove_zsts.rs b/compiler/rustc_mir_transform/src/remove_zsts.rs index 09379fc252072..98f4009c15665 100644 --- a/compiler/rustc_mir_transform/src/remove_zsts.rs +++ b/compiler/rustc_mir_transform/src/remove_zsts.rs @@ -2,6 +2,7 @@ use rustc_middle::mir::visit::*; use rustc_middle::mir::*; +use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::{self, Ty, TyCtxt}; use crate::PassPolicy; diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 1ddd77b9c16ce..faf7039d498b1 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -1,9 +1,9 @@ use std::{assert_matches, fmt, iter}; use rustc_abi::{ExternAbi, FIRST_VARIANT, FieldIdx, VariantIdx}; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::thin_vec::ThinVec; use rustc_hir as hir; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def_id::DefId; use rustc_index::{Idx, IndexVec}; use rustc_middle::mir::visit::{MutVisitor, PlaceContext}; @@ -281,7 +281,7 @@ pub fn build_drop_shim<'tcx>( let mut blocks = IndexVec::with_capacity(2); let block = |blocks: &mut IndexVec<_, _>, kind| { blocks.push(BasicBlockData::new( - Some(Terminator { source_info, kind, attributes: ThinVec::new() }), + Some(Terminator { source_info, kind, loop_hint_attrs: ThinVec::new() }), false, )) }; @@ -338,7 +338,7 @@ pub fn build_drop_shim<'tcx>( call_source: CallSource::Misc, fn_span: span, }, - attributes: ThinVec::new(), + loop_hint_attrs: ThinVec::new(), }); } else { let patch = { @@ -491,7 +491,11 @@ fn build_thread_local_shim<'tcx>(tcx: TyCtxt<'tcx>, shim: ty::ShimKind<'tcx>) -> Rvalue::ThreadLocalRef(def_id), ))), )], - Some(Terminator { source_info, kind: TerminatorKind::Return, attributes: ThinVec::new() }), + Some(Terminator { + source_info, + kind: TerminatorKind::Return, + loop_hint_attrs: ThinVec::new(), + }), false, )]); @@ -577,7 +581,7 @@ impl<'tcx> CloneShimBuilder<'tcx> { let source_info = self.source_info(); self.blocks.push(BasicBlockData::new_stmts( statements, - Some(Terminator { source_info, kind, attributes: ThinVec::new() }), + Some(Terminator { source_info, kind, loop_hint_attrs: ThinVec::new() }), is_cleanup, )) } @@ -923,7 +927,7 @@ fn build_call_shim<'tcx>( let block = |blocks: &mut IndexVec<_, _>, statements, kind, is_cleanup| { blocks.push(BasicBlockData::new_stmts( statements, - Some(Terminator { source_info, kind, attributes: ThinVec::new() }), + Some(Terminator { source_info, kind, loop_hint_attrs: ThinVec::new() }), is_cleanup, )) }; @@ -1050,7 +1054,11 @@ pub(super) fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> Body<'_> { let start_block = BasicBlockData::new_stmts( vec![statement], - Some(Terminator { source_info, kind: TerminatorKind::Return, attributes: ThinVec::new() }), + Some(Terminator { + source_info, + kind: TerminatorKind::Return, + loop_hint_attrs: ThinVec::new(), + }), false, ); @@ -1133,7 +1141,11 @@ fn build_fn_ptr_as_ptr_shim<'tcx>( let start_block = BasicBlockData::new_stmts( statements, - Some(Terminator { source_info, kind: TerminatorKind::Return, attributes: ThinVec::new() }), + Some(Terminator { + source_info, + kind: TerminatorKind::Return, + loop_hint_attrs: ThinVec::new(), + }), false, ); let source = MirSource::from_shim(ty::ShimKind::FnPtrAsPtr(def_id, self_ty)); @@ -1168,7 +1180,11 @@ fn build_fn_ptr_from_ptr_shim<'tcx>( let start_block = BasicBlockData::new_stmts( statements, - Some(Terminator { source_info, kind: TerminatorKind::Return, attributes: ThinVec::new() }), + Some(Terminator { + source_info, + kind: TerminatorKind::Return, + loop_hint_attrs: ThinVec::new(), + }), false, ); let source = MirSource::from_shim(ty::ShimKind::FnPtrFromPtr(def_id, self_ty)); @@ -1266,7 +1282,11 @@ fn build_construct_coroutine_by_move_shim<'tcx>( let statements = vec![stmt]; let start_block = BasicBlockData::new_stmts( statements, - Some(Terminator { source_info, kind: TerminatorKind::Return, attributes: ThinVec::new() }), + Some(Terminator { + source_info, + kind: TerminatorKind::Return, + loop_hint_attrs: ThinVec::new(), + }), false, ); diff --git a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs index 1d91bc1cab988..fcc28b20d25a0 100644 --- a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs +++ b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs @@ -1,4 +1,4 @@ -use rustc_hir::attrs::lang_items::LangItem; +use rustc_attr_ir::lang_items::LangItem; use rustc_hir::def_id::DefId; use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource}; use rustc_index::{Idx, IndexVec}; @@ -86,7 +86,7 @@ pub(super) fn build_async_drop_shim<'tcx>( let mut blocks = IndexVec::with_capacity(2); let block = |blocks: &mut IndexVec<_, _>, kind| { blocks.push(BasicBlockData::new( - Some(Terminator { source_info, kind, attributes: ThinVec::new() }), + Some(Terminator { source_info, kind, loop_hint_attrs: ThinVec::new() }), false, )) }; @@ -394,7 +394,7 @@ fn build_adrop_for_adrop_shim<'tcx>( fn_span: span, }, - attributes: ThinVec::new(), + loop_hint_attrs: ThinVec::new(), }), false, )); @@ -419,12 +419,16 @@ fn build_adrop_for_adrop_shim<'tcx>( fn_span: span, }, - attributes: ThinVec::new(), + loop_hint_attrs: ThinVec::new(), }), false, )); blocks.push(BasicBlockData::new( - Some(Terminator { source_info, kind: TerminatorKind::Return, attributes: ThinVec::new() }), + Some(Terminator { + source_info, + kind: TerminatorKind::Return, + loop_hint_attrs: ThinVec::new(), + }), false, )); diff --git a/compiler/rustc_mir_transform/src/sroa.rs b/compiler/rustc_mir_transform/src/sroa.rs index 4ed824db17718..591c84bb17689 100644 --- a/compiler/rustc_mir_transform/src/sroa.rs +++ b/compiler/rustc_mir_transform/src/sroa.rs @@ -1,6 +1,6 @@ use rustc_abi::FieldIdx; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; -use rustc_hir::attrs::lang_items::LangItem; use rustc_index::IndexVec; use rustc_index::bit_set::{DenseBitSet, GrowableBitSet}; use rustc_middle::mir::visit::*; diff --git a/compiler/rustc_mir_transform/src/ssa_range_prop.rs b/compiler/rustc_mir_transform/src/ssa_range_prop.rs index 79c6868bf87c2..f3f34ce11fe1a 100644 --- a/compiler/rustc_mir_transform/src/ssa_range_prop.rs +++ b/compiler/rustc_mir_transform/src/ssa_range_prop.rs @@ -155,12 +155,16 @@ impl<'tcx> MutVisitor<'tcx> for RangeSet<'tcx, '_, '_> { fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) { self.super_terminator(terminator, location); match &terminator.kind { - TerminatorKind::Assert { cond, expected, target, .. } + TerminatorKind::Assert { cond, expected, target, msg, .. } if let Some(place) = cond.place() && self.is_ssa(place) => { let successor = Location { block: *target, statement_index: 0 }; - if location.strictly_dominates(successor, &self.dominators) { + if location.strictly_dominates(successor, &self.dominators) + // Don't propagate range information from built-in overflow checks, + // this MIR might be lowered without them! + && !msg.is_optional_overflow_check() + { let val = *expected as u128; let range = WrappingRange { start: val, end: val }; self.insert_range(place, successor, range); diff --git a/compiler/rustc_mir_transform/src/tail_copy_to_move.rs b/compiler/rustc_mir_transform/src/tail_copy_to_move.rs new file mode 100644 index 0000000000000..e97844748cefb --- /dev/null +++ b/compiler/rustc_mir_transform/src/tail_copy_to_move.rs @@ -0,0 +1,258 @@ +//! Rewrite final-use copies before return into moves. +//! +//! # The problem +//! +//! MIR building represents reads of values whose type is `Copy` using +//! `Operand::Copy`, including when such a local is returned. If that local's +//! address has ever been observed, `MoveElimination` conservatively considers +//! it live until its `StorageDead` or function exit. This keeps the local live +//! across `_0 = copy local`, so its live range overlaps with the return place +//! and `MoveElimination` cannot unify the source local with `_0`. +//! +//! # The solution +//! +//! At function return, all local allocations are about to become invalid +//! anyway. After borrowck, this pass can therefore turn a final-use `Copy` into +//! a `Move`, as long as shortening the source local's live range has no +//! observable effect before the return happens. Concretely, between the +//! transformed copy (now a move) and the return, there may only be writes to +//! unborrowed locals, storage markers, nops, and gotos. +//! +//! # The algorithm +//! +//! Start from every `Return` terminator, with `_0` treated as used by the +//! return. Then scan predecessor blocks backward through `Goto` edges, forming +//! a return-tail tree. The scan maintains `used_after`, the set of locals +//! accessed later on that path. +//! +//! A `Copy` operand is rewritten to a `Move` when its base local is not in +//! `used_after`. Then any locals touched by that operand, including +//! index-projection locals, are added to `used_after` before the backward scan +//! continues. +//! +//! The scan stops when accessing an indirect place because that may access any +//! borrowed local, which would make the pass unable to prove any useful final +//! uses. It also stops at writes to borrowed locals, because those can create a +//! new address-observed allocation range whose overlap with an earlier borrowed +//! local must be preserved. + +use std::ops::ControlFlow; + +use rustc_index::bit_set::DenseBitSet; +use rustc_middle::mir::*; +use rustc_middle::ty::TyCtxt; +use rustc_mir_dataflow::impls::borrowed_locals; + +use crate::PassPolicy; + +pub(super) struct TailCopyToMove; + +impl<'tcx> crate::MirPass<'tcx> for TailCopyToMove { + fn policy(&self, ctx: &crate::PassCtx<'_>) -> PassPolicy { + PassPolicy::optional( + ctx.mir_opt_level() >= 2 && ctx.opts.unstable_opts.mir_move_elimination, + ) + } + + #[tracing::instrument(level = "trace", skip(self, _tcx, body))] + fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + let borrowed = borrowed_locals(body); + let predecessors = body.basic_blocks.predecessors().clone(); + let mut stack = Vec::new(); + + // A return terminator implicitly uses the return place. Walking + // backward through assignments records the locals accessed later on + // this path. + for (bb, data) in body.basic_blocks.iter_enumerated() { + if matches!(data.terminator().kind, TerminatorKind::Return) { + let mut used_after = DenseBitSet::new_empty(body.local_decls.len()); + used_after.insert(RETURN_PLACE); + stack.push(TailState { block: bb, used_after }); + } + } + + while let Some(mut state) = stack.pop() { + // `scan_block` rewrites final-use copies in this block and updates + // `used_after` to the locals whose allocation is accessed after the + // block starts. If the block is not pure tail code, this path is + // done. + if scan_block(body, state.block, &mut state.used_after, &borrowed).is_break() { + continue; + } + + // Continue through predecessor blocks only when the predecessor's + // terminator is a plain `Goto` to this block. Other terminators are + // control-flow or effect boundaries. + let mut first = None; + for pred in predecessors[state.block].iter().copied() { + let terminator = body.basic_blocks[pred].terminator(); + if let TerminatorKind::Goto { target } = terminator.kind { + debug_assert_eq!(target, state.block); + if first.is_none() { + first = Some(pred); + } else { + stack.push(TailState { block: pred, used_after: state.used_after.clone() }); + } + } + } + + // Avoid cloning the bitset for the first predecessor. + if let Some(pred) = first { + stack.push(TailState { block: pred, used_after: state.used_after }); + } + } + } +} + +struct TailState { + block: BasicBlock, + used_after: DenseBitSet, +} + +/// Scan a block backward while the return-tail invariant still holds. +/// +/// The invariant is that a whole-local `Copy` can be changed to a `Move` only +/// if this path has no later access to that local's allocation before +/// returning, and no later operation whose observable behavior could depend on +/// ending an address-observed local's allocation early. `used_after` tracks +/// those later local-allocation accesses. +fn scan_block<'tcx>( + body: &mut Body<'tcx>, + block: BasicBlock, + used_after: &mut DenseBitSet, + borrowed: &DenseBitSet, +) -> ControlFlow<()> { + for statement in body.basic_blocks.as_mut_preserves_cfg()[block].statements.iter_mut().rev() { + match &mut statement.kind { + // Under the local lifetime semantics from RFC 3943, `StorageLive` + // does not allocate, and `StorageDead` has no effect if the local + // was already freed by a move. These markers therefore do not + // affect whether a copy can be treated as a final use. + StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::Nop => {} + StatementKind::Assign((place, rhs)) => { + // Accessing an indirect place may touch any borrowed local, so + // continuing would require treating all borrowed locals as used + // after this point. + if place.is_indirect_first_projection() { + return ControlFlow::Break(()); + } + + // Writing to a borrowed local can start a new allocation range. + // Shortening an earlier borrowed local could remove an overlap + // with that new range. + if borrowed.contains(place.local) { + return ControlFlow::Break(()); + } + + // A destination write accesses the base local, and evaluating + // the destination may also access projection locals, such as an + // index. + record_place_locals(*place, used_after); + + // This pass only models `Use` and `Aggregate` rvalues whose + // operands are direct. Other rvalues are outside the + // conservative return-tail shape handled here. + process_rvalue(rhs, used_after)?; + } + StatementKind::SetDiscriminant { place, .. } => { + // Accessing an indirect place may touch any borrowed local, so + // continuing would require treating all borrowed locals as used + // after this point. + if place.is_indirect_first_projection() { + return ControlFlow::Break(()); + } + + // Writing to a borrowed local can start a new allocation range. + // Shortening an earlier borrowed local could remove an overlap + // with that new range. + if borrowed.contains(place.local) { + return ControlFlow::Break(()); + } + + // `SetDiscriminant` has a validity invariant on the rest of the + // place, so treat the base local as accessed along with any + // projection locals. + record_place_locals(**place, used_after); + } + _ => { + // Anything else may perform effects or evaluate places in ways + // this pass does not model, so it is not part of the pure + // return tail. + return ControlFlow::Break(()); + } + } + } + + ControlFlow::Continue(()) +} + +/// Records all locals used in a place, including `Index` projections in +/// `used_after`. +fn record_place_locals<'tcx>(place: Place<'tcx>, used_after: &mut DenseBitSet) { + for local in place.as_ref().accessed_locals() { + used_after.insert(local); + } +} + +/// Process the RHS of an assignment in a pure return tail. +fn process_rvalue<'tcx>( + rvalue: &mut Rvalue<'tcx>, + used_after: &mut DenseBitSet, +) -> ControlFlow<()> { + match rvalue { + Rvalue::Use(operand, _) => process_operand(operand, used_after), + Rvalue::Aggregate(_, operands) => { + // Operands are evaluated left-to-right. We scan them right-to-left + // so `used_after` includes uses later in the same statement. If an + // operand accesses an indirect place, only earlier operands and + // earlier statements are outside the pure tail. + for operand in operands.iter_mut().rev() { + process_operand(operand, used_after)?; + } + + ControlFlow::Continue(()) + } + _ => { + // This pass doesn't model other rvalues, so they are not part of + // the pure return tail. + ControlFlow::Break(()) + } + } +} + +/// Process one operand in an rvalue. +fn process_operand<'tcx>( + operand: &mut Operand<'tcx>, + used_after: &mut DenseBitSet, +) -> ControlFlow<()> { + let place = match operand { + Operand::Copy(place) | Operand::Move(place) if place.is_indirect_first_projection() => { + // Accessing an indirect place may touch any borrowed local. + // Continuing would require treating all borrowed locals as used + // after this point, which would prevent the useful copy-to-move + // rewrites this pass is looking for. + return ControlFlow::Break(()); + } + Operand::Copy(place) => { + let place = *place; + // No later operation in the scanned tail accesses this local's + // allocation, so this copy is a final use on the current return + // path and can be represented as a move. + if !used_after.contains(place.local) { + *operand = Operand::Move(place); + } + Some(place) + } + Operand::Move(place) => Some(*place), + Operand::Constant(_) | Operand::RuntimeChecks(_) => None, + }; + + if let Some(place) = place { + // Reading an operand place accesses its base local, and evaluating its + // projections may access additional locals, such as the index local in + // `place[index]`. + record_place_locals(place, used_after); + } + + ControlFlow::Continue(()) +} diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index b11c5c8d1da5a..28a508bde9eca 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -1,9 +1,9 @@ //! Validates the MIR to ensure that invariants are upheld. use rustc_abi::{ExternAbi, FIRST_VARIANT, Size}; +use rustc_attr_ir::InlineAttr; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_hir::attrs::InlineAttr; -use rustc_hir::attrs::lang_items::LangItem; use rustc_index::IndexVec; use rustc_index::bit_set::DenseBitSet; use rustc_infer::infer::TyCtxtInferExt; diff --git a/compiler/rustc_monomorphize/Cargo.toml b/compiler/rustc_monomorphize/Cargo.toml index ce5b90e2593a8..b2edfed1f5ce2 100644 --- a/compiler/rustc_monomorphize/Cargo.toml +++ b/compiler/rustc_monomorphize/Cargo.toml @@ -7,6 +7,7 @@ edition = "2024" # tidy-alphabetical-start rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } +rustc_attr_ir = { path = "../rustc_attr_ir" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_hir = { path = "../rustc_hir" } diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 3e16760ba3c00..bfe3ba91bb6bc 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -208,12 +208,12 @@ use std::cell::OnceCell; use std::ops::ControlFlow; +use rustc_attr_ir::InlineAttr; +use rustc_attr_ir::lang_items::LangItem; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync::{Lock, par_for_each_in}; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_hir as hir; -use rustc_hir::attrs::InlineAttr; -use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; diff --git a/compiler/rustc_monomorphize/src/diagnostics.rs b/compiler/rustc_monomorphize/src/diagnostics.rs index a1930dbea3cda..7635050603c41 100644 --- a/compiler/rustc_monomorphize/src/diagnostics.rs +++ b/compiler/rustc_monomorphize/src/diagnostics.rs @@ -96,7 +96,7 @@ pub(crate) struct EncounteredErrorWhileInstantiatingGlobalAsm { #[derive(Diagnostic)] #[diag("using `fn main` requires the standard library")] #[help( - "use `#![no_main]` to bypass the Rust generated entrypoint and declare a platform specific entrypoint yourself, usually with `#[no_mangle]`" + "use `#![no_main]` to bypass the Rust generated entrypoint and declare a platform specific entrypoint yourself, usually with `#[unsafe(no_mangle)]`" )] pub(crate) struct StartNotFound; diff --git a/compiler/rustc_monomorphize/src/graph_checks/statics.rs b/compiler/rustc_monomorphize/src/graph_checks/statics.rs index 4a6416843fded..e954077f27972 100644 --- a/compiler/rustc_monomorphize/src/graph_checks/statics.rs +++ b/compiler/rustc_monomorphize/src/graph_checks/statics.rs @@ -100,7 +100,7 @@ pub(super) fn check_static_initializers_are_acyclic<'tcx, 'a, 'b>( // Emit errors for all cycles for nodes in nodes_of_sccs.iter_mut().filter(|nodes| is_cyclic(nodes)) { // We sort the nodes by their Span to have consistent error line numbers - nodes.sort_by_key(|node| tcx.def_span(statics[node.index()])); + nodes.sort_by_key(|node| tcx.def_span(statics[node.index()]).lo_hi()); let head_def = statics[nodes[0].index()]; let head_span = tcx.def_span(head_def); diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index 16a4a04e8eb39..ec52a837e1a51 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -4,7 +4,7 @@ #![feature(once_cell_get_mut)] // tidy-alphabetical-end -use rustc_hir::attrs::lang_items::LangItem; +use rustc_attr_ir::lang_items::LangItem; use rustc_middle::query::TyCtxtAt; use rustc_middle::traits; use rustc_middle::ty::adjustment::CustomCoerceUnsized; diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 32de7a3d5f7ab..16df895b0f728 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -98,12 +98,12 @@ use std::fs::{self, File}; use std::io::Write; use std::path::{Path, PathBuf}; +use rustc_attr_ir::lang_items::LangItem; +use rustc_attr_ir::{InlineAttr, Linkage}; use rustc_data_structures::either::Either; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sync::par_join; use rustc_data_structures::unord::{UnordMap, UnordSet}; -use rustc_hir::attrs::lang_items::LangItem; -use rustc_hir::attrs::{InlineAttr, Linkage}; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, DefIdSet, LOCAL_CRATE}; use rustc_hir::definitions::DefPathDataName; @@ -1252,7 +1252,6 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> MonoItemPartitio Linkage::WeakODR => "WeakODR", Linkage::Internal => "Internal", Linkage::ExternalWeak => "ExternalWeak", - Linkage::Common => "Common", }; output.push('['); diff --git a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs index f0f0ebaf2b3bb..75c7513758cd0 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs @@ -5,8 +5,8 @@ use rustc_type_ir::inherent::*; use rustc_type_ir::solve::{Goal, QueryInput}; use rustc_type_ir::{ self as ty, Canonical, CanonicalParamEnvCacheEntry, CanonicalVarKind, CanonicalizerState, - Flags, InferCtxtLike, Interner, PlaceholderConst, PlaceholderType, PredicateProxy, Region, - TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, + Const, Flags, InferCtxtLike, Interner, PlaceholderConst, PlaceholderType, PredicateProxy, + Region, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, }; use thin_vec::ThinVec; @@ -525,7 +525,7 @@ impl, I: Interner> TypeFolder for Canonicaliz } } - fn fold_const(&mut self, c: I::Const) -> I::Const { + fn fold_const(&mut self, c: Const) -> Const { if !c.flags().intersects(NEEDS_CANONICAL) { return c; } diff --git a/compiler/rustc_next_trait_solver/src/canonical/mod.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs index fc9024333bf44..655ab64145db5 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/mod.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/mod.rs @@ -18,8 +18,8 @@ use rustc_type_ir::relate::{ self, Relate, RelateResult, TypeRelation, VarianceDiagInfo, relate_args_invariantly, }; use rustc_type_ir::{ - self as ty, Canonical, CanonicalVarKind, CanonicalVarValues, InferCtxtLike, Interner, Region, - TypeFoldable, TypingMode, TypingModeEqWrapper, + self as ty, Canonical, CanonicalVarKind, CanonicalVarValues, Const, InferCtxtLike, Interner, + Region, TypeFoldable, TypingMode, TypingModeEqWrapper, }; use thin_vec::ThinVec; use tracing::instrument; @@ -173,7 +173,7 @@ where // recreated universes here. delegate.insert_placeholder_assumptions( new_universe, - Some(rustc_type_ir::region_constraint::Assumptions::empty()), + rustc_type_ir::region_constraint::Assumptions::empty(), ); } } @@ -421,7 +421,7 @@ where } #[instrument(skip(self), level = "trace")] - fn consts(&mut self, a: I::Const, b: I::Const) -> RelateResult { + fn consts(&mut self, a: Const, b: Const) -> RelateResult> { if a == b { return Ok(a); } diff --git a/compiler/rustc_next_trait_solver/src/coherence.rs b/compiler/rustc_next_trait_solver/src/coherence.rs index e37e69a617bbd..ad6fc98601f69 100644 --- a/compiler/rustc_next_trait_solver/src/coherence.rs +++ b/compiler/rustc_next_trait_solver/src/coherence.rs @@ -5,7 +5,7 @@ use derive_where::derive_where; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::SolverAdtLangItem; use rustc_type_ir::{ - self as ty, InferCtxtLike, Interner, Region, TrivialTypeTraversalImpls, TypeVisitable, + self as ty, Const, InferCtxtLike, Interner, Region, TrivialTypeTraversalImpls, TypeVisitable, TypeVisitableExt, TypeVisitor, }; use tracing::instrument; @@ -469,7 +469,7 @@ where /// As these should be quite rare as const arguments and especially rare as impl /// parameters, allowing uncovered const parameters in impls seems more useful /// than allowing `impl Trait for i32` to compile. - fn visit_const(&mut self, _c: I::Const) -> Self::Result { + fn visit_const(&mut self, _c: Const) -> Self::Result { ControlFlow::Continue(()) } } diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs index 0a307f766ebb2..24e321fc68c67 100644 --- a/compiler/rustc_next_trait_solver/src/delegate.rs +++ b/compiler/rustc_next_trait_solver/src/delegate.rs @@ -5,7 +5,7 @@ use rustc_type_ir::solve::{ Certainty, ComputeGoalFastPathOutcome, FetchEligibleAssocItemResponse, Goal, NoSolution, VisibleForLeakCheck, }; -use rustc_type_ir::{self as ty, CanonicalizerState, InferCtxtLike, Interner, TypeFoldable}; +use rustc_type_ir::{self as ty, CanonicalizerState, Const, InferCtxtLike, Interner, TypeFoldable}; /// `SolverDelegate` is one of the two traits in the `rustc_type_ir` shared abstraction layer /// between rustc and rust-analyzer abstracting over the [InferCtxt][inferctxt-doc], which had to be @@ -63,7 +63,7 @@ pub trait SolverDelegate: Deref + Sized { normalize_ty: impl FnOnce( ty::Unnormalized::Ty>, ) -> Result<::Ty, E>, - ) -> Result::Const>, E>; + ) -> Result>, E>; // FIXME: This only is here because `wf::obligations` is in `rustc_trait_selection`! fn well_formed_goals( @@ -112,7 +112,7 @@ pub trait SolverDelegate: Deref + Sized { &self, src: ::Ty, dst: ::Ty, - assume: ::Const, + assume: Const, ) -> Result; /// Obtain canonicalizer state, either by allocating it afresh (the default) or by reusing diff --git a/compiler/rustc_next_trait_solver/src/normalize.rs b/compiler/rustc_next_trait_solver/src/normalize.rs index ab3a92da4ba54..c7d8d2919089b 100644 --- a/compiler/rustc_next_trait_solver/src/normalize.rs +++ b/compiler/rustc_next_trait_solver/src/normalize.rs @@ -2,8 +2,8 @@ use std::fmt::Debug; use rustc_type_ir::inherent::*; use rustc_type_ir::{ - self as ty, AliasTerm, Binder, FallibleTypeFolder, InferCtxtLike, Interner, PredicateProxy, - TypeFoldable, TypeSuperFoldable, TypeVisitableExt, UniverseIndex, + self as ty, AliasTerm, Binder, Const, FallibleTypeFolder, InferCtxtLike, Interner, + PredicateProxy, TypeFoldable, TypeSuperFoldable, TypeVisitableExt, UniverseIndex, }; use tracing::instrument; @@ -147,7 +147,7 @@ where } #[instrument(level = "trace", skip(self), ret)] - fn try_fold_const(&mut self, ct: I::Const) -> Result { + fn try_fold_const(&mut self, ct: Const) -> Result, Self::Error> { let infcx = self.infcx; let original = ct; diff --git a/compiler/rustc_next_trait_solver/src/placeholder.rs b/compiler/rustc_next_trait_solver/src/placeholder.rs index 84811a101fb11..fdf702248196a 100644 --- a/compiler/rustc_next_trait_solver/src/placeholder.rs +++ b/compiler/rustc_next_trait_solver/src/placeholder.rs @@ -3,8 +3,9 @@ use core::panic; use rustc_type_ir::data_structures::IndexMap; use rustc_type_ir::inherent::*; use rustc_type_ir::{ - self as ty, InferCtxtLike, Interner, PlaceholderConst, PlaceholderRegion, PlaceholderType, - PredicateProxy, Region, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, + self as ty, Const, InferCtxtLike, Interner, PlaceholderConst, PlaceholderRegion, + PlaceholderType, PredicateProxy, Region, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitableExt, }; use tracing::debug; @@ -74,7 +75,7 @@ where // context to compute placeholder assumptions for the binders it enters. infcx.insert_placeholder_assumptions( *new, - Some(rustc_type_ir::region_constraint::Assumptions::empty()), + rustc_type_ir::region_constraint::Assumptions::empty(), ); } } @@ -160,7 +161,7 @@ where } } - fn fold_const(&mut self, ct: I::Const) -> I::Const { + fn fold_const(&mut self, ct: Const) -> Const { match ct.kind() { ty::ConstKind::Bound(ty::BoundVarIndexKind::Bound(debruijn), _) if debruijn.as_usize() + 1 @@ -310,7 +311,7 @@ where } } - fn fold_const(&mut self, ct: I::Const) -> I::Const { + fn fold_const(&mut self, ct: Const) -> Const { let ct = self.infcx.shallow_resolve_const(ct); if let ty::ConstKind::Placeholder(p) = ct.kind() { let replace_var = self.mapped_consts.get(&p); diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index 485568850bee0..ae538441499e0 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -14,7 +14,7 @@ use rustc_type_ir::solve::{ RerunNonErased, RerunReason, RerunResultExt, SizedTraitKind, StalledOnCoroutines, }; use rustc_type_ir::{ - self as ty, AliasTy, Interner, MayBeErased, Region, TypeFlags, TypeFoldable, TypeFolder, + self as ty, AliasTy, Const, Interner, MayBeErased, Region, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, Unnormalized, Upcast, elaborate, }; @@ -1091,8 +1091,12 @@ where // we still use modulo regions here. This is fine as specialization currently // assumes that specializing impls have to be always applicable, meaning that // the only allowed region constraints may be constraints also present on the default impl. + // + // If both impls have the same response, dropping victim doesn't change the + // result. We need to do so here as we don't merge distinct impl candidates. if matches!(allow_inference_constraints, AllowInferenceConstraints::Yes) || has_only_region_constraints(c.result) + || c.result == candidates[i].result { if self.cx().impl_specializes(other_def_id, victim_def_id) { candidates.remove(i); @@ -1466,7 +1470,7 @@ where } } - fn visit_const(&mut self, ct: I::Const) -> Self::Result { + fn visit_const(&mut self, ct: Const) -> Self::Result { let ct = self.ecx.replace_bound_vars(ct, &mut self.universes); let Ok(ct) = self.ecx.structurally_normalize_const(self.param_env, ct) else { return ControlFlow::Break(Err(NoSolution)); diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index 4f061956765b8..c92fdea176670 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -60,7 +60,8 @@ where | ty::Placeholder(..) | ty::Alias(ty::IsRigid::No, _) | ty::Bound(..) - | ty::Infer(_) => { + | ty::Infer(_) + | ty::Alias(ty::IsRigid::Yes, ty::AliasTy { kind: ty::Opaque { .. }, .. }) => { panic!("unexpected type `{ty:?}`") } @@ -106,15 +107,6 @@ where .map(Unnormalized::skip_norm_wip) .collect(), )), - - ty::Alias(ty::IsRigid::Yes, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => { - // We can resolve the `impl Trait` to its concrete type, - // which enforces a DAG between the functions requiring - // the auto trait bounds in question. - Ok(ty::Binder::dummy(vec![ - cx.type_of(def_id.into()).instantiate(cx, args).skip_norm_wip(), - ])) - } } } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 3a4875c1d0951..044dc89978124 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -5,7 +5,7 @@ use std::ops::ControlFlow; use rustc_macros::StableHash; use rustc_type_ir::data_structures::HashSet; use rustc_type_ir::inherent::*; -use rustc_type_ir::region_constraint::{self, RegionConstraint}; +use rustc_type_ir::region_constraint::RegionConstraint; use rustc_type_ir::relate::Relate; use rustc_type_ir::relate::solver_relating::RelateExt; use rustc_type_ir::search_graph::{ @@ -17,7 +17,7 @@ use rustc_type_ir::solve::{ RerunNonErased, RerunReason, RerunResultExt, SmallCopySet, TyOrConstInferVar, }; use rustc_type_ir::{ - self as ty, CanonicalVarValues, ClauseKind, InferCtxtLike, Interner, MayBeErased, + self as ty, CanonicalVarValues, ClauseKind, Const, InferCtxtLike, Interner, MayBeErased, OpaqueTypeKey, PredicateKind, PredicateProxy, Region, RegionVid, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, max_universe, }; @@ -63,6 +63,8 @@ enum CurrentGoalKind { /// These are currently the only goals whose impl where-clauses are considered to be /// productive steps. CoinductiveTrait, + /// A `ProjectionGoal`. These are temporarily special wrt to cycle handling. + Projection, // FIXME: Consider renaming `PredicateKind::NormalizesTo` to match with this /// Unlike other goals, `NormalizesTo` goals aren't independent goals but just implementation /// details for handling projections of associated terms. When we encounter a `Projection` goal @@ -89,6 +91,7 @@ impl CurrentGoalKind { CurrentGoalKind::Misc } } + ty::PredicateKind::Clause(ty::ClauseKind::Projection(_)) => CurrentGoalKind::Projection, ty::PredicateKind::NormalizesTo(_) => { CurrentGoalKind::ProjectionComputeAssocTermCandidate } @@ -428,43 +431,29 @@ where } /// Computes the `PathKind` for the step from the current goal to the - /// nested goal required due to `source`. + /// nested goal. See #136824 for a more detailed reasoning for this why we care about + /// the step from a goal to its nested goals. /// - /// See #136824 for a more detailed reasoning for this behavior. We - /// consider cycles to be coinductive if they 'step into' a where-clause - /// of a coinductive trait. We will likely extend this function in the future - /// and will need to clearly document it in the rustc-dev-guide before - /// stabilization. - pub(super) fn step_kind_for_source(&self, source: GoalSource) -> PathKind { - match source { - // We treat these goals as unknown for now. It is likely that most miscellaneous - // nested goals will be converted to an inductive variant in the future. - // - // Having unknown cycles is always the safer option, as changing that to either - // succeed or hard error is backwards compatible. If we incorrectly treat a cycle - // as inductive even though it should not be, it may be unsound during coherence and - // fixing it may cause inference breakage or introduce ambiguity. - GoalSource::Misc => PathKind::Unknown, - GoalSource::NormalizeGoal(path_kind) => path_kind, - GoalSource::ImplWhereBound => match self.current_goal_kind { - // We currently only consider a cycle coinductive if it steps - // into a where-clause of a coinductive trait. - CurrentGoalKind::CoinductiveTrait => PathKind::Coinductive, - // We probably want to make all traits coinductive in the future, - // so we treat cycles involving where-clauses of not-yet coinductive - // traits as ambiguous for now. - CurrentGoalKind::Misc | CurrentGoalKind::ProjectionComputeAssocTermCandidate => { - PathKind::Unknown - } - }, - // Relating types is always unproductive. If we were to map proof trees to - // corecursive functions as explained in #136824, relating types never - // introduces a constructor which could cause the recursion to be guarded. - GoalSource::TypeRelating => PathKind::Inductive, - // These goal sources are likely unproductive and can be changed to - // `PathKind::Inductive`. Keeping them as unknown until we're confident - // about this and have an example where it is necessary. - GoalSource::AliasBoundConstCondition | GoalSource::AliasWellFormed => PathKind::Unknown, + /// For now we're entirely ignoring the reason why the current goal depends on the + /// nested goal and instead only depend on the current goal itself. This closely + /// matches the old solver. This is something we'll likely improve as we go forward + /// afterwards. + // FIXME(-Znext-solver=coinductive): Nothing interesting going on here right now + // and we're ignoring the goal source. We should change this going forward. + pub(super) fn step_kind_to_nested(&self, _source: GoalSource) -> PathKind { + match self.current_goal_kind { + // We currently consider a cycle involving a trait goal for a coinductive + // trait as coinductive as long as it otherwise only includes normalization. + CurrentGoalKind::CoinductiveTrait => PathKind::Coinductive, + // We do need to treat cycles involving only coinductive trait goals + // and normalization as coinductive due to trait-system-refactor-initiative#10. + CurrentGoalKind::ProjectionComputeAssocTermCandidate | CurrentGoalKind::Projection => { + PathKind::Unknown + } + // We probably want to make all traits coinductive in the future, + // so we treat cycles involving where-clauses of not-yet coinductive + // traits as ambiguous for now. + CurrentGoalKind::Misc => PathKind::ForcedAmbiguity, } } @@ -635,12 +624,119 @@ where source: GoalSource, goal: Goal, ) -> Result, NoSolutionOrRerunNonErased> { - let (normalization_nested_goals, goal_evaluation) = - self.evaluate_goal_raw(source, goal, LowerAvailableDepth::Yes)?; + let (normalization_nested_goals, goal_evaluation) = self.evaluate_goal_raw(source, goal)?; assert!(normalization_nested_goals.is_empty()); Ok(goal_evaluation) } + fn evaluate_in_search_graph( + &mut self, + canonical_goal: I::CanonicalInput, + step_kind: PathKind, + ) -> (Result, NoSolution>, AccessedOpaques) { + let increase_depth_for_nested = + match canonical_goal.canonical.value.goal.predicate.kind().skip_binder() { + // We don't lower the available depth for the `NormalizesTo` goal, as evaluating + // it is an extra step only exists in the new solver that behaves like a function + // call rather than an independent nested goal evaluation. So, decreasing the + // available depth may end up regressions which hit the recursion limits for crates + // compiled well with the old solver. + ty::PredicateKind::NormalizesTo(_) => LowerAvailableDepth::No, + // We also don't lower depth for witness and rigid opaque when proving auto traits. + // This is to mitigate the overflow FCW warnings in deeply nested async calls. + // See #159228. + // + // We're eventually going to remove the witness type so it's okay to ignore the + // depth. + // For rigid opaques, revealing hidden types can be viewed as normalization so it's + // consistent with the `NormalizesTo` reasoning above. + ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => { + if self.cx().trait_is_auto(pred.trait_ref.def_id) { + match pred.self_ty().kind() { + ty::CoroutineWitness(..) => LowerAvailableDepth::No, + ty::Alias( + ty::IsRigid::Yes, + ty::AliasTy { kind: ty::Opaque { def_id, .. }, .. }, + ) => { + // We only want to skip lowering depth when the proving is done via + // auto trait leakage. If the goal can be proved via item bounds, + // we should lower depth faithfully. + // + // FIXME: We can have param env candidates via TAIT or RTN. + // Ideally we'd instead lower the depth for the nested goal instead. + // Implementing this is a bit harder. + if self + .cx() + .item_self_bounds(def_id.into()) + .skip_binder() + .into_iter() + .any(|bound| { + bound + .as_trait_clause() + .is_some_and(|b| b.def_id() == pred.def_id()) + }) + { + LowerAvailableDepth::Yes + } else { + LowerAvailableDepth::No + } + } + ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Str + | ty::Pat(..) + | ty::FnPtr(..) + | ty::Array(..) + | ty::Slice(..) + | ty::RawPtr(..) + | ty::Never + | ty::Tuple(..) + | ty::UnsafeBinder(_) + | ty::Param(..) + | ty::Placeholder(..) + | ty::Bound(..) + | ty::Infer(..) + | ty::Alias(_, _) + | ty::Ref(_, _, _) + | ty::Adt(_, _) + | ty::Foreign(_) + | ty::Dynamic(..) + | ty::Error(_) + | ty::FnDef(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) => LowerAvailableDepth::Yes, + } + } else { + LowerAvailableDepth::Yes + } + } + ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(_)) + | ty::PredicateKind::Clause(ty::ClauseKind::Projection(_)) + | ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(_)) + | ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(_)) + | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(_, _)) + | ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_)) + | ty::PredicateKind::Subtype(_) + | ty::PredicateKind::Coerce(_) + | ty::PredicateKind::DynCompatible(_) + | ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) + | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(_)) + | ty::PredicateKind::ConstEquate(_, _) + | ty::PredicateKind::Ambiguous => LowerAvailableDepth::Yes, + }; + self.search_graph.evaluate_goal( + self.cx(), + canonical_goal, + step_kind, + increase_depth_for_nested, + &mut inspect::ProofTreeBuilder::new_noop(), + ) + } + /// Recursively evaluates `goal`, returning the nested goals in case /// the nested goal is a `NormalizesTo` goal. /// @@ -652,7 +748,6 @@ where &mut self, source: GoalSource, goal: Goal, - increase_depth_for_nested: LowerAvailableDepth, ) -> Result<(NestedNormalizationGoals, GoalEvaluation), NoSolutionOrRerunNonErased> { // We only care about one entry per `OpaqueTypeKey` here, // so we only canonicalize the lookup table and ignore @@ -662,7 +757,7 @@ where let (goal, opaque_types) = self.delegate.deeply_resolve_via_unification_table((goal, opaque_types)); let typing_mode = self.typing_mode(); - let step_kind = self.step_kind_for_source(source); + let step_kind = self.step_kind_to_nested(source); let tracing_span = tracing::span!( Level::DEBUG, @@ -720,13 +815,8 @@ where TypingMode::ErasedNotCoherence(MayBeErased), ); - let (canonical_result, accessed_opaques) = self.search_graph.evaluate_goal( - self.cx(), - canonical_goal, - step_kind, - increase_depth_for_nested, - &mut inspect::ProofTreeBuilder::new_noop(), - ); + let (canonical_result, accessed_opaques) = + self.evaluate_in_search_graph(canonical_goal, step_kind); let should_rerun = should_rerun_after_erased_canonicalization( accessed_opaques, @@ -760,13 +850,8 @@ where let (orig_values, canonical_goal) = canonicalize_goal(self.delegate, goal, &opaque_types, typing_mode); - let (canonical_result, accessed_opaques) = self.search_graph.evaluate_goal( - self.cx(), - canonical_goal, - step_kind, - increase_depth_for_nested, - &mut inspect::ProofTreeBuilder::new_noop(), - ); + let (canonical_result, accessed_opaques) = + self.evaluate_in_search_graph(canonical_goal, step_kind); assert!( !accessed_opaques.might_rerun(), "we run without TypingMode::ErasedNotCoherence, so opaques are available, and we don't retry if the outer typing mode is ErasedNotCoherence: {accessed_opaques:?} after {goal:?}" @@ -1010,11 +1095,8 @@ where source: GoalSource, mut goal: Goal, ) -> Result<(), NoSolutionOrRerunNonErased> { - goal.predicate = self.normalize( - GoalSource::NormalizeGoal(self.step_kind_for_source(source)), - goal.param_env, - ty::Unnormalized::new_wip(goal.predicate), - )?; + goal.predicate = + self.normalize(goal.param_env, ty::Unnormalized::new_wip(goal.predicate))?; self.inspect.add_goal(self.delegate, self.max_input_universe, source, goal); if let Some(GoalEvaluation { goal, certainty, has_changed: _, stalled_on }) = @@ -1057,7 +1139,7 @@ where ty } - pub(super) fn next_const_infer(&mut self) -> I::Const { + pub(super) fn next_const_infer(&mut self) -> Const { let ct = self.delegate.next_const_infer(); self.inspect.add_var_value(ct); ct @@ -1154,7 +1236,7 @@ where ControlFlow::Continue(()) } - fn visit_const(&mut self, c: I::Const) -> Self::Result { + fn visit_const(&mut self, c: Const) -> Self::Result { match c.kind() { ty::ConstKind::Infer(ty::InferConst::Var(vid)) => { if let ty::TermKind::Const(term) = self.term.kind() @@ -1240,10 +1322,12 @@ where let goals = self.delegate.relate(param_env, lhs, variance, rhs, self.origin_span)?; for &goal in goals.iter() { let source = match goal.predicate.kind().skip_binder() { - ty::PredicateKind::Subtype { .. } - | ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) => { - GoalSource::TypeRelating + ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) => { + GoalSource::Normalization } + // FIXME(-Znext-solver=coinductive): subtyping goals should + // likely be unproductive + ty::PredicateKind::Subtype { .. } => GoalSource::Misc, // FIXME(-Znext-solver=coinductive): should these WF goals also be unproductive? ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => GoalSource::Misc, p => unreachable!("unexpected nested goal in `relate`: {p:?}"), @@ -1289,13 +1373,19 @@ where f: impl FnOnce(&mut Self, T) -> U, ) -> U { self.delegate.enter_forall_without_assumptions(value, |value| { + // Invariant: we shouldn't insert empty assumptions if assumptions computation fails. + // When handling placeholder constraints, we rely on vacancies to force ambiguity. let u = self.delegate.universe(); - let assumptions = if self.cx().assumptions_on_binders() { - self.region_assumptions_for_placeholders_in_universe(value.clone(), u, param_env) - } else { - None - }; - self.delegate.insert_placeholder_assumptions(u, assumptions); + if self.cx().assumptions_on_binders() + && let Some(assumptions) = self.region_assumptions_for_placeholders_in_universe( + value.clone(), + u, + param_env, + ) + { + self.delegate.insert_placeholder_assumptions(u, assumptions); + } + f(self, value) }) } @@ -1409,14 +1499,17 @@ where &mut self, param_env: I::ParamEnv, alias_const: ty::AliasConst, - ) -> Result, NoSolutionOrRerunNonErased> { + ) -> Result>, NoSolutionOrRerunNonErased> { if self.typing_mode().is_erased_not_coherence() { - match self.opaque_accesses.rerun_always(RerunReason::EvaluateConst)? {} + let resolved = self.deeply_resolve_ignoring_regions(alias_const); + // FIXME: get rid of this once GCE is removed + let is_gce = self.cx().features().generic_const_exprs(); + if resolved.has_opaque_types() || is_gce { + match self.opaque_accesses.rerun_always(RerunReason::EvaluateConst)? {} + } } - self.delegate.evaluate_const(param_env, alias_const, |ty| { - self.normalize(GoalSource::Misc, param_env, ty) - }) + self.delegate.evaluate_const(param_env, alias_const, |ty| self.normalize(param_env, ty)) } pub(super) fn evaluate_const_and_instantiate_projection_term( @@ -1431,7 +1524,7 @@ where self.eq(param_env, expected_term, evaluated.into())?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } - None if self.cx().features().generic_const_args() => { + None if self.cx().features().gca_const_items() => { // HACK(khyperia): calling `deeply_resolve_ignoring_regions` here shouldn't be necessary, // `try_evaluate_const` calls `deeply_resolve_ignoring_regions` already. However, we want // to check `has_non_region_infer` against the type with vars resolved (i.e. check @@ -1470,7 +1563,7 @@ where &mut self, src: I::Ty, dst: I::Ty, - assume: I::Const, + assume: Const, ) -> Result { self.delegate.is_transmutable(dst, src, assume) } @@ -1669,10 +1762,6 @@ where let region_constraints = if self.cx().assumptions_on_binders() { ExternalRegionConstraints::NextGen(if let Certainty::Yes = certainty { let constraint = self.delegate.get_solver_region_constraint(); - debug_assert_eq!( - constraint, - region_constraint::propagate_ambiguity(constraint.clone()) - ); constraint } else { RegionConstraint::new_true() @@ -1702,7 +1791,6 @@ where pub(super) fn normalize>( &mut self, - source: GoalSource, param_env: I::ParamEnv, value: ty::Unnormalized, ) -> Result { @@ -1718,6 +1806,7 @@ where let infer_term = self.next_term_infer_of_alias_kind(alias_term); let pred = ty::ProjectionClause { projection_term: alias_term, term: infer_term }; let goal = Goal::new(self.cx(), param_env, pred); + let source = GoalSource::Normalization; self.inspect.add_goal(self.delegate, self.max_input_universe, source, goal); let GoalEvaluation { goal, certainty, has_changed: _, stalled_on } = self.evaluate_goal(source, goal, None)?; @@ -1760,7 +1849,7 @@ fn filter_irrelevant_region_constraints( } t.super_visit_with(self); } - fn visit_const(&mut self, c: I::Const) { + fn visit_const(&mut self, c: Const) { // The same goes for consts. if !c.has_infer_regions() { return; diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs index 5ecc06b30f33b..8a4d3fba99a39 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs @@ -8,11 +8,10 @@ use rustc_type_ir::outlives::{Component, push_outlives_components}; use rustc_type_ir::region_constraint::TransitiveRelationBuilder; use rustc_type_ir::region_constraint::{ And, Assumptions, LeafRegionConstraint, Or, eagerly_handle_placeholders_in_universe, - propagate_ambiguity, }; use rustc_type_ir::{ - AliasTy, Binder, ClauseKind, InferCtxtLike, Interner, Region, TypeVisitable, TypeVisitableExt, - TypeVisitor, UniverseIndex, + AliasTy, Binder, ClauseKind, Const, InferCtxtLike, Interner, Region, TypeVisitable, + TypeVisitableExt, TypeVisitor, UniverseIndex, }; use tracing::{debug, instrument}; @@ -63,7 +62,7 @@ where ); } - fn visit_const(&mut self, c: I::Const) { + fn visit_const(&mut self, c: Const) { self.out.extend( self.ecx .well_formed_goals(self.param_env, c.into()) @@ -115,17 +114,27 @@ where pub(super) fn eagerly_handle_placeholders(&mut self) -> Result { let constraint = self.delegate.get_solver_region_constraint(); - let smallest_universe = self.max_input_universe.index(); - let largest_universe = self.delegate.universe().index(); - debug!(?smallest_universe, largest_universe); + let smallest_universe = self.max_input_universe; + let largest_universe = self.delegate.universe(); + debug!("smallest_universe={smallest_universe:#?}, largest_universe={largest_universe:#?}"); - let constraint = ((smallest_universe + 1)..=largest_universe) - .map(|u| UniverseIndex::from_usize(u)) + // We rely on placeholder assumptions to evaluate region constraints with placeholders. + // But we can fail to compute assumptions due to unresolved type variables. In that case, + // we conservatively bail with ambiguity. + if !self + .delegate + .has_placeholder_assumptions(((smallest_universe + 1)..=largest_universe).into()) + { + return Ok(Certainty::AMBIGUOUS); + } + + // Walk around that `Step` trait is nightly only. + let constraint = ((smallest_universe.index() + 1)..=largest_universe.index()) + .map(UniverseIndex::from_usize) .rev() .fold(constraint, |constraint, u| { eagerly_handle_placeholders_in_universe(&**self.delegate, constraint, u) }); - let constraint = propagate_ambiguity(constraint); debug!("final constraint={:?}", constraint); self.delegate.overwrite_solver_region_constraint(constraint.clone(), self.origin_span); @@ -164,7 +173,9 @@ where Or::new_leaf(PlaceholderTyOutlives(Ty::new_placeholder(self.cx(), *p), r, ())) } Alias(_, alias) => self.destructure_alias_outlives(*alias, r), - UnresolvedInferenceVariable(_) => Or::new_ambig(()), + UnresolvedInferenceVariable(_) => { + panic!("Shouldn't destructure type outlives when ty vars exist") + } Param(_) => panic!("Params should have been canonicalized to placeholders"), EscapingAlias(components) => self.destructure_components(components, r), } diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 10ee038005d8e..099c755d78427 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -23,7 +23,7 @@ mod trait_goals; use derive_where::derive_where; use rustc_type_ir::inherent::*; pub use rustc_type_ir::solve::*; -use rustc_type_ir::{self as ty, Interner, Region, TypeVisitableExt}; +use rustc_type_ir::{self as ty, Const, Interner, Region, TypeVisitableExt}; use tracing::instrument; pub use self::eval_ctxt::{ @@ -79,6 +79,34 @@ fn has_only_region_constraints(response: ty::Canonical( + a: &CanonicalResponse, + b: &CanonicalResponse, +) -> bool { + let CanonicalResponse { + max_universe: a_max_universe, + var_kinds: a_var_kinds, + value: + Response { + var_values: a_var_values, + certainty: a_certainty, + external_constraints: a_external_constraints, + }, + } = a; + + let ExternalConstraintsData { region_constraints: _, opaque_types, normalization_nested_goals } = + &**a_external_constraints; + + a_max_universe == &b.max_universe + && a_var_kinds == &b.var_kinds + && a_var_values == &b.value.var_values + && a_certainty == &b.value.certainty + && opaque_types == &b.value.external_constraints.opaque_types + && normalization_nested_goals == &b.value.external_constraints.normalization_nested_goals +} + impl<'a, D, I> EvalCtxt<'a, D> where D: SolverDelegate, @@ -90,16 +118,7 @@ where goal: Goal>, ) -> QueryResultOrRerunNonErased { let ty::OutlivesClause(ty, lt) = goal.predicate; - let ty = self.normalize(GoalSource::Misc, goal.param_env, ty::Unnormalized::new_wip(ty))?; - - if self.cx().assumptions_on_binders() { - use rustc_type_ir::region_constraint::RegionConstraint; - - let constraint = self.destructure_type_outlives(ty, lt); - self.register_solver_region_constraint(RegionConstraint::new_from_or(constraint)); - } else { - self.register_ty_outlives(ty, lt); - } + let ty = self.normalize(goal.param_env, ty::Unnormalized::new_wip(ty))?; // The normalized type can still contain non-rigid higher ranked aliases if their // normalization ends up with ambiguity. Or we have non-rigid aliases inside rigid ones. @@ -109,6 +128,18 @@ where if ty.has_non_region_infer() || ty.has_non_rigid_aliases() { self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } else { + // We drop region constraints in ambiguous response so there's no need to add them in + // the ambiguous branch. Also this guarantees we don't have ty vars when destructuring + // type outlives. + if self.cx().assumptions_on_binders() { + use rustc_type_ir::region_constraint::RegionConstraint; + + let constraint = self.destructure_type_outlives(ty, lt); + self.register_solver_region_constraint(RegionConstraint::new_from_or(constraint)); + } else { + self.register_ty_outlives(ty, lt); + } + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } } @@ -205,7 +236,7 @@ where #[instrument(level = "trace", skip(self))] fn compute_const_evaluatable_goal( &mut self, - Goal { param_env, predicate: ct }: Goal, + Goal { param_env, predicate: ct }: Goal>, ) -> QueryResultOrRerunNonErased { match ct.kind() { ty::ConstKind::Alias(ty::IsRigid::Yes, _) @@ -248,7 +279,7 @@ where #[instrument(level = "trace", skip(self), ret)] fn compute_const_arg_has_type_goal( &mut self, - goal: Goal, + goal: Goal, I::Ty)>, ) -> QueryResultOrRerunNonErased { let (ct, ty) = goal.predicate; let ct = self.structurally_normalize_const(goal.param_env, ct)?; @@ -315,13 +346,36 @@ where candidate.result.value.certainty == Certainty::Yes && has_no_inference_or_external_constraints(candidate.result) }); - if let Some((i, c)) = always_applicable { - return Some((c.result, MergeCandidateInfo::AlwaysApplicable(i))); + + if let Some((i, candidate)) = always_applicable { + return Some((candidate.result, MergeCandidateInfo::AlwaysApplicable(i))); } let one: CanonicalResponse = candidates[0].result; - if candidates[1..].iter().all(|candidate| candidate.result == one) { - return Some((one, MergeCandidateInfo::EqualResponse)); + + if candidates[1..] + .iter() + .all(|candidate| equal_response_modulo_region_constraints(&one, &candidate.result)) + { + let region_constraints = &one.value.external_constraints.region_constraints; + if candidates[1..].iter().all(|candidate| { + &candidate.result.value.external_constraints.region_constraints + == region_constraints + }) { + return Some((one, MergeCandidateInfo::EqualResponse)); + } + + // If candidates differ only in region constraints, their merged region + // constraints are an `Or` of their respective constraints. If one of them + // has no region constraints, the `Or` constraint evaluates to `true`. + // + // This is a special case of `-Zassumptions-on-binders` and should be + // replaced eventually. + if let Some(candidate) = candidates.iter().find(|candidate| { + candidate.result.value.external_constraints.region_constraints.is_empty() + }) { + return Some((candidate.result, MergeCandidateInfo::EqualResponse)); + } } None @@ -375,8 +429,8 @@ where fn structurally_normalize_const( &mut self, param_env: I::ParamEnv, - ct: I::Const, - ) -> Result { + ct: Const, + ) -> Result, NoSolutionOrRerunNonErased> { self.structurally_normalize_term(param_env, ct.into()).map(|term| term.expect_const()) } @@ -402,7 +456,7 @@ where ); // We normalize the self type to be able to relate it with // types from candidates. - self.add_goal(GoalSource::TypeRelating, projection_goal)?; + self.add_goal(GoalSource::Normalization, projection_goal)?; self.try_evaluate_added_goals()?; Ok(self.deeply_resolve_ignoring_regions(normalized_term)) } else { diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs index cb878f2c54878..4203ae8c509cc 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs @@ -8,7 +8,8 @@ use rustc_type_ir::solve::{ RerunNonErased, RerunReason, RerunResultExt, }; use rustc_type_ir::{ - self as ty, FieldInfo, Interner, NormalizesTo, PredicateKind, Region, Unnormalized, Upcast as _, + self as ty, Const, FieldInfo, Interner, NormalizesTo, PredicateKind, Region, Unnormalized, + Upcast as _, }; use tracing::instrument; @@ -438,7 +439,7 @@ where let term = match target_item_kind { ty::AliasTermKind::ProjectionTy { .. } => { let t = cx.type_of(target_item_def_id).instantiate(cx, target_args); - let t = ecx.normalize(GoalSource::Misc, goal.param_env, t)?; + let t = ecx.normalize(goal.param_env, t)?; t.into() } ty::AliasTermKind::ProjectionConst { def_id } @@ -446,7 +447,7 @@ where cx.const_of_item(ty::AliasConstKind::Projection { def_id }) => { let c = c.instantiate(cx, target_args); - let c = ecx.normalize(GoalSource::Misc, goal.param_env, c)?; + let c = ecx.normalize(goal.param_env, c)?; c.into() } ty::AliasTermKind::ProjectionConst { .. } => { diff --git a/compiler/rustc_next_trait_solver/src/solve/project_goals/free_alias.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals/free_alias.rs index 4481e1bc144ac..dfcc89b91a4f2 100644 --- a/compiler/rustc_next_trait_solver/src/solve/project_goals/free_alias.rs +++ b/compiler/rustc_next_trait_solver/src/solve/project_goals/free_alias.rs @@ -34,14 +34,14 @@ where let actual = match free_alias.kind { ty::AliasTermKind::FreeTy { def_id } => { let free = cx.type_of(def_id.into()).instantiate(cx, free_alias.args); - let free = self.normalize(GoalSource::Misc, goal.param_env, free)?; + let free = self.normalize(goal.param_env, free)?; free.into() } ty::AliasTermKind::FreeConst { def_id } if let Some(free) = cx.const_of_item(ty::AliasConstKind::Free { def_id }) => { let free = free.instantiate(cx, free_alias.args); - let free = self.normalize(GoalSource::Misc, goal.param_env, free)?; + let free = self.normalize(goal.param_env, free)?; free.into() } diff --git a/compiler/rustc_next_trait_solver/src/solve/project_goals/inherent.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals/inherent.rs index d519d1e538f1a..45ece5a0589fb 100644 --- a/compiler/rustc_next_trait_solver/src/solve/project_goals/inherent.rs +++ b/compiler/rustc_next_trait_solver/src/solve/project_goals/inherent.rs @@ -45,7 +45,7 @@ where let normalized: I::Term = match inherent_kind { ty::AliasTermKind::InherentTy { def_id } => { let inherent = cx.type_of(def_id.into()).instantiate(cx, inherent_args); - let inherent = self.normalize(GoalSource::Misc, goal.param_env, inherent)?; + let inherent = self.normalize(goal.param_env, inherent)?; inherent.into() } ty::AliasTermKind::InherentConstImpl { def_id } @@ -53,7 +53,7 @@ where cx.const_of_item(ty::AliasConstKind::InherentImpl { def_id }) => { let inherent = inherent.instantiate(cx, inherent_args); - let normalized_ct = self.normalize(GoalSource::Misc, goal.param_env, inherent)?; + let normalized_ct = self.normalize(goal.param_env, inherent)?; let normalized = normalized_ct.into(); let term = ty::AliasTerm::new_from_args(cx, inherent_kind, inherent_args); self.push_const_arg_has_type_goal(goal.param_env, term, normalized)?; diff --git a/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs index db326e6d736a4..f6521e579d722 100644 --- a/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs @@ -3,7 +3,6 @@ mod free_alias; mod inherent; mod opaque_types; -use rustc_type_ir::search_graph::LowerAvailableDepth; use rustc_type_ir::solve::QueryResultOrRerunNonErased; use rustc_type_ir::{self as ty, Interner, ProjectionClause}; use tracing::{instrument, trace}; @@ -70,16 +69,7 @@ where let ( NestedNormalizationGoals(nested_goals), GoalEvaluation { goal: _, certainty, stalled_on: _, has_changed: _ }, - ) = self.evaluate_goal_raw( - GoalSource::TypeRelating, - normalizes_to, - // We don't lower thr available depth for this `NormalizesTo` goal, as evaluating - // it is an extra step only exists in the new solver that behaves like a function - // call rather than an independent nested goal evaluation. So, decreasing the - // available depth may end up regressions which hit the recursion limits for crates - // compiled well with the old solver. - LowerAvailableDepth::No, - )?; + ) = self.evaluate_goal_raw(GoalSource::Normalization, normalizes_to)?; trace!(?nested_goals); diff --git a/compiler/rustc_next_trait_solver/src/solve/project_goals/opaque_types.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals/opaque_types.rs index 2f56056449779..048c8bb45361b 100644 --- a/compiler/rustc_next_trait_solver/src/solve/project_goals/opaque_types.rs +++ b/compiler/rustc_next_trait_solver/src/solve/project_goals/opaque_types.rs @@ -99,8 +99,7 @@ where _ => re, }) }); - let actual = - self.normalize(GoalSource::Misc, goal.param_env, actual)?; + let actual = self.normalize(goal.param_env, actual)?; self.eq(goal.param_env, expected, actual)?; } TypingMode::Coherence @@ -145,7 +144,7 @@ where _ => re, }) }); - let actual = self.normalize(GoalSource::Misc, goal.param_env, actual)?; + let actual = self.normalize(goal.param_env, actual)?; self.eq(goal.param_env, expected, actual)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) .map_err(Into::into) @@ -154,7 +153,7 @@ where TypingMode::Reflection | TypingMode::PostAnalysis | TypingMode::Codegen => { // FIXME: Add an assertion that opaque type storage is empty. let actual = cx.type_of(def_id.into()).instantiate(cx, opaque_ty.args); - let actual = self.normalize(GoalSource::Misc, goal.param_env, actual)?; + let actual = self.normalize(goal.param_env, actual)?; self.eq(goal.param_env, expected, actual)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) .map_err(Into::into) diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index e147a139a65e1..ecd175d694d7d 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -1,9 +1,9 @@ use std::convert::Infallible; use std::marker::PhantomData; +use rustc_type_ir::Interner; use rustc_type_ir::search_graph::{self, PathKind}; use rustc_type_ir::solve::{AccessedOpaques, Certainty, NoSolution, QueryResult, RerunResultExt}; -use rustc_type_ir::{Interner, MayBeErased, TypingMode}; use crate::canonical::response_no_constraints_raw; use crate::delegate::SolverDelegate; @@ -52,29 +52,21 @@ where PathKind::Unknown | PathKind::ForcedAmbiguity => { response_no_constraints(cx, input, Certainty::overflow(false)) } - // Even though we know these cycles to be unproductive, we still return - // overflow during coherence. This is both as we are not 100% confident in - // the implementation yet and any incorrect errors would be unsound there. + // Even though we know some cycles to be unproductive, we still treat them + // as unknown for now. This is both as we are not 100% confident in the + // implementation yet and any incorrect errors would be unsound there. + // // The affected cases are also fairly artificial and not necessarily desirable // so keeping this as ambiguity is fine for now. // - // See `tests/ui/traits/next-solver/cycles/unproductive-in-coherence.rs` for an - // example where this would matter. We likely should change these cycles to `NoSolution` - // even in coherence once this is a bit more settled. - PathKind::Inductive => match input.typing_mode.0 { - TypingMode::Coherence => { - response_no_constraints(cx, input, Certainty::overflow(false)) - } - TypingMode::Typeck { .. } - | TypingMode::PostTypeckUntilBorrowck { .. } - | TypingMode::Reflection - | TypingMode::PostBorrowck { .. } - | TypingMode::PostAnalysis - | TypingMode::Codegen - | TypingMode::ErasedNotCoherence(MayBeErased) => { - (Err(NoSolution), AccessedOpaques::default()) - } - }, + // See `tests/ui/traits/next-solver/cycles/unproductive-in-coherence.rs` and + // `tests/ui/traits/next-solver/overflow/recursive-self-normalization-simple.rs` + // for examples where this would matter. + // + // FIXME(-Znext-solver=coinductive): Long term, we probably do want to + // return `NoSolution` here. This should happen separately from the + // stabilization of the new solver. + PathKind::Inductive => unreachable!(), } } diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 294c887b5062c..32eecd4e44b38 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -5,7 +5,7 @@ use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::SolverTraitLangItem; use rustc_type_ir::solve::{ - AliasBoundKind, CandidatePreferenceMode, CanonicalResponse, MaybeInfo, + AliasBoundKind, CandidatePreferenceMode, CanonicalResponse, ExternalConstraintsData, MaybeInfo, NoSolutionOrRerunNonErased, OpaqueTypesJank, QueryResultOrRerunNonErased, RerunNonErased, RerunReason, RerunResultExt, SizedTraitKind, }; @@ -236,19 +236,11 @@ where // when merging candidates anyways. // // See tests/ui/impl-trait/auto-trait-leakage/avoid-query-cycle-via-item-bound.rs. - if let ty::Alias(is_rigid, ty::AliasTy { kind: ty::Opaque { def_id }, .. }) = + if let ty::Alias(is_rigid, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) = goal.predicate.self_ty().kind() { debug_assert!(is_rigid == ty::IsRigid::Yes); - - for item_bound in cx.item_self_bounds(def_id.into()).skip_binder() { - if item_bound - .as_trait_clause() - .is_some_and(|b| b.def_id() == goal.predicate.def_id()) - { - return Err(NoSolution.into()); - } - } + return ecx.consider_auto_trait_candidate_for_opaque_ty(goal, def_id, args); } // We need to make sure to stall any coroutines we are inferring to avoid query cycles. @@ -911,47 +903,50 @@ where if goal.predicate.polarity != ty::ClausePolarity::Positive { return Err(NoSolution.into()); } - if let ty::Adt(def, args) = goal.predicate.self_ty().kind() - && let Some(FieldInfo { base, ty, .. }) = - def.field_representing_type_info(ecx.cx(), args) - && { - let sized_trait = ecx.cx().require_trait_lang_item(SolverTraitLangItem::Sized); - // FIXME: add better support for builtin impls of traits that check for the bounds - // on the trait definition in std. - - // NOTE: these bounds have to be kept in sync with the definition of the `Field` - // trait in `library/core/src/field.rs` as well as the old trait solver `fn - // assemble_candidates_for_field_trait` in - // `compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs`. - ecx.add_goal( - GoalSource::ImplWhereBound, - Goal { - param_env: goal.param_env, - predicate: TraitRef::new(ecx.cx(), sized_trait, [base]).upcast(ecx.cx()), - }, - )?; - ecx.add_goal( - GoalSource::ImplWhereBound, - Goal { - param_env: goal.param_env, - predicate: TraitRef::new(ecx.cx(), sized_trait, [ty]).upcast(ecx.cx()), - }, - )?; - // FIXME(field_projections): This function does some questionable incomplete stuff by - // returning `Err(NoSolution)` on ambiguity. - ecx.try_evaluate_added_goals()? == Certainty::Yes - } - && match base.kind() { - ty::Adt(def, _) => def.is_struct() && !def.is_packed(), - ty::Tuple(..) => true, - _ => false, - } - { - ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc) - .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) - } else { - Err(NoSolution.into()) + + let ty::Adt(def, args) = goal.predicate.self_ty().kind() else { + return Err(NoSolution.into()); + }; + + let Some(FieldInfo { base, ty, .. }) = def.field_representing_type_info(ecx.cx(), args) + else { + return Err(NoSolution.into()); + }; + + match base.kind() { + ty::Adt(def, _) if def.is_struct() && !def.is_packed() => {} + ty::Adt(..) => return Err(NoSolution.into()), + ty::Tuple(..) => {} + _ => unreachable!(), } + + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { + let sized_trait = ecx.cx().require_trait_lang_item(SolverTraitLangItem::Sized); + + // FIXME: add better support for builtin impls of traits that check for the bounds + // on the trait definition in std. + // + // NOTE: these bounds have to be kept in sync with the definition of the `Field` + // trait in `library/core/src/field.rs` as well as the old trait solver `fn + // assemble_candidates_for_field_trait` in + // `compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs`. + ecx.add_goal( + GoalSource::ImplWhereBound, + Goal { + param_env: goal.param_env, + predicate: TraitRef::new(ecx.cx(), sized_trait, [base]).upcast(ecx.cx()), + }, + )?; + ecx.add_goal( + GoalSource::ImplWhereBound, + Goal { + param_env: goal.param_env, + predicate: TraitRef::new(ecx.cx(), sized_trait, [ty]).upcast(ecx.cx()), + }, + )?; + + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) } } @@ -1096,7 +1091,8 @@ where .auto_traits() .into_iter() .chain(a_data.principal_def_id().into_iter().flat_map(|principal_def_id| { - elaborate::supertrait_def_ids(self.cx(), principal_def_id) + self.cx() + .supertrait_def_ids(principal_def_id) .filter(|def_id| self.cx().trait_is_auto(*def_id)) })) .collect(); @@ -1288,6 +1284,77 @@ where .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) } + fn consider_auto_trait_candidate_for_opaque_ty( + &mut self, + goal: Goal>, + def_id: I::OpaqueTyId, + args: I::GenericArgs, + ) -> Result, NoSolutionOrRerunNonErased> { + let cx = self.cx(); + let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc); + + for item_bound in cx.item_self_bounds(def_id.into()).skip_binder() { + if item_bound.as_trait_clause().is_some_and(|b| b.def_id() == goal.predicate.def_id()) { + return Err(NoSolution.into()); + } + } + + let candidate = self.probe_trait_candidate(source).enter(|ecx| { + let hidden_ty = cx.type_of(def_id.into()).instantiate(cx, args).skip_norm_wip(); + ecx.add_goal( + GoalSource::ImplWhereBound, + goal.with(cx, goal.predicate.with_replaced_self_ty(cx, hidden_ty)), + )?; + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + })?; + + // Proving an auto trait for the hidden type must not constrain inference + // variables, as that would leak the hidden type itself. + if !candidate.result.value.var_values.is_identity_modulo_regions() { + return self.forced_ambiguity(MaybeInfo::AMBIGUOUS); + } + + let ExternalConstraintsData { + region_constraints: _, + ref opaque_types, + ref normalization_nested_goals, + } = *candidate.result.value.external_constraints; + debug_assert!(normalization_nested_goals.is_empty()); + + // New defining uses may leak an opaque's hidden type into the caller's + // inference state. This is safe after typeck, where hidden types are already + // fixed and only regions are inferred, but not during typeck while hidden + // types may still contain inference variables. + if !opaque_types.is_empty() { + let typing_mode = self.typing_mode(); + + match typing_mode { + // We're inferring regions of opaque types, but + // the type itself is already fully known, no way + // to leak the hidden type. + TypingMode::PostTypeckUntilBorrowck { .. } => {} + // We're inferring the hidden type of opaques, could + // leak types through it. + TypingMode::Typeck { .. } => { + return self.forced_ambiguity(MaybeInfo::AMBIGUOUS); + } + // we never add new uses to the opaque type storage + TypingMode::Coherence + | TypingMode::PostBorrowck { .. } + | TypingMode::Reflection + | TypingMode::PostAnalysis + | TypingMode::Codegen + | TypingMode::ErasedNotCoherence(MayBeErased) => { + unreachable!( + "we never add new uses to opaque types in typing mode {typing_mode:?}" + ); + } + } + } + + Ok(candidate) + } + // Return `Some` if there is an impl (built-in or user provided) that may // hold for the self type of the goal, which for coherence and soundness // purposes must disqualify the built-in auto impl assembled by considering @@ -1492,19 +1559,77 @@ where } } + /// Like `try_merge_candidates`, but returns `None` if there are multiple user-written + /// impls, or a user-written impl and a builtin impl, in which case we bail with ambiguity. + /// + /// Candidates with equal responses can still come from conflicting impls, ex - two + /// overlapping impls which both apply due to impossible where-clauses. We don't want + /// to merge these. Multiple builtin impls are still merged as usual. This is not + /// done for marker traits, as their impls are allowed to overlap. + #[instrument(level = "trace", skip(self), ret)] + fn try_merge_impl_candidates( + &mut self, + trait_def_id: I::TraitId, + candidates: &[Candidate], + ) -> Option> { + let is_marker = self.cx().trait_is_marker(trait_def_id); + let all_builtin = candidates + .iter() + .all(|candidate| matches!(candidate.source, CandidateSource::BuiltinImpl(_))); + + if is_marker || all_builtin { + self.try_merge_candidates(candidates).map(|(response, _)| response) + } else if candidates.len() > 1 { + None + } else { + candidates.first().map(|candidate| candidate.result) + } + } + + fn merge_candidates_or_bail_with_ambiguity( + &mut self, + candidates: &[Candidate], + proven_via: TraitGoalProvenVia, + ) -> (CanonicalResponse, Option) { + if let Some((response, _)) = self.try_merge_candidates(candidates) { + (response, Some(proven_via)) + } else { + (self.bail_with_ambiguity(candidates), None) + } + } + + fn merge_impl_candidates_or_bail_with_ambiguity( + &mut self, + trait_def_id: I::TraitId, + candidates: &[Candidate], + ) -> (CanonicalResponse, Option) { + debug_assert!(candidates.iter().all(|c| matches!( + c.source, + CandidateSource::Impl(_) | CandidateSource::BuiltinImpl(_) + ))); + match self.try_merge_impl_candidates(trait_def_id, candidates) { + Some(response) => (response, Some(TraitGoalProvenVia::Misc)), + None => (self.bail_with_ambiguity(candidates), None), + } + } + #[instrument(level = "debug", skip(self), ret)] pub(super) fn merge_trait_candidates( &mut self, - candidate_preference_mode: CandidatePreferenceMode, + trait_def_id: I::TraitId, mut candidates: Vec>, failed_candidate_info: FailedCandidateInfo, ) -> Result<(CanonicalResponse, Option), NoSolution> { + if candidates.is_empty() { + return Err(NoSolution); + } + + let candidate_preference_mode = CandidatePreferenceMode::compute(self.cx(), trait_def_id); if self.typing_mode().is_coherence() { - return if let Some((response, _)) = self.try_merge_candidates(&candidates) { - Ok((response, Some(TraitGoalProvenVia::Misc))) - } else { - self.flounder(&candidates).map(|r| (r, None)) - }; + return Ok(match self.try_merge_impl_candidates(trait_def_id, &candidates) { + Some(response) => (response, Some(TraitGoalProvenVia::Misc)), + None => (self.bail_with_ambiguity(&candidates), None), + }); } // We prefer trivial builtin candidates, i.e. builtin impls without any @@ -1531,11 +1656,10 @@ where let alias_bounds: Vec<_> = candidates .extract_if(.., |c| matches!(c.source, CandidateSource::AliasBound(..))) .collect(); - return if let Some((response, _)) = self.try_merge_candidates(&alias_bounds) { - Ok((response, Some(TraitGoalProvenVia::AliasBound))) - } else { - Ok((self.bail_with_ambiguity(&alias_bounds), None)) - }; + return Ok(self.merge_candidates_or_bail_with_ambiguity( + &alias_bounds, + TraitGoalProvenVia::AliasBound, + )); } // If there are non-global where-bounds, prefer where-bounds @@ -1587,11 +1711,10 @@ where let alias_bounds: Vec<_> = candidates .extract_if(.., |c| matches!(c.source, CandidateSource::AliasBound(_))) .collect(); - return if let Some((response, _)) = self.try_merge_candidates(&alias_bounds) { - Ok((response, Some(TraitGoalProvenVia::AliasBound))) - } else { - Ok((self.bail_with_ambiguity(&alias_bounds), None)) - }; + return Ok(self.merge_candidates_or_bail_with_ambiguity( + &alias_bounds, + TraitGoalProvenVia::AliasBound, + )); } self.filter_specialized_impls(AllowInferenceConstraints::No, &mut candidates); @@ -1601,21 +1724,16 @@ where // is still reported as being proven-via the param-env so that rigid projections // operate correctly. Otherwise, drop all global where-bounds before merging the // remaining candidates. - let proven_via = if candidates + let only_global_where_bounds = candidates .iter() - .all(|c| matches!(c.source, CandidateSource::ParamEnv(ParamEnvSource::Global))) - { - TraitGoalProvenVia::ParamEnv + .all(|c| matches!(c.source, CandidateSource::ParamEnv(ParamEnvSource::Global))); + if only_global_where_bounds { + Ok(self + .merge_candidates_or_bail_with_ambiguity(&candidates, TraitGoalProvenVia::ParamEnv)) } else { candidates .retain(|c| !matches!(c.source, CandidateSource::ParamEnv(ParamEnvSource::Global))); - TraitGoalProvenVia::Misc - }; - - if let Some((response, _)) = self.try_merge_candidates(&candidates) { - Ok((response, Some(proven_via))) - } else { - self.flounder(&candidates).map(|r| (r, None)) + Ok(self.merge_impl_candidates_or_bail_with_ambiguity(trait_def_id, &candidates)) } } @@ -1627,9 +1745,7 @@ where { let (candidates, failed_candidate_info) = self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All)?; - let candidate_preference_mode = - CandidatePreferenceMode::compute(self.cx(), goal.predicate.def_id()); - self.merge_trait_candidates(candidate_preference_mode, candidates, failed_candidate_info) + self.merge_trait_candidates(goal.predicate.def_id(), candidates, failed_candidate_info) .map_err(Into::into) } diff --git a/compiler/rustc_parse/src/diagnostics.rs b/compiler/rustc_parse/src/diagnostics.rs index 829fc5a600e8a..54d91eca01327 100644 --- a/compiler/rustc_parse/src/diagnostics.rs +++ b/compiler/rustc_parse/src/diagnostics.rs @@ -257,7 +257,7 @@ pub(crate) enum InvalidComparisonOperatorSub { pub(crate) struct InvalidLogicalOperator { #[primary_span] pub span: Span, - pub incorrect: String, + pub incorrect: Symbol, #[subdiagnostic] pub sub: InvalidLogicalOperatorSub, } @@ -797,7 +797,7 @@ pub(crate) struct EqFieldInit { #[derive(Diagnostic)] #[diag("unexpected token: `...`")] -pub(crate) struct DotDotDot { +pub(crate) struct DotDotDotExprOp { #[primary_span] #[suggestion( "use `..` for an exclusive range", @@ -816,7 +816,7 @@ pub(crate) struct DotDotDot { #[derive(Diagnostic)] #[diag("unexpected token: `<-`")] -pub(crate) struct LeftArrowOperator { +pub(crate) struct LArrowExprOp { #[primary_span] #[suggestion( "if you meant to write a comparison against a negative value, add a space in between `<` and `-`", @@ -1537,9 +1537,9 @@ pub(crate) struct ExpectedIdentifier { pub help_cannot_start_number: Option, } -impl<'a, G> Diagnostic<'a, G> for ExpectedIdentifier { +impl<'a> Diagnostic<'a> for ExpectedIdentifier { #[track_caller] - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let token_descr = TokenDescription::from_token(&self.token); let mut add_token = true; @@ -1603,9 +1603,9 @@ pub(crate) struct ExpectedSemi { pub sugg: ExpectedSemiSugg, } -impl<'a, G> Diagnostic<'a, G> for ExpectedSemi { +impl<'a> Diagnostic<'a> for ExpectedSemi { #[track_caller] - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a> { let token_descr = TokenDescription::from_token(&self.token); let mut add_token = true; @@ -1656,8 +1656,8 @@ pub(crate) enum ExpectedSemiSugg { style = "short" )] ChangeToSemi(#[primary_span] Span), - #[suggestion("add `;` here", code = ";", applicability = "machine-applicable", style = "short")] - AddSemi(#[primary_span] Span), + #[suggestion("add `;` here", code = ";", style = "short")] + AddSemi(#[primary_span] Span, #[applicability] Applicability), } #[derive(Diagnostic)] @@ -2010,7 +2010,7 @@ pub(crate) struct FnTraitMissingParen { } impl Subdiagnostic for FnTraitMissingParen { - fn add_to_diag(self, diag: &mut Diag<'_, G>) { + fn add_to_diag(self, diag: &mut Diag<'_>) { diag.span_label(self.span, msg!("`Fn` bounds require arguments in parentheses")); diag.span_suggestion_short( self.span.shrink_to_hi(), @@ -2441,6 +2441,23 @@ pub(crate) struct AssociatedStaticItemNotAllowed { pub span: Span, } +#[derive(Subdiagnostic)] +pub(crate) enum FieldNotAllowedInTraitSugg { + #[help("consider using a method instead: `fn {$ident}(&self) -> {$ty};`")] + Method { ident: String, ty: String }, + #[note("`self` can only appear as a method receiver; consider `fn method(self: {$ty})`")] + SelfReceiver { ty: String }, +} + +#[derive(Diagnostic)] +#[diag("fields are not allowed in trait definitions")] +pub(crate) struct FieldNotAllowedInTrait { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub sugg: FieldNotAllowedInTraitSugg, +} + #[derive(Diagnostic)] #[diag("crate name using dashes are not valid in `extern crate` statements")] pub(crate) struct ExternCrateNameWithDashes { @@ -3697,7 +3714,7 @@ pub(crate) struct UseDerefMacro { } impl Subdiagnostic for UseDerefMacro { - fn add_to_diag(self, diag: &mut Diag<'_, G>) { + fn add_to_diag(self, diag: &mut Diag<'_>) { let Self { field, before, after } = self; let mut parts = Vec::new(); @@ -4355,7 +4372,7 @@ pub(crate) struct HiddenUnicodeCodepointsDiagLabels { } impl Subdiagnostic for HiddenUnicodeCodepointsDiagLabels { - fn add_to_diag(self, diag: &mut Diag<'_, G>) { + fn add_to_diag(self, diag: &mut Diag<'_>) { for (c, span) in self.spans { diag.span_label(span, format!("{c:?}")); } @@ -4369,7 +4386,7 @@ pub(crate) enum HiddenUnicodeCodepointsDiagSub { // Used because of multiple multipart_suggestion and note impl Subdiagnostic for HiddenUnicodeCodepointsDiagSub { - fn add_to_diag(self, diag: &mut Diag<'_, G>) { + fn add_to_diag(self, diag: &mut Diag<'_>) { match self { HiddenUnicodeCodepointsDiagSub::Escape { spans } => { diag.multipart_suggestion_with_style( @@ -4651,3 +4668,15 @@ pub(crate) struct SuggestIntroduceTypeParameter { pub span: Span, pub parameters: String, } + +#[derive(Subdiagnostic)] +#[suggestion( + "you might have meant to write a diverging block on a refutable `let` statement by using `let-else` + for more information, visit ", + code = " else ", + applicability = "maybe-incorrect" +)] +pub(crate) struct MissingElseInLet { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 2afb55b02e2e6..03d5634fc359c 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -1,6 +1,6 @@ use diagnostics::make_errors_for_mismatched_closing_delims; use rustc_ast::ast::{self, AttrStyle}; -use rustc_ast::token::{self, CommentKind, Delimiter, IdentIsRaw, Token, TokenKind}; +use rustc_ast::token::{self, CommentKind, Delimiter, IdentKind, Token, TokenKind}; use rustc_ast::tokenstream::TokenStream; use rustc_ast::util::unicode::{TEXT_FLOW_CONTROL_CHARS, contains_text_flow_control_chars}; use rustc_errors::codes::*; @@ -238,7 +238,34 @@ impl<'psess, 'src> Lexer<'psess, 'src> { self.dcx().emit_err(crate::diagnostics::CannotBeRawIdent { span, ident: sym }); } self.psess.raw_identifier_spans.push(span); - token::Ident(sym, IdentIsRaw::Yes) + token::Ident(sym, IdentKind::Raw) + } + rustc_lexer::TokenKind::ForcedKeywordIdent => { + let span = self.mk_sp(start, self.pos); + + if span.edition().at_least_rust_2021() { + let sym = nfc_normalize(self.str_from(start + BytePos(2))); + self.psess.symbol_gallery.insert(sym, span); + self.psess.gated_spans.gate(sym::forced_keywords, span); + token::Ident(sym, IdentKind::ForcedKeyword) + } else { + // Reset the state so that only the `k` was consumed. + self.pos = start + BytePos(1); + self.cursor = Cursor::new(&str_before[1..], FrontmatterAllowed::No); + + self.psess.buffer_lint( + RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, + span, + ast::CRATE_NODE_ID, + crate::diagnostics::ReservedPrefixLint { + subject: "this".into(), + kind: "forced keyword", + edition: Edition::Edition2021, + sugg: self.mk_sp(start, self.pos).shrink_to_hi(), + } + ); + self.ident(start) + } } rustc_lexer::TokenKind::UnknownPrefix => { self.report_unknown_prefix(start); @@ -252,7 +279,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { let lifetime_name = self.str_from(start); self.last_lifetime = Some(self.mk_sp(start, start + BytePos(1))); let ident = Symbol::intern(lifetime_name); - token::Lifetime(ident, IdentIsRaw::No) + token::Lifetime(ident, IdentKind::Normal) } rustc_lexer::TokenKind::InvalidIdent // Do not recover an identifier with emoji if the codepoint is a confusable @@ -270,7 +297,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { .entry(sym) .or_default() .push(span); - token::Ident(sym, IdentIsRaw::No) + token::Ident(sym, IdentKind::Normal) } // split up (raw) c string literals to an ident and a string literal when edition < // 2021. @@ -337,7 +364,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { .with_span(span) .stash(span, StashKey::LifetimeIsChar); } - token::Lifetime(lifetime_name, IdentIsRaw::No) + token::Lifetime(lifetime_name, IdentKind::Normal) } rustc_lexer::TokenKind::RawLifetime => { self.last_lifetime = Some(self.mk_sp(start, start + BytePos(1))); @@ -387,7 +414,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { // Make sure we mark this as a raw identifier. self.psess.raw_identifier_spans.push(span); - token::Lifetime(sym, IdentIsRaw::Yes) + token::Lifetime(sym, IdentKind::Raw) } else { // Reset the state so we just lex the `'r`. self.pos = start + BytePos(2); @@ -407,7 +434,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { ); let lifetime_name = nfc_normalize(self.str_from(start)); - token::Lifetime(lifetime_name, IdentIsRaw::No) + token::Lifetime(lifetime_name, IdentKind::Normal) } } rustc_lexer::TokenKind::Semi => token::Semi, @@ -497,7 +524,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { let sym = nfc_normalize(self.str_from(start)); let span = self.mk_sp(start, self.pos); self.psess.symbol_gallery.insert(sym, span); - token::Ident(sym, IdentIsRaw::No) + token::Ident(sym, IdentKind::Normal) } /// Detect usages of Unicode codepoints changing the direction of the text on screen and loudly @@ -970,7 +997,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { escaped_char(bad_char) ), ) - .emit() + .emit_fatal() } fn report_unterminated_raw_string( @@ -1004,7 +1031,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { ); } - err.emit() + err.emit_fatal() } fn report_unterminated_block_comment(&self, start: BytePos, doc_style: Option) -> ! { @@ -1050,7 +1077,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { ); } - err.emit(); + err.emit_fatal(); } // RFC 3101 introduced the idea of (reserved) prefixes. As of Rust 2021, diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs index 9176192c95640..f01caf8d9efed 100644 --- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs +++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs @@ -177,7 +177,7 @@ pub(crate) fn emit_unescape_error( foreign_escape_suggestion(&mut diag, (&ec, span), err_span); } - diag.emit() + diag.emit_err() } EscapeError::TooShortHexEscape => dcx.emit_err(UnescapeError::TooShortHexEscape(err_span)), EscapeError::InvalidCharInHexEscape | EscapeError::InvalidCharInUnicodeEscape => { @@ -227,7 +227,7 @@ pub(crate) fn emit_unescape_error( Applicability::MaybeIncorrect, ); } - err.emit() + err.emit_err() } EscapeError::OutOfRangeHexEscape => { let mut err = dcx.struct_span_err(err_span, "out of range hex escape"); @@ -247,7 +247,7 @@ pub(crate) fn emit_unescape_error( )); } - err.emit() + err.emit_err() } EscapeError::LeadingUnderscoreUnicodeEscape => { let (c, span) = last_char(); diff --git a/compiler/rustc_parse/src/lexer/unicode_chars.rs b/compiler/rustc_parse/src/lexer/unicode_chars.rs index 826d193634a45..a934bb62d6a9c 100644 --- a/compiler/rustc_parse/src/lexer/unicode_chars.rs +++ b/compiler/rustc_parse/src/lexer/unicode_chars.rs @@ -307,7 +307,7 @@ pub(super) static UNICODE_ARRAY: &[(char, &str, &str)] = &[ // fancier error recovery to it, as there will be less overall work to do this way. const ASCII_ARRAY: &[(&str, &str, Option)] = &[ (" ", "Space", None), - ("_", "Underscore", Some(token::Ident(kw::Underscore, token::IdentIsRaw::No))), + ("_", "Underscore", Some(token::Ident(kw::Underscore, token::IdentKind::Normal))), ("-", "Minus/Hyphen", Some(token::Minus)), (",", "Comma", Some(token::Comma)), (";", "Semicolon", Some(token::Semi)), diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 16a438b5387ee..edd280743e437 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -1,8 +1,8 @@ //! The main parser interface. // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(debug_closure_helpers))] #![cfg_attr(test, feature(iter_order_by))] -#![feature(debug_closure_helpers)] #![feature(default_field_values)] #![feature(deref_patterns)] #![feature(iter_intersperse)] @@ -160,16 +160,16 @@ pub fn new_parser_from_file<'a>( if let Some(sp) = sp { err.span(sp); } - err.emit() + err.emit_fatal() }); new_parser_from_source_file(psess, source_file, strip_tokens) } -pub fn utf8_error( +pub fn utf8_error( sm: &SourceMap, path: &str, sp: Option, - err: &mut Diag<'_, G>, + err: &mut Diag<'_>, utf8err: Utf8Error, contents: &[u8], ) { diff --git a/compiler/rustc_parse/src/parser/asm.rs b/compiler/rustc_parse/src/parser/asm.rs index 5c177af086565..92b0b96fab165 100644 --- a/compiler/rustc_parse/src/parser/asm.rs +++ b/compiler/rustc_parse/src/parser/asm.rs @@ -1,7 +1,7 @@ use rustc_ast::{self as ast, AsmMacro}; use rustc_span::{Span, Symbol, kw}; -use super::{ExpKeywordPair, ForceCollect, IdentIsRaw, Trailing, UsePreAttrPos}; +use super::{ExpKeywordPair, ForceCollect, IdentKind, Trailing, UsePreAttrPos}; use crate::{PResult, Parser, diagnostics, exp, token}; /// An argument to one of the `asm!` macros. The argument is syntactically valid, but is otherwise @@ -368,7 +368,7 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>) -> PResult<'a, Vec<(Symbol, Span)>> fn parse_reg<'a>(p: &mut Parser<'a>) -> PResult<'a, ast::InlineAsmRegOrRegClass> { p.expect(exp!(OpenParen))?; let result = match p.token.uninterpolate().kind { - token::Ident(name, IdentIsRaw::No) => ast::InlineAsmRegOrRegClass::RegClass(name), + token::Ident(name, IdentKind::Normal) => ast::InlineAsmRegOrRegClass::RegClass(name), token::Literal(token::Lit { kind: token::LitKind::Str, symbol, suffix: _ }) => { ast::InlineAsmRegOrRegClass::Reg(symbol) } diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 40dbda2466de4..14ea328601a02 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -1,9 +1,9 @@ use std::mem::take; use std::ops::{Deref, DerefMut}; -use ast::token::IdentIsRaw; +use ast::token::IdentKind; use rustc_ast::token::{self, Lit, LitKind, Token, TokenKind}; -use rustc_ast::util::parser::AssocOp; +use rustc_ast::util::parser::{AssocOp, ExprPrecedence}; use rustc_ast::{ self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode, Block, BlockCheckMode, Expr, ExprKind, GenericArg, GenericArgs, Generics, Item, ItemKind, @@ -141,64 +141,6 @@ impl AttemptLocalParseRecovery { } } -/// Information for emitting suggestions and recovering from -/// C-style `i++`, `--i`, etc. -#[derive(Debug, Copy, Clone)] -struct IncDecRecovery { - /// Is this increment/decrement its own statement? - standalone: IsStandalone, - /// Is this an increment or decrement? - op: IncOrDec, - /// Is this pre- or postfix? - fixity: UnaryFixity, -} - -/// Is an increment or decrement expression its own statement? -#[derive(Debug, Copy, Clone)] -enum IsStandalone { - /// It's standalone, i.e., its own statement. - Standalone, - /// It's a subexpression, i.e., *not* standalone. - Subexpr, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -enum IncOrDec { - Inc, - Dec, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -enum UnaryFixity { - Pre, - Post, -} - -impl IncOrDec { - fn chr(&self) -> char { - match self { - Self::Inc => '+', - Self::Dec => '-', - } - } - - fn name(&self) -> &'static str { - match self { - Self::Inc => "increment", - Self::Dec => "decrement", - } - } -} - -impl std::fmt::Display for UnaryFixity { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Pre => write!(f, "prefix"), - Self::Post => write!(f, "postfix"), - } - } -} - /// Checks if the given `lookup` identifier is similar to any keyword symbol in `candidates`. /// /// This is a specialized version of [`Symbol::find_similar`] that constructs an error when a @@ -211,22 +153,6 @@ fn find_similar_kw(lookup: Ident, candidates: &[Symbol]) -> Option }) } -struct MultiSugg { - msg: String, - patches: Vec<(Span, String)>, - applicability: Applicability, -} - -impl MultiSugg { - fn emit(self, err: &mut Diag<'_>) { - err.multipart_suggestion(self.msg, self.patches, self.applicability); - } - - fn emit_verbose(self, err: &mut Diag<'_>) { - err.multipart_suggestion(self.msg, self.patches, self.applicability); - } -} - /// SnapshotParser is used to create a snapshot of the parser /// without causing duplicate errors being emitted when the `Parser` /// is dropped. @@ -274,7 +200,7 @@ impl<'a> Parser<'a> { pub(super) fn expected_ident_found( &mut self, recover: bool, - ) -> PResult<'a, (Ident, IdentIsRaw)> { + ) -> PResult<'a, (Ident, IdentKind)> { let valid_follow = &[ TokenKind::Eq, TokenKind::Colon, @@ -302,11 +228,11 @@ impl<'a> Parser<'a> { let bad_token = self.token; // suggest prepending a keyword in identifier position with `r#` - let suggest_raw = if let Some((ident, IdentIsRaw::No)) = self.token.ident() + let suggest_raw = if let Some((ident, IdentKind::Normal)) = self.token.ident() && ident.is_raw_guess() && self.look_ahead(1, |t| valid_follow.contains(&t.kind)) { - recovered_ident = Some((ident, IdentIsRaw::Yes)); + recovered_ident = Some((ident, IdentKind::Raw)); // `Symbol::to_string()` is different from `Symbol::into_diag_arg()`, // which uses `Symbol::to_ident_string()` and "helpfully" adds an implicit `r#` @@ -317,22 +243,23 @@ impl<'a> Parser<'a> { None }; - let suggest_remove_comma = - if self.token == token::Comma && self.look_ahead(1, |t| t.is_ident()) { - if recover { - self.bump(); - recovered_ident = self.ident_or_err(false).ok(); - }; - - Some(SuggRemoveComma { span: bad_token.span }) - } else { - None + let suggest_remove_comma = if self.token == token::Comma + && let Some(ident) = self.look_ahead(1, Token::ident) + { + if recover { + self.bump(); + recovered_ident = Some(ident); }; + Some(SuggRemoveComma { span: bad_token.span }) + } else { + None + }; + let help_cannot_start_number = self.is_lit_bad_ident().map(|(len, valid_portion)| { let (invalid, valid) = self.token.span.split_at(len as u32); - recovered_ident = Some((Ident::new(valid_portion, valid), IdentIsRaw::No)); + recovered_ident = Some((Ident::new(valid_portion, valid), IdentKind::Normal)); HelpIdentifierStartsWithNumber { num_span: invalid } }); @@ -351,7 +278,7 @@ impl<'a> Parser<'a> { if self.token == token::Lt { // Let's check if the previous token could denote the start of an item // whose kind can have generics. - if let Some((Ident { name, .. }, IdentIsRaw::No)) = self.prev_token.ident() + if let Some(Ident { name, .. }) = self.prev_token.non_raw_ident() && let kw::Fn | kw::Type | kw::Struct | kw::Enum | kw::Union | kw::Trait = name { match self.parse_generics() { @@ -513,7 +440,7 @@ impl<'a> Parser<'a> { span, token: self.token, unexpected_token_label: Some(self.token.span), - sugg: ExpectedSemiSugg::AddSemi(span), + sugg: ExpectedSemiSugg::AddSemi(span, Applicability::MachineApplicable), }); return Ok(guar); } @@ -582,7 +509,7 @@ impl<'a> Parser<'a> { ); } - if let Some((ident, IdentIsRaw::No)) = self.prev_token.ident() + if let Some(ident) = self.prev_token.non_raw_ident() && let "def" | "fun" | "func" | "function" = ident.name.as_str() { err.span_suggestion_short( @@ -616,9 +543,9 @@ impl<'a> Parser<'a> { // positive for a `cr#` that wasn't intended to start a c-string literal, but identifying // that in the parser requires unbounded lookahead, so we only add a hint to the existing // error rather than replacing it entirely. - if ((self.prev_token == TokenKind::Ident(sym::character('c'), IdentIsRaw::No) + if ((self.prev_token == TokenKind::Ident(sym::character('c'), IdentKind::Normal) && matches!(&self.token.kind, TokenKind::Literal(token::Lit { kind: token::Str, .. }))) - || (self.prev_token == TokenKind::Ident(sym::cr, IdentIsRaw::No) + || (self.prev_token == TokenKind::Ident(sym::cr, IdentKind::Normal) && matches!( &self.token.kind, TokenKind::Literal(token::Lit { kind: token::Str, .. }) | token::Pound @@ -683,7 +610,7 @@ impl<'a> Parser<'a> { if self.check_too_many_raw_str_terminators(&mut err) { if expected.contains(&TokenType::Semi) && self.eat(exp!(Semi)) { - let guar = err.emit(); + let guar = err.emit_err(); return Ok(guar); } else { return Err(err); @@ -797,7 +724,8 @@ impl<'a> Parser<'a> { span, token: self.token, unexpected_token_label: Some(self.token.span), - sugg: ExpectedSemiSugg::AddSemi(span), + // A semicolon may discard the intended return value of a cfg-gated tail expression. + sugg: ExpectedSemiSugg::AddSemi(span, Applicability::MaybeIncorrect), }); let attr_span = match &expr.attrs[..] { [] => unreachable!(), @@ -829,7 +757,7 @@ impl<'a> Parser<'a> { (expr.span.shrink_to_lo(), "{ ".to_string()), (expr.span.shrink_to_hi(), " }".to_string()), ], - Applicability::MachineApplicable, + Applicability::MaybeIncorrect, ); // Special handling for `#[cfg(...)]` chains @@ -844,7 +772,7 @@ impl<'a> Parser<'a> { Ok(next_attr) => next_attr, Err(inner_err) => { inner_err.cancel(); - return err.emit(); + return err.emit_err(); } } && let ast::AttrKind::Normal(next_attr_kind) = next_attr.kind @@ -856,7 +784,7 @@ impl<'a> Parser<'a> { Ok(next_expr) => next_expr, Err(inner_err) => { inner_err.cancel(); - return err.emit(); + return err.emit_err(); } }; // We have for sure @@ -885,12 +813,13 @@ impl<'a> Parser<'a> { "it seems like you are trying to provide different expressions depending on \ `cfg`, consider using `if cfg!(..)`", sugg, - Applicability::MachineApplicable, + // Unlike `#[cfg]`, `if cfg!` still checks disabled branches. + Applicability::MaybeIncorrect, ); } } - err.emit() + err.emit_err() } fn check_too_many_raw_str_terminators(&mut self, err: &mut Diag<'_>) -> bool { @@ -1015,7 +944,7 @@ impl<'a> Parser<'a> { ], Applicability::MaybeIncorrect, ); - let guar = err.emit(); + let guar = err.emit_err(); self.eat_to_tokens(&[exp!(CloseBrace)]); guar } @@ -1032,7 +961,7 @@ impl<'a> Parser<'a> { ], Applicability::MaybeIncorrect, ); - err.emit() + err.emit_err() } _ if token.kind != token::OpenBrace => { // We don't have a heuristic to correctly identify where the block @@ -1249,7 +1178,7 @@ impl<'a> Parser<'a> { // The subsequent expression is valid. Mark // `expr` as erroneous and emit `e` now, but // return `Ok` so parsing can continue. - let guar = e.emit(); + let guar = e.emit_err(); *expr = self.mk_expr_err(expr.span.to(self.prev_token.span), guar); return Ok(guar); } @@ -1633,8 +1562,8 @@ impl<'a> Parser<'a> { self.bump(); // `+` let _bounds = self.parse_generic_bounds()?; let sub = match &ty.kind { - TyKind::Ref(_lifetime, mut_ty) => { - let lo = mut_ty.ty.span.shrink_to_lo(); + TyKind::Ref(_lifetime, inner_ty, _) => { + let lo = inner_ty.span.shrink_to_lo(); let hi = self.prev_token.span.shrink_to_hi(); BadTypePlusSub::AddParen { suggestion: AddParen { lo, hi } } } @@ -1649,146 +1578,6 @@ impl<'a> Parser<'a> { Ok(()) } - pub(super) fn recover_from_prefix_increment( - &mut self, - operand_expr: Box, - op_span: Span, - start_stmt: bool, - ) -> PResult<'a, Box> { - let standalone = if start_stmt { IsStandalone::Standalone } else { IsStandalone::Subexpr }; - let kind = IncDecRecovery { standalone, op: IncOrDec::Inc, fixity: UnaryFixity::Pre }; - self.recover_from_inc_dec(operand_expr, kind, op_span) - } - - pub(super) fn recover_from_postfix_increment( - &mut self, - operand_expr: Box, - op_span: Span, - start_stmt: bool, - ) -> PResult<'a, Box> { - let kind = IncDecRecovery { - standalone: if start_stmt { IsStandalone::Standalone } else { IsStandalone::Subexpr }, - op: IncOrDec::Inc, - fixity: UnaryFixity::Post, - }; - self.recover_from_inc_dec(operand_expr, kind, op_span) - } - - pub(super) fn recover_from_postfix_decrement( - &mut self, - operand_expr: Box, - op_span: Span, - start_stmt: bool, - ) -> PResult<'a, Box> { - let kind = IncDecRecovery { - standalone: if start_stmt { IsStandalone::Standalone } else { IsStandalone::Subexpr }, - op: IncOrDec::Dec, - fixity: UnaryFixity::Post, - }; - self.recover_from_inc_dec(operand_expr, kind, op_span) - } - - fn recover_from_inc_dec( - &mut self, - base: Box, - kind: IncDecRecovery, - op_span: Span, - ) -> PResult<'a, Box> { - let mut err = self.dcx().struct_span_err( - op_span, - format!("Rust has no {} {} operator", kind.fixity, kind.op.name()), - ); - err.span_label(op_span, format!("not a valid {} operator", kind.fixity)); - - let help_base_case = |mut err: Diag<'_, ErrorGuaranteed>, base| { - err.help(format!("use `{}= 1` instead", kind.op.chr())); - err.emit(); - Ok(base) - }; - - // (pre, post) - let spans = match kind.fixity { - UnaryFixity::Pre => (op_span, base.span.shrink_to_hi()), - UnaryFixity::Post => (base.span.shrink_to_lo(), op_span), - }; - - match kind.standalone { - IsStandalone::Standalone => { - self.inc_dec_standalone_suggest(kind, spans).emit_verbose(&mut err) - } - IsStandalone::Subexpr => { - let Ok(base_src) = self.span_to_snippet(base.span) else { - return help_base_case(err, base); - }; - match kind.fixity { - UnaryFixity::Pre => { - self.prefix_inc_dec_suggest(base_src, kind, spans).emit(&mut err) - } - UnaryFixity::Post => { - // won't suggest since we can not handle the precedences - // for example: `a + b++` has been parsed (a + b)++ and we can not suggest here - if !matches!(base.kind, ExprKind::Binary(_, _, _)) { - self.postfix_inc_dec_suggest(base_src, kind, spans).emit(&mut err) - } - } - } - } - } - Err(err) - } - - fn prefix_inc_dec_suggest( - &mut self, - base_src: String, - kind: IncDecRecovery, - (pre_span, post_span): (Span, Span), - ) -> MultiSugg { - MultiSugg { - msg: format!("use `{}= 1` instead", kind.op.chr()), - patches: vec![ - (pre_span, "{ ".to_string()), - (post_span, format!(" {}= 1; {} }}", kind.op.chr(), base_src)), - ], - applicability: Applicability::MachineApplicable, - } - } - - fn postfix_inc_dec_suggest( - &mut self, - base_src: String, - kind: IncDecRecovery, - (pre_span, post_span): (Span, Span), - ) -> MultiSugg { - let tmp_var = if base_src.trim() == "tmp" { "tmp_" } else { "tmp" }; - MultiSugg { - msg: format!("use `{}= 1` instead", kind.op.chr()), - patches: vec![ - (pre_span, format!("{{ let {tmp_var} = ")), - (post_span, format!("; {} {}= 1; {} }}", base_src, kind.op.chr(), tmp_var)), - ], - applicability: Applicability::HasPlaceholders, - } - } - - fn inc_dec_standalone_suggest( - &mut self, - kind: IncDecRecovery, - (pre_span, post_span): (Span, Span), - ) -> MultiSugg { - let mut patches = Vec::new(); - - if !pre_span.is_empty() { - patches.push((pre_span, String::new())); - } - - patches.push((post_span, format!(" {}= 1", kind.op.chr()))); - MultiSugg { - msg: format!("use `{}= 1` instead", kind.op.chr()), - patches, - applicability: Applicability::MachineApplicable, - } - } - /// Tries to recover from associated item paths like `[T]::AssocItem` / `(T, U)::AssocItem`. /// Attempts to convert the base expression/pattern/type into a type, parses the `::AssocItem` /// tail, and combines them into a `::AssocItem` expression/pattern/type. @@ -1933,26 +1722,35 @@ impl<'a> Parser<'a> { &mut self, await_sp: Span, ) -> PResult<'a, Box> { - let (hi, expr, is_question) = if self.token == token::Bang { + let (hi, expr_span, is_question) = if self.token == token::Bang { // Handle `await!()`. self.recover_await_macro()? } else { self.recover_await_prefix(await_sp)? }; - let (sp, guar) = self.error_on_incorrect_await(await_sp, hi, &expr, is_question); + let (sp, guar) = self.error_on_incorrect_await(await_sp, hi, expr_span, is_question); let expr = self.mk_expr_err(await_sp.to(sp), guar); self.maybe_recover_from_bad_qpath(expr) } - fn recover_await_macro(&mut self) -> PResult<'a, (Span, Box, bool)> { + fn recover_await_macro(&mut self) -> PResult<'a, (Span, Span, bool)> { self.expect(exp!(Bang))?; self.expect(exp!(OpenParen))?; + let open = self.prev_token.span; let expr = self.parse_expr()?; self.expect(exp!(CloseParen))?; - Ok((self.prev_token.span, expr, false)) + let close = self.prev_token.span; + // Keep parentheses when needed for `.await`, e.g. `(&mut future).await`. + // Use the delimiters to preserve any comments around the operand. + let expr_span = if expr.precedence() < ExprPrecedence::Unambiguous { + open.to(close) + } else { + open.shrink_to_hi().to(close.shrink_to_lo()) + }; + Ok((close, expr_span, false)) } - fn recover_await_prefix(&mut self, await_sp: Span) -> PResult<'a, (Span, Box, bool)> { + fn recover_await_prefix(&mut self, await_sp: Span) -> PResult<'a, (Span, Span, bool)> { let is_question = self.eat(exp!(Question)); // Handle `await? `. let expr = if self.token == token::OpenBrace { // Handle `await { }`. @@ -1966,22 +1764,22 @@ impl<'a> Parser<'a> { err.span_label(await_sp, format!("while parsing this incorrect await expression")); err })?; - Ok((expr.span, expr, is_question)) + Ok((expr.span, expr.span, is_question)) } fn error_on_incorrect_await( &self, lo: Span, hi: Span, - expr: &Expr, + expr_span: Span, is_question: bool, ) -> (Span, ErrorGuaranteed) { let span = lo.to(hi); let guar = self.dcx().emit_err(IncorrectAwait { span, suggestion: AwaitSuggestion { - removal: lo.until(expr.span), - dot_await: expr.span.shrink_to_hi(), + removal: lo.until(expr_span), + dot_await: expr_span.shrink_to_hi().to(hi.shrink_to_hi()), question_mark: if is_question { "?" } else { "" }, }, }); @@ -2048,7 +1846,7 @@ impl<'a> Parser<'a> { "r#", Applicability::MachineApplicable, ); - let guar = err.emit(); + let guar = err.emit_err(); Ok(self.mk_expr_err(lo.to(hi), guar)) } else { Err(self.expected_expression_found()) // The user isn't trying to invoke the try! macro @@ -2095,7 +1893,7 @@ impl<'a> Parser<'a> { lo: Span, err: Diag<'a>, ) -> Box { - let guar = err.emit(); + let guar = err.emit_err(); // Recover from parse error, callers expect the closing delim to be consumed. self.consume_block(open, close, ConsumeClosingDelim::Yes); self.mk_expr(lo.to(self.prev_token.span), ExprKind::Err(guar)) @@ -2354,16 +2152,32 @@ impl<'a> Parser<'a> { let pat = self.parse_pat_no_top_alt(Some(Expected::ArgumentName), None)?; self.expect(exp!(Colon))?; let ty = self.parse_ty()?; - self.dcx().emit_err(PatternMethodParamWithoutBody { - span: pat.span, - target: match context { - FnContext::Trait => "methods without bodies", - FnContext::FunctionPtrType => "function pointer types", - FnContext::ParenthesizedArgumentList => "parenthesized argument list", - FnContext::Free => unreachable!("This method is not called in free functions, as patterns are always allowed there"), - FnContext::Impl => unreachable!("This method is not called in impls, as patterns are always allowed there"), - }, - }); + match context { + FnContext::Trait + | FnContext::FunctionPtrType + | FnContext::ParenthesizedArgumentList => { + self.dcx().emit_err(PatternMethodParamWithoutBody { + span: pat.span, + target: if context == FnContext::Trait { + "methods without bodies" + } else if context == FnContext::FunctionPtrType { + "function pointer types" + } else { + "parenthesized argument list" + }, + }); + } + FnContext::Free | FnContext::Impl => { + self.dcx().span_delayed_bug( + pat.span, + if context == FnContext::Free { + "This method is not called in free functions, as patterns are always allowed there" + } else { + "This method is not called in impls, as patterns are always allowed there" + }, + ); + } + } // Pretend the pattern is `_`, to avoid duplicate errors from AST validation. let pat = Box::new(Pat { kind: PatKind::Wild, span: pat.span, id: ast::DUMMY_NODE_ID }); @@ -2422,7 +2236,7 @@ impl<'a> Parser<'a> { }; let mut err = self.dcx().struct_span_err(span, msg); let sp = self.psess.source_map().start_point(self.token.span); - if let Some(sp) = self.psess.ambiguous_block_expr_parse.borrow().get(&sp) { + if let Some(sp) = self.psess.complete_stmt_exprs_before_bin_op_lookalike.borrow().get(&sp) { err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp)); } err.span_label(span, "expected expression"); @@ -2473,6 +2287,44 @@ impl<'a> Parser<'a> { } } + /// Handle encountering a lifetime in a generic argument list that is not + /// followed by a `,` or `>`. + /// We emit an error and check if the lifetime is followed by a type + /// in, e.g. `Foo<'a T>`. + /// In this case, it is likely the user meant either `Foo<&'a T>` or `Foo<'a, T>`. + /// We give both suggestions, then emit an error. Note that we do not try to recover, + /// since we cannot know which of the two suggestions is correct and emitting either + /// of the two types could cause confusing errors further on. + pub(super) fn handle_lifetime_arg_preceding_type(&mut self, span: Span) -> PResult<'a, ()> { + let snapshot = self.create_snapshot_for_diagnostic(); + let (mutbl, ty) = self.parse_ref_ty_no_leading_ampersand(); + self.restore_snapshot(snapshot); + if ty.is_none() { + // The lifetime is not followed by a type, do nothing. + return Ok(()); + } + // If we find `'a mut T`, suggesting to add a comma is wrong. + let suggest_comma = mutbl.is_not(); + // Add `>` to the list of expected tokens. + self.check(exp!(Gt)); + let mut err = self.unexpected().unwrap_err(); + err.span_suggestion_verbose( + span.shrink_to_lo(), + "you might have meant to write a reference type here", + "&", + Applicability::MaybeIncorrect, + ); + if suggest_comma { + err.span_suggestion_verbose( + span.shrink_to_hi(), + "use a comma to separate type parameters", + ",", + Applicability::MaybeIncorrect, + ); + } + Err(err) + } + /// Handle encountering a symbol in a generic argument list that is not a `,` or `>`. In this /// case, we emit an error and try to suggest enclosing a const argument in braces if it looks /// like the user has forgotten them. @@ -2624,7 +2476,7 @@ impl<'a> Parser<'a> { "=", Applicability::MaybeIncorrect, ); - let guar = err.emit(); + let guar = err.emit_err(); let value = self.mk_expr_err(start.to(expr.span), guar); return Ok(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value })); } else if snapshot.token == token::Colon @@ -2638,7 +2490,7 @@ impl<'a> Parser<'a> { "::", Applicability::MaybeIncorrect, ); - let guar = err.emit(); + let guar = err.emit_err(); return Ok(GenericArg::Type( self.mk_ty(start.to(expr.span), TyKind::Err(guar)), )); @@ -2690,7 +2542,7 @@ impl<'a> Parser<'a> { vec![(span.shrink_to_lo(), "{ ".to_string()), (span.shrink_to_hi(), " }".to_string())], Applicability::MaybeIncorrect, ); - let guar = err.emit(); + let guar = err.emit_err(); let value = self.mk_expr_err(span, guar); GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value }) } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 8238a6518e41d..bf99bbe26f087 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -4,7 +4,7 @@ use core::mem; use core::ops::{Bound, ControlFlow}; use ast::mut_visit::{self, MutVisitor}; -use ast::token::IdentIsRaw; +use ast::token::IdentKind; use ast::{ForLoopKind, MatchKind, Pat, Path, PathSegment, Recovered}; use rustc_ast::token::{self, Delimiter, InvisibleOrigin, MetaVarKind, Token, TokenKind}; use rustc_ast::util::case::Case; @@ -34,8 +34,9 @@ use super::{ AttrWrapper, BlockMode, ClosureSpans, ExpTokenPair, ForceCollect, Parser, PathStyle, Restrictions, SemiColonMode, SeqSep, TokenType, Trailing, UsePreAttrPos, }; -use crate::diagnostics::ExprParenthesesNeeded; -use crate::{diagnostics, exp, maybe_recover_from_interpolated_ty_qpath}; +use crate::{exp, maybe_recover_from_interpolated_ty_qpath}; + +pub(super) mod diagnostics; #[derive(Debug)] pub(super) enum DestructuredFloat { @@ -56,7 +57,11 @@ impl<'a> Parser<'a> { self.current_closure.take(); self.parse_expr_res(Restrictions::empty()) } - + #[inline] + pub fn parse_expr_in_let(&mut self) -> PResult<'a, Box> { + self.current_closure.take(); + self.parse_expr_res(Restrictions::IN_LET) + } /// Parses an expression, forcing tokens to be collected. pub fn parse_expr_force_collect(&mut self) -> PResult<'a, Box> { self.current_closure.take(); @@ -144,14 +149,13 @@ impl<'a> Parser<'a> { mut lhs: Box, ) -> PResult<'a, (Box, bool)> { let mut parsed_something = false; - if !self.should_continue_as_assoc_expr(&lhs) { + if self.expr_is_complete(&lhs) && !self.recover_from_bin_op_after_complete_stmt_expr(&lhs) { return Ok((lhs, parsed_something)); } self.expected_token_types.insert(TokenType::Operator); while let Some(op) = self.check_assoc_op() { let lhs_span = self.interpolated_or_expr_span(&lhs); - let cur_op_span = self.token.span; let restrictions = if op.node.is_assign_like() { self.restrictions & Restrictions::NO_STRUCT_LITERAL } else { @@ -165,236 +169,94 @@ impl<'a> Parser<'a> { } { break; } - // Check for deprecated `...` syntax - if self.token == token::DotDotDot && op.node == AssocOp::Range(RangeLimits::Closed) { - self.err_dotdotdot_syntax(self.token.span); - } - if self.token == token::LArrow { - self.err_larrow_operator(self.token.span); - } + self.reject_dotdotdot_expr_op(); + self.reject_larrow_expr_op(); parsed_something = true; self.bump(); - if op.node.is_comparison() { - if let Some(expr) = self.check_no_chained_comparison(&lhs, &op)? { - return Ok((expr, parsed_something)); - } - } - - // Look for JS' `===` and `!==` and recover - if let AssocOp::Binary(bop @ BinOpKind::Eq | bop @ BinOpKind::Ne) = op.node - && self.token == token::Eq - && self.prev_token.span.hi() == self.token.span.lo() - { - let sp = op.span.to(self.token.span); - let sugg = bop.as_str().into(); - let invalid = format!("{sugg}="); - self.dcx().emit_err(diagnostics::InvalidComparisonOperator { - span: sp, - invalid: invalid.clone(), - sub: diagnostics::InvalidComparisonOperatorSub::Correctable { - span: sp, - invalid, - correct: sugg, - }, - }); - self.bump(); - } - - // Look for PHP's `<>` and recover - if op.node == AssocOp::Binary(BinOpKind::Lt) - && self.token == token::Gt - && self.prev_token.span.hi() == self.token.span.lo() - { - let sp = op.span.to(self.token.span); - self.dcx().emit_err(diagnostics::InvalidComparisonOperator { - span: sp, - invalid: "<>".into(), - sub: diagnostics::InvalidComparisonOperatorSub::Correctable { - span: sp, - invalid: "<>".into(), - correct: "!=".into(), - }, - }); - self.bump(); - } - - // Look for C++'s `<=>` and recover - if op.node == AssocOp::Binary(BinOpKind::Le) - && self.token == token::Gt - && self.prev_token.span.hi() == self.token.span.lo() - { - let sp = op.span.to(self.token.span); - self.dcx().emit_err(diagnostics::InvalidComparisonOperator { - span: sp, - invalid: "<=>".into(), - sub: diagnostics::InvalidComparisonOperatorSub::Spaceship(sp), - }); - self.bump(); - } - - if self.prev_token == token::Plus - && self.token == token::Plus - && self.prev_token.span.between(self.token.span).is_empty() - { - let op_span = self.prev_token.span.to(self.token.span); - // Eat the second `+` - self.bump(); - lhs = self.recover_from_postfix_increment(lhs, op_span, starts_stmt)?; - continue; - } - if self.prev_token == token::Minus - && self.token == token::Minus - && self.prev_token.span.between(self.token.span).is_empty() - && !self.look_ahead(1, |tok| tok.can_begin_expr()) + if op.node.is_comparison() + && let Some(expr) = self.check_no_chained_comparison(&lhs, &op)? { - let op_span = self.prev_token.span.to(self.token.span); - // Eat the second `-` - self.bump(); - lhs = self.recover_from_postfix_decrement(lhs, op_span, starts_stmt)?; - continue; + return Ok((expr, parsed_something)); } - let op_span = op.span; - let op = op.node; - // Special cases: - if op == AssocOp::Cast { - lhs = self.parse_assoc_op_cast(lhs, lhs_span, op_span, ExprKind::Cast)?; - continue; - } else if let AssocOp::Range(limits) = op { - // If we didn't have to handle `x..`/`x..=`, it would be pretty easy to - // generalise it to the Fixity::None code. - lhs = self.parse_expr_range(prec, lhs, limits, cur_op_span)?; - break; - } + self.recover_from_strict_eq_op(op); + self.recover_from_diamond_ne_op(); + self.recover_from_spaceship_cmp_op(); + self.recover_from_postfix_inc_op(&lhs, starts_stmt)?; + self.recover_from_postfix_dec_op(&lhs, starts_stmt)?; - let min_prec = match op.fixity() { + let min_prec = match op.node.fixity() { Fixity::Right => Bound::Included(prec), Fixity::Left | Fixity::None => Bound::Excluded(prec), }; - let rhs = self.with_res(restrictions - Restrictions::STMT_EXPR, |this| { - this.parse_expr_assoc(min_prec) - })?; - let span = self.mk_expr_sp(&lhs, lhs_span, op_span, rhs.span); - lhs = match op { + let finish_parsing_bin_op = |this: &mut Self| { + let rhs = this.with_res(restrictions - Restrictions::STMT_EXPR, |this| { + this.parse_expr_assoc(min_prec) + })?; + let span = this.mk_expr_sp(&lhs, lhs_span, op.span, rhs.span); + Ok((rhs, span)) + }; + + lhs = match op.node { AssocOp::Binary(ast_op) => { - let binary = self.mk_binary(respan(cur_op_span, ast_op), lhs, rhs); - self.mk_expr(span, binary) + let (rhs, span) = finish_parsing_bin_op(self)?; + self.mk_expr(span, self.mk_binary(respan(op.span, ast_op), lhs, rhs)) } - AssocOp::Assign => self.mk_expr(span, ExprKind::Assign(lhs, rhs, cur_op_span)), AssocOp::AssignOp(aop) => { - let aopexpr = self.mk_assign_op(respan(cur_op_span, aop), lhs, rhs); - self.mk_expr(span, aopexpr) + let (rhs, span) = finish_parsing_bin_op(self)?; + self.mk_expr(span, self.mk_assign_op(respan(op.span, aop), lhs, rhs)) } - AssocOp::Cast | AssocOp::Range(_) => { - self.dcx().span_bug(span, "AssocOp should have been handled by special case") + AssocOp::Assign => { + let (rhs, span) = finish_parsing_bin_op(self)?; + self.mk_expr(span, ExprKind::Assign(lhs, rhs, op.span)) } + AssocOp::Cast => self.parse_assoc_op_cast(lhs, lhs_span, op.span)?, + AssocOp::Range(limits) => self.parse_expr_range(min_prec, lhs, limits, op.span)?, }; - } - Ok((lhs, parsed_something)) - } - - fn should_continue_as_assoc_expr(&mut self, lhs: &Expr) -> bool { - match (self.expr_is_complete(lhs), AssocOp::from_token(&self.token)) { - // Semi-statement forms are odd: - // See https://github.com/rust-lang/rust/issues/29071 - (true, None) => false, - (false, _) => true, // Continue parsing the expression. - // An exhaustive check is done in the following block, but these are checked first - // because they *are* ambiguous but also reasonable looking incorrect syntax, so we - // want to keep their span info to improve diagnostics in these cases in a later stage. - (true, Some(AssocOp::Binary( - BinOpKind::Mul | // `{ 42 } *foo = bar;` or `{ 42 } * 3` - BinOpKind::Sub | // `{ 42 } -5` - BinOpKind::Add | // `{ 42 } + 42` (unary plus) - BinOpKind::And | // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }` - BinOpKind::Or | // `{ 42 } || 42` ("logical or" or closure) - BinOpKind::BitOr // `{ 42 } | 42` or `{ 42 } |x| 42` - ))) => { - // These cases are ambiguous and can't be identified in the parser alone. - // - // Bitwise AND is left out because guessing intent is hard. We can make - // suggestions based on the assumption that double-refs are rarely intentional, - // and closures are distinct enough that they don't get mixed up with their - // return value. - let sp = self.psess.source_map().start_point(self.token.span); - self.psess.ambiguous_block_expr_parse.borrow_mut().insert(sp, lhs.span); - false - } - (true, Some(op)) if !op.can_continue_expr_unambiguously() => false, - (true, Some(_)) => { - self.error_found_expr_would_be_stmt(lhs); - true + if let AssocOp::Range(_) = op.node { + break; } } - } - /// We've found an expression that would be parsed as a statement, - /// but the next token implies this should be parsed as an expression. - /// For example: `if let Some(x) = x { x } else { 0 } / 2`. - fn error_found_expr_would_be_stmt(&self, lhs: &Expr) { - self.dcx().emit_err(diagnostics::FoundExprWouldBeStmt { - span: self.token.span, - token: pprust::token_to_string(&self.token), - suggestion: ExprParenthesesNeeded::surrounding(lhs.span), - }); + Ok((lhs, parsed_something)) } /// Possibly translate the current token to an associative operator. /// The method does not advance the current token. - /// - /// Also performs recovery for `and` / `or` which are mistaken for `&&` and `||` respectively. pub(super) fn check_assoc_op(&self) -> Option> { - let (op, span) = match (AssocOp::from_token(&self.token), self.token.ident()) { - // When parsing const expressions, stop parsing when encountering `>`. - ( - Some( - AssocOp::Binary(BinOpKind::Shr | BinOpKind::Gt | BinOpKind::Ge) - | AssocOp::AssignOp(AssignOpKind::ShrAssign), - ), - _, - ) if self.restrictions.contains(Restrictions::CONST_EXPR) => { - return None; - } - // When recovering patterns as expressions, stop parsing when encountering an - // assignment `=`, an alternative `|`, or a range `..`. - ( - Some( - AssocOp::Assign - | AssocOp::AssignOp(_) - | AssocOp::Binary(BinOpKind::BitOr) - | AssocOp::Range(_), - ), - _, - ) if self.restrictions.contains(Restrictions::IS_PAT) => { - return None; - } - (Some(op), _) => (op, self.token.span), - (None, Some((Ident { name: sym::and, span }, IdentIsRaw::No))) - if self.may_recover() => - { - self.dcx().emit_err(diagnostics::InvalidLogicalOperator { - span: self.token.span, - incorrect: "and".into(), - sub: diagnostics::InvalidLogicalOperatorSub::Conjunction(self.token.span), - }); - (AssocOp::Binary(BinOpKind::And), span) - } - (None, Some((Ident { name: sym::or, span }, IdentIsRaw::No))) if self.may_recover() => { - self.dcx().emit_err(diagnostics::InvalidLogicalOperator { - span: self.token.span, - incorrect: "or".into(), - sub: diagnostics::InvalidLogicalOperatorSub::Disjunction(self.token.span), - }); - (AssocOp::Binary(BinOpKind::Or), span) - } - _ => return None, - }; - Some(respan(span, op)) + let op = AssocOp::from_token(&self.token); + + // When parsing const expressions, stop parsing when encountering `>`. + if self.restrictions.contains(Restrictions::CONST_EXPR) + && let Some(op) = op + && let AssocOp::Binary(BinOpKind::Shr | BinOpKind::Gt | BinOpKind::Ge) + | AssocOp::AssignOp(AssignOpKind::ShrAssign) = op + { + return None; + } + + // When recovering patterns as expressions, stop parsing when encountering an + // assignment `=`, an alternative `|`, or a range `..`. + if self.restrictions.contains(Restrictions::IS_PAT) + && let Some(op) = op + && let AssocOp::Assign + | AssocOp::AssignOp(_) + | AssocOp::Binary(BinOpKind::BitOr) + | AssocOp::Range(_) = op + { + return None; + } + + if let Some(op) = op { + return Some(respan(self.token.span, op)); + } + + self.recover_from_alpha_logic_op() } /// Checks if this expression is a successfully parsed statement. @@ -406,7 +268,7 @@ impl<'a> Parser<'a> { /// The other two variants are handled in `parse_prefix_range_expr` below. fn parse_expr_range( &mut self, - prec: ExprPrecedence, + min_prec: Bound, lhs: Box, limits: RangeLimits, cur_op_span: Span, @@ -414,7 +276,7 @@ impl<'a> Parser<'a> { let rhs = if self.is_at_start_of_range_notation_rhs() { let maybe_lt = self.token; Some( - self.parse_expr_assoc(Bound::Excluded(prec)) + self.parse_expr_assoc(min_prec) .map_err(|err| self.maybe_err_dotdotlt_syntax(maybe_lt, err))?, ) } else { @@ -441,14 +303,11 @@ impl<'a> Parser<'a> { /// Parses prefix-forms of range notation: `..expr`, `..`, `..=expr`. fn parse_expr_prefix_range(&mut self, attrs: AttrWrapper) -> PResult<'a, Box> { if !attrs.is_empty() { - let err = diagnostics::DotDotRangeAttribute { span: self.token.span }; + let err = crate::diagnostics::DotDotRangeAttribute { span: self.token.span }; self.dcx().emit_err(err); } - // Check for deprecated `...` syntax. - if self.token == token::DotDotDot { - self.err_dotdotdot_syntax(self.token.span); - } + self.reject_dotdotdot_expr_op(); debug_assert!( self.token.is_range_separator(), @@ -513,15 +372,18 @@ impl<'a> Parser<'a> { } // `+lit` token::Plus if this.look_ahead(1, |tok| tok.is_numeric_lit()) => { - let mut err = diagnostics::LeadingPlusNotSupported { + let mut err = crate::diagnostics::LeadingPlusNotSupported { span: lo, remove_plus: None, add_parentheses: None, }; // a block on the LHS might have been intended to be an expression instead - if let Some(sp) = this.psess.ambiguous_block_expr_parse.borrow().get(&lo) { - err.add_parentheses = Some(ExprParenthesesNeeded::surrounding(*sp)); + if let Some(sp) = + this.psess.complete_stmt_exprs_before_bin_op_lookalike.borrow().get(&lo) + { + err.add_parentheses = + Some(crate::diagnostics::ExprParenthesesNeeded::surrounding(*sp)); } else { err.remove_plus = Some(lo); } @@ -539,8 +401,14 @@ impl<'a> Parser<'a> { this.bump(); this.bump(); - let operand_expr = this.parse_expr_dot_or_call(attrs)?; - this.recover_from_prefix_increment(operand_expr, pre_span, starts_stmt) + let operand = this.parse_expr_dot_or_call(attrs)?; + return Err(this.report_inc_dec_op( + &operand, + starts_stmt, + diagnostics::IncOrDec::Inc, + diagnostics::UnaryFixity::Pre, + pre_span, + )); } token::Ident(..) if this.token.is_keyword(kw::Move) @@ -551,7 +419,7 @@ impl<'a> Parser<'a> { token::Ident(..) if this.may_recover() && this.is_mistaken_not_ident_negation() => { make_it!(this, attrs, |this, _| this.recover_not_expr(lo)) } - _ => return this.parse_expr_dot_or_call(attrs), + _ => this.parse_expr_dot_or_call(attrs), } } @@ -574,7 +442,7 @@ impl<'a> Parser<'a> { /// Recover on `~expr` in favor of `!expr`. fn recover_tilde_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { - self.dcx().emit_err(diagnostics::TildeAsUnaryOperator(lo)); + self.dcx().emit_err(crate::diagnostics::TildeAsUnaryOperator(lo)); self.parse_expr_unary(lo, UnOp::Not) } @@ -593,7 +461,7 @@ impl<'a> Parser<'a> { let token_cannot_continue_expr = |t: &Token| match t.uninterpolate().kind { // These tokens can start an expression after `!`, but // can't continue an expression after an ident - token::Ident(name, is_raw) => token::ident_can_begin_expr(name, t.span, is_raw), + token::Ident(name, kind) => token::ident_can_begin_expr(name, t.span, kind), token::Literal(..) | token::Pound => true, _ => t.is_metavar_expr(), }; @@ -605,14 +473,14 @@ impl<'a> Parser<'a> { let negated_token = self.look_ahead(1, |t| *t); let sub_diag = if negated_token.is_numeric_lit() { - diagnostics::NotAsNegationOperatorSub::SuggestNotBitwise + crate::diagnostics::NotAsNegationOperatorSub::SuggestNotBitwise } else if negated_token.is_bool_lit() { - diagnostics::NotAsNegationOperatorSub::SuggestNotLogical + crate::diagnostics::NotAsNegationOperatorSub::SuggestNotLogical } else { - diagnostics::NotAsNegationOperatorSub::SuggestNotDefault + crate::diagnostics::NotAsNegationOperatorSub::SuggestNotDefault }; - self.dcx().emit_err(diagnostics::NotAsNegationOperator { + self.dcx().emit_err(crate::diagnostics::NotAsNegationOperator { negated: negated_token.span, negated_desc: super::token_descr(&negated_token), // Span the `not` plus trailing whitespace to avoid @@ -645,17 +513,17 @@ impl<'a> Parser<'a> { lhs: Box, lhs_span: Span, op_span: Span, - expr_kind: fn(Box, Box) -> ExprKind, ) -> PResult<'a, Box> { - let mk_expr = |this: &mut Self, lhs: Box, rhs: Box| { - this.mk_expr(this.mk_expr_sp(&lhs, lhs_span, op_span, rhs.span), expr_kind(lhs, rhs)) + let mk_expr = |this: &mut Self, rhs: Box| { + let span = this.mk_expr_sp(&lhs, lhs_span, op_span, rhs.span); + this.mk_expr(span, ExprKind::Cast(lhs, rhs)) }; // Save the state of the parser before parsing type normally, in case there is a // LessThan comparison after this cast. let parser_snapshot_before_type = self.clone(); let cast_expr = match self.parse_as_cast_ty() { - Ok(rhs) => mk_expr(self, lhs, rhs), + Ok(rhs) => mk_expr(self, rhs), Err(type_err) => { if !self.may_recover() { return Err(type_err); @@ -666,66 +534,32 @@ impl<'a> Parser<'a> { // `usize < y` as a type with generic arguments. let parser_snapshot_after_type = mem::replace(self, parser_snapshot_before_type); - // Check for typo of `'a: loop { break 'a }` with a missing `'`. - match (&lhs.kind, &self.token.kind) { - ( - // `foo: ` - ExprKind::Path(None, ast::Path { segments, .. }), - token::Ident(kw::For | kw::Loop | kw::While, IdentIsRaw::No), - ) if let [segment] = segments.as_slice() => { - let snapshot = self.create_snapshot_for_diagnostic(); - let label = Label { - ident: Ident::from_str_and_span( - &format!("'{}", segment.ident), - segment.ident.span, - ), - }; - match self.parse_expr_labeled(label, false) { - Ok(expr) => { - type_err.cancel(); - self.dcx().emit_err(diagnostics::MalformedLoopLabel { - span: label.ident.span, - suggestion: label.ident.span.shrink_to_lo(), - }); - return Ok(expr); - } - Err(err) => { - err.cancel(); - self.restore_snapshot(snapshot); - } - } - } - _ => {} - } - match self.parse_path(PathStyle::Expr) { Ok(path) => { let span_after_type = parser_snapshot_after_type.token.span; - let expr = mk_expr( - self, - lhs, - self.mk_ty(path.span, TyKind::Path(None, path.clone())), - ); + let expr = + mk_expr(self, self.mk_ty(path.span, TyKind::Path(None, path.clone()))); let args_span = self.look_ahead(1, |t| t.span).to(span_after_type); match self.token.kind { - token::Lt => { - self.dcx().emit_err(diagnostics::ComparisonInterpretedAsGeneric { + token::Lt => self.dcx().emit_err( + crate::diagnostics::ComparisonInterpretedAsGeneric { comparison: self.token.span, r#type: pprust::path_to_string(&path), args: args_span, - suggestion: diagnostics::ComparisonInterpretedAsGenericSugg { - left: expr.span.shrink_to_lo(), - right: expr.span.shrink_to_hi(), - }, - }) - } + suggestion: + crate::diagnostics::ComparisonInterpretedAsGenericSugg { + left: expr.span.shrink_to_lo(), + right: expr.span.shrink_to_hi(), + }, + }, + ), token::Shl => { - self.dcx().emit_err(diagnostics::ShiftInterpretedAsGeneric { + self.dcx().emit_err(crate::diagnostics::ShiftInterpretedAsGeneric { shift: self.token.span, r#type: pprust::path_to_string(&path), args: args_span, - suggestion: diagnostics::ShiftInterpretedAsGenericSugg { + suggestion: crate::diagnostics::ShiftInterpretedAsGenericSugg { left: expr.span.shrink_to_lo(), right: expr.span.shrink_to_hi(), }, @@ -763,48 +597,38 @@ impl<'a> Parser<'a> { // written `((&x) as T)[0]`. let span = cast_expr.span; - let with_postfix = self.parse_expr_dot_or_call_with(AttrVec::new(), cast_expr, span)?; // Check if an illegal postfix operator has been added after the cast. // If the resulting expression is not a cast, it is an illegal postfix operator. if !matches!(with_postfix.kind, ExprKind::Cast(_, _)) { - let msg = format!( - "cast cannot be followed by {}", - match with_postfix.kind { - ExprKind::Index(..) => "indexing", - ExprKind::Try(_) => "`?`", - ExprKind::Field(_, _) => "a field access", - ExprKind::MethodCall(_) => "a method call", - ExprKind::Call(_, _) => "a function call", - ExprKind::Await(_, _) => "`.await`", - ExprKind::Use(_, _) => "`.use`", - ExprKind::Yield(YieldKind::Postfix(_)) => "`.yield`", - ExprKind::Match(_, _, MatchKind::Postfix) => "a postfix match", - ExprKind::Err(_) => return Ok(with_postfix), - _ => unreachable!( - "did not expect {:?} as an illegal postfix operator following cast", - with_postfix.kind - ), - } - ); - let mut err = self.dcx().struct_span_err(span, msg); - - let suggest_parens = |err: &mut Diag<'_>| { - let suggestions = vec![ - (span.shrink_to_lo(), "(".to_string()), - (span.shrink_to_hi(), ")".to_string()), - ]; - err.multipart_suggestion( + let kind = match with_postfix.kind { + ExprKind::Index(..) => "indexing", + ExprKind::Try(_) => "`?`", + ExprKind::Field(_, _) => "a field access", + ExprKind::MethodCall(_) => "a method call", + ExprKind::Call(_, _) => "a function call", + ExprKind::Await(_, _) => "`.await`", + ExprKind::Use(_, _) => "`.use`", + ExprKind::Yield(YieldKind::Postfix(_)) => "`.yield`", + ExprKind::Match(_, _, MatchKind::Postfix) => "a postfix match", + ExprKind::Err(_) => return Ok(with_postfix), + _ => unreachable!( + "did not expect {:?} as an illegal postfix operator following cast", + with_postfix.kind + ), + }; + self.dcx() + .struct_span_err(span, format!("cast cannot be followed by {kind}")) + .with_multipart_suggestion( "try surrounding the expression in parentheses", - suggestions, + vec![ + (span.shrink_to_lo(), "(".to_string()), + (span.shrink_to_hi(), ")".to_string()), + ], Applicability::MachineApplicable, - ); - }; - - suggest_parens(&mut err); - - err.emit(); + ) + .emit(); }; Ok(with_postfix) } @@ -835,8 +659,10 @@ impl<'a> Parser<'a> { } fn error_remove_borrow_lifetime(&self, span: Span, lt_span: Span) { - self.dcx() - .emit_err(diagnostics::LifetimeInBorrowExpression { span, lifetime_span: lt_span }); + self.dcx().emit_err(crate::diagnostics::LifetimeInBorrowExpression { + span, + lifetime_span: lt_span, + }); } /// Parse `mut?` or `[ raw | pin ] [ const | mut ]`. @@ -875,7 +701,7 @@ impl<'a> Parser<'a> { lo: Span, ) -> PResult<'a, Box> { let mut res = loop { - let has_question = if self.prev_token == TokenKind::Ident(kw::Return, IdentIsRaw::No) { + let has_question = if self.prev_token.is_keyword(kw::Return) { // We are using noexpect here because we don't expect a `?` directly after // a `return` which could be suggested otherwise. self.eat_noexpect(&token::Question) @@ -887,7 +713,7 @@ impl<'a> Parser<'a> { e = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Try(e)); continue; } - let has_dot = if self.prev_token == TokenKind::Ident(kw::Return, IdentIsRaw::No) { + let has_dot = if self.prev_token.is_keyword(kw::Return) { // We are using noexpect here because we don't expect a `.` directly after // a `return` which could be suggested otherwise. self.eat_noexpect(&token::Dot) @@ -895,7 +721,7 @@ impl<'a> Parser<'a> { // Recovery for `expr->suffix`. self.bump(); let span = self.prev_token.span; - self.dcx().emit_err(diagnostics::ExprRArrowCall { span }); + self.dcx().emit_err(crate::diagnostics::ExprRArrowCall { span }); true } else { self.eat(exp!(Dot)) @@ -957,7 +783,7 @@ impl<'a> Parser<'a> { // We end up with the `sym` (`1`) token in `self.prev_token` and a dot in // `self.token`. assert!(suffix.is_none()); - self.token = Token::new(token::Ident(sym, IdentIsRaw::No), ident_span); + self.token = Token::new(token::Ident(sym, IdentKind::Normal), ident_span); self.bump_with((Token::new(token::Dot, dot_span), self.token_spacing)); self.mk_expr_tuple_field_access(lo, ident_span, base, sym, None) } @@ -973,7 +799,7 @@ impl<'a> Parser<'a> { // the `sym2` (`2` or `2e3`) token in `self.prev_token` and the following // token in `self.token`. let next_token2 = - Token::new(token::Ident(sym2, IdentIsRaw::No), ident2_span); + Token::new(token::Ident(sym2, IdentKind::Normal), ident2_span); self.bump_with((next_token2, self.token_spacing)); self.bump(); let base1 = @@ -1018,7 +844,7 @@ impl<'a> Parser<'a> { } _ => (span, actual), }; - self.dcx().emit_err(diagnostics::UnexpectedTokenAfterDot { span, actual }); + self.dcx().emit_err(crate::diagnostics::UnexpectedTokenAfterDot { span, actual }); } /// We need an identifier or integer, but the next token is a float. @@ -1135,7 +961,7 @@ impl<'a> Parser<'a> { // Parse this both to give helpful error messages and to // verify it can be done with this parser setup. ExprKind::Index(ref left, ref _right, span) => { - self.dcx().emit_err(diagnostics::ArrayIndexInOffsetOf(span)); + self.dcx().emit_err(crate::diagnostics::ArrayIndexInOffsetOf(span)); current = left; } ExprKind::Lit(token::Lit { @@ -1144,10 +970,12 @@ impl<'a> Parser<'a> { suffix, }) => { if let Some(suffix) = suffix { - self.dcx().emit_err(diagnostics::InvalidLiteralSuffixOnTupleIndex { - span: current.span, - suffix, - }); + self.dcx().emit_err( + crate::diagnostics::InvalidLiteralSuffixOnTupleIndex { + span: current.span, + suffix, + }, + ); } match self.break_up_float(symbol, current.span) { // 1e2 @@ -1187,14 +1015,15 @@ impl<'a> Parser<'a> { fields.insert(start_idx, *ident) } _ => { - self.dcx().emit_err(diagnostics::InvalidOffsetOf(current.span)); + self.dcx() + .emit_err(crate::diagnostics::InvalidOffsetOf(current.span)); break; } } break; } _ => { - self.dcx().emit_err(diagnostics::InvalidOffsetOf(current.span)); + self.dcx().emit_err(crate::diagnostics::InvalidOffsetOf(current.span)); break; } } @@ -1204,12 +1033,12 @@ impl<'a> Parser<'a> { break; } else if trailing_dot.is_none() { // This loop should only repeat if there is a trailing dot. - self.dcx().emit_err(diagnostics::InvalidOffsetOf(self.token.span)); + self.dcx().emit_err(crate::diagnostics::InvalidOffsetOf(self.token.span)); break; } } if let Some(dot) = trailing_dot { - self.dcx().emit_err(diagnostics::InvalidOffsetOf(dot)); + self.dcx().emit_err(crate::diagnostics::InvalidOffsetOf(dot)); } Ok(fields.into_iter().collect()) } @@ -1223,7 +1052,7 @@ impl<'a> Parser<'a> { suffix: Option, ) -> Box { if let Some(suffix) = suffix { - self.dcx().emit_err(diagnostics::InvalidLiteralSuffixOnTupleIndex { + self.dcx().emit_err(crate::diagnostics::InvalidLiteralSuffixOnTupleIndex { span: ident_span, suffix, }); @@ -1246,7 +1075,7 @@ impl<'a> Parser<'a> { Err(err) if self.is_expected_raw_ref_mut() && self.token_cursor.depth() == call_depth => { - let guar = err.emit(); + let guar = err.emit_err(); // Preserve the call expression so later passes can still diagnose the callee, // while treating the malformed `&raw ` argument as an error expression. let args = self.recover_raw_ref_call_args(guar); @@ -1309,25 +1138,23 @@ impl<'a> Parser<'a> { { err.cancel(); let type_str = pprust::path_to_string(&path); - self.dcx() - .create_err(diagnostics::ParenthesesWithStructFields { - span, - braces_for_struct: diagnostics::BracesForStructLiteral { - first: open_paren, - second: close_paren, - r#type: type_str.clone(), - }, - no_fields_for_fn: diagnostics::NoFieldsForFnCall { - r#type: type_str, - fields: fields - .into_iter() - .map(|field| field.span.until(field.expr.span)) - .collect(), - }, - }) - .emit() + self.dcx().emit_err(crate::diagnostics::ParenthesesWithStructFields { + span, + braces_for_struct: crate::diagnostics::BracesForStructLiteral { + first: open_paren, + second: close_paren, + r#type: type_str.clone(), + }, + no_fields_for_fn: crate::diagnostics::NoFieldsForFnCall { + r#type: type_str, + fields: fields + .into_iter() + .map(|field| field.span.until(field.expr.span)) + .collect(), + }, + }) } else { - err.emit() + err.emit_err() }; Ok(self.mk_expr_err(span, guar)) } @@ -1419,7 +1246,7 @@ impl<'a> Parser<'a> { if let Some(args) = seg.args { // See `StashKey::GenericInFieldExpr` for more info on why we stash this. self.dcx() - .create_err(diagnostics::FieldExpressionWithGeneric(args.span())) + .create_err(crate::diagnostics::FieldExpressionWithGeneric(args.span())) .stash(seg.ident.span, StashKey::GenericInFieldExpr); } @@ -1485,13 +1312,20 @@ impl<'a> Parser<'a> { if let Some(expr) = this.maybe_recover_bad_struct_literal_path(false)? { return Ok(expr); } + if let Some(arr) = this.recover_from_c_array(lo) { + return Ok(arr); + } this.parse_expr_block(None, lo, BlockCheckMode::Default) } else if this.check(exp!(Or)) || this.check(exp!(OrOr)) { this.parse_expr_closure().map_err(|mut err| { // If the input is something like `if a { 1 } else { 2 } | if a { 3 } else { 4 }` // then suggest parens around the lhs. - if let Some(sp) = this.psess.ambiguous_block_expr_parse.borrow().get(&lo) { - err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp)); + if let Some(sp) = + this.psess.complete_stmt_exprs_before_bin_op_lookalike.borrow().get(&lo) + { + err.subdiagnostic(crate::diagnostics::ExprParenthesesNeeded::surrounding( + *sp, + )); } err }) @@ -1570,6 +1404,7 @@ impl<'a> Parser<'a> { // or `async gen {}` and `async gen move {}` // FIXME: (async) gen closures aren't yet parsed. // FIXME(gen_blocks): Parse `gen async` and suggest swap + // FIXME(forced_keywords): Allow k#gen blocks prior to Rust 2024, too! if this.token_uninterpolated_span().at_least_rust_2024() && this.is_gen_block(kw::Gen, at_async as usize) { @@ -1689,7 +1524,8 @@ impl<'a> Parser<'a> { let (span, kind) = if self.eat(exp!(Bang)) { // MACRO INVOCATION expression if qself.is_some() { - self.dcx().emit_err(diagnostics::MacroInvocationWithQualifiedPath(path.span)); + self.dcx() + .emit_err(crate::diagnostics::MacroInvocationWithQualifiedPath(path.span)); } let lo = path.span; let mac = Box::new(MacCall { path, args: self.parse_delim_args()? }); @@ -1734,7 +1570,7 @@ impl<'a> Parser<'a> { { let (lit, _) = self.recover_unclosed_char(label_.ident, Parser::mk_token_lit_char, |self_| { - self_.dcx().create_err(diagnostics::UnexpectedTokenAfterLabel { + self_.dcx().create_err(crate::diagnostics::UnexpectedTokenAfterLabel { span: self_.token.span, remove_label: None, enclose_in_block: None, @@ -1746,7 +1582,7 @@ impl<'a> Parser<'a> { && (self.check_noexpect(&TokenKind::Comma) || self.check_noexpect(&TokenKind::Gt)) { // We're probably inside of a `Path<'a>` that needs a turbofish - let guar = self.dcx().emit_err(diagnostics::UnexpectedTokenAfterLabel { + let guar = self.dcx().emit_err(crate::diagnostics::UnexpectedTokenAfterLabel { span: self.token.span, remove_label: None, enclose_in_block: None, @@ -1754,7 +1590,7 @@ impl<'a> Parser<'a> { consume_colon = false; Ok(self.mk_expr_err(lo, guar)) } else { - let mut err = diagnostics::UnexpectedTokenAfterLabel { + let mut err = crate::diagnostics::UnexpectedTokenAfterLabel { span: self.token.span, remove_label: None, enclose_in_block: None, @@ -1791,7 +1627,7 @@ impl<'a> Parser<'a> { return expr; } - err.enclose_in_block = Some(diagnostics::UnexpectedTokenAfterLabelSugg { + err.enclose_in_block = Some(crate::diagnostics::UnexpectedTokenAfterLabelSugg { left: span.shrink_to_lo(), right: span.shrink_to_hi(), }); @@ -1807,7 +1643,7 @@ impl<'a> Parser<'a> { }?; if !ate_colon && consume_colon { - self.dcx().emit_err(diagnostics::RequireColonAfterLabeledExpression { + self.dcx().emit_err(crate::diagnostics::RequireColonAfterLabeledExpression { span: expr.span, label: lo, label_end: lo.between(tok_sp), @@ -1842,7 +1678,7 @@ impl<'a> Parser<'a> { "'", Applicability::MaybeIncorrect, ) - .emit() + .emit_err() }); let name = ident.without_first_quote().name; mk_lit_char(name, ident.span) @@ -1856,7 +1692,7 @@ impl<'a> Parser<'a> { self.bump(); // `catch` let span = lo.to(self.prev_token.span); - self.dcx().emit_err(diagnostics::DoCatchSyntaxRemoved { span }); + self.dcx().emit_err(crate::diagnostics::DoCatchSyntaxRemoved { span }); self.parse_try_block(lo) } @@ -1916,9 +1752,9 @@ impl<'a> Parser<'a> { // The value expression can be a labeled loop, see issue #86948, e.g.: // `loop { break 'label: loop { break 'label 42; }; }` let lexpr = self.parse_expr_labeled(label, true)?; - self.dcx().emit_err(diagnostics::LabeledLoopInBreak { + self.dcx().emit_err(crate::diagnostics::LabeledLoopInBreak { span: lexpr.span, - sub: diagnostics::WrapInParentheses::Expression { + sub: crate::diagnostics::WrapInParentheses::Expression { left: lexpr.span.shrink_to_lo(), right: lexpr.span.shrink_to_hi(), }, @@ -1945,8 +1781,8 @@ impl<'a> Parser<'a> { BREAK_WITH_LABEL_AND_LOOP, lo.to(expr.span), ast::CRATE_NODE_ID, - diagnostics::BreakWithLabelAndLoop { - sub: diagnostics::BreakWithLabelAndLoopSub { + crate::diagnostics::BreakWithLabelAndLoop { + sub: crate::diagnostics::BreakWithLabelAndLoopSub { left: span.shrink_to_lo(), right: span.shrink_to_hi(), }, @@ -2027,9 +1863,10 @@ impl<'a> Parser<'a> { self.bump(); // `builtin` self.bump(); // `#` - let Some((ident, IdentIsRaw::No)) = self.token.ident() else { - let err = - self.dcx().create_err(diagnostics::ExpectedBuiltinIdent { span: self.token.span }); + let Some((ident, IdentKind::Normal)) = self.token.ident() else { + let err = self + .dcx() + .create_err(crate::diagnostics::ExpectedBuiltinIdent { span: self.token.span }); return Err(err); }; self.psess.gated_spans.gate(sym::builtin_syntax, ident.span); @@ -2039,7 +1876,7 @@ impl<'a> Parser<'a> { let ret = if let Some(res) = parse(self, lo, ident)? { Ok(res) } else { - let err = self.dcx().create_err(diagnostics::UnknownBuiltinConstruct { + let err = self.dcx().create_err(crate::diagnostics::UnknownBuiltinConstruct { span: lo.to(ident.span), name: ident, }); @@ -2141,7 +1978,7 @@ impl<'a> Parser<'a> { }; // On an error path, eagerly consider a lifetime to be an unclosed character lit, if that // makes sense. - if let Some((ident, IdentIsRaw::No)) = self.token.lifetime() + if let Some((ident, IdentKind::Normal)) = self.token.lifetime() && could_be_unclosed_char_literal(ident) { let lt = self.expect_lifetime(); @@ -2188,7 +2025,7 @@ impl<'a> Parser<'a> { } }); if let Some(recovered) = recovered { - self.dcx().emit_err(diagnostics::FloatLiteralRequiresIntegerPart { + self.dcx().emit_err(crate::diagnostics::FloatLiteralRequiresIntegerPart { span: recovered.span, suggestion: recovered.span.shrink_to_lo(), }); @@ -2213,7 +2050,9 @@ impl<'a> Parser<'a> { } }; match self.token.uninterpolate().kind { - token::Ident(name, IdentIsRaw::No) if name.is_bool_lit() => { + token::Ident(name, IdentKind::Normal | IdentKind::ForcedKeyword) + if name.is_bool_lit() => + { self.bump(); Some(token::Lit::new(token::Bool, name, None)) } @@ -2307,39 +2146,6 @@ impl<'a> Parser<'a> { } } - fn is_array_like_block(&mut self) -> bool { - self.token.kind == TokenKind::OpenBrace - && self - .look_ahead(1, |t| matches!(t.kind, TokenKind::Ident(..) | TokenKind::Literal(_))) - && self.look_ahead(2, |t| t == &token::Comma) - && self.look_ahead(3, |t| t.can_begin_expr()) - } - - /// Emits a suggestion if it looks like the user meant an array but - /// accidentally used braces, causing the code to be interpreted as a block - /// expression. - fn maybe_suggest_brackets_instead_of_braces(&mut self, lo: Span) -> Option> { - let mut snapshot = self.create_snapshot_for_diagnostic(); - match snapshot.parse_expr_array_or_repeat(exp!(CloseBrace)) { - Ok(arr) => { - let guar = self.dcx().emit_err(diagnostics::ArrayBracketsInsteadOfBraces { - span: arr.span, - sub: diagnostics::ArrayBracketsInsteadOfBracesSugg { - left: lo, - right: snapshot.prev_token.span, - }, - }); - - self.restore_snapshot(snapshot); - Some(self.mk_expr_err(arr.span, guar)) - } - Err(e) => { - e.cancel(); - None - } - } - } - fn suggest_missing_semicolon_before_array( &self, prev_span: Span, @@ -2370,7 +2176,7 @@ impl<'a> Parser<'a> { .span_to_snippet(snapshot.token.span) .is_ok_and(|snippet| snippet == "]") => { - return Err(self.dcx().create_err(diagnostics::MissingSemicolonBeforeArray { + return Err(self.dcx().create_err(crate::diagnostics::MissingSemicolonBeforeArray { open_delim: open_delim_span, semicolon: prev_span.shrink_to_hi(), })); @@ -2389,17 +2195,11 @@ impl<'a> Parser<'a> { lo: Span, blk_mode: BlockCheckMode, ) -> PResult<'a, Box> { - if self.may_recover() && self.is_array_like_block() { - if let Some(arr) = self.maybe_suggest_brackets_instead_of_braces(lo) { - return Ok(arr); - } - } - if self.token.is_metavar_block() { - self.dcx().emit_err(diagnostics::InvalidBlockMacroSegment { + self.dcx().emit_err(crate::diagnostics::InvalidBlockMacroSegment { span: self.token.span, context: lo.to(self.token.span), - wrap: diagnostics::WrapInExplicitBlock { + wrap: crate::diagnostics::WrapInExplicitBlock { lo: self.token.span.shrink_to_lo(), hi: self.token.span.shrink_to_hi(), }, @@ -2571,9 +2371,9 @@ impl<'a> Parser<'a> { // Check for `move async` and recover if self.check_keyword(exp!(Async)) { let move_async_span = self.token.span.with_lo(self.prev_token.span.data().lo); - Err(self - .dcx() - .create_err(diagnostics::AsyncMoveOrderIncorrect { span: move_async_span })) + Err(self.dcx().create_err(crate::diagnostics::AsyncMoveOrderIncorrect { + span: move_async_span, + })) } else { Ok(CaptureBy::Value { move_kw: move_kw_span }) } @@ -2583,9 +2383,9 @@ impl<'a> Parser<'a> { // Check for `use async` and recover if self.check_keyword(exp!(Async)) { let use_async_span = self.token.span.with_lo(self.prev_token.span.data().lo); - Err(self - .dcx() - .create_err(diagnostics::AsyncUseOrderIncorrect { span: use_async_span })) + Err(self.dcx().create_err(crate::diagnostics::AsyncUseOrderIncorrect { + span: use_async_span, + })) } else { Ok(CaptureBy::Use { use_kw: use_kw_span }) } @@ -2667,10 +2467,10 @@ impl<'a> Parser<'a> { ExprKind::Binary(Spanned { span: binop_span, .. }, _, right) if let ExprKind::Block(_, None) = right.kind => { - let guar = this.dcx().emit_err(diagnostics::IfExpressionMissingThenBlock { + let guar = this.dcx().emit_err(crate::diagnostics::IfExpressionMissingThenBlock { if_span: lo, missing_then_block_sub: - diagnostics::IfExpressionMissingThenBlockSub::UnfinishedCondition( + crate::diagnostics::IfExpressionMissingThenBlockSub::UnfinishedCondition( cond_span.shrink_to_lo().to(*binop_span), ), let_else_sub: None, @@ -2678,10 +2478,11 @@ impl<'a> Parser<'a> { std::mem::replace(right, this.mk_expr_err(binop_span.shrink_to_hi(), guar)) } ExprKind::Block(_, None) => { - let guar = this.dcx().emit_err(diagnostics::IfExpressionMissingCondition { - if_span: lo.with_neighbor(cond.span).shrink_to_hi(), - block_span: self.psess.source_map().start_point(cond_span), - }); + let guar = + this.dcx().emit_err(crate::diagnostics::IfExpressionMissingCondition { + if_span: lo.with_neighbor(cond.span).shrink_to_hi(), + block_span: self.psess.source_map().start_point(cond_span), + }); std::mem::replace(&mut cond, this.mk_expr_err(cond_span.shrink_to_hi(), guar)) } _ => { @@ -2699,13 +2500,14 @@ impl<'a> Parser<'a> { if let Some(block) = recover_block_from_condition(self) { block } else { - let let_else_sub = matches!(cond.kind, ExprKind::Let(..)) - .then(|| diagnostics::IfExpressionLetSomeSub { if_span: lo.until(cond_span) }); + let let_else_sub = matches!(cond.kind, ExprKind::Let(..)).then(|| { + crate::diagnostics::IfExpressionLetSomeSub { if_span: lo.until(cond_span) } + }); - let guar = self.dcx().emit_err(diagnostics::IfExpressionMissingThenBlock { + let guar = self.dcx().emit_err(crate::diagnostics::IfExpressionMissingThenBlock { if_span: lo, missing_then_block_sub: - diagnostics::IfExpressionMissingThenBlockSub::AddThenBlock( + crate::diagnostics::IfExpressionMissingThenBlockSub::AddThenBlock( cond_span.shrink_to_hi(), ), let_else_sub, @@ -2798,9 +2600,9 @@ impl<'a> Parser<'a> { /// Parses a `let $pat = $expr` pseudo-expression. fn parse_expr_let(&mut self, restrictions: Restrictions) -> PResult<'a, Box> { let recovered: Recovered = if !restrictions.contains(Restrictions::ALLOW_LET) { - let err = diagnostics::ExpectedExpressionFoundLet { + let err = crate::diagnostics::ExpectedExpressionFoundLet { span: self.token.span, - reason: diagnostics::ForbiddenLetReason::OtherForbidden, + reason: crate::diagnostics::ForbiddenLetReason::OtherForbidden, missing_let: None, comparison: None, }; @@ -2822,7 +2624,7 @@ impl<'a> Parser<'a> { CommaRecoveryMode::LikelyTuple, )?; if self.token == token::EqEq { - self.dcx().emit_err(diagnostics::ExpectedEqForLetExpr { + self.dcx().emit_err(crate::diagnostics::ExpectedEqForLetExpr { span: self.token.span, sugg_span: self.token.span, }); @@ -2888,7 +2690,7 @@ impl<'a> Parser<'a> { || matches!(cond.kind, ExprKind::MacCall(..))) => { - self.dcx().emit_err(diagnostics::ExpectedElseBlock { + self.dcx().emit_err(crate::diagnostics::ExpectedElseBlock { first_tok_span, first_tok, else_span, @@ -2924,7 +2726,7 @@ impl<'a> Parser<'a> { let attributes = x0.span.until(branch_span); let last = xn.span; let ctx = if is_ctx_else { "else" } else { "if" }; - self.dcx().emit_err(diagnostics::OuterAttributeNotAllowedOnIfElse { + self.dcx().emit_err(crate::diagnostics::OuterAttributeNotAllowedOnIfElse { last, branch_span, ctx_span, @@ -2939,7 +2741,7 @@ impl<'a> Parser<'a> { && let BinOpKind::And = binop && let ExprKind::If(cond, ..) = &right.kind { - Err(self.dcx().create_err(diagnostics::UnexpectedIfWithIf( + Err(self.dcx().create_err(crate::diagnostics::UnexpectedIfWithIf( binop_span.shrink_to_hi().to(cond.span.shrink_to_lo()), ))) } else { @@ -2989,12 +2791,12 @@ impl<'a> Parser<'a> { let right = self.prev_token.span.between(self.look_ahead(1, |t| t.span)); self.bump(); // ) err.cancel(); - self.dcx().emit_err(diagnostics::ParenthesesInForHead { + self.dcx().emit_err(crate::diagnostics::ParenthesesInForHead { span, // With e.g. `for (x) in y)` this would replace `(x) in y)` // with `x) in y)` which is syntactically invalid. // However, this is prevented before we get here. - sugg: diagnostics::ParenthesesInForHeadSugg { left, right }, + sugg: crate::diagnostics::ParenthesesInForHeadSugg { left, right }, }); Ok((self.mk_pat(start_span.to(right), ast::PatKind::Wild), expr)) } else { @@ -3029,7 +2831,7 @@ impl<'a> Parser<'a> { && self.token.kind != token::OpenBrace && self.may_recover() { - let guar = self.dcx().emit_err(diagnostics::MissingExpressionInForLoop { + let guar = self.dcx().emit_err(crate::diagnostics::MissingExpressionInForLoop { span: expr.span.shrink_to_lo(), }); let err_expr = self.mk_expr(expr.span, ExprKind::Err(guar)); @@ -3071,7 +2873,7 @@ impl<'a> Parser<'a> { let else_span = self.token.span; self.bump(); let else_clause = self.parse_expr_else()?; - self.dcx().emit_err(diagnostics::LoopElseNotSupported { + self.dcx().emit_err(crate::diagnostics::LoopElseNotSupported { span: else_span.to(else_clause.span), loop_kind, loop_kw, @@ -3085,18 +2887,18 @@ impl<'a> Parser<'a> { // Possibly using JS syntax (#75311). let span = self.token.span; self.bump(); - (span, Some(diagnostics::MissingInInForLoopSub::InNotOf(span))) + (span, Some(crate::diagnostics::MissingInInForLoopSub::InNotOf(span))) } else if self.eat(exp!(Eq)) { let span = self.prev_token.span; - (span, Some(diagnostics::MissingInInForLoopSub::InNotEq(span))) + (span, Some(crate::diagnostics::MissingInInForLoopSub::InNotEq(span))) } else { let span = self.prev_token.span.between(self.token.span); let sub = (!self.for_loop_head_has_in()) - .then_some(diagnostics::MissingInInForLoopSub::AddIn(span)); + .then_some(crate::diagnostics::MissingInInForLoopSub::AddIn(span)); (span, sub) }; - self.dcx().emit_err(diagnostics::MissingInInForLoop { span, sub }); + self.dcx().emit_err(crate::diagnostics::MissingInInForLoop { span, sub }); } /// Whether the `for` loop header already contains an `in` before its body. @@ -3163,10 +2965,10 @@ impl<'a> Parser<'a> { } pub(crate) fn eat_label(&mut self) -> Option {} +unsafe impl Sync for RawVecInner {} +impl UnwindSafe for RawVecInner {} + impl RawVec { /// Creates the biggest possible `RawVec` (on the system heap) /// without allocating. If `T` has positive size, then this makes a @@ -298,7 +306,7 @@ impl RawVec { } /// Gets a raw pointer to the start of the allocation. Note that this is - /// `Unique::dangling()` if `capacity == 0` or `T` is zero-sized. In the former case, you must + /// `NonNull::dangling()` if `capacity == 0` or `T` is zero-sized. In the former case, you must /// be careful. #[inline] pub(crate) const fn ptr(&self) -> *mut T { @@ -487,7 +495,7 @@ const impl RawVecInner { // matches the size requested. If that ever changes, the capacity // here should change to `ptr.len() / size_of::()`. Ok(Self { - ptr: Unique::from(ptr.cast()), + ptr: ptr.cast(), // SAFETY: We return early if `T` is a ZST, and if `capacity` would // overflow an isize layout creation would have returned early as well. cap: unsafe { Cap::new_unchecked(capacity) }, @@ -582,7 +590,7 @@ const impl RawVecInner { impl RawVecInner { #[inline] const fn new_in(alloc: A, align: Alignment) -> Self { - let ptr = Unique::from_non_null(NonNull::without_provenance(align.as_nonzero_usize())); + let ptr = NonNull::without_provenance(align.as_nonzero_usize()); // `cap: 0` means "unallocated". zero-sized types are ignored. Self { ptr, cap: ZERO_CAP, alloc } } @@ -608,13 +616,13 @@ impl RawVecInner { #[inline] const unsafe fn from_raw_parts_in(ptr: *mut u8, cap: Cap, alloc: A) -> Self { // SAFETY: Upheld by caller. - Self { ptr: unsafe { Unique::new_unchecked(ptr) }, cap, alloc } + Self { ptr: unsafe { NonNull::new_unchecked(ptr) }, cap, alloc } } #[inline] #[rustc_const_unstable(feature = "const_heap", issue = "79597")] const unsafe fn from_nonnull_in(ptr: NonNull, cap: Cap, alloc: A) -> Self { - Self { ptr: Unique::from(ptr), cap, alloc } + Self { ptr, cap, alloc } } #[inline] @@ -624,7 +632,7 @@ impl RawVecInner { #[inline] const fn non_null(&self) -> NonNull { - self.ptr.cast().as_non_null_ptr() + self.ptr.cast() } #[inline] @@ -655,7 +663,7 @@ impl RawVecInner { unsafe { let alloc_size = elem_layout.size().unchecked_mul(self.cap.as_inner()); let layout = Layout::from_size_align_unchecked(alloc_size, elem_layout.align()); - Some((self.ptr.into(), layout)) + Some((self.ptr, layout)) } } } @@ -793,7 +801,7 @@ impl RawVecInner { // Allocators currently return a `NonNull<[u8]>` whose length matches // the size requested. If that ever changes, the capacity here should // change to `ptr.len() / size_of::()`. - self.ptr = Unique::from(ptr.cast()); + self.ptr = ptr.cast(); // SAFETY: Upheld by caller. self.cap = unsafe { Cap::new_unchecked(cap) }; } @@ -865,9 +873,7 @@ impl RawVecInner { // SAFETY: T isn't a ZST if we're here and `ptr` is our pointer that `current_memory` // ensures was allocated with `layout`. unsafe { self.alloc.deallocate(ptr, layout) }; - self.ptr = - // SAFETY: Alignment is guaranteed to be nonzero. - unsafe { Unique::new_unchecked(ptr::without_provenance_mut(elem_layout.align())) }; + self.ptr = NonNull::without_provenance(elem_layout.alignment().as_nonzero_usize()); self.cap = ZERO_CAP; } else { // SAFETY: `cap` is less than the previous capacity, which must have fit in an diff --git a/library/alloc/src/raw_vec/tests.rs b/library/alloc/src/raw_vec/tests.rs index 15f48c03dc54c..f325f8b441b32 100644 --- a/library/alloc/src/raw_vec/tests.rs +++ b/library/alloc/src/raw_vec/tests.rs @@ -84,7 +84,7 @@ struct ZST; // A `RawVec` holding zero-sized elements should always look like this. fn zst_sanity(v: &RawVec) { assert_eq!(v.capacity(), usize::MAX); - assert_eq!(v.ptr(), core::ptr::Unique::::dangling().as_ptr()); + assert_eq!(v.ptr(), core::ptr::dangling_mut::()); assert_eq!(unsafe { v.inner.current_memory(T::LAYOUT) }, None); } diff --git a/library/alloc/src/rcs/arc.rs b/library/alloc/src/rcs/arc.rs new file mode 100644 index 0000000000000..8ae3c094b2910 --- /dev/null +++ b/library/alloc/src/rcs/arc.rs @@ -0,0 +1,5211 @@ +use core::any::Any; +use core::cell::CloneFromCell; +#[cfg(not(no_global_oom_handling))] +use core::clone::TrivialClone; +use core::clone::{CloneToUninit, Share, UseCloned}; +use core::cmp::Ordering; +use core::hash::{Hash, Hasher}; +#[cfg(not(no_global_oom_handling))] +use core::iter; +use core::marker::{PhantomData, Unsize}; +use core::mem::{self, Alignment, ManuallyDrop}; +use core::num::NonZeroUsize; +use core::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn, LegacyReceiver}; +#[cfg(not(no_global_oom_handling))] +use core::ops::{Residual, Try}; +use core::panic::{RefUnwindSafe, UnwindSafe}; +use core::pin::{Pin, PinSafePointer}; +use core::ptr::{self, NonNull}; +#[cfg(not(no_global_oom_handling))] +use core::slice::from_raw_parts_mut; +use core::sync::atomic::Ordering::{Acquire, Relaxed, Release}; +use core::sync::atomic::{self, Atomic}; +use core::{borrow, fmt, hint, intrinsics}; + +#[cfg(not(no_global_oom_handling))] +use crate::alloc::handle_alloc_error; +use crate::alloc::{ + AllocError, Allocator, AllocatorClone, AllocatorNightly, Global, Layout, StaticAllocator, +}; +use crate::borrow::{Cow, ToOwned}; +use crate::boxed::Box; +use crate::rc::is_dangling; +#[cfg(not(no_global_oom_handling))] +use crate::string::String; +#[cfg(not(no_global_oom_handling))] +use crate::vec::Vec; + +/// A soft limit on the amount of references that may be made to an `Arc`. +/// +/// Going above this limit will abort your program (although not +/// necessarily) at _exactly_ `MAX_REFCOUNT + 1` references. +/// Trying to go above it might call a `panic` (if not actually going above it). +/// +/// This is a global invariant, and also applies when using a compare-exchange loop. +/// +/// See comment in `Arc::clone`. +const MAX_REFCOUNT: usize = (isize::MAX) as usize; + +#[cold] +#[cfg_attr(not(panic = "immediate-abort"), inline(never))] +#[cfg_attr(panic = "immediate-abort", inline)] +#[track_caller] +fn panic_arc_overflow() -> ! { + panic!("Arc counter overflow"); +} + +#[cfg(not(sanitize = "thread"))] +macro_rules! acquire { + ($x:expr) => { + atomic::fence(Acquire) + }; +} + +// ThreadSanitizer does not support memory fences. To avoid false positive +// reports in Arc / Weak implementation use atomic loads for synchronization +// instead. +#[cfg(sanitize = "thread")] +macro_rules! acquire { + ($x:expr) => { + $x.load(Acquire) + }; +} + +/// A thread-safe reference-counting pointer. 'Arc' stands for 'Atomically +/// Reference Counted'. +/// +/// The type `Arc` provides shared ownership of a value of type `T`, +/// allocated in the heap. Invoking [`clone`][clone] on `Arc` produces +/// a new `Arc` instance, which points to the same allocation on the heap as the +/// source `Arc`, while increasing a reference count. When the last `Arc` +/// pointer to a given allocation is destroyed, the value stored in that allocation (often +/// referred to as "inner value") is also dropped. +/// +/// Shared references in Rust disallow mutation by default, and `Arc` is no +/// exception: you cannot generally obtain a mutable reference to something +/// inside an `Arc`. If you do need to mutate through an `Arc`, you have several options: +/// +/// 1. Use interior mutability with synchronization primitives like [`Mutex`][mutex], +/// [`RwLock`][rwlock], or one of the [`Atomic`][atomic] types. +/// +/// 2. Use clone-on-write semantics with [`Arc::make_mut`] which provides efficient mutation +/// without requiring interior mutability. This approach clones the data only when +/// needed (when there are multiple references) and can be more efficient when mutations +/// are infrequent. +/// +/// 3. Use [`Arc::get_mut`] when you know your `Arc` is not shared (has a reference count of 1), +/// which provides direct mutable access to the inner value without any cloning. +/// +/// ``` +/// use std::sync::Arc; +/// +/// let mut data = Arc::new(vec![1, 2, 3]); +/// +/// // This will clone the vector only if there are other references to it +/// Arc::make_mut(&mut data).push(4); +/// +/// assert_eq!(*data, vec![1, 2, 3, 4]); +/// ``` +/// +/// **Note**: This type is only available on platforms that support atomic +/// loads and stores of pointers, which includes all platforms that support +/// the `std` crate but not all those which only support [`alloc`](crate). +/// This may be detected at compile time using `#[cfg(target_has_atomic = "ptr")]`. +/// +/// ## Thread Safety +/// +/// Unlike [`Rc`], `Arc` uses atomic operations for its reference +/// counting. This means that it is thread-safe. The disadvantage is that +/// atomic operations are more expensive than ordinary memory accesses. If you +/// are not sharing reference-counted allocations between threads, consider using +/// [`Rc`] for lower overhead. [`Rc`] is a safe default, because the +/// compiler will catch any attempt to send an [`Rc`] between threads. +/// However, a library might choose `Arc` in order to give library consumers +/// more flexibility. +/// +/// `Arc` will implement [`Send`] and [`Sync`] as long as the `T` implements +/// [`Send`] and [`Sync`]. Why can't you put a non-thread-safe type `T` in an +/// `Arc` to make it thread-safe? This may be a bit counter-intuitive at +/// first: after all, isn't the point of `Arc` thread safety? The key is +/// this: `Arc` makes it thread safe to have multiple ownership of the same +/// data, but it doesn't add thread safety to its data. Consider +/// Arc<[RefCell\]>. [`RefCell`] isn't [`Sync`], and if `Arc` was always +/// [`Send`], Arc<[RefCell\]> would be as well. But then we'd have a problem: +/// [`RefCell`] is not thread safe; it keeps track of the borrowing count using +/// non-atomic operations. +/// +/// In the end, this means that you may need to pair `Arc` with some sort of +/// [`std::sync`] type, usually [`Mutex`][mutex]. +/// +/// ## Breaking cycles with `Weak` +/// +/// The [`downgrade`][downgrade] method can be used to create a non-owning +/// [`Weak`] pointer. A [`Weak`] pointer can be [`upgrade`][upgrade]d +/// to an `Arc`, but this will return [`None`] if the value stored in the allocation has +/// already been dropped. In other words, `Weak` pointers do not keep the value +/// inside the allocation alive; however, they *do* keep the allocation +/// (the backing store for the value) alive. +/// +/// A cycle between `Arc` pointers will never be deallocated. For this reason, +/// [`Weak`] is used to break cycles. For example, a tree could have +/// strong `Arc` pointers from parent nodes to children, and [`Weak`] +/// pointers from children back to their parents. +/// +/// # Cloning references +/// +/// Creating a new reference from an existing reference-counted pointer is done using the +/// `Clone` trait implemented for [`Arc`][Arc] and [`Weak`][Weak]. +/// +/// ``` +/// use std::sync::Arc; +/// let foo = Arc::new(vec![1.0, 2.0, 3.0]); +/// // The two syntaxes below are equivalent. +/// let a = foo.clone(); +/// let b = Arc::clone(&foo); +/// // a, b, and foo are all Arcs that point to the same memory location +/// ``` +/// +/// ## `Deref` behavior +/// +/// `Arc` automatically dereferences to `T` (via the [`Deref`] trait), +/// so you can call `T`'s methods on a value of type `Arc`. To avoid name +/// clashes with `T`'s methods, the methods of `Arc` itself are associated +/// functions, called using [fully qualified syntax]: +/// +/// ``` +/// use std::sync::Arc; +/// +/// let my_arc = Arc::new(()); +/// let my_weak = Arc::downgrade(&my_arc); +/// ``` +/// +/// `Arc`'s implementations of traits like `Clone` may also be called using +/// fully qualified syntax. Some people prefer to use fully qualified syntax, +/// while others prefer using method-call syntax. +/// +/// ``` +/// use std::sync::Arc; +/// +/// let arc = Arc::new(()); +/// // Method-call syntax +/// let arc2 = arc.clone(); +/// // Fully qualified syntax +/// let arc3 = Arc::clone(&arc); +/// ``` +/// +/// [`Weak`][Weak] does not auto-dereference to `T`, because the inner value may have +/// already been dropped. +/// +/// [`Rc`]: crate::rc::Rc +/// [clone]: Clone::clone +/// [mutex]: ../../std/sync/struct.Mutex.html +/// [rwlock]: ../../std/sync/struct.RwLock.html +/// [atomic]: core::sync::atomic +/// [downgrade]: Arc::downgrade +/// [upgrade]: Weak::upgrade +/// [RefCell\]: core::cell::RefCell +/// [`RefCell`]: core::cell::RefCell +/// [`std::sync`]: ../../std/sync/index.html +/// [`Arc::clone(&from)`]: Arc::clone +/// [fully qualified syntax]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#fully-qualified-syntax-for-disambiguation-calling-methods-with-the-same-name +/// +/// # Examples +/// +/// Sharing some immutable data between threads: +/// +/// ``` +/// use std::sync::Arc; +/// use std::thread; +/// +/// let five = Arc::new(5); +/// +/// for _ in 0..10 { +/// let five = Arc::clone(&five); +/// +/// thread::spawn(move || { +/// println!("{five:?}"); +/// }); +/// } +/// ``` +/// +/// Sharing a mutable [`AtomicUsize`]: +/// +/// [`AtomicUsize`]: core::sync::atomic::AtomicUsize "sync::atomic::AtomicUsize" +/// +/// ``` +/// use std::sync::Arc; +/// use std::sync::atomic::{AtomicUsize, Ordering}; +/// use std::thread; +/// +/// let val = Arc::new(AtomicUsize::new(5)); +/// +/// for _ in 0..10 { +/// let val = Arc::clone(&val); +/// +/// thread::spawn(move || { +/// let v = val.fetch_add(1, Ordering::Relaxed); +/// println!("{v:?}"); +/// }); +/// } +/// ``` +/// +/// See the [`rc` documentation][rc_examples] for more examples of reference +/// counting in general. +/// +/// [rc_examples]: crate::rc#examples +#[doc(search_unbox)] +#[rustc_diagnostic_item = "Arc"] +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_insignificant_dtor] +#[diagnostic::on_move( + message = "the type `{Self}` does not implement `Copy`", + label = "this move could be avoided by cloning the original `{Self}`, which is inexpensive", + note = "consider using `Arc::clone`" +)] +pub struct Arc< + T: ?Sized, + // FIXME: When stabilizing this parameter, the `AllocatorNightly` + // bound on the `From` impl *must* be weaked to `Allocator`, + // as `Box` is fundamental! + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] A: Allocator = Global, +> { + ptr: NonNull>, + phantom: PhantomData>, + alloc: A, +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for Arc {} +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Sync for Arc {} + +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl UnwindSafe + for Arc +{ +} + +#[unstable(feature = "coerce_unsized", issue = "18598")] +impl, U: ?Sized, A: Allocator> CoerceUnsized> for Arc {} + +#[unstable(feature = "dispatch_from_dyn", issue = "none")] +impl, U: ?Sized> DispatchFromDyn> for Arc {} + +// SAFETY: `Arc::clone` doesn't access any `Cell`s which could contain the `Arc` being cloned. +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl CloneFromCell for Arc {} + +impl Arc { + unsafe fn from_inner(ptr: NonNull>) -> Self { + // SAFETY: Upheld by caller. + unsafe { Self::from_inner_in(ptr, Global) } + } + + unsafe fn from_ptr(ptr: *mut ArcInner) -> Self { + // SAFETY: Upheld by caller. + unsafe { Self::from_ptr_in(ptr, Global) } + } +} + +impl Arc { + #[inline] + fn into_inner_with_allocator(this: Self) -> (NonNull>, A) { + let this = mem::ManuallyDrop::new(this); + // SAFETY: Pointer is valid for reads. + (this.ptr, unsafe { ptr::read(&this.alloc) }) + } + + #[inline] + unsafe fn from_inner_in(ptr: NonNull>, alloc: A) -> Self { + Self { ptr, phantom: PhantomData, alloc } + } + + #[inline] + unsafe fn from_ptr_in(ptr: *mut ArcInner, alloc: A) -> Self { + // SAFETY: Upheld by caller. + unsafe { Self::from_inner_in(NonNull::new_unchecked(ptr), alloc) } + } +} + +/// `Weak` is a version of [`Arc`] that holds a non-owning reference to the +/// managed allocation. +/// +/// The allocation is accessed by calling [`upgrade`] on the `Weak` +/// pointer, which returns an [Option]<[Arc]\>. +/// +/// Since a `Weak` reference does not count towards ownership, it will not +/// prevent the value stored in the allocation from being dropped, and `Weak` itself makes no +/// guarantees about the value still being present. Thus it may return [`None`] +/// when [`upgrade`]d. Note however that a `Weak` reference *does* prevent the allocation +/// itself (the backing store) from being deallocated. +/// +/// A `Weak` pointer is useful for keeping a temporary reference to the allocation +/// managed by [`Arc`] without preventing its inner value from being dropped. It is also used to +/// prevent circular references between [`Arc`] pointers, since mutual owning references +/// would never allow either [`Arc`] to be dropped. For example, a tree could +/// have strong [`Arc`] pointers from parent nodes to children, and `Weak` +/// pointers from children back to their parents. +/// +/// The typical way to obtain a `Weak` pointer is to call [`Arc::downgrade`]. +/// +/// [`upgrade`]: Weak::upgrade +#[stable(feature = "arc_weak", since = "1.4.0")] +#[rustc_diagnostic_item = "ArcWeak"] +pub struct Weak< + T: ?Sized, + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] A: Allocator = Global, +> { + // This is a `NonNull` to allow optimizing the size of this type in enums, + // but it is not necessarily a valid pointer. + // `Weak::new` sets this to `usize::MAX` so that it doesn’t need + // to allocate space on the heap. That's not a value a real pointer + // will ever have because ArcInner has alignment at least 2. + ptr: NonNull>, + alloc: A, +} + +#[stable(feature = "arc_weak", since = "1.4.0")] +unsafe impl Send for Weak {} +#[stable(feature = "arc_weak", since = "1.4.0")] +unsafe impl Sync for Weak {} + +#[unstable(feature = "coerce_unsized", issue = "18598")] +impl, U: ?Sized, A: Allocator> CoerceUnsized> for Weak {} +#[unstable(feature = "dispatch_from_dyn", issue = "none")] +impl, U: ?Sized> DispatchFromDyn> for Weak {} + +// SAFETY: `Weak::clone` doesn't access any `Cell`s which could contain the `Weak` being cloned. +#[unstable(feature = "cell_get_cloned", issue = "145329")] +unsafe impl CloneFromCell for Weak {} + +#[stable(feature = "arc_weak", since = "1.4.0")] +impl fmt::Debug for Weak { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "(Weak)") + } +} + +// This is repr(C) to future-proof against possible field-reordering, which +// would interfere with otherwise safe [into|from]_raw() of transmutable +// inner types. +// Unlike RcInner, repr(align(2)) is not strictly required because atomic types +// have the alignment same as its size, but we use it for consistency and clarity. +#[repr(C, align(2))] +struct ArcInner { + strong: Atomic, + + // the value usize::MAX acts as a sentinel for temporarily "locking" the + // weak count, preventing `Arc::downgrade` from racing to create new + // `Weak` references. `Arc::is_unique` (which backs `Arc::get_mut`) + // needs to observe both the strong and weak counts as indicating + // uniqueness in one logical atomic step; since they live in separate + // atomic words, it locks the weak count while reading the strong + // count to keep the two reads consistent. + weak: Atomic, + + data: T, +} + +/// Calculate layout for `ArcInner` using the inner value's layout +fn arcinner_layout_for_value_layout(layout: Layout) -> Layout { + // Calculate layout using the given value layout. + // Previously, layout was calculated on the expression + // `&*(ptr as *const ArcInner)`, but this created a misaligned + // reference (see #54908). + Layout::new::>() + .extend(layout) + .unwrap_or_else(|_| panic!("capacity overflow")) + .0 + .pad_to_align() +} + +unsafe impl Send for ArcInner {} +unsafe impl Sync for ArcInner {} + +impl Arc { + /// Constructs a new `Arc`. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// ``` + #[cfg(not(no_global_oom_handling))] + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn new(data: T) -> Arc { + // Start the weak pointer count as 1 which is the weak pointer that's + // held by all the strong pointers (kinda), see std/rc.rs for more info + let x: Box<_> = Box::new(ArcInner { + strong: atomic::AtomicUsize::new(1), + weak: atomic::AtomicUsize::new(1), + data, + }); + // SAFETY: Pointer is valid. + unsafe { Self::from_inner(Box::into_non_null(x)) } + } + + /// Constructs a new `Arc` while giving you a `Weak` to the allocation, + /// to allow you to construct a `T` which holds a weak pointer to itself. + /// + /// Generally, a structure circularly referencing itself, either directly or + /// indirectly, should not hold a strong reference to itself to prevent a memory leak. + /// Using this function, you get access to the weak pointer during the + /// initialization of `T`, before the `Arc` is created, such that you can + /// clone and store it inside the `T`. + /// + /// `new_cyclic` first allocates the managed allocation for the `Arc`, + /// then calls your closure, giving it a `Weak` to this allocation, + /// and only afterwards completes the construction of the `Arc` by placing + /// the `T` returned from your closure into the allocation. + /// + /// Since the new `Arc` is not fully-constructed until `Arc::new_cyclic` + /// returns, calling [`upgrade`] on the weak reference inside your closure will + /// fail and result in a `None` value. + /// + /// # Panics + /// + /// If `data_fn` panics, the panic is propagated to the caller, and the + /// temporary [`Weak`] is dropped normally. + /// + /// # Example + /// + /// ``` + /// # #![allow(dead_code)] + /// use std::sync::{Arc, Weak}; + /// + /// struct Gadget { + /// me: Weak, + /// } + /// + /// impl Gadget { + /// /// Constructs a reference counted Gadget. + /// fn new() -> Arc { + /// // `me` is a `Weak` pointing at the new allocation of the + /// // `Arc` we're constructing. + /// Arc::new_cyclic(|me| { + /// // Create the actual struct here. + /// Gadget { me: me.clone() } + /// }) + /// } + /// + /// /// Returns a reference counted pointer to Self. + /// fn me(&self) -> Arc { + /// self.me.upgrade().unwrap() + /// } + /// } + /// ``` + /// [`upgrade`]: Weak::upgrade + #[cfg(not(no_global_oom_handling))] + #[inline] + #[stable(feature = "arc_new_cyclic", since = "1.60.0")] + pub fn new_cyclic(data_fn: F) -> Arc + where + F: FnOnce(&Weak) -> T, + { + Self::new_cyclic_in(data_fn, Global) + } + + /// Constructs a new `Arc` with uninitialized contents. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let mut five = Arc::::new_uninit(); + /// + /// // Deferred initialization: + /// Arc::get_mut(&mut five).unwrap().write(5); + /// + /// let five = unsafe { five.assume_init() }; + /// + /// assert_eq!(*five, 5) + /// ``` + #[cfg(not(no_global_oom_handling))] + #[inline] + #[stable(feature = "new_uninit", since = "1.82.0")] + #[must_use] + pub fn new_uninit() -> Arc> { + // ignore-tidy-undocumented-unsafe + unsafe { + Arc::from_ptr(Arc::allocate_for_layout( + Layout::new::(), + |layout| Global.allocate(layout), + <*mut u8>::cast, + )) + } + } + + /// Constructs a new `Arc` with uninitialized contents, with the memory + /// being filled with `0` bytes. + /// + /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and incorrect usage + /// of this method. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let zero = Arc::::new_zeroed(); + /// let zero = unsafe { zero.assume_init() }; + /// + /// assert_eq!(*zero, 0) + /// ``` + /// + /// [zeroed]: mem::MaybeUninit::zeroed + #[cfg(not(no_global_oom_handling))] + #[inline] + #[stable(feature = "new_zeroed_alloc", since = "1.92.0")] + #[must_use] + pub fn new_zeroed() -> Arc> { + // ignore-tidy-undocumented-unsafe + unsafe { + Arc::from_ptr(Arc::allocate_for_layout( + Layout::new::(), + |layout| Global.allocate_zeroed(layout), + <*mut u8>::cast, + )) + } + } + + /// Constructs a new `Pin>`. If `T` does not implement `Unpin`, then + /// `data` will be pinned in memory and unable to be moved. + #[cfg(not(no_global_oom_handling))] + #[stable(feature = "pin", since = "1.33.0")] + #[must_use] + pub fn pin(data: T) -> Pin> { + // SAFETY: We own and create the pinned pointer. + unsafe { Pin::new_unchecked(Arc::new(data)) } + } + + /// Constructs a new `Pin>`, return an error if allocation fails. + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] + #[inline] + pub fn try_pin(data: T) -> Result>, AllocError> { + // SAFETY: We own and create the pinned pointer. + unsafe { Ok(Pin::new_unchecked(Arc::try_new(data)?)) } + } + + /// Constructs a new `Arc`, returning an error if allocation fails. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_ext)] + /// use std::sync::Arc; + /// + /// let five = Arc::try_new(5)?; + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] + #[inline] + pub fn try_new(data: T) -> Result, AllocError> { + // Start the weak pointer count as 1 which is the weak pointer that's + // held by all the strong pointers (kinda), see std/rc.rs for more info + let x: Box<_> = Box::try_new(ArcInner { + strong: atomic::AtomicUsize::new(1), + weak: atomic::AtomicUsize::new(1), + data, + })?; + // SAFETY: Pointer is valid. + unsafe { Ok(Self::from_inner(Box::into_non_null(x))) } + } + + /// Constructs a new `Arc` with uninitialized contents, returning an error + /// if allocation fails. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_ext)] + /// + /// use std::sync::Arc; + /// + /// let mut five = Arc::::try_new_uninit()?; + /// + /// // Deferred initialization: + /// Arc::get_mut(&mut five).unwrap().write(5); + /// + /// let five = unsafe { five.assume_init() }; + /// + /// assert_eq!(*five, 5); + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] + pub fn try_new_uninit() -> Result>, AllocError> { + // ignore-tidy-undocumented-unsafe + unsafe { + Ok(Arc::from_ptr(Arc::try_allocate_for_layout( + Layout::new::(), + |layout| Global.allocate(layout), + <*mut u8>::cast, + )?)) + } + } + + /// Constructs a new `Arc` with uninitialized contents, with the memory + /// being filled with `0` bytes, returning an error if allocation fails. + /// + /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and incorrect usage + /// of this method. + /// + /// # Examples + /// + /// ``` + /// #![feature( allocator_ext)] + /// + /// use std::sync::Arc; + /// + /// let zero = Arc::::try_new_zeroed()?; + /// let zero = unsafe { zero.assume_init() }; + /// + /// assert_eq!(*zero, 0); + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + /// + /// [zeroed]: mem::MaybeUninit::zeroed + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] + pub fn try_new_zeroed() -> Result>, AllocError> { + // ignore-tidy-undocumented-unsafe + unsafe { + Ok(Arc::from_ptr(Arc::try_allocate_for_layout( + Layout::new::(), + |layout| Global.allocate_zeroed(layout), + <*mut u8>::cast, + )?)) + } + } +} + +impl Arc { + /// Constructs a new `Arc` in the provided allocator. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_ext)] + /// + /// use std::sync::Arc; + /// use std::alloc::System; + /// + /// let five = Arc::new_in(5, System); + /// ``` + #[inline] + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] + pub fn new_in(data: T, alloc: A) -> Arc { + // Start the weak pointer count as 1 which is the weak pointer that's + // held by all the strong pointers (kinda), see std/rc.rs for more info + let x = Box::new_in( + ArcInner { + strong: atomic::AtomicUsize::new(1), + weak: atomic::AtomicUsize::new(1), + data, + }, + alloc, + ); + let (ptr, alloc) = Box::into_non_null_with_allocator(x); + // SAFETY: Pointer is valid. + unsafe { Self::from_inner_in(ptr, alloc) } + } + + /// Constructs a new `Arc` with uninitialized contents in the provided allocator. + /// + /// # Examples + /// + /// ``` + /// #![feature(get_mut_unchecked)] + /// #![feature(allocator_ext)] + /// + /// use std::sync::Arc; + /// use std::alloc::System; + /// + /// let mut five = Arc::::new_uninit_in(System); + /// + /// let five = unsafe { + /// // Deferred initialization: + /// Arc::get_mut_unchecked(&mut five).as_mut_ptr().write(5); + /// + /// five.assume_init() + /// }; + /// + /// assert_eq!(*five, 5) + /// ``` + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] + #[inline] + pub fn new_uninit_in(alloc: A) -> Arc, A> { + // ignore-tidy-undocumented-unsafe + unsafe { + Arc::from_ptr_in( + Arc::allocate_for_layout( + Layout::new::(), + |layout| alloc.allocate(layout), + <*mut u8>::cast, + ), + alloc, + ) + } + } + + /// Constructs a new `Arc` with uninitialized contents, with the memory + /// being filled with `0` bytes, in the provided allocator. + /// + /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and incorrect usage + /// of this method. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_ext)] + /// + /// use std::sync::Arc; + /// use std::alloc::System; + /// + /// let zero = Arc::::new_zeroed_in(System); + /// let zero = unsafe { zero.assume_init() }; + /// + /// assert_eq!(*zero, 0) + /// ``` + /// + /// [zeroed]: mem::MaybeUninit::zeroed + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] + #[inline] + pub fn new_zeroed_in(alloc: A) -> Arc, A> { + // ignore-tidy-undocumented-unsafe + unsafe { + Arc::from_ptr_in( + Arc::allocate_for_layout( + Layout::new::(), + |layout| alloc.allocate_zeroed(layout), + <*mut u8>::cast, + ), + alloc, + ) + } + } + + /// Constructs a new `Arc` in the given allocator while giving you a `Weak` to the allocation, + /// to allow you to construct a `T` which holds a weak pointer to itself. + /// + /// Generally, a structure circularly referencing itself, either directly or + /// indirectly, should not hold a strong reference to itself to prevent a memory leak. + /// Using this function, you get access to the weak pointer during the + /// initialization of `T`, before the `Arc` is created, such that you can + /// clone and store it inside the `T`. + /// + /// `new_cyclic_in` first allocates the managed allocation for the `Arc`, + /// then calls your closure, giving it a `Weak` to this allocation, + /// and only afterwards completes the construction of the `Arc` by placing + /// the `T` returned from your closure into the allocation. + /// + /// Since the new `Arc` is not fully-constructed until `Arc::new_cyclic_in` + /// returns, calling [`upgrade`] on the weak reference inside your closure will + /// fail and result in a `None` value. + /// + /// # Panics + /// + /// If `data_fn` panics, the panic is propagated to the caller, and the + /// temporary [`Weak`] is dropped normally. + /// + /// # Example + /// + /// See [`new_cyclic`] + /// + /// [`new_cyclic`]: Arc::new_cyclic + /// [`upgrade`]: Weak::upgrade + #[cfg(not(no_global_oom_handling))] + #[inline] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] + pub fn new_cyclic_in(data_fn: F, alloc: A) -> Arc + where + F: FnOnce(&Weak) -> T, + { + // Construct the inner in the "uninitialized" state with a single + // weak reference. + let (uninit_ptr, alloc) = Box::into_non_null_with_allocator(Box::new_in( + ArcInner { + strong: atomic::AtomicUsize::new(0), + weak: atomic::AtomicUsize::new(1), + data: mem::MaybeUninit::::uninit(), + }, + alloc, + )); + let init_ptr: NonNull> = uninit_ptr.cast(); + + let weak = Weak { ptr: init_ptr, alloc }; + + // It's important we don't give up ownership of the weak pointer, or + // else the memory might be freed by the time `data_fn` returns. If + // we really wanted to pass ownership, we could create an additional + // weak pointer for ourselves, but this would result in additional + // updates to the weak reference count which might not be necessary + // otherwise. + let data = data_fn(&weak); + + // Now we can properly initialize the inner value and turn our weak + // reference into a strong reference. + let inner = init_ptr.as_ptr(); + // ignore-tidy-undocumented-unsafe + unsafe { + ptr::write(&raw mut (*inner).data, data); + + // The above write to the data field must be visible to any threads which + // observe a non-zero strong count. Therefore we need at least "Release" ordering + // in order to synchronize with the `compare_exchange_weak` in `Weak::upgrade`. + // + // "Acquire" ordering is not required. When considering the possible behaviors + // of `data_fn` we only need to look at what it could do with a reference to a + // non-upgradeable `Weak`: + // - It can *clone* the `Weak`, increasing the weak reference count. + // - It can drop those clones, decreasing the weak reference count (but never to zero). + // + // These side effects do not impact us in any way, and no other side effects are + // possible with safe code alone. + let prev_value = (*inner).strong.fetch_add(1, Release); + debug_assert_eq!(prev_value, 0, "No prior strong references should exist"); + + // Strong references should collectively own a shared weak reference, + // so don't run the destructor for our old weak reference. + // Calling into_raw_with_allocator has the double effect of giving us back the allocator, + // and forgetting the weak reference. + let alloc = weak.into_raw_with_allocator().1; + + Arc::from_inner_in(init_ptr, alloc) + } + } + + /// Constructs a new `Pin>` in the provided allocator. If `T` does not implement `Unpin`, + /// then `data` will be pinned in memory and unable to be moved. + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] + #[inline] + pub fn pin_in(data: T, alloc: A) -> Pin> + where + A: StaticAllocator, + { + // SAFETY: We own and create the pinned pointer. + unsafe { Pin::new_unchecked(Arc::new_in(data, alloc)) } + } + + /// Constructs a new `Pin>` in the provided allocator, return an error if allocation + /// fails. + #[inline] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] + pub fn try_pin_in(data: T, alloc: A) -> Result>, AllocError> + where + A: StaticAllocator, + { + // SAFETY: We own and create the pinned pointer. + unsafe { Ok(Pin::new_unchecked(Arc::try_new_in(data, alloc)?)) } + } + + /// Constructs a new `Arc` in the provided allocator, returning an error if allocation fails. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_ext)] + /// + /// use std::sync::Arc; + /// use std::alloc::System; + /// + /// let five = Arc::try_new_in(5, System)?; + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] + #[inline] + pub fn try_new_in(data: T, alloc: A) -> Result, AllocError> { + // Start the weak pointer count as 1 which is the weak pointer that's + // held by all the strong pointers (kinda), see std/rc.rs for more info + let x = Box::try_new_in( + ArcInner { + strong: atomic::AtomicUsize::new(1), + weak: atomic::AtomicUsize::new(1), + data, + }, + alloc, + )?; + let (ptr, alloc) = Box::into_non_null_with_allocator(x); + // SAFETY: Pointer is valid since we created it. + Ok(unsafe { Self::from_inner_in(ptr, alloc) }) + } + + /// Constructs a new `Arc` with uninitialized contents, in the provided allocator, returning an + /// error if allocation fails. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_ext)] + /// #![feature(get_mut_unchecked)] + /// + /// use std::sync::Arc; + /// use std::alloc::System; + /// + /// let mut five = Arc::::try_new_uninit_in(System)?; + /// + /// let five = unsafe { + /// // Deferred initialization: + /// Arc::get_mut_unchecked(&mut five).as_mut_ptr().write(5); + /// + /// five.assume_init() + /// }; + /// + /// assert_eq!(*five, 5); + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] + #[inline] + pub fn try_new_uninit_in(alloc: A) -> Result, A>, AllocError> { + // ignore-tidy-undocumented-unsafe + unsafe { + Ok(Arc::from_ptr_in( + Arc::try_allocate_for_layout( + Layout::new::(), + |layout| alloc.allocate(layout), + <*mut u8>::cast, + )?, + alloc, + )) + } + } + + /// Constructs a new `Arc` with uninitialized contents, with the memory + /// being filled with `0` bytes, in the provided allocator, returning an error if allocation + /// fails. + /// + /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and incorrect usage + /// of this method. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_ext)] + /// + /// use std::sync::Arc; + /// use std::alloc::System; + /// + /// let zero = Arc::::try_new_zeroed_in(System)?; + /// let zero = unsafe { zero.assume_init() }; + /// + /// assert_eq!(*zero, 0); + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + /// + /// [zeroed]: mem::MaybeUninit::zeroed + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] + #[inline] + pub fn try_new_zeroed_in(alloc: A) -> Result, A>, AllocError> { + // ignore-tidy-undocumented-unsafe + unsafe { + Ok(Arc::from_ptr_in( + Arc::try_allocate_for_layout( + Layout::new::(), + |layout| alloc.allocate_zeroed(layout), + <*mut u8>::cast, + )?, + alloc, + )) + } + } + /// Returns the inner value, if the `Arc` has exactly one strong reference. + /// + /// Otherwise, an [`Err`] is returned with the same `Arc` that was + /// passed in. + /// + /// This will succeed even if there are outstanding weak references. + /// + /// It is strongly recommended to use [`Arc::into_inner`] instead if you don't + /// keep the `Arc` in the [`Err`] case. + /// Immediately dropping the [`Err`]-value, as the expression + /// `Arc::try_unwrap(this).ok()` does, can cause the strong count to + /// drop to zero and the inner value of the `Arc` to be dropped. + /// For instance, if two threads execute such an expression in parallel, + /// there is a race condition without the possibility of unsafety: + /// The threads could first both check whether they own the last instance + /// in `Arc::try_unwrap`, determine that they both do not, and then both + /// discard and drop their instance in the call to [`ok`][`Result::ok`]. + /// In this scenario, the value inside the `Arc` is safely destroyed + /// by exactly one of the threads, but neither thread will ever be able + /// to use the value. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let x = Arc::new(3); + /// assert_eq!(Arc::try_unwrap(x), Ok(3)); + /// + /// let x = Arc::new(4); + /// let _y = Arc::clone(&x); + /// assert_eq!(*Arc::try_unwrap(x).unwrap_err(), 4); + /// ``` + #[inline] + #[stable(feature = "arc_unique", since = "1.4.0")] + pub fn try_unwrap(this: Self) -> Result { + if this.inner().strong.compare_exchange(1, 0, Relaxed, Relaxed).is_err() { + return Err(this); + } + + acquire!(this.inner().strong); + + let this = ManuallyDrop::new(this); + // SAFETY: Pointer is valid for reads, contains initialised memory, + // and not dropped multiple times (we return it). + let elem: T = unsafe { ptr::read(&this.ptr.as_ref().data) }; + // SAFETY: As above, but we explicitly drop the allocator only once + // upon creating and dropping a weak pointer. + let alloc: A = unsafe { ptr::read(&this.alloc) }; // copy the allocator + + // Make a weak pointer to clean up the implicit strong-weak reference + let _weak = Weak { ptr: this.ptr, alloc }; + + Ok(elem) + } + + /// Returns the inner value, if the `Arc` has exactly one strong reference. + /// + /// Otherwise, [`None`] is returned and the `Arc` is dropped. + /// + /// This will succeed even if there are outstanding weak references. + /// + /// If `Arc::into_inner` is called on every clone of this `Arc`, + /// it is guaranteed that exactly one of the calls returns the inner value. + /// This means in particular that the inner value is not dropped. + /// + /// [`Arc::try_unwrap`] is conceptually similar to `Arc::into_inner`, but it + /// is meant for different use-cases. If used as a direct replacement + /// for `Arc::into_inner` anyway, such as with the expression + /// [Arc::try_unwrap]\(this).[ok][Result::ok](), then it does + /// **not** give the same guarantee as described in the previous paragraph. + /// For more information, see the examples below and read the documentation + /// of [`Arc::try_unwrap`]. + /// + /// # Examples + /// + /// Minimal example demonstrating the guarantee that `Arc::into_inner` gives. + /// ``` + /// use std::sync::Arc; + /// + /// let x = Arc::new(3); + /// let y = Arc::clone(&x); + /// + /// // Two threads calling `Arc::into_inner` on both clones of an `Arc`: + /// let x_thread = std::thread::spawn(|| Arc::into_inner(x)); + /// let y_thread = std::thread::spawn(|| Arc::into_inner(y)); + /// + /// let x_inner_value = x_thread.join().unwrap(); + /// let y_inner_value = y_thread.join().unwrap(); + /// + /// // One of the threads is guaranteed to receive the inner value: + /// assert!(matches!( + /// (x_inner_value, y_inner_value), + /// (None, Some(3)) | (Some(3), None) + /// )); + /// // The result could also be `(None, None)` if the threads called + /// // `Arc::try_unwrap(x).ok()` and `Arc::try_unwrap(y).ok()` instead. + /// ``` + /// + /// A more practical example demonstrating the need for `Arc::into_inner`: + /// ``` + /// use std::sync::Arc; + /// + /// // Definition of a simple singly linked list using `Arc`: + /// #[derive(Clone)] + /// struct LinkedList(Option>>); + /// struct Node(T, Option>>); + /// + /// // Dropping a long `LinkedList` relying on the destructor of `Arc` + /// // can cause a stack overflow. To prevent this, we can provide a + /// // manual `Drop` implementation that does the destruction in a loop: + /// impl Drop for LinkedList { + /// fn drop(&mut self) { + /// let mut link = self.0.take(); + /// while let Some(arc_node) = link.take() { + /// if let Some(Node(_value, next)) = Arc::into_inner(arc_node) { + /// link = next; + /// } + /// } + /// } + /// } + /// + /// // Implementation of `new` and `push` omitted + /// impl LinkedList { + /// /* ... */ + /// # fn new() -> Self { + /// # LinkedList(None) + /// # } + /// # fn push(&mut self, x: T) { + /// # self.0 = Some(Arc::new(Node(x, self.0.take()))); + /// # } + /// } + /// + /// // The following code could have still caused a stack overflow + /// // despite the manual `Drop` impl if that `Drop` impl had used + /// // `Arc::try_unwrap(arc).ok()` instead of `Arc::into_inner(arc)`. + /// + /// // Create a long list and clone it + /// let mut x = LinkedList::new(); + /// let size = 100000; + /// # let size = if cfg!(miri) { 100 } else { size }; + /// for i in 0..size { + /// x.push(i); // Adds i to the front of x + /// } + /// let y = x.clone(); + /// + /// // Drop the clones in parallel + /// let x_thread = std::thread::spawn(|| drop(x)); + /// let y_thread = std::thread::spawn(|| drop(y)); + /// x_thread.join().unwrap(); + /// y_thread.join().unwrap(); + /// ``` + #[inline] + #[stable(feature = "arc_into_inner", since = "1.70.0")] + pub fn into_inner(this: Self) -> Option { + // Make sure that the ordinary `Drop` implementation isn’t called as well + let mut this = mem::ManuallyDrop::new(this); + + // Following the implementation of `drop` and `drop_slow` + if this.inner().strong.fetch_sub(1, Release) != 1 { + return None; + } + + acquire!(this.inner().strong); + + // SAFETY: This mirrors the line + // + // unsafe { ptr::drop_in_place(Self::get_mut_unchecked(self)) }; + // + // in `drop_slow`. Instead of dropping the value behind the pointer, + // it is read and eventually returned; `ptr::read` has the same + // safety conditions as `ptr::drop_in_place`. + let inner = unsafe { ptr::read(Self::get_mut_unchecked(&mut this)) }; + // SAFETY: Pointer is valid for reads. + let alloc = unsafe { ptr::read(&this.alloc) }; + + drop(Weak { ptr: this.ptr, alloc }); + + Some(inner) + } + + /// Maps the value in an `Arc`, reusing the allocation if possible. + /// + /// `f` is called on a reference to the value in the `Arc`, and the result is returned, also in + /// an `Arc`. + /// + /// Note: this is an associated function, which means that you have + /// to call it as `Arc::map(a, f)` instead of `r.map(a)`. This + /// is so that there is no conflict with a method on the inner type. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let r = Arc::new(7); + /// let new = Arc::map(r, |i| i + 7); + /// assert_eq!(*new, 14); + /// ``` + #[cfg(not(no_global_oom_handling))] + #[stable(feature = "smart_pointer_map", since = "1.100.0")] + pub fn map(this: Self, f: impl FnOnce(&T) -> U) -> Arc { + if size_of::() == size_of::() + && align_of::() == align_of::() + && Arc::is_unique(&this) + { + // ignore-tidy-undocumented-unsafe + unsafe { + let (ptr, alloc) = Arc::into_raw_with_allocator(this); + let value = ptr.read(); + let mut allocation = Arc::from_raw_in(ptr.cast::>(), alloc); + + Arc::get_mut_unchecked(&mut allocation).write(f(&value)); + allocation.assume_init() + } + } else { + let output = f(&*this); + let (ptr, alloc) = Arc::into_raw_with_allocator(this); + // ignore-tidy-undocumented-unsafe + unsafe { Arc::decrement_strong_count_in(ptr, &alloc) } + + Arc::new_in(output, alloc) + } + } + + /// Attempts to map the value in an `Arc`, reusing the allocation if possible. + /// + /// `f` is called on a reference to the value in the `Arc`, and if the operation succeeds, the + /// result is returned, also in an `Arc`. + /// + /// Note: this is an associated function, which means that you have + /// to call it as `Arc::try_map(a, f)` instead of `a.try_map(f)`. This + /// is so that there is no conflict with a method on the inner type. + /// + /// # Examples + /// + /// ``` + /// #![feature(smart_pointer_try_map)] + /// + /// use std::sync::Arc; + /// + /// let b = Arc::new(7); + /// let new = Arc::try_map(b, |&i| u32::try_from(i)).unwrap(); + /// assert_eq!(*new, 7); + /// ``` + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "smart_pointer_try_map", issue = "144419")] + pub fn try_map( + this: Self, + f: impl FnOnce(&T) -> R, + ) -> >>::TryType + where + R: Try, + R::Residual: Residual>, + { + if size_of::() == size_of::() + && align_of::() == align_of::() + && Arc::is_unique(&this) + { + // ignore-tidy-undocumented-unsafe + unsafe { + let (ptr, alloc) = Arc::into_raw_with_allocator(this); + let value = ptr.read(); + let mut allocation = + Arc::from_raw_in(ptr.cast::>(), alloc); + + Arc::get_mut_unchecked(&mut allocation).write(f(&value)?); + try { allocation.assume_init() } + } + } else { + let output = f(&*this)?; + let (ptr, alloc) = Arc::into_raw_with_allocator(this); + // ignore-tidy-undocumented-unsafe + unsafe { Arc::decrement_strong_count_in(ptr, &alloc) } + + try { Arc::new_in(output, alloc) } + } + } +} + +impl Arc<[T]> { + /// Constructs a new atomically reference-counted slice with uninitialized contents. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let mut values = Arc::<[u32]>::new_uninit_slice(3); + /// + /// // Deferred initialization: + /// let data = Arc::get_mut(&mut values).unwrap(); + /// data[0].write(1); + /// data[1].write(2); + /// data[2].write(3); + /// + /// let values = unsafe { values.assume_init() }; + /// + /// assert_eq!(*values, [1, 2, 3]) + /// ``` + #[cfg(not(no_global_oom_handling))] + #[inline] + #[stable(feature = "new_uninit", since = "1.82.0")] + #[must_use] + pub fn new_uninit_slice(len: usize) -> Arc<[mem::MaybeUninit]> { + // ignore-tidy-undocumented-unsafe + unsafe { Arc::from_ptr(Arc::allocate_for_slice(len)) } + } + + /// Constructs a new atomically reference-counted slice with uninitialized contents, with the memory being + /// filled with `0` bytes. + /// + /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and + /// incorrect usage of this method. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let values = Arc::<[u32]>::new_zeroed_slice(3); + /// let values = unsafe { values.assume_init() }; + /// + /// assert_eq!(*values, [0, 0, 0]) + /// ``` + /// + /// [zeroed]: mem::MaybeUninit::zeroed + #[cfg(not(no_global_oom_handling))] + #[inline] + #[stable(feature = "new_zeroed_alloc", since = "1.92.0")] + #[must_use] + pub fn new_zeroed_slice(len: usize) -> Arc<[mem::MaybeUninit]> { + // ignore-tidy-undocumented-unsafe + unsafe { + Arc::from_ptr(Arc::allocate_for_layout( + Layout::array::(len).unwrap(), + |layout| Global.allocate_zeroed(layout), + |mem| mem.cast::().cast_slice(len) as *mut ArcInner<[mem::MaybeUninit]>, + )) + } + } +} + +impl Arc<[T], A> { + /// Constructs a new atomically reference-counted slice with uninitialized contents in the + /// provided allocator. + /// + /// # Examples + /// + /// ``` + /// #![feature(get_mut_unchecked)] + /// #![feature(allocator_ext)] + /// + /// use std::sync::Arc; + /// use std::alloc::System; + /// + /// let mut values = Arc::<[u32], _>::new_uninit_slice_in(3, System); + /// + /// let values = unsafe { + /// // Deferred initialization: + /// Arc::get_mut_unchecked(&mut values)[0].as_mut_ptr().write(1); + /// Arc::get_mut_unchecked(&mut values)[1].as_mut_ptr().write(2); + /// Arc::get_mut_unchecked(&mut values)[2].as_mut_ptr().write(3); + /// + /// values.assume_init() + /// }; + /// + /// assert_eq!(*values, [1, 2, 3]) + /// ``` + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] + #[inline] + pub fn new_uninit_slice_in(len: usize, alloc: A) -> Arc<[mem::MaybeUninit], A> { + // ignore-tidy-undocumented-unsafe + unsafe { Arc::from_ptr_in(Arc::allocate_for_slice_in(len, &alloc), alloc) } + } + + /// Constructs a new atomically reference-counted slice with uninitialized contents, with the memory being + /// filled with `0` bytes, in the provided allocator. + /// + /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and + /// incorrect usage of this method. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_ext)] + /// + /// use std::sync::Arc; + /// use std::alloc::System; + /// + /// let values = Arc::<[u32], _>::new_zeroed_slice_in(3, System); + /// let values = unsafe { values.assume_init() }; + /// + /// assert_eq!(*values, [0, 0, 0]) + /// ``` + /// + /// [zeroed]: mem::MaybeUninit::zeroed + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] + #[inline] + pub fn new_zeroed_slice_in(len: usize, alloc: A) -> Arc<[mem::MaybeUninit], A> { + // ignore-tidy-undocumented-unsafe + unsafe { + Arc::from_ptr_in( + Arc::allocate_for_layout( + Layout::array::(len).unwrap(), + |layout| alloc.allocate_zeroed(layout), + |mem| mem.cast::().cast_slice(len) as *mut ArcInner<[mem::MaybeUninit]>, + ), + alloc, + ) + } + } + + /// Converts the reference-counted slice into a reference-counted array. + /// + /// This operation does not reallocate; the underlying array of the slice is simply reinterpreted as an array type. + /// + /// # Errors + /// + /// Returns the original `Arc<[T]>` in the `Err` variant if `self.len()` does not equal `N`. + /// + /// # Examples + /// + /// ``` + /// #![feature(alloc_slice_into_array)] + /// use std::sync::Arc; + /// + /// let arc_slice: Arc<[i32]> = Arc::new([1, 2, 3]); + /// + /// let arc_array: Arc<[i32; 3]> = arc_slice.into_array().unwrap(); + /// ``` + #[unstable(feature = "alloc_slice_into_array", issue = "148082")] + #[inline] + pub fn into_array(self) -> Result, Self> { + if self.len() == N { + let (ptr, alloc) = Self::into_raw_with_allocator(self); + let ptr = ptr as *const [T; N]; + + // SAFETY: The underlying array of a slice has the exact same layout as an actual array `[T; N]` if `N` is equal to the slice's length. + let me = unsafe { Arc::from_raw_in(ptr, alloc) }; + Ok(me) + } else { + Err(self) + } + } +} + +impl Arc, A> { + /// Converts to `Arc`. + /// + /// # Safety + /// + /// As with [`MaybeUninit::assume_init`], + /// it is up to the caller to guarantee that the inner value + /// really is in an initialized state. + /// Calling this when the content is not yet fully initialized + /// causes immediate undefined behavior. + /// + /// [`MaybeUninit::assume_init`]: mem::MaybeUninit::assume_init + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let mut five = Arc::::new_uninit(); + /// + /// // Deferred initialization: + /// Arc::get_mut(&mut five).unwrap().write(5); + /// + /// let five = unsafe { five.assume_init() }; + /// + /// assert_eq!(*five, 5) + /// ``` + #[stable(feature = "new_uninit", since = "1.82.0")] + #[must_use = "`self` will be dropped if the result is not used"] + #[inline] + pub unsafe fn assume_init(self) -> Arc { + let (ptr, alloc) = Arc::into_inner_with_allocator(self); + // ignore-tidy-undocumented-unsafe + unsafe { Arc::from_inner_in(ptr.cast(), alloc) } + } +} + +impl Arc { + /// Constructs a new `Arc` with a clone of `value`. + /// + /// # Examples + /// + /// ``` + /// #![feature(clone_from_ref)] + /// use std::sync::Arc; + /// + /// let hello: Arc = Arc::clone_from_ref("hello"); + /// ``` + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "clone_from_ref", issue = "149075")] + pub fn clone_from_ref(value: &T) -> Arc { + Arc::clone_from_ref_in(value, Global) + } + + /// Constructs a new `Arc` with a clone of `value`, returning an error if allocation fails + /// + /// # Examples + /// + /// ``` + /// #![feature(clone_from_ref)] + /// use std::sync::Arc; + /// + /// let hello: Arc = Arc::try_clone_from_ref("hello")?; + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + #[unstable(feature = "clone_from_ref", issue = "149075")] + //#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] + pub fn try_clone_from_ref(value: &T) -> Result, AllocError> { + Arc::try_clone_from_ref_in(value, Global) + } +} + +impl Arc { + /// Constructs a new `Arc` with a clone of `value` in the provided allocator. + /// + /// # Examples + /// + /// ``` + /// #![feature(clone_from_ref)] + /// #![feature(allocator_ext)] + /// use std::sync::Arc; + /// use std::alloc::System; + /// + /// let hello: Arc = Arc::clone_from_ref_in("hello", System); + /// ``` + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "clone_from_ref", issue = "149075")] + //#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] + pub fn clone_from_ref_in(value: &T, alloc: A) -> Arc { + // `in_progress` drops the allocation if we panic before finishing initializing it. + let mut in_progress: UniqueArcUninit = UniqueArcUninit::new(value, alloc); + + // Initialize with clone of value. + // ignore-tidy-undocumented-unsafe + unsafe { + // Clone. If the clone panics, `in_progress` will be dropped and clean up. + value.clone_to_uninit(in_progress.data_ptr().cast()); + // Cast type of pointer, now that it is initialized. + in_progress.into_arc() + } + } + + /// Constructs a new `Arc` with a clone of `value` in the provided allocator, returning an error if allocation fails + /// + /// # Examples + /// + /// ``` + /// #![feature(clone_from_ref)] + /// #![feature(allocator_ext)] + /// use std::sync::Arc; + /// use std::alloc::System; + /// + /// let hello: Arc = Arc::try_clone_from_ref_in("hello", System)?; + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + #[unstable(feature = "clone_from_ref", issue = "149075")] + //#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] + pub fn try_clone_from_ref_in(value: &T, alloc: A) -> Result, AllocError> { + // `in_progress` drops the allocation if we panic before finishing initializing it. + let mut in_progress: UniqueArcUninit = UniqueArcUninit::try_new(value, alloc)?; + + // Initialize with clone of value. + // ignore-tidy-undocumented-unsafe + let initialized_clone = unsafe { + // Clone. If the clone panics, `in_progress` will be dropped and clean up. + value.clone_to_uninit(in_progress.data_ptr().cast()); + // Cast type of pointer, now that it is initialized. + in_progress.into_arc() + }; + + Ok(initialized_clone) + } +} + +impl Arc<[mem::MaybeUninit], A> { + /// Converts to `Arc<[T]>`. + /// + /// # Safety + /// + /// As with [`MaybeUninit::assume_init`], + /// it is up to the caller to guarantee that the inner value + /// really is in an initialized state. + /// Calling this when the content is not yet fully initialized + /// causes immediate undefined behavior. + /// + /// [`MaybeUninit::assume_init`]: mem::MaybeUninit::assume_init + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let mut values = Arc::<[u32]>::new_uninit_slice(3); + /// + /// // Deferred initialization: + /// let data = Arc::get_mut(&mut values).unwrap(); + /// data[0].write(1); + /// data[1].write(2); + /// data[2].write(3); + /// + /// let values = unsafe { values.assume_init() }; + /// + /// assert_eq!(*values, [1, 2, 3]) + /// ``` + #[stable(feature = "new_uninit", since = "1.82.0")] + #[must_use = "`self` will be dropped if the result is not used"] + #[inline] + pub unsafe fn assume_init(self) -> Arc<[T], A> { + let (ptr, alloc) = Arc::into_inner_with_allocator(self); + // SAFETY: Upheld by caller. + unsafe { Arc::from_ptr_in(ptr.as_ptr() as _, alloc) } + } +} + +impl Arc { + /// Constructs an `Arc` from a raw pointer. + /// + /// The raw pointer must have been previously returned by a call to + /// [`Arc::into_raw`][into_raw] or [`Arc::into_raw_with_allocator`][into_raw_with_allocator]. + /// + /// # Safety + /// + /// * Creating a `Arc` from a pointer other than one returned from + /// [`Arc::into_raw`][into_raw] or [`Arc::into_raw_with_allocator`][into_raw_with_allocator] + /// is undefined behavior. + /// * If `U` is sized, it must have the same size and alignment as `T`. This + /// is trivially true if `U` is `T`. + /// * If `U` is unsized, its data pointer must have the same size and + /// alignment as `T`. This is trivially true if `Arc` was constructed + /// through `Arc` and then converted to `Arc` through an [unsized + /// coercion]. + /// * Note that if `U` or `U`'s data pointer is not `T` but has the same size + /// and alignment, this is basically like transmuting references of + /// different types. See [`mem::transmute`][transmute] for more information + /// on what restrictions apply in this case. + /// * The raw pointer must point to a block of memory allocated by the global allocator. + /// * The user of `from_raw` has to make sure a specific value of `T` is only + /// dropped once. + /// + /// This function is unsafe because improper use may lead to memory unsafety, + /// even if the returned `Arc` is never accessed. + /// + /// [into_raw]: Arc::into_raw + /// [into_raw_with_allocator]: Arc::into_raw_with_allocator + /// [transmute]: core::mem::transmute + /// [unsized coercion]: https://doc.rust-lang.org/reference/type-coercions.html#unsized-coercions + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let x = Arc::new("hello".to_owned()); + /// let x_ptr = Arc::into_raw(x); + /// + /// unsafe { + /// // Convert back to an `Arc` to prevent leak. + /// let x = Arc::from_raw(x_ptr); + /// assert_eq!(&*x, "hello"); + /// + /// // Further calls to `Arc::from_raw(x_ptr)` would be memory-unsafe. + /// } + /// + /// // The memory was freed when `x` went out of scope above, so `x_ptr` is now dangling! + /// ``` + /// + /// Convert a slice back into its original array: + /// + /// ``` + /// use std::sync::Arc; + /// + /// let x: Arc<[u32]> = Arc::new([1, 2, 3]); + /// let x_ptr: *const [u32] = Arc::into_raw(x); + /// + /// unsafe { + /// let x: Arc<[u32; 3]> = Arc::from_raw(x_ptr.cast::<[u32; 3]>()); + /// assert_eq!(&*x, &[1, 2, 3]); + /// } + /// ``` + #[inline] + #[stable(feature = "rc_raw", since = "1.17.0")] + pub unsafe fn from_raw(ptr: *const T) -> Self { + // SAFETY: Upheld by caller. + unsafe { Arc::from_raw_in(ptr, Global) } + } + + /// Consumes the `Arc`, returning the wrapped pointer. + /// + /// To avoid a memory leak the pointer must be converted back to an `Arc` using + /// [`Arc::from_raw`]. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let x = Arc::new("hello".to_owned()); + /// let x_ptr = Arc::into_raw(x); + /// assert_eq!(unsafe { &*x_ptr }, "hello"); + /// # // Prevent leaks for Miri. + /// # drop(unsafe { Arc::from_raw(x_ptr) }); + /// ``` + #[must_use = "losing the pointer will leak memory"] + #[stable(feature = "rc_raw", since = "1.17.0")] + #[rustc_never_returns_null_ptr] + pub fn into_raw(this: Self) -> *const T { + let this = ManuallyDrop::new(this); + Self::as_ptr(&*this) + } + + /// Increments the strong reference count on the `Arc` associated with the + /// provided pointer by one. + /// + /// # Safety + /// + /// The pointer must have been obtained through `Arc::into_raw` and must satisfy the + /// same layout requirements specified in [`Arc::from_raw_in`][from_raw_in]. + /// The associated `Arc` instance must be valid (i.e. the strong count must be at + /// least 1) for the duration of this method, and `ptr` must point to a block of memory + /// allocated by the global allocator. + /// + /// [from_raw_in]: Arc::from_raw_in + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// unsafe { + /// let ptr = Arc::into_raw(five); + /// Arc::increment_strong_count(ptr); + /// + /// // This assertion is deterministic because we haven't shared + /// // the `Arc` between threads. + /// let five = Arc::from_raw(ptr); + /// assert_eq!(2, Arc::strong_count(&five)); + /// # // Prevent leaks for Miri. + /// # Arc::decrement_strong_count(ptr); + /// } + /// ``` + #[inline] + #[stable(feature = "arc_mutate_strong_count", since = "1.51.0")] + pub unsafe fn increment_strong_count(ptr: *const T) { + // SAFETY: Upheld by caller. + unsafe { Arc::increment_strong_count_in(ptr, Global) } + } + + /// Decrements the strong reference count on the `Arc` associated with the + /// provided pointer by one. + /// + /// # Safety + /// + /// The pointer must have been obtained through `Arc::into_raw` and must satisfy the + /// same layout requirements specified in [`Arc::from_raw_in`][from_raw_in]. + /// The associated `Arc` instance must be valid (i.e. the strong count must be at + /// least 1) when invoking this method, and `ptr` must point to a block of memory + /// allocated by the global allocator. This method can be used to release the final + /// `Arc` and backing storage, but **should not** be called after the final `Arc` has been + /// released. + /// + /// [from_raw_in]: Arc::from_raw_in + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// unsafe { + /// let ptr = Arc::into_raw(five); + /// Arc::increment_strong_count(ptr); + /// + /// // Those assertions are deterministic because we haven't shared + /// // the `Arc` between threads. + /// let five = Arc::from_raw(ptr); + /// assert_eq!(2, Arc::strong_count(&five)); + /// Arc::decrement_strong_count(ptr); + /// assert_eq!(1, Arc::strong_count(&five)); + /// } + /// ``` + #[inline] + #[stable(feature = "arc_mutate_strong_count", since = "1.51.0")] + pub unsafe fn decrement_strong_count(ptr: *const T) { + // SAFETY: Upheld by caller. + unsafe { Arc::decrement_strong_count_in(ptr, Global) } + } + + /// Gets the number of strong (`Arc`) pointers to the allocation behind the given raw + /// pointer. + /// + /// This method does not consume or drop the `Arc` behind this pointer. + /// + /// # Safety + /// + /// The pointer must point to (and have valid metadata for) the value inside a live `Arc` + /// allocation, such as a pointer returned by [`Arc::into_raw`], + /// [`Arc::into_raw_with_allocator`], or [`Arc::as_ptr`]. + /// `T` must have the same alignment as that value. + /// The associated `Arc` instance must be valid (i.e. the strong count must be at + /// least 1) for the duration of this method. + /// + /// Using this method correctly also requires extra care: another thread can change the + /// strong count at any time, including between calling this method and acting on the + /// result. + /// + /// # Examples + /// + /// ``` + /// #![feature(arc_raw_get_strong)] + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// let _also_five = Arc::clone(&five); + /// let ptr = Arc::into_raw(five); + /// + /// unsafe { + /// // This assertion is deterministic because we haven't shared + /// // the `Arc` between threads. + /// assert_eq!(2, Arc::strong_count_from_raw(ptr)); + /// + /// // Convert back to an `Arc` to avoid leaking memory. + /// let five = Arc::from_raw(ptr); + /// assert_eq!(2, Arc::strong_count(&five)); + /// } + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "arc_raw_get_strong", issue = "157021")] + pub unsafe fn strong_count_from_raw(ptr: *const T) -> usize { + // SAFETY: Upheld by caller. + let offset = unsafe { data_offset(ptr) }; + // Reverse the offset to find the original ArcInner. + // SAFETY: Caller ensures this pointer was to an `Arc` allocation, + // so offsetting must be inbounds. + let arc_ptr = unsafe { ptr.byte_sub(offset) as *mut ArcInner }; + // SAFETY: Per the above, an `ArcInner` is stored here. + unsafe { (*arc_ptr).strong.load(Relaxed) } + } +} + +impl Arc { + /// Returns a reference to the underlying allocator. + /// + /// Note: this is an associated function, which means that you have + /// to call it as `Arc::allocator(&a)` instead of `a.allocator()`. This + /// is so that there is no conflict with a method on the inner type. + #[inline] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] + pub fn allocator(this: &Self) -> &A { + &this.alloc + } + + /// Consumes the `Arc`, returning the wrapped pointer and allocator. + /// + /// To avoid a memory leak the pointer must be converted back to an `Arc` using + /// [`Arc::from_raw_in`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_ext)] + /// use std::sync::Arc; + /// use std::alloc::System; + /// + /// let x = Arc::new_in("hello".to_owned(), System); + /// let (ptr, alloc) = Arc::into_raw_with_allocator(x); + /// assert_eq!(unsafe { &*ptr }, "hello"); + /// let x = unsafe { Arc::from_raw_in(ptr, alloc) }; + /// assert_eq!(&*x, "hello"); + /// ``` + #[must_use = "losing the pointer will leak memory"] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] + pub fn into_raw_with_allocator(this: Self) -> (*const T, A) { + let this = mem::ManuallyDrop::new(this); + let ptr = Self::as_ptr(&this); + // SAFETY: `this` is ManuallyDrop so the allocator will not be double-dropped + let alloc = unsafe { ptr::read(&this.alloc) }; + (ptr, alloc) + } + + /// Provides a raw pointer to the data. + /// + /// The counts are not affected in any way and the `Arc` is not consumed. The pointer is valid for + /// as long as there are strong counts in the `Arc`. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let x = Arc::new("hello".to_owned()); + /// let y = Arc::clone(&x); + /// let x_ptr = Arc::as_ptr(&x); + /// assert_eq!(x_ptr, Arc::as_ptr(&y)); + /// assert_eq!(unsafe { &*x_ptr }, "hello"); + /// ``` + #[must_use] + #[stable(feature = "rc_as_ptr", since = "1.45.0")] + #[rustc_never_returns_null_ptr] + pub fn as_ptr(this: &Self) -> *const T { + let ptr: *mut ArcInner = NonNull::as_ptr(this.ptr); + + // SAFETY: This cannot go through Deref::deref or ArcInnerPtr::inner because + // this is required to retain raw/mut provenance such that e.g. `get_mut` can + // write through the pointer after the Arc is recovered through `from_raw`. + unsafe { &raw mut (*ptr).data } + } + + /// Constructs an `Arc` from a raw pointer. + /// + /// The raw pointer must have been previously returned by a call to [`Arc::into_raw`][into_raw] or [`Arc::into_raw_with_allocator`][into_raw_with_allocator]. + /// + /// # Safety + /// + /// * Creating a `Arc` from a pointer other than one returned from + /// [`Arc::into_raw`][into_raw] or [`Arc::into_raw_with_allocator`][into_raw_with_allocator] + /// is undefined behavior. + /// * If `U` is sized, it must have the same size and alignment as `T`. This + /// is trivially true if `U` is `T`. + /// * If `U` is unsized, its data pointer must have the same size and + /// alignment as `T`. This is trivially true if `Arc` was constructed + /// through `Arc` and then converted to `Arc` through an [unsized + /// coercion]. + /// * Note that if `U` or `U`'s data pointer is not `T` but has the same size + /// and alignment, this is basically like transmuting references of + /// different types. See [`mem::transmute`][transmute] for more information + /// on what restrictions apply in this case. + /// * The raw pointer must point to a block of memory allocated by `alloc` + /// * The user of `from_raw` has to make sure a specific value of `T` is only + /// dropped once. + /// + /// This function is unsafe because improper use may lead to memory unsafety, + /// even if the returned `Arc` is never accessed. + /// + /// [into_raw]: Arc::into_raw + /// [into_raw_with_allocator]: Arc::into_raw_with_allocator + /// [transmute]: core::mem::transmute + /// [unsized coercion]: https://doc.rust-lang.org/reference/type-coercions.html#unsized-coercions + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_ext)] + /// + /// use std::sync::Arc; + /// use std::alloc::System; + /// + /// let x = Arc::new_in("hello".to_owned(), System); + /// let (x_ptr, alloc) = Arc::into_raw_with_allocator(x); + /// + /// unsafe { + /// // Convert back to an `Arc` to prevent leak. + /// let x = Arc::from_raw_in(x_ptr, System); + /// assert_eq!(&*x, "hello"); + /// + /// // Further calls to `Arc::from_raw(x_ptr)` would be memory-unsafe. + /// } + /// + /// // The memory was freed when `x` went out of scope above, so `x_ptr` is now dangling! + /// ``` + /// + /// Convert a slice back into its original array: + /// + /// ``` + /// #![feature(allocator_ext)] + /// + /// use std::sync::Arc; + /// use std::alloc::System; + /// + /// let x: Arc<[u32], _> = Arc::new_in([1, 2, 3], System); + /// let x_ptr: *const [u32] = Arc::into_raw_with_allocator(x).0; + /// + /// unsafe { + /// let x: Arc<[u32; 3], _> = Arc::from_raw_in(x_ptr.cast::<[u32; 3]>(), System); + /// assert_eq!(&*x, &[1, 2, 3]); + /// } + /// ``` + #[inline] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] + pub unsafe fn from_raw_in(ptr: *const T, alloc: A) -> Self { + // SAFETY: Upheld by caller. + unsafe { + let offset = data_offset(ptr); + + // Reverse the offset to find the original ArcInner. + let arc_ptr = ptr.byte_sub(offset) as *mut ArcInner; + + Self::from_ptr_in(arc_ptr, alloc) + } + } + + /// Creates a new [`Weak`] pointer to this allocation. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// let weak_five = Arc::downgrade(&five); + /// ``` + #[must_use = "this returns a new `Weak` pointer, \ + without modifying the original `Arc`"] + #[stable(feature = "arc_weak", since = "1.4.0")] + pub fn downgrade(this: &Self) -> Weak + where + A: AllocatorClone, + { + // This Relaxed is OK because we're checking the value in the CAS + // below. + let mut cur = this.inner().weak.load(Relaxed); + + loop { + // check if the weak counter is currently "locked"; if so, spin. + if cur == usize::MAX { + hint::spin_loop(); + cur = this.inner().weak.load(Relaxed); + continue; + } + + // We can't allow the refcount to increase much past `MAX_REFCOUNT`. + if cur > MAX_REFCOUNT { + panic_arc_overflow(); + } + // NOTE: this code currently ignores the possibility of overflow + // into usize::MAX; in general both Rc and Arc need to be adjusted + // to deal with overflow. + + // Unlike with Clone(), we need this to be an Acquire read to + // synchronize with the write coming from `is_unique`, so that the + // events prior to that write happen before this read. + match this.inner().weak.compare_exchange_weak(cur, cur + 1, Acquire, Relaxed) { + Ok(_) => { + // Make sure we do not create a dangling Weak + debug_assert!(!is_dangling(this.ptr.as_ptr())); + return Weak { ptr: this.ptr, alloc: this.alloc.clone() }; + } + Err(old) => cur = old, + } + } + } + + /// Gets the number of [`Weak`] pointers to this allocation. + /// + /// # Safety + /// + /// This method by itself is safe, but using it correctly requires extra care. + /// Another thread can change the weak count at any time, + /// including potentially between calling this method and acting on the result. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// let _weak_five = Arc::downgrade(&five); + /// + /// // This assertion is deterministic because we haven't shared + /// // the `Arc` or `Weak` between threads. + /// assert_eq!(1, Arc::weak_count(&five)); + /// ``` + #[inline] + #[must_use] + #[stable(feature = "arc_counts", since = "1.15.0")] + pub fn weak_count(this: &Self) -> usize { + let cnt = this.inner().weak.load(Relaxed); + // If the weak count is currently locked, the value of the + // count was 0 just before taking the lock. + if cnt == usize::MAX { 0 } else { cnt - 1 } + } + + /// Gets the number of strong (`Arc`) pointers to this allocation. + /// + /// # Safety + /// + /// This method by itself is safe, but using it correctly requires extra care. + /// Another thread can change the strong count at any time, + /// including potentially between calling this method and acting on the result. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// let _also_five = Arc::clone(&five); + /// + /// // This assertion is deterministic because we haven't shared + /// // the `Arc` between threads. + /// assert_eq!(2, Arc::strong_count(&five)); + /// ``` + #[inline] + #[must_use] + #[stable(feature = "arc_counts", since = "1.15.0")] + pub fn strong_count(this: &Self) -> usize { + this.inner().strong.load(Relaxed) + } + + /// Increments the strong reference count on the `Arc` associated with the + /// provided pointer by one. + /// + /// # Safety + /// + /// The pointer must have been obtained through `Arc::into_raw` and must satisfy the + /// same layout requirements specified in [`Arc::from_raw_in`][from_raw_in]. + /// The associated `Arc` instance must be valid (i.e. the strong count must be at + /// least 1) for the duration of this method, and `ptr` must point to a block of memory + /// allocated by `alloc`. + /// + /// [from_raw_in]: Arc::from_raw_in + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_ext)] + /// + /// use std::sync::Arc; + /// use std::alloc::System; + /// + /// let five = Arc::new_in(5, System); + /// + /// unsafe { + /// let (ptr, _alloc) = Arc::into_raw_with_allocator(five); + /// Arc::increment_strong_count_in(ptr, System); + /// + /// // This assertion is deterministic because we haven't shared + /// // the `Arc` between threads. + /// let five = Arc::from_raw_in(ptr, System); + /// assert_eq!(2, Arc::strong_count(&five)); + /// # // Prevent leaks for Miri. + /// # Arc::decrement_strong_count_in(ptr, System); + /// } + /// ``` + #[inline] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] + pub unsafe fn increment_strong_count_in(ptr: *const T, alloc: A) + where + A: AllocatorClone, + { + // Retain Arc, but don't touch refcount by wrapping in ManuallyDrop + // SAFETY: Upheld by caller. + let arc = unsafe { mem::ManuallyDrop::new(Arc::from_raw_in(ptr, alloc)) }; + // Now increase refcount, but don't drop new refcount either + let _arc_clone: mem::ManuallyDrop<_> = arc.clone(); + } + + /// Decrements the strong reference count on the `Arc` associated with the + /// provided pointer by one. + /// + /// # Safety + /// + /// The pointer must have been obtained through `Arc::into_raw` and must satisfy the + /// same layout requirements specified in [`Arc::from_raw_in`][from_raw_in]. + /// The associated `Arc` instance must be valid (i.e. the strong count must be at + /// least 1) when invoking this method, and `ptr` must point to a block of memory + /// allocated by `alloc`. This method can be used to release the final + /// `Arc` and backing storage, but **should not** be called after the final `Arc` has been + /// released. + /// + /// [from_raw_in]: Arc::from_raw_in + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_ext)] + /// + /// use std::sync::Arc; + /// use std::alloc::System; + /// + /// let five = Arc::new_in(5, System); + /// + /// unsafe { + /// let (ptr, _alloc) = Arc::into_raw_with_allocator(five); + /// Arc::increment_strong_count_in(ptr, System); + /// + /// // Those assertions are deterministic because we haven't shared + /// // the `Arc` between threads. + /// let five = Arc::from_raw_in(ptr, System); + /// assert_eq!(2, Arc::strong_count(&five)); + /// Arc::decrement_strong_count_in(ptr, System); + /// assert_eq!(1, Arc::strong_count(&five)); + /// } + /// ``` + #[inline] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] + pub unsafe fn decrement_strong_count_in(ptr: *const T, alloc: A) { + // SAFETY: Upheld by caller. + unsafe { drop(Arc::from_raw_in(ptr, alloc)) }; + } + + #[inline] + fn inner(&self) -> &ArcInner { + // SAFETY: While this arc is alive we're guaranteed + // that the inner pointer is valid. Furthermore, we know that the + // `ArcInner` structure itself is `Sync` if the inner data is + // `Sync` as well, so we're ok loaning out an immutable pointer to these + // contents. + unsafe { self.ptr.as_ref() } + } + + // Non-inlined part of `drop`. + #[inline(never)] + unsafe fn drop_slow(&mut self) { + // Drop the weak ref collectively held by all strong references when this + // variable goes out of scope. This ensures that the memory is deallocated + // even if the destructor of `T` panics. + // Take a reference to `self.alloc` instead of cloning because 1. it'll last long + // enough, and 2. you should be able to drop `Arc`s with unclonable allocators + let _weak = Weak { ptr: self.ptr, alloc: &self.alloc }; + + // Destroy the data at this time, even though we must not free the box + // allocation itself (there might still be weak pointers lying around). + // We cannot use `get_mut_unchecked` here, because `self.alloc` is borrowed. + // ignore-tidy-undocumented-unsafe + unsafe { ptr::drop_in_place(&mut (*self.ptr.as_ptr()).data) }; + } + + /// Returns `true` if the two `Arc`s point to the same allocation in a vein similar to + /// [`ptr::eq`]. This function ignores the metadata of `dyn Trait` pointers. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// let same_five = Arc::clone(&five); + /// let other_five = Arc::new(5); + /// + /// assert!(Arc::ptr_eq(&five, &same_five)); + /// assert!(!Arc::ptr_eq(&five, &other_five)); + /// ``` + /// + /// [`ptr::eq`]: core::ptr::eq "ptr::eq" + #[inline] + #[must_use] + #[stable(feature = "ptr_eq", since = "1.17.0")] + pub fn ptr_eq(this: &Self, other: &Self) -> bool { + ptr::addr_eq(this.ptr.as_ptr(), other.ptr.as_ptr()) + } +} + +impl Arc { + /// Allocates an `ArcInner` with sufficient space for + /// a possibly-unsized inner value where the value has the layout provided. + /// + /// The function `mem_to_arcinner` is called with the data pointer + /// and must return back a (potentially fat)-pointer for the `ArcInner`. + #[cfg(not(no_global_oom_handling))] + unsafe fn allocate_for_layout( + value_layout: Layout, + allocate: impl FnOnce(Layout) -> Result, AllocError>, + mem_to_arcinner: impl FnOnce(*mut u8) -> *mut ArcInner, + ) -> *mut ArcInner { + let layout = arcinner_layout_for_value_layout(value_layout); + + let ptr = allocate(layout).unwrap_or_else(|_| handle_alloc_error(layout)); + + // ignore-tidy-undocumented-unsafe + unsafe { Self::initialize_arcinner(ptr, layout, mem_to_arcinner) } + } + + /// Allocates an `ArcInner` with sufficient space for + /// a possibly-unsized inner value where the value has the layout provided, + /// returning an error if allocation fails. + /// + /// The function `mem_to_arcinner` is called with the data pointer + /// and must return back a (potentially fat)-pointer for the `ArcInner`. + unsafe fn try_allocate_for_layout( + value_layout: Layout, + allocate: impl FnOnce(Layout) -> Result, AllocError>, + mem_to_arcinner: impl FnOnce(*mut u8) -> *mut ArcInner, + ) -> Result<*mut ArcInner, AllocError> { + let layout = arcinner_layout_for_value_layout(value_layout); + + let ptr = allocate(layout)?; + + // ignore-tidy-undocumented-unsafe + let inner = unsafe { Self::initialize_arcinner(ptr, layout, mem_to_arcinner) }; + + Ok(inner) + } + + unsafe fn initialize_arcinner( + ptr: NonNull<[u8]>, + layout: Layout, + mem_to_arcinner: impl FnOnce(*mut u8) -> *mut ArcInner, + ) -> *mut ArcInner { + let inner = mem_to_arcinner(ptr.as_non_null_ptr().as_ptr()); + // SAFETY: Upheld by caller. + debug_assert_eq!(unsafe { Layout::for_value_raw(inner) }, layout); + + // ignore-tidy-undocumented-unsafe + unsafe { + (&raw mut (*inner).strong).write(atomic::AtomicUsize::new(1)); + (&raw mut (*inner).weak).write(atomic::AtomicUsize::new(1)); + } + + inner + } +} + +impl Arc { + /// Allocates an `ArcInner` with sufficient space for an unsized inner value. + #[inline] + #[cfg(not(no_global_oom_handling))] + unsafe fn allocate_for_ptr_in(ptr: *const T, alloc: &A) -> *mut ArcInner { + // Allocate for the `ArcInner` using the given value. + // ignore-tidy-undocumented-unsafe + unsafe { + Arc::allocate_for_layout( + Layout::for_value_raw(ptr), + |layout| alloc.allocate(layout), + |mem| mem.with_metadata_of(ptr as *const ArcInner), + ) + } + } + + #[cfg(not(no_global_oom_handling))] + fn from_box_in(src: Box) -> Arc { + // ignore-tidy-undocumented-unsafe + unsafe { + let value_size = size_of_val(&*src); + let ptr = Self::allocate_for_ptr_in(&*src, Box::allocator(&src)); + + // Copy value as bytes + ptr::copy_nonoverlapping( + (&raw const *src) as *const u8, + (&raw mut (*ptr).data) as *mut u8, + value_size, + ); + + // Free the allocation without dropping its contents + let (bptr, alloc) = Box::into_raw_with_allocator(src); + let src = Box::from_raw_in(bptr as *mut mem::ManuallyDrop, &alloc); + drop(src); + + Self::from_ptr_in(ptr, alloc) + } + } +} + +impl Arc<[T]> { + /// Allocates an `ArcInner<[T]>` with the given length. + #[cfg(not(no_global_oom_handling))] + unsafe fn allocate_for_slice(len: usize) -> *mut ArcInner<[T]> { + // ignore-tidy-undocumented-unsafe + unsafe { + Self::allocate_for_layout( + Layout::array::(len).unwrap(), + |layout| Global.allocate(layout), + |mem| mem.cast::().cast_slice(len) as *mut ArcInner<[T]>, + ) + } + } + + /// Copy elements from slice into newly allocated `Arc<[T]>` + /// + /// Unsafe because the caller must either take ownership, bind `T: Copy` or + /// bind `T: TrivialClone`. + #[cfg(not(no_global_oom_handling))] + unsafe fn copy_from_slice(v: &[T]) -> Arc<[T]> { + // ignore-tidy-undocumented-unsafe + unsafe { + let ptr = Self::allocate_for_slice(v.len()); + + ptr::copy_nonoverlapping(v.as_ptr(), (&raw mut (*ptr).data) as *mut T, v.len()); + + Self::from_ptr(ptr) + } + } + + /// Constructs an `Arc<[T]>` from an iterator known to be of a certain size. + /// + /// Behavior is undefined should the size be wrong. + #[cfg(not(no_global_oom_handling))] + unsafe fn from_iter_exact(iter: impl Iterator, len: usize) -> Arc<[T]> { + // Panic guard while cloning T elements. + // In the event of a panic, elements that have been written + // into the new ArcInner will be dropped, then the memory freed. + struct Guard { + mem: NonNull, + elems: *mut T, + layout: Layout, + n_elems: usize, + } + + impl Drop for Guard { + fn drop(&mut self) { + // ignore-tidy-undocumented-unsafe + unsafe { + let slice = from_raw_parts_mut(self.elems, self.n_elems); + ptr::drop_in_place(slice); + + Global.deallocate(self.mem, self.layout); + } + } + } + + // ignore-tidy-undocumented-unsafe + unsafe { + let ptr = Self::allocate_for_slice(len); + + let mem = ptr as *mut _ as *mut u8; + let layout = Layout::for_value_raw(ptr); + + // Pointer to first element + let elems = (&raw mut (*ptr).data) as *mut T; + + let mut guard = Guard { mem: NonNull::new_unchecked(mem), elems, layout, n_elems: 0 }; + + for (i, item) in iter.enumerate() { + ptr::write(elems.add(i), item); + guard.n_elems += 1; + } + + // All clear. Forget the guard so it doesn't free the new ArcInner. + mem::forget(guard); + + Self::from_ptr(ptr) + } + } +} + +impl Arc<[T], A> { + /// Allocates an `ArcInner<[T]>` with the given length. + #[inline] + #[cfg(not(no_global_oom_handling))] + unsafe fn allocate_for_slice_in(len: usize, alloc: &A) -> *mut ArcInner<[T]> { + // ignore-tidy-undocumented-unsafe + unsafe { + Arc::allocate_for_layout( + Layout::array::(len).unwrap(), + |layout| alloc.allocate(layout), + |mem| mem.cast::().cast_slice(len) as *mut ArcInner<[T]>, + ) + } + } +} + +/// Specialization trait used for `From<&[T]>`. +#[cfg(not(no_global_oom_handling))] +trait ArcFromSlice { + fn from_slice(slice: &[T]) -> Self; +} + +#[cfg(not(no_global_oom_handling))] +impl ArcFromSlice for Arc<[T]> { + #[inline] + default fn from_slice(v: &[T]) -> Self { + // ignore-tidy-undocumented-unsafe + unsafe { Self::from_iter_exact(v.iter().cloned(), v.len()) } + } +} + +#[cfg(not(no_global_oom_handling))] +impl ArcFromSlice for Arc<[T]> { + #[inline] + fn from_slice(v: &[T]) -> Self { + // SAFETY: `T` implements `TrivialClone`, so this is sound and equivalent + // to the above. + unsafe { Arc::copy_from_slice(v) } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Arc { + /// Makes a clone of the `Arc` pointer. + /// + /// This creates another pointer to the same allocation, increasing the + /// strong reference count. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// let _ = Arc::clone(&five); + /// ``` + #[inline] + fn clone(&self) -> Arc { + // Using a relaxed ordering is alright here, as knowledge of the + // original reference prevents other threads from erroneously deleting + // the object. + // + // As explained in the [Boost documentation][1], Increasing the + // reference counter can always be done with memory_order_relaxed: New + // references to an object can only be formed from an existing + // reference, and passing an existing reference from one thread to + // another must already provide any required synchronization. + // + // [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html) + let old_size = self.inner().strong.fetch_add(1, Relaxed); + + // However we need to guard against massive refcounts in case someone is `mem::forget`ing + // Arcs. If we don't do this the count can overflow and users will use-after free. This + // branch will never be taken in any realistic program. We abort because such a program is + // incredibly degenerate, and we don't care to support it. + // + // This check is not 100% water-proof: we error when the refcount grows beyond `isize::MAX`. + // But we do that check *after* having done the increment, so there is a chance here that + // the worst already happened and we actually do overflow the `usize` counter. However, that + // requires the counter to grow from `isize::MAX` to `usize::MAX` between the increment + // above and the `abort` below, which seems exceedingly unlikely. + // + // This is a global invariant, and also applies when using a compare-exchange loop to increment + // counters in other methods. + // Otherwise, the counter could be brought to an almost-overflow using a compare-exchange loop, + // and then overflow using a few `fetch_add`s. + if old_size > MAX_REFCOUNT { + intrinsics::abort_immediate(); + } + + // SAFETY: Pointer is valid & allocator corresponds to the one used to allocate it. + unsafe { Self::from_inner_in(self.ptr, self.alloc.clone()) } + } +} + +#[unstable(feature = "ergonomic_clones", issue = "132290")] +impl UseCloned for Arc {} + +#[unstable(feature = "share_trait", issue = "156756")] +impl Share for Arc {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Deref for Arc { + type Target = T; + + #[inline] + fn deref(&self) -> &T { + &self.inner().data + } +} + +// The API of this pointer type enforces that if the `T` is pinned, then *all* +// clones of this `Arc` are wrapped as `Pin>`. Since an `&Arc` +// could be used to obtain an `Arc` that is not wrapped in `Pin` (and later +// used with `Arc::get_mut`), this means that this type treats `&Arc` as +// evidence that the `T` is not pinned. The implementations of various traits +// are written accordingly. Since this type is not fundamental, downstream +// crates cannot provide malicious implementations of any of the traits relevant +// for `Pin`. +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] +unsafe impl PinSafePointer for Arc {} + +#[unstable(feature = "deref_pure_trait", issue = "87121")] +unsafe impl DerefPure for Arc {} + +#[unstable(feature = "legacy_receiver_trait", issue = "none")] +impl LegacyReceiver for Arc {} + +#[cfg(not(no_global_oom_handling))] +impl Arc { + /// Makes a mutable reference into the given `Arc`. + /// + /// If there are other `Arc` pointers to the same allocation, then `make_mut` will + /// [`clone`] the inner value to a new allocation to ensure unique ownership. This is also + /// referred to as clone-on-write. + /// + /// However, if there are no other `Arc` pointers to this allocation, but some [`Weak`] + /// pointers, then the [`Weak`] pointers will be dissociated and the inner value will not + /// be cloned. + /// + /// See also [`get_mut`], which will fail rather than cloning the inner value + /// or dissociating [`Weak`] pointers. + /// + /// [`clone`]: Clone::clone + /// [`get_mut`]: Arc::get_mut + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let mut data = Arc::new(5); + /// + /// *Arc::make_mut(&mut data) += 1; // Won't clone anything + /// let mut other_data = Arc::clone(&data); // Won't clone inner data + /// *Arc::make_mut(&mut data) += 1; // Clones inner data + /// *Arc::make_mut(&mut data) += 1; // Won't clone anything + /// *Arc::make_mut(&mut other_data) *= 2; // Won't clone anything + /// + /// // Now `data` and `other_data` point to different allocations. + /// assert_eq!(*data, 8); + /// assert_eq!(*other_data, 12); + /// ``` + /// + /// [`Weak`] pointers will be dissociated: + /// + /// ``` + /// use std::sync::Arc; + /// + /// let mut data = Arc::new(75); + /// let weak = Arc::downgrade(&data); + /// + /// assert!(75 == *data); + /// assert!(75 == *weak.upgrade().unwrap()); + /// + /// *Arc::make_mut(&mut data) += 1; + /// + /// assert!(76 == *data); + /// assert!(weak.upgrade().is_none()); + /// ``` + #[inline] + #[stable(feature = "arc_unique", since = "1.4.0")] + pub fn make_mut(this: &mut Self) -> &mut T { + let size_of_val = size_of_val::(&**this); + + // Note that we hold both a strong reference and a weak reference. + // Thus, releasing our strong reference only will not, by itself, cause + // the memory to be deallocated. + // + // Use Acquire to ensure that we see any writes to `weak` that happen + // before release writes (i.e., decrements) to `strong`. Since we hold a + // weak count, there's no chance the ArcInner itself could be + // deallocated. + if this.inner().strong.compare_exchange(1, 0, Acquire, Relaxed).is_err() { + // Another strong pointer exists, so we must clone. + *this = Arc::clone_from_ref_in(&**this, this.alloc.clone()); + } else if this.inner().weak.load(Relaxed) != 1 { + // Relaxed suffices in the above because this is fundamentally an + // optimization: we are always racing with weak pointers being + // dropped. Worst case, we end up allocated a new Arc unnecessarily. + + // We removed the last strong ref, but there are additional weak + // refs remaining. We'll move the contents to a new Arc, and + // invalidate the other weak refs. + + // Note that it is not possible for the read of `weak` to yield + // usize::MAX (i.e., locked), since the weak count can only be + // locked by a thread with a strong reference. + + // Guard against panics while using the allocator. + // If we unwind before the Arc is overwritten, we expose a strong + // count of 0, resulting in a UAF (#155746, #157203). + // Until the new Arc is written, the old Arc must remain valid + struct Guard<'a, T: ?Sized> { + inner: &'a ArcInner, + } + impl<'a, T: ?Sized> Drop for Guard<'a, T> { + fn drop(&mut self) { + self.inner.strong.store(1, Release); + } + } + let guard = Guard { inner: this.inner() }; + + // Can just steal the data, all that's left is Weaks + // Note that this can panic in two ways: + // - The allocation can fail + // - The allocator clone can fail + let mut in_progress: UniqueArcUninit = + UniqueArcUninit::new(&**this, this.alloc.clone()); + + // ignore-tidy-undocumented-unsafe + unsafe { + // Initialize `in_progress` with move of **this. + // We have to express this in terms of bytes because `T: ?Sized`; there is no + // operation that just copies a value based on its `size_of_val()`. + ptr::copy_nonoverlapping( + ptr::from_ref(&**this).cast::(), + in_progress.data_ptr().cast::(), + size_of_val, + ); + + // We are now safe from panics. + mem::forget(guard); + + // Materialize our own implicit weak pointer, so that it can clean + // up the ArcInner as needed. + // Make sure the allocator is not leaked when the Arc is overwritten. + // Only drop at the end of the scope to avoid panics. + let _weak = Weak { ptr: this.ptr, alloc: ptr::read(&this.alloc) }; + + ptr::write(this, in_progress.into_arc()); + } + } else { + // We were the sole reference of either kind; bump back up the + // strong ref count. + this.inner().strong.store(1, Release); + } + + // SAFETY: As with `get_mut()`, our reference was + // either unique to begin with, or became one upon cloning the contents. + unsafe { Self::get_mut_unchecked(this) } + } +} + +impl Arc { + /// If we have the only reference to `T` then unwrap it. Otherwise, clone `T` and return the + /// clone. + /// + /// Assuming `arc_t` is of type `Arc`, this function is functionally equivalent to + /// `(*arc_t).clone()`, but will avoid cloning the inner value where possible. + /// + /// # Examples + /// + /// ``` + /// # use std::{ptr, sync::Arc}; + /// let inner = String::from("test"); + /// let ptr = inner.as_ptr(); + /// + /// let arc = Arc::new(inner); + /// let inner = Arc::unwrap_or_clone(arc); + /// // The inner value was not cloned + /// assert!(ptr::eq(ptr, inner.as_ptr())); + /// + /// let arc = Arc::new(inner); + /// let arc2 = arc.clone(); + /// let inner = Arc::unwrap_or_clone(arc); + /// // Because there were 2 references, we had to clone the inner value. + /// assert!(!ptr::eq(ptr, inner.as_ptr())); + /// // `arc2` is the last reference, so when we unwrap it we get back + /// // the original `String`. + /// let inner = Arc::unwrap_or_clone(arc2); + /// assert!(ptr::eq(ptr, inner.as_ptr())); + /// ``` + #[inline] + #[stable(feature = "arc_unwrap_or_clone", since = "1.76.0")] + pub fn unwrap_or_clone(this: Self) -> T { + Arc::try_unwrap(this).unwrap_or_else(|arc| (*arc).clone()) + } +} + +impl Arc { + /// Returns a mutable reference into the given `Arc`, if there are + /// no other `Arc` or [`Weak`] pointers to the same allocation. + /// + /// Returns [`None`] otherwise, because it is not safe to + /// mutate a shared value. + /// + /// See also [`make_mut`][make_mut], which will [`clone`][clone] + /// the inner value when there are other `Arc` pointers. + /// + /// [make_mut]: Arc::make_mut + /// [clone]: Clone::clone + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let mut x = Arc::new(3); + /// *Arc::get_mut(&mut x).unwrap() = 4; + /// assert_eq!(*x, 4); + /// + /// let _y = Arc::clone(&x); + /// assert!(Arc::get_mut(&mut x).is_none()); + /// ``` + #[inline] + #[stable(feature = "arc_unique", since = "1.4.0")] + pub fn get_mut(this: &mut Self) -> Option<&mut T> { + if Self::is_unique(this) { + // SAFETY: We're guaranteed that the pointer + // returned is the *only* pointer that will ever be returned to T. Our + // reference count is guaranteed to be 1 at this point, and we required + // the Arc itself to be `mut`, so we're returning the only possible + // reference to the inner data. + unsafe { Some(Arc::get_mut_unchecked(this)) } + } else { + None + } + } + + /// Returns a mutable reference into the given `Arc`, + /// without any check. + /// + /// See also [`get_mut`], which is safe and does appropriate checks. + /// + /// [`get_mut`]: Arc::get_mut + /// + /// # Safety + /// + /// If any other `Arc` or [`Weak`] pointers to the same allocation exist, then + /// they must not be dereferenced or have active borrows for the duration + /// of the returned borrow, and their inner type must be exactly the same as the + /// inner type of this Arc (including lifetimes). This is trivially the case if no + /// such pointers exist, for example immediately after `Arc::new`. + /// + /// # Examples + /// + /// ``` + /// #![feature(get_mut_unchecked)] + /// + /// use std::sync::Arc; + /// + /// let mut x = Arc::new(String::new()); + /// unsafe { + /// Arc::get_mut_unchecked(&mut x).push_str("foo") + /// } + /// assert_eq!(*x, "foo"); + /// ``` + /// Other `Arc` pointers to the same allocation must be to the same type. + /// ```no_run + /// #![feature(get_mut_unchecked)] + /// + /// use std::sync::Arc; + /// + /// let x: Arc = Arc::from("Hello, world!"); + /// let mut y: Arc<[u8]> = x.clone().into(); + /// unsafe { + /// // this is Undefined Behavior, because x's inner type is str, not [u8] + /// Arc::get_mut_unchecked(&mut y).fill(0xff); // 0xff is invalid in UTF-8 + /// } + /// println!("{}", &*x); // Invalid UTF-8 in a str + /// ``` + /// Other `Arc` pointers to the same allocation must be to the exact same type, including lifetimes. + /// ```no_run + /// #![feature(get_mut_unchecked)] + /// + /// use std::sync::Arc; + /// + /// let x: Arc<&str> = Arc::new("Hello, world!"); + /// { + /// let s = String::from("Oh, no!"); + /// let mut y: Arc<&str> = x.clone(); + /// unsafe { + /// // this is Undefined Behavior, because x's inner type + /// // is &'long str, not &'short str + /// *Arc::get_mut_unchecked(&mut y) = &s; + /// } + /// } + /// println!("{}", &*x); // Use-after-free + /// ``` + #[inline] + #[unstable(feature = "get_mut_unchecked", issue = "63292")] + pub unsafe fn get_mut_unchecked(this: &mut Self) -> &mut T { + // We are careful to *not* create a reference covering the "count" fields, as + // this would alias with concurrent access to the reference counts (e.g. by `Weak`). + // ignore-tidy-undocumented-unsafe + unsafe { &mut (*this.ptr.as_ptr()).data } + } + + /// Determine whether this is the unique reference to the underlying data. + /// + /// Returns `true` if there are no other `Arc` or [`Weak`] pointers to the same allocation; + /// returns `false` otherwise. + /// + /// If this function returns `true`, then is guaranteed to be safe to call [`get_mut_unchecked`] + /// on this `Arc`, so long as no clones occur in between. + /// + /// # Examples + /// + /// ``` + /// #![feature(arc_is_unique)] + /// + /// use std::sync::Arc; + /// + /// let x = Arc::new(3); + /// assert!(Arc::is_unique(&x)); + /// + /// let y = Arc::clone(&x); + /// assert!(!Arc::is_unique(&x)); + /// drop(y); + /// + /// // Weak references also count, because they could be upgraded at any time. + /// let z = Arc::downgrade(&x); + /// assert!(!Arc::is_unique(&x)); + /// ``` + /// + /// # Pointer invalidation + /// + /// This function will always return the same value as `Arc::get_mut(arc).is_some()`. However, + /// unlike that operation it does not produce any mutable references to the underlying data, + /// meaning no pointers to the data inside the `Arc` are invalidated by the call. Thus, the + /// following code is valid, even though it would be UB if it used `Arc::get_mut`: + /// + /// ``` + /// #![feature(arc_is_unique)] + /// + /// use std::sync::Arc; + /// + /// let arc = Arc::new(5); + /// let pointer: *const i32 = &*arc; + /// assert!(Arc::is_unique(&arc)); + /// assert_eq!(unsafe { *pointer }, 5); + /// ``` + /// + /// # Atomic orderings + /// + /// Concurrent drops to other `Arc` pointers to the same allocation will synchronize with this + /// call - that is, this call performs an `Acquire` operation on the underlying strong and weak + /// ref counts. This ensures that calling `get_mut_unchecked` is safe. + /// + /// Note that this operation requires locking the weak ref count, so concurrent calls to + /// `downgrade` may spin-loop for a short period of time. + /// + /// [`get_mut_unchecked`]: Self::get_mut_unchecked + #[inline] + #[unstable(feature = "arc_is_unique", issue = "138938")] + pub fn is_unique(this: &Self) -> bool { + // lock the weak pointer count if we appear to be the sole weak pointer + // holder. + // + // The acquire label here ensures a happens-before relationship with any + // writes to `strong` (in particular in `Weak::upgrade`) prior to decrements + // of the `weak` count (via `Weak::drop`, which uses release). If the upgraded + // weak ref was never dropped, the CAS here will fail so we do not care to synchronize. + if this.inner().weak.compare_exchange(1, usize::MAX, Acquire, Relaxed).is_ok() { + // This needs to be an `Acquire` to synchronize with the decrement of the `strong` + // counter in `drop` -- the only access that happens when any but the last reference + // is being dropped. + let unique = this.inner().strong.load(Acquire) == 1; + + // The release write here synchronizes with a read in `downgrade`, + // effectively preventing the above read of `strong` from happening + // after the write. + this.inner().weak.store(1, Release); // release the lock + unique + } else { + false + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Arc { + /// Drops the `Arc`. + /// + /// This will decrement the strong reference count. If the strong reference + /// count reaches zero then the only other references (if any) are + /// [`Weak`], so we `drop` the inner value. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// struct Foo; + /// + /// impl Drop for Foo { + /// fn drop(&mut self) { + /// println!("dropped!"); + /// } + /// } + /// + /// let foo = Arc::new(Foo); + /// let foo2 = Arc::clone(&foo); + /// + /// drop(foo); // Doesn't print anything + /// drop(foo2); // Prints "dropped!" + /// ``` + #[inline] + fn drop(&mut self) { + // Because `fetch_sub` is already atomic, we do not need to synchronize + // with other threads unless we are going to delete the object. This + // same logic applies to the below `fetch_sub` to the `weak` count. + if self.inner().strong.fetch_sub(1, Release) != 1 { + return; + } + + // This fence is needed to prevent reordering of use of the data and + // deletion of the data. Because it is marked `Release`, the decreasing + // of the reference count synchronizes with this `Acquire` fence. This + // means that use of the data happens before decreasing the reference + // count, which happens before this fence, which happens before the + // deletion of the data. + // + // As explained in the [Boost documentation][1], + // + // > It is important to enforce any possible access to the object in one + // > thread (through an existing reference) to *happen before* deleting + // > the object in a different thread. This is achieved by a "release" + // > operation after dropping a reference (any access to the object + // > through this reference must obviously happened before), and an + // > "acquire" operation before deleting the object. + // + // In particular, while the contents of an Arc are usually immutable, it's + // possible to have interior writes to something like a Mutex. Since a + // Mutex is not acquired when it is deleted, we can't rely on its + // synchronization logic to make writes in thread A visible to a destructor + // running in thread B. + // + // Also note that the Acquire fence here could probably be replaced with an + // Acquire load, which could improve performance in highly-contended + // situations. See [2]. + // + // [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html) + // [2]: (https://github.com/rust-lang/rust/pull/41714) + acquire!(self.inner().strong); + + // Make sure we aren't trying to "drop" the shared static for empty slices + // used by Default::default. + debug_assert!( + !ptr::addr_eq(self.ptr.as_ptr(), &STATIC_INNER_SLICE.inner), + "Arcs backed by a static should never reach a strong count of 0. \ + Likely decrement_strong_count or from_raw were called too many times.", + ); + + // ignore-tidy-undocumented-unsafe + unsafe { + self.drop_slow(); + } + } +} + +impl Arc { + /// Attempts to downcast the `Arc` to a concrete type. + /// + /// # Examples + /// + /// ``` + /// use std::any::Any; + /// use std::sync::Arc; + /// + /// fn print_if_string(value: Arc) { + /// if let Ok(string) = value.downcast::() { + /// println!("String ({}): {}", string.len(), string); + /// } + /// } + /// + /// let my_string = "Hello World".to_string(); + /// print_if_string(Arc::new(my_string)); + /// print_if_string(Arc::new(0i8)); + /// ``` + #[inline] + #[stable(feature = "rc_downcast", since = "1.29.0")] + pub fn downcast(self) -> Result, Self> + where + T: Any + Send + Sync, + { + if (*self).is::() { + // SAFETY: Check ensures the typecast is okay. + unsafe { + let (ptr, alloc) = Arc::into_inner_with_allocator(self); + Ok(Arc::from_inner_in(ptr.cast(), alloc)) + } + } else { + Err(self) + } + } + + /// Downcasts the `Arc` to a concrete type. + /// + /// For a safe alternative see [`downcast`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(downcast_unchecked)] + /// + /// use std::any::Any; + /// use std::sync::Arc; + /// + /// let x: Arc = Arc::new(1_usize); + /// + /// unsafe { + /// assert_eq!(*x.downcast_unchecked::(), 1); + /// } + /// ``` + /// + /// # Safety + /// + /// The contained value must be of type `T`. Calling this method + /// with the incorrect type is *undefined behavior*. + /// + /// + /// [`downcast`]: Self::downcast + #[inline] + #[unstable(feature = "downcast_unchecked", issue = "90850")] + pub unsafe fn downcast_unchecked(self) -> Arc + where + T: Any + Send + Sync, + { + // SAFETY: Upheld by caller. + unsafe { + let (ptr, alloc) = Arc::into_inner_with_allocator(self); + Arc::from_inner_in(ptr.cast(), alloc) + } + } +} + +impl Weak { + /// Constructs a new `Weak`, without allocating any memory. + /// Calling [`upgrade`] on the return value always gives [`None`]. + /// + /// [`upgrade`]: Weak::upgrade + /// + /// # Examples + /// + /// ``` + /// use std::sync::Weak; + /// + /// let empty: Weak = Weak::new(); + /// assert!(empty.upgrade().is_none()); + /// ``` + #[inline] + #[stable(feature = "downgraded_weak", since = "1.10.0")] + #[rustc_const_stable(feature = "const_weak_new", since = "1.73.0")] + #[must_use] + pub const fn new() -> Weak { + Weak { ptr: NonNull::without_provenance(NonZeroUsize::MAX), alloc: Global } + } +} + +impl Weak { + /// Constructs a new `Weak`, without allocating any memory, technically in the provided + /// allocator. + /// Calling [`upgrade`] on the return value always gives [`None`]. + /// + /// [`upgrade`]: Weak::upgrade + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_ext)] + /// + /// use std::sync::Weak; + /// use std::alloc::System; + /// + /// let empty: Weak = Weak::new_in(System); + /// assert!(empty.upgrade().is_none()); + /// ``` + #[inline] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] + pub fn new_in(alloc: A) -> Weak { + Weak { ptr: NonNull::without_provenance(NonZeroUsize::MAX), alloc } + } +} + +/// Helper type to allow accessing the reference counts without +/// making any assertions about the data field. +struct WeakInner<'a> { + weak: &'a Atomic, + strong: &'a Atomic, +} + +impl Weak { + /// Converts a raw pointer previously created by [`into_raw`] back into `Weak`. + /// + /// This can be used to safely get a strong reference (by calling [`upgrade`] + /// later) or to deallocate the weak count by dropping the `Weak`. + /// + /// It takes ownership of one weak reference (with the exception of pointers created by [`new`], + /// as these don't own anything; the method still works on them). + /// + /// # Safety + /// + /// The pointer must have originated from the [`into_raw`] and must still own its potential + /// weak reference, and must point to a block of memory allocated by global allocator. + /// + /// It is allowed for the strong count to be 0 at the time of calling this. Nevertheless, this + /// takes ownership of one weak reference currently represented as a raw pointer (the weak + /// count is not modified by this operation) and therefore it must be paired with a previous + /// call to [`into_raw`]. + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, Weak}; + /// + /// let strong = Arc::new("hello".to_owned()); + /// + /// let raw_1 = Arc::downgrade(&strong).into_raw(); + /// let raw_2 = Arc::downgrade(&strong).into_raw(); + /// + /// assert_eq!(2, Arc::weak_count(&strong)); + /// + /// assert_eq!("hello", &*unsafe { Weak::from_raw(raw_1) }.upgrade().unwrap()); + /// assert_eq!(1, Arc::weak_count(&strong)); + /// + /// drop(strong); + /// + /// // Decrement the last weak count. + /// assert!(unsafe { Weak::from_raw(raw_2) }.upgrade().is_none()); + /// ``` + /// + /// [`new`]: Weak::new + /// [`into_raw`]: Weak::into_raw + /// [`upgrade`]: Weak::upgrade + #[inline] + #[stable(feature = "weak_into_raw", since = "1.45.0")] + pub unsafe fn from_raw(ptr: *const T) -> Self { + // SAFETY: Upheld by caller. + unsafe { Weak::from_raw_in(ptr, Global) } + } + + /// Consumes the `Weak` and turns it into a raw pointer. + /// + /// This converts the weak pointer into a raw pointer, while still preserving the ownership of + /// one weak reference (the weak count is not modified by this operation). It can be turned + /// back into the `Weak` with [`from_raw`]. + /// + /// The same restrictions of accessing the target of the pointer as with + /// [`as_ptr`] apply. + /// + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, Weak}; + /// + /// let strong = Arc::new("hello".to_owned()); + /// let weak = Arc::downgrade(&strong); + /// let raw = weak.into_raw(); + /// + /// assert_eq!(1, Arc::weak_count(&strong)); + /// assert_eq!("hello", unsafe { &*raw }); + /// + /// drop(unsafe { Weak::from_raw(raw) }); + /// assert_eq!(0, Arc::weak_count(&strong)); + /// ``` + /// + /// [`from_raw`]: Weak::from_raw + /// [`as_ptr`]: Weak::as_ptr + #[must_use = "losing the pointer will leak memory"] + #[stable(feature = "weak_into_raw", since = "1.45.0")] + pub fn into_raw(self) -> *const T { + ManuallyDrop::new(self).as_ptr() + } +} + +impl Weak { + /// Returns a reference to the underlying allocator. + #[inline] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] + pub fn allocator(&self) -> &A { + &self.alloc + } + + /// Returns a raw pointer to the object `T` pointed to by this `Weak`. + /// + /// The pointer is valid only if there are some strong references. The pointer may be dangling, + /// unaligned or even [`null`] otherwise. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// use std::ptr; + /// + /// let strong = Arc::new("hello".to_owned()); + /// let weak = Arc::downgrade(&strong); + /// // Both point to the same object + /// assert!(ptr::eq(&*strong, weak.as_ptr())); + /// // The strong here keeps it alive, so we can still access the object. + /// assert_eq!("hello", unsafe { &*weak.as_ptr() }); + /// + /// drop(strong); + /// // But not any more. We can do weak.as_ptr(), but accessing the pointer would lead to + /// // undefined behavior. + /// // assert_eq!("hello", unsafe { &*weak.as_ptr() }); + /// ``` + /// + /// [`null`]: core::ptr::null "ptr::null" + #[must_use] + #[stable(feature = "weak_into_raw", since = "1.45.0")] + pub fn as_ptr(&self) -> *const T { + let ptr: *mut ArcInner = NonNull::as_ptr(self.ptr); + + if is_dangling(ptr) { + // If the pointer is dangling, we return the sentinel directly. This cannot be + // a valid payload address, as the payload is at least as aligned as ArcInner (usize). + ptr as *const T + } else { + // SAFETY: if is_dangling returns false, then the pointer is dereferenceable. + // The payload may be dropped at this point, and we have to maintain provenance, + // so use raw pointer manipulation. + unsafe { &raw mut (*ptr).data } + } + } + + /// Consumes the `Weak`, returning the wrapped pointer and allocator. + /// + /// This converts the weak pointer into a raw pointer, while still preserving the ownership of + /// one weak reference (the weak count is not modified by this operation). It can be turned + /// back into the `Weak` with [`from_raw_in`]. + /// + /// The same restrictions of accessing the target of the pointer as with + /// [`as_ptr`] apply. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_ext)] + /// use std::sync::{Arc, Weak}; + /// use std::alloc::System; + /// + /// let strong = Arc::new_in("hello".to_owned(), System); + /// let weak = Arc::downgrade(&strong); + /// let (raw, alloc) = weak.into_raw_with_allocator(); + /// + /// assert_eq!(1, Arc::weak_count(&strong)); + /// assert_eq!("hello", unsafe { &*raw }); + /// + /// drop(unsafe { Weak::from_raw_in(raw, alloc) }); + /// assert_eq!(0, Arc::weak_count(&strong)); + /// ``` + /// + /// [`from_raw_in`]: Weak::from_raw_in + /// [`as_ptr`]: Weak::as_ptr + #[must_use = "losing the pointer will leak memory"] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] + pub fn into_raw_with_allocator(self) -> (*const T, A) { + let this = mem::ManuallyDrop::new(self); + let result = this.as_ptr(); + // SAFETY: `this` is ManuallyDrop so the allocator will not be double-dropped + let alloc = unsafe { ptr::read(&this.alloc) }; + (result, alloc) + } + + /// Converts a raw pointer previously created by [`into_raw`] back into `Weak` in the provided + /// allocator. + /// + /// This can be used to safely get a strong reference (by calling [`upgrade`] + /// later) or to deallocate the weak count by dropping the `Weak`. + /// + /// It takes ownership of one weak reference (with the exception of pointers created by [`new`], + /// as these don't own anything; the method still works on them). + /// + /// # Safety + /// + /// The pointer must have originated from the [`into_raw`] and must still own its potential + /// weak reference, and must point to a block of memory allocated by `alloc`. + /// + /// It is allowed for the strong count to be 0 at the time of calling this. Nevertheless, this + /// takes ownership of one weak reference currently represented as a raw pointer (the weak + /// count is not modified by this operation) and therefore it must be paired with a previous + /// call to [`into_raw`]. + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, Weak}; + /// + /// let strong = Arc::new("hello".to_owned()); + /// + /// let raw_1 = Arc::downgrade(&strong).into_raw(); + /// let raw_2 = Arc::downgrade(&strong).into_raw(); + /// + /// assert_eq!(2, Arc::weak_count(&strong)); + /// + /// assert_eq!("hello", &*unsafe { Weak::from_raw(raw_1) }.upgrade().unwrap()); + /// assert_eq!(1, Arc::weak_count(&strong)); + /// + /// drop(strong); + /// + /// // Decrement the last weak count. + /// assert!(unsafe { Weak::from_raw(raw_2) }.upgrade().is_none()); + /// ``` + /// + /// [`new`]: Weak::new + /// [`into_raw`]: Weak::into_raw + /// [`upgrade`]: Weak::upgrade + #[inline] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] + pub unsafe fn from_raw_in(ptr: *const T, alloc: A) -> Self { + // See Weak::as_ptr for context on how the input pointer is derived. + + let ptr = if is_dangling(ptr) { + // This is a dangling Weak. + ptr as *mut ArcInner + } else { + // Otherwise, we're guaranteed the pointer came from a nondangling Weak. + // SAFETY: data_offset is safe to call, as ptr references a real (potentially dropped) T. + let offset = unsafe { data_offset(ptr) }; + // Thus, we reverse the offset to get the whole ArcInner. + // SAFETY: the pointer originated from a Weak, so this offset is safe. + unsafe { ptr.byte_sub(offset) as *mut ArcInner } + }; + + // SAFETY: we now have recovered the original Weak pointer, so can create the Weak. + Weak { ptr: unsafe { NonNull::new_unchecked(ptr) }, alloc } + } +} + +impl Weak { + /// Attempts to upgrade the `Weak` pointer to an [`Arc`], delaying + /// dropping of the inner value if successful. + /// + /// Returns [`None`] in the following cases: + /// + /// 1. The inner value has since been dropped or moved out. + /// + /// 2. This `Weak` does not point to an allocation. + /// + /// 3. The owning reference this `Weak` is associated with is either not fully-constructed or does not allow an upgrade. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// let weak_five = Arc::downgrade(&five); + /// + /// let strong_five: Option> = weak_five.upgrade(); + /// assert!(strong_five.is_some()); + /// + /// // Destroy all strong pointers. + /// drop(strong_five); + /// drop(five); + /// + /// assert!(weak_five.upgrade().is_none()); + /// ``` + #[must_use = "this returns a new `Arc`, \ + without modifying the original weak pointer"] + #[stable(feature = "arc_weak", since = "1.4.0")] + pub fn upgrade(&self) -> Option> + where + A: AllocatorClone, + { + #[inline] + fn checked_increment(n: usize) -> Option { + // Any write of 0 we can observe leaves the field in permanently zero state. + if n == 0 { + return None; + } + // See comments in `Arc::clone` for why we do this (for `mem::forget`). + if n > MAX_REFCOUNT { + panic_arc_overflow(); + } + Some(n + 1) + } + + // We use a CAS loop to increment the strong count instead of a + // fetch_add as this function should never take the reference count + // from zero to one. + // + // Relaxed is fine for the failure case because we don't have any expectations about the new state. + // Acquire is necessary for the success case to synchronise with `Arc::new_cyclic`, when the inner + // value can be initialized after `Weak` references have already been created. In that case, we + // expect to observe the fully initialized value. + if self.inner()?.strong.try_update(Acquire, Relaxed, checked_increment).is_ok() { + // SAFETY: pointer is not null, verified in checked_increment + unsafe { Some(Arc::from_inner_in(self.ptr, self.alloc.clone())) } + } else { + None + } + } + + /// Gets the number of strong (`Arc`) pointers pointing to this allocation. + /// + /// If `self` was created using [`Weak::new`], this will return 0. + #[must_use] + #[stable(feature = "weak_counts", since = "1.41.0")] + pub fn strong_count(&self) -> usize { + if let Some(inner) = self.inner() { inner.strong.load(Relaxed) } else { 0 } + } + + /// Gets an approximation of the number of `Weak` pointers pointing to this + /// allocation. + /// + /// If `self` was created using [`Weak::new`], or if there are no remaining + /// strong pointers, this will return 0. + /// + /// # Accuracy + /// + /// Due to implementation details, the returned value can be off by 1 in + /// either direction when other threads are manipulating any `Arc`s or + /// `Weak`s pointing to the same allocation. + #[must_use] + #[stable(feature = "weak_counts", since = "1.41.0")] + pub fn weak_count(&self) -> usize { + if let Some(inner) = self.inner() { + let weak = inner.weak.load(Acquire); + let strong = inner.strong.load(Relaxed); + if strong == 0 { + 0 + } else { + // Since we observed that there was at least one strong pointer + // after reading the weak count, we know that the implicit weak + // reference (present whenever any strong references are alive) + // was still around when we observed the weak count, and can + // therefore safely subtract it. + weak - 1 + } + } else { + 0 + } + } + + /// Returns `None` when the pointer is dangling and there is no allocated `ArcInner`, + /// (i.e., when this `Weak` was created by `Weak::new`). + #[inline] + fn inner(&self) -> Option> { + let ptr = self.ptr.as_ptr(); + if is_dangling(ptr) { + None + } else { + // We are careful to *not* create a reference covering the "data" field, as + // the field may be mutated concurrently (for example, if the last `Arc` + // is dropped, the data field will be dropped in-place). + // ignore-tidy-undocumented-unsafe + Some(unsafe { WeakInner { strong: &(*ptr).strong, weak: &(*ptr).weak } }) + } + } + + /// Returns `true` if the two `Weak`s point to the same allocation similar to [`ptr::eq`], or if + /// both don't point to any allocation (because they were created with `Weak::new()`). However, + /// this function ignores the metadata of `dyn Trait` pointers. + /// + /// # Notes + /// + /// Since this compares pointers it means that `Weak::new()` will equal each + /// other, even though they don't point to any allocation. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let first_rc = Arc::new(5); + /// let first = Arc::downgrade(&first_rc); + /// let second = Arc::downgrade(&first_rc); + /// + /// assert!(first.ptr_eq(&second)); + /// + /// let third_rc = Arc::new(5); + /// let third = Arc::downgrade(&third_rc); + /// + /// assert!(!first.ptr_eq(&third)); + /// ``` + /// + /// Comparing `Weak::new`. + /// + /// ``` + /// use std::sync::{Arc, Weak}; + /// + /// let first = Weak::new(); + /// let second = Weak::new(); + /// assert!(first.ptr_eq(&second)); + /// + /// let third_rc = Arc::new(()); + /// let third = Arc::downgrade(&third_rc); + /// assert!(!first.ptr_eq(&third)); + /// ``` + /// + /// [`ptr::eq`]: core::ptr::eq "ptr::eq" + #[inline] + #[must_use] + #[stable(feature = "weak_ptr_eq", since = "1.39.0")] + pub fn ptr_eq(&self, other: &Self) -> bool { + ptr::addr_eq(self.ptr.as_ptr(), other.ptr.as_ptr()) + } +} + +#[stable(feature = "arc_weak", since = "1.4.0")] +impl Clone for Weak { + /// Makes a clone of the `Weak` pointer that points to the same allocation. + /// + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, Weak}; + /// + /// let weak_five = Arc::downgrade(&Arc::new(5)); + /// + /// let _ = Weak::clone(&weak_five); + /// ``` + #[inline] + fn clone(&self) -> Weak { + if let Some(inner) = self.inner() { + // See comments in Arc::clone() for why this is relaxed. This can use a + // fetch_add (ignoring the lock) because the weak count is only locked + // where are *no other* weak pointers in existence. (So we can't be + // running this code in that case). + let old_size = inner.weak.fetch_add(1, Relaxed); + + // See comments in Arc::clone() for why we do this (for mem::forget). + if old_size > MAX_REFCOUNT { + intrinsics::abort_immediate(); + } + } + + Weak { ptr: self.ptr, alloc: self.alloc.clone() } + } +} + +#[unstable(feature = "ergonomic_clones", issue = "132290")] +impl UseCloned for Weak {} + +#[stable(feature = "downgraded_weak", since = "1.10.0")] +impl Default for Weak { + /// Constructs a new `Weak`, without allocating memory. + /// Calling [`upgrade`] on the return value always + /// gives [`None`]. + /// + /// [`upgrade`]: Weak::upgrade + /// + /// # Examples + /// + /// ``` + /// use std::sync::Weak; + /// + /// let empty: Weak = Default::default(); + /// assert!(empty.upgrade().is_none()); + /// ``` + fn default() -> Weak { + Weak::new() + } +} + +#[stable(feature = "arc_weak", since = "1.4.0")] +unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Weak { + /// Drops the `Weak` pointer. + /// + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, Weak}; + /// + /// struct Foo; + /// + /// impl Drop for Foo { + /// fn drop(&mut self) { + /// println!("dropped!"); + /// } + /// } + /// + /// let foo = Arc::new(Foo); + /// let weak_foo = Arc::downgrade(&foo); + /// let other_weak_foo = Weak::clone(&weak_foo); + /// + /// drop(weak_foo); // Doesn't print anything + /// drop(foo); // Prints "dropped!" + /// + /// assert!(other_weak_foo.upgrade().is_none()); + /// ``` + fn drop(&mut self) { + // If we find out that we were the last weak pointer, then its time to + // deallocate the data entirely. See the discussion in Arc::drop() about + // the memory orderings + // + // It's not necessary to check for the locked state here, because the + // weak count can only be locked if there was precisely one weak ref, + // meaning that drop could only subsequently run ON that remaining weak + // ref, which can only happen after the lock is released. + let inner = if let Some(inner) = self.inner() { inner } else { return }; + + if inner.weak.fetch_sub(1, Release) == 1 { + acquire!(inner.weak); + + // Make sure we aren't trying to "deallocate" the shared static for empty slices + // used by Default::default. + debug_assert!( + !ptr::addr_eq(self.ptr.as_ptr(), &STATIC_INNER_SLICE.inner), + "Arc/Weaks backed by a static should never be deallocated. \ + Likely decrement_strong_count or from_raw were called too many times.", + ); + + // ignore-tidy-undocumented-unsafe + unsafe { + self.alloc.deallocate(self.ptr.cast(), Layout::for_value_raw(self.ptr.as_ptr())) + } + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +trait ArcEqIdent { + fn eq(&self, other: &Arc) -> bool; + fn ne(&self, other: &Arc) -> bool; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ArcEqIdent for Arc { + #[inline] + default fn eq(&self, other: &Arc) -> bool { + **self == **other + } + #[inline] + default fn ne(&self, other: &Arc) -> bool { + **self != **other + } +} + +/// We're doing this specialization here, and not as a more general optimization on `&T`, because it +/// would otherwise add a cost to all equality checks on refs. We assume that `Arc`s are used to +/// store large values, that are slow to clone, but also heavy to check for equality, causing this +/// cost to pay off more easily. It's also more likely to have two `Arc` clones, that point to +/// the same value, than two `&T`s. +/// +/// We can only do this when `T: Eq` as a `PartialEq` might be deliberately irreflexive. +#[stable(feature = "rust1", since = "1.0.0")] +impl ArcEqIdent for Arc { + #[inline] + fn eq(&self, other: &Arc) -> bool { + ptr::eq(self.ptr.as_ptr(), other.ptr.as_ptr()) || **self == **other + } + + #[inline] + fn ne(&self, other: &Arc) -> bool { + !ptr::eq(self.ptr.as_ptr(), other.ptr.as_ptr()) && **self != **other + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq for Arc { + /// Equality for two `Arc`s. + /// + /// Two `Arc`s are equal if their inner values are equal, even if they are + /// stored in different allocation. + /// + /// If `T` also implements `Eq` (implying reflexivity of equality), + /// two `Arc`s that point to the same allocation are always equal. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// assert!(five == Arc::new(5)); + /// ``` + #[inline] + fn eq(&self, other: &Arc) -> bool { + ArcEqIdent::eq(self, other) + } + + /// Inequality for two `Arc`s. + /// + /// Two `Arc`s are not equal if their inner values are not equal. + /// + /// If `T` also implements `Eq` (implying reflexivity of equality), + /// two `Arc`s that point to the same value are always equal. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// assert!(five != Arc::new(6)); + /// ``` + #[inline] + fn ne(&self, other: &Arc) -> bool { + ArcEqIdent::ne(self, other) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialOrd for Arc { + /// Partial comparison for two `Arc`s. + /// + /// The two are compared by calling `partial_cmp()` on their inner values. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// use std::cmp::Ordering; + /// + /// let five = Arc::new(5); + /// + /// assert_eq!(Some(Ordering::Less), five.partial_cmp(&Arc::new(6))); + /// ``` + fn partial_cmp(&self, other: &Arc) -> Option { + (**self).partial_cmp(&**other) + } + + /// Less-than comparison for two `Arc`s. + /// + /// The two are compared by calling `<` on their inner values. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// assert!(five < Arc::new(6)); + /// ``` + fn lt(&self, other: &Arc) -> bool { + *(*self) < *(*other) + } + + /// 'Less than or equal to' comparison for two `Arc`s. + /// + /// The two are compared by calling `<=` on their inner values. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// assert!(five <= Arc::new(5)); + /// ``` + fn le(&self, other: &Arc) -> bool { + *(*self) <= *(*other) + } + + /// Greater-than comparison for two `Arc`s. + /// + /// The two are compared by calling `>` on their inner values. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// assert!(five > Arc::new(4)); + /// ``` + fn gt(&self, other: &Arc) -> bool { + *(*self) > *(*other) + } + + /// 'Greater than or equal to' comparison for two `Arc`s. + /// + /// The two are compared by calling `>=` on their inner values. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// assert!(five >= Arc::new(5)); + /// ``` + fn ge(&self, other: &Arc) -> bool { + *(*self) >= *(*other) + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl Ord for Arc { + /// Comparison for two `Arc`s. + /// + /// The two are compared by calling `cmp()` on their inner values. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// use std::cmp::Ordering; + /// + /// let five = Arc::new(5); + /// + /// assert_eq!(Ordering::Less, five.cmp(&Arc::new(6))); + /// ``` + fn cmp(&self, other: &Arc) -> Ordering { + (**self).cmp(&**other) + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl Eq for Arc {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for Arc { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&**self, f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for Arc { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Pointer for Arc { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Pointer::fmt(&(&raw const **self), f) + } +} + +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "rust1", since = "1.0.0")] +impl Default for Arc { + /// Creates a new `Arc`, with the `Default` value for `T`. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let x: Arc = Default::default(); + /// assert_eq!(*x, 0); + /// ``` + fn default() -> Arc { + // ignore-tidy-undocumented-unsafe + unsafe { + Self::from_inner(Box::into_non_null(Box::write( + Box::new_uninit(), + ArcInner { + strong: atomic::AtomicUsize::new(1), + weak: atomic::AtomicUsize::new(1), + data: T::default(), + }, + ))) + } + } +} + +/// Struct to hold the static `ArcInner` used for empty `Arc` as +/// returned by `Default::default`. +/// +/// Layout notes: +/// * `repr(align(16))` so we can use it for `[T]` with `align_of::() <= 16`. +/// * `repr(C)` so `inner` is at offset 0 (and thus guaranteed to actually be aligned to 16). +/// * `[u8; 1]` (to be initialized with 0) so it can be used for `Arc`. +#[repr(C, align(16))] +struct SliceArcInnerForStatic { + inner: ArcInner<[u8; 1]>, +} +#[cfg(not(no_global_oom_handling))] +const MAX_STATIC_INNER_SLICE_ALIGNMENT: usize = 16; + +static STATIC_INNER_SLICE: SliceArcInnerForStatic = SliceArcInnerForStatic { + inner: ArcInner { + strong: atomic::AtomicUsize::new(1), + weak: atomic::AtomicUsize::new(1), + data: [0], + }, +}; + +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "more_rc_default_impls", since = "1.80.0")] +impl Default for Arc { + /// Creates an empty str inside an Arc + /// + /// This may or may not share an allocation with other Arcs. + #[inline] + fn default() -> Self { + let arc: Arc<[u8]> = Default::default(); + debug_assert!(core::str::from_utf8(&arc).is_ok()); + let (ptr, alloc) = Arc::into_inner_with_allocator(arc); + // ignore-tidy-undocumented-unsafe + unsafe { Arc::from_ptr_in(ptr.as_ptr() as *mut ArcInner, alloc) } + } +} + +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "more_rc_default_impls", since = "1.80.0")] +impl Default for Arc { + /// Creates an empty CStr inside an Arc + /// + /// This may or may not share an allocation with other Arcs. + #[inline] + fn default() -> Self { + use core::ffi::CStr; + let inner: NonNull> = NonNull::from(&STATIC_INNER_SLICE.inner); + let inner: NonNull> = + NonNull::new(inner.as_ptr() as *mut ArcInner).unwrap(); + // `this` semantically is the Arc "owned" by the static, so make sure not to drop it. + let this: mem::ManuallyDrop> = + // ignore-tidy-undocumented-unsafe + unsafe { mem::ManuallyDrop::new(Arc::from_inner(inner)) }; + (*this).clone() + } +} + +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "more_rc_default_impls", since = "1.80.0")] +impl Default for Arc<[T]> { + /// Creates an empty `[T]` inside an Arc + /// + /// This may or may not share an allocation with other Arcs. + #[inline] + fn default() -> Self { + if align_of::() <= MAX_STATIC_INNER_SLICE_ALIGNMENT { + // We take a reference to the whole struct instead of the ArcInner<[u8; 1]> inside it so + // we don't shrink the range of bytes the ptr is allowed to access under Stacked Borrows. + // (Miri complains on 32-bit targets with Arc<[Align16]> otherwise.) + // (Note that NonNull::from(&STATIC_INNER_SLICE.inner) is fine under Tree Borrows.) + let inner: NonNull = NonNull::from(&STATIC_INNER_SLICE); + let inner: NonNull> = inner.cast(); + // `this` semantically is the Arc "owned" by the static, so make sure not to drop it. + let this: mem::ManuallyDrop> = + // ignore-tidy-undocumented-unsafe + unsafe { mem::ManuallyDrop::new(Arc::from_inner(inner)) }; + return (*this).clone(); + } + + // If T's alignment is too large for the static, make a new unique allocation. + let arr: [T; 0] = []; + Arc::from(arr) + } +} + +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "pin_default_impls", since = "1.91.0")] +impl Default for Pin> +where + T: ?Sized, + Arc: Default, +{ + #[inline] + fn default() -> Self { + // SAFETY: We own and create the pinned pointer. + unsafe { Pin::new_unchecked(Arc::::default()) } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Hash for Arc { + fn hash(&self, state: &mut H) { + (**self).hash(state) + } +} + +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "from_for_ptrs", since = "1.6.0")] +impl From for Arc { + /// Converts a `T` into an `Arc` + /// + /// The conversion moves the value into a + /// newly allocated `Arc`. It is equivalent to + /// calling `Arc::new(t)`. + /// + /// # Example + /// ```rust + /// # use std::sync::Arc; + /// let x = 5; + /// let arc = Arc::new(5); + /// + /// assert_eq!(Arc::from(x), arc); + /// ``` + fn from(t: T) -> Self { + Arc::new(t) + } +} + +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "shared_from_array", since = "1.74.0")] +impl From<[T; N]> for Arc<[T]> { + /// Converts a [`[T; N]`](prim@array) into an `Arc<[T]>`. + /// + /// The conversion moves the array into a newly allocated `Arc`. + /// + /// # Example + /// + /// ``` + /// # use std::sync::Arc; + /// let original: [i32; 3] = [1, 2, 3]; + /// let shared: Arc<[i32]> = Arc::from(original); + /// assert_eq!(&[1, 2, 3], &shared[..]); + /// ``` + #[inline] + fn from(v: [T; N]) -> Arc<[T]> { + Arc::<[T; N]>::from(v) + } +} + +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "shared_from_slice", since = "1.21.0")] +impl From<&[T]> for Arc<[T]> { + /// Allocates a reference-counted slice and fills it by cloning `v`'s items. + /// + /// # Example + /// + /// ``` + /// # use std::sync::Arc; + /// let original: &[i32] = &[1, 2, 3]; + /// let shared: Arc<[i32]> = Arc::from(original); + /// assert_eq!(&[1, 2, 3], &shared[..]); + /// ``` + #[inline] + fn from(v: &[T]) -> Arc<[T]> { + >::from_slice(v) + } +} + +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "shared_from_mut_slice", since = "1.84.0")] +impl From<&mut [T]> for Arc<[T]> { + /// Allocates a reference-counted slice and fills it by cloning `v`'s items. + /// + /// # Example + /// + /// ``` + /// # use std::sync::Arc; + /// let mut original = [1, 2, 3]; + /// let original: &mut [i32] = &mut original; + /// let shared: Arc<[i32]> = Arc::from(original); + /// assert_eq!(&[1, 2, 3], &shared[..]); + /// ``` + #[inline] + fn from(v: &mut [T]) -> Arc<[T]> { + Arc::from(&*v) + } +} + +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "shared_from_slice", since = "1.21.0")] +impl From<&str> for Arc { + /// Allocates a reference-counted `str` and copies `v` into it. + /// + /// # Example + /// + /// ``` + /// # use std::sync::Arc; + /// let shared: Arc = Arc::from("eggplant"); + /// assert_eq!("eggplant", &shared[..]); + /// ``` + #[inline] + fn from(v: &str) -> Arc { + let arc = Arc::<[u8]>::from(v.as_bytes()); + // ignore-tidy-undocumented-unsafe + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const str) } + } +} + +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "shared_from_mut_slice", since = "1.84.0")] +impl From<&mut str> for Arc { + /// Allocates a reference-counted `str` and copies `v` into it. + /// + /// # Example + /// + /// ``` + /// # use std::sync::Arc; + /// let mut original = String::from("eggplant"); + /// let original: &mut str = &mut original; + /// let shared: Arc = Arc::from(original); + /// assert_eq!("eggplant", &shared[..]); + /// ``` + #[inline] + fn from(v: &mut str) -> Arc { + Arc::from(&*v) + } +} + +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "shared_from_slice", since = "1.21.0")] +impl From for Arc { + /// Allocates a reference-counted `str` and copies `v` into it. + /// + /// # Example + /// + /// ``` + /// # use std::sync::Arc; + /// let unique: String = "eggplant".to_owned(); + /// let shared: Arc = Arc::from(unique); + /// assert_eq!("eggplant", &shared[..]); + /// ``` + #[inline] + fn from(v: String) -> Arc { + Arc::from(&v[..]) + } +} + +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "shared_from_slice", since = "1.21.0")] +impl From> for Arc { + /// Move a boxed object to a new, reference-counted allocation. + /// + /// # Example + /// + /// ``` + /// # use std::sync::Arc; + /// let unique: Box = Box::from("eggplant"); + /// let shared: Arc = Arc::from(unique); + /// assert_eq!("eggplant", &shared[..]); + /// ``` + #[inline] + fn from(v: Box) -> Arc { + Arc::from_box_in(v) + } +} + +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "shared_from_slice", since = "1.21.0")] +impl From> for Arc<[T], A> { + /// Allocates a reference-counted slice and moves `v`'s items into it. + /// + /// # Example + /// + /// ``` + /// # use std::sync::Arc; + /// let unique: Vec = vec![1, 2, 3]; + /// let shared: Arc<[i32]> = Arc::from(unique); + /// assert_eq!(&[1, 2, 3], &shared[..]); + /// ``` + #[inline] + fn from(v: Vec) -> Arc<[T], A> { + // ignore-tidy-undocumented-unsafe + unsafe { + let (vec_ptr, len, cap, alloc) = v.into_raw_parts_with_allocator(); + + let rc_ptr = Self::allocate_for_slice_in(len, &alloc); + ptr::copy_nonoverlapping(vec_ptr, (&raw mut (*rc_ptr).data) as *mut T, len); + + // Create a `Vec` with length 0, to deallocate the buffer + // without dropping its contents or the allocator + let _ = Vec::from_raw_parts_in(vec_ptr, 0, cap, &alloc); + + Self::from_ptr_in(rc_ptr, alloc) + } + } +} + +#[stable(feature = "shared_from_cow", since = "1.45.0")] +impl<'a, B> From> for Arc +where + B: ToOwned + ?Sized, + Arc: From<&'a B> + From, +{ + /// Creates an atomically reference-counted pointer from a clone-on-write + /// pointer by copying its content. + /// + /// # Example + /// + /// ```rust + /// # use std::sync::Arc; + /// # use std::borrow::Cow; + /// let cow: Cow<'_, str> = Cow::Borrowed("eggplant"); + /// let shared: Arc = Arc::from(cow); + /// assert_eq!("eggplant", &shared[..]); + /// ``` + #[inline] + fn from(cow: Cow<'a, B>) -> Arc { + match cow { + Cow::Borrowed(s) => Arc::from(s), + Cow::Owned(s) => Arc::from(s), + } + } +} + +#[stable(feature = "shared_from_str", since = "1.62.0")] +impl From> for Arc<[u8]> { + /// Converts an atomically reference-counted string slice into a byte slice. + /// + /// # Example + /// + /// ``` + /// # use std::sync::Arc; + /// let string: Arc = Arc::from("eggplant"); + /// let bytes: Arc<[u8]> = Arc::from(string); + /// assert_eq!("eggplant".as_bytes(), bytes.as_ref()); + /// ``` + #[inline] + fn from(rc: Arc) -> Self { + // SAFETY: `str` has the same layout as `[u8]`. + unsafe { Arc::from_raw(Arc::into_raw(rc) as *const [u8]) } + } +} + +#[stable(feature = "boxed_slice_try_from", since = "1.43.0")] +impl TryFrom> for Arc<[T; N], A> { + type Error = Arc<[T], A>; + + fn try_from(boxed_slice: Arc<[T], A>) -> Result { + if boxed_slice.len() == N { + let (ptr, alloc) = Arc::into_inner_with_allocator(boxed_slice); + // ignore-tidy-undocumented-unsafe + Ok(unsafe { Arc::from_inner_in(ptr.cast(), alloc) }) + } else { + Err(boxed_slice) + } + } +} + +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "shared_from_iter", since = "1.37.0")] +impl FromIterator for Arc<[T]> { + /// Takes each element in the `Iterator` and collects it into an `Arc<[T]>`. + /// + /// # Performance characteristics + /// + /// ## The general case + /// + /// In the general case, collecting into `Arc<[T]>` is done by first + /// collecting into a `Vec`. That is, when writing the following: + /// + /// ```rust + /// # use std::sync::Arc; + /// let evens: Arc<[u8]> = (0..10).filter(|&x| x % 2 == 0).collect(); + /// # assert_eq!(&*evens, &[0, 2, 4, 6, 8]); + /// ``` + /// + /// this behaves as if we wrote: + /// + /// ```rust + /// # use std::sync::Arc; + /// let evens: Arc<[u8]> = (0..10).filter(|&x| x % 2 == 0) + /// .collect::>() // The first set of allocations happens here. + /// .into(); // A second allocation for `Arc<[T]>` happens here. + /// # assert_eq!(&*evens, &[0, 2, 4, 6, 8]); + /// ``` + /// + /// This will allocate as many times as needed for constructing the `Vec` + /// and then it will allocate once for turning the `Vec` into the `Arc<[T]>`. + /// + /// ## Iterators of known length + /// + /// When your `Iterator` implements `TrustedLen` and is of an exact size, + /// a single allocation will be made for the `Arc<[T]>`. For example: + /// + /// ```rust + /// # use std::sync::Arc; + /// let evens: Arc<[u8]> = (0..10).collect(); // Just a single allocation happens here. + /// # assert_eq!(&*evens, &*(0..10).collect::>()); + /// ``` + fn from_iter>(iter: I) -> Self { + ToArcSlice::to_arc_slice(iter.into_iter()) + } +} + +#[cfg(not(no_global_oom_handling))] +/// Specialization trait used for collecting into `Arc<[T]>`. +trait ToArcSlice: Iterator + Sized { + fn to_arc_slice(self) -> Arc<[T]>; +} + +#[cfg(not(no_global_oom_handling))] +impl> ToArcSlice for I { + default fn to_arc_slice(self) -> Arc<[T]> { + self.collect::>().into() + } +} + +#[cfg(not(no_global_oom_handling))] +impl> ToArcSlice for I { + fn to_arc_slice(self) -> Arc<[T]> { + // This is the case for a `TrustedLen` iterator. + let (low, high) = self.size_hint(); + if let Some(high) = high { + debug_assert_eq!( + low, + high, + "TrustedLen iterator's size hint is not exact: {:?}", + (low, high) + ); + + // SAFETY: We need to ensure that the iterator has an exact length and we have. + unsafe { Arc::from_iter_exact(self, low) } + } else { + // TrustedLen contract guarantees that `upper_bound == None` implies an iterator + // length exceeding `usize::MAX`. + // The default implementation would collect into a vec which would panic. + // Thus we panic here immediately without invoking `Vec` code. + panic!("capacity overflow"); + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl borrow::Borrow for Arc { + fn borrow(&self) -> &T { + self + } +} + +#[stable(since = "1.5.0", feature = "smart_ptr_as_ref")] +impl AsRef for Arc { + fn as_ref(&self) -> &T { + self + } +} + +#[stable(feature = "pin", since = "1.33.0")] +impl Unpin for Arc {} + +/// Gets the offset within an `ArcInner` for the payload behind a pointer. +/// +/// # Safety +/// +/// The pointer must point to (and have valid metadata for) a previously +/// valid instance of T, but the T is allowed to be dropped. +unsafe fn data_offset(ptr: *const T) -> usize { + // Align the unsized value to the end of the ArcInner. + // Because ArcInner is repr(C), it will always be the last field in memory. + // SAFETY: since the only unsized types possible are slices, trait objects, + // and extern types, the input safety requirement is currently enough to + // satisfy the requirements of Alignment::of_val_raw; this is an implementation + // detail of the language that must not be relied upon outside of std. + unsafe { data_offset_alignment(Alignment::of_val_raw(ptr)) } +} + +#[inline] +fn data_offset_alignment(alignment: Alignment) -> usize { + let layout = Layout::new::>(); + layout.size() + layout.padding_needed_for(alignment) +} + +/// A unique owning pointer to an [`ArcInner`] **that does not imply the contents are initialized,** +/// but will deallocate it (without dropping the value) when dropped. +/// +/// This is a helper for [`Arc::make_mut()`] to ensure correct cleanup on panic. +struct UniqueArcUninit { + ptr: NonNull>, + layout_for_value: Layout, + alloc: Option, +} + +impl UniqueArcUninit { + /// Allocates an ArcInner with layout suitable to contain `for_value` or a clone of it. + #[cfg(not(no_global_oom_handling))] + fn new(for_value: &T, alloc: A) -> UniqueArcUninit { + let layout = Layout::for_value(for_value); + // ignore-tidy-undocumented-unsafe + let ptr = unsafe { + Arc::allocate_for_layout( + layout, + |layout_for_arcinner| alloc.allocate(layout_for_arcinner), + |mem| mem.with_metadata_of(ptr::from_ref(for_value) as *const ArcInner), + ) + }; + Self { ptr: NonNull::new(ptr).unwrap(), layout_for_value: layout, alloc: Some(alloc) } + } + + /// Allocates an ArcInner with layout suitable to contain `for_value` or a clone of it, + /// returning an error if allocation fails. + fn try_new(for_value: &T, alloc: A) -> Result, AllocError> { + let layout = Layout::for_value(for_value); + // ignore-tidy-undocumented-unsafe + let ptr = unsafe { + Arc::try_allocate_for_layout( + layout, + |layout_for_arcinner| alloc.allocate(layout_for_arcinner), + |mem| mem.with_metadata_of(ptr::from_ref(for_value) as *const ArcInner), + )? + }; + Ok(Self { ptr: NonNull::new(ptr).unwrap(), layout_for_value: layout, alloc: Some(alloc) }) + } + + /// Returns the pointer to be written into to initialize the [`Arc`]. + fn data_ptr(&mut self) -> *mut T { + let offset = data_offset_alignment(self.layout_for_value.alignment()); + // ignore-tidy-undocumented-unsafe + unsafe { self.ptr.as_ptr().byte_add(offset) as *mut T } + } + + /// Upgrade this into a normal [`Arc`]. + /// + /// # Safety + /// + /// The data must have been initialized (by writing to [`Self::data_ptr()`]). + unsafe fn into_arc(self) -> Arc { + let mut this = ManuallyDrop::new(self); + let ptr = this.ptr.as_ptr(); + let alloc = this.alloc.take().unwrap(); + + // SAFETY: The pointer is valid as per `UniqueArcUninit::new`, and the caller is responsible + // for having initialized the data. + unsafe { Arc::from_ptr_in(ptr, alloc) } + } +} + +impl Drop for UniqueArcUninit { + fn drop(&mut self) { + // SAFETY: + // * new() produced a pointer safe to deallocate. + // * We own the pointer unless into_arc() was called, which forgets us. + unsafe { + self.alloc.take().unwrap().deallocate( + self.ptr.cast(), + arcinner_layout_for_value_layout(self.layout_for_value), + ); + } + } +} + +#[stable(feature = "arc_error", since = "1.52.0")] +impl core::error::Error for Arc { + #[allow(deprecated)] + fn cause(&self) -> Option<&dyn core::error::Error> { + core::error::Error::cause(&**self) + } + + fn source(&self) -> Option<&(dyn core::error::Error + 'static)> { + core::error::Error::source(&**self) + } + + fn provide<'a>(&'a self, req: &mut core::error::Request<'a>) { + core::error::Error::provide(&**self, req); + } +} + +/// A uniquely owned [`Arc`]. +/// +/// This represents an `Arc` that is known to be uniquely owned -- that is, have exactly one strong +/// reference. Multiple weak pointers can be created, but attempts to upgrade those to strong +/// references will fail unless the `UniqueArc` they point to has been converted into a regular `Arc`. +/// +/// Because it is uniquely owned, the contents of a `UniqueArc` can be freely mutated. A common +/// use case is to have an object be mutable during its initialization phase but then have it become +/// immutable and converted to a normal `Arc`. +/// +/// This can be used as a flexible way to create cyclic data structures, as in the example below. +/// +/// ``` +/// #![feature(unique_rc_arc)] +/// use std::sync::{Arc, Weak, UniqueArc}; +/// +/// struct Gadget { +/// me: Weak, +/// } +/// +/// fn create_gadget() -> Option> { +/// let mut rc = UniqueArc::new(Gadget { +/// me: Weak::new(), +/// }); +/// rc.me = UniqueArc::downgrade(&rc); +/// Some(UniqueArc::into_arc(rc)) +/// } +/// +/// create_gadget().unwrap(); +/// ``` +/// +/// An advantage of using `UniqueArc` over [`Arc::new_cyclic`] to build cyclic data structures is that +/// [`Arc::new_cyclic`]'s `data_fn` parameter cannot be async or return a [`Result`]. As shown in the +/// previous example, `UniqueArc` allows for more flexibility in the construction of cyclic data, +/// including fallible or async constructors. +#[unstable(feature = "unique_rc_arc", issue = "112566")] +pub struct UniqueArc< + T: ?Sized, + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] A: Allocator = Global, +> { + ptr: NonNull>, + // Define the ownership of `ArcInner` for drop-check + _marker: PhantomData>, + // Invariance is necessary for soundness: once other `Weak` + // references exist, we already have a form of shared mutability! + _marker2: PhantomData<*mut T>, + alloc: A, +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +unsafe impl Send for UniqueArc {} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +unsafe impl Sync for UniqueArc {} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +// #[unstable(feature = "coerce_unsized", issue = "18598")] +impl, U: ?Sized, A: Allocator> CoerceUnsized> + for UniqueArc +{ +} + +//#[unstable(feature = "unique_rc_arc", issue = "112566")] +#[unstable(feature = "dispatch_from_dyn", issue = "none")] +impl, U: ?Sized> DispatchFromDyn> for UniqueArc {} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl fmt::Display for UniqueArc { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&**self, f) + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl fmt::Debug for UniqueArc { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl fmt::Pointer for UniqueArc { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Pointer::fmt(&(&raw const **self), f) + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl borrow::Borrow for UniqueArc { + fn borrow(&self) -> &T { + self + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl borrow::BorrowMut for UniqueArc { + fn borrow_mut(&mut self) -> &mut T { + self + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl AsRef for UniqueArc { + fn as_ref(&self) -> &T { + self + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl AsMut for UniqueArc { + fn as_mut(&mut self) -> &mut T { + self + } +} + +#[cfg(not(no_global_oom_handling))] +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl From for UniqueArc { + #[inline(always)] + fn from(value: T) -> Self { + Self::new(value) + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl Unpin for UniqueArc {} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl PartialEq for UniqueArc { + /// Equality for two `UniqueArc`s. + /// + /// Two `UniqueArc`s are equal if their inner values are equal. + /// + /// # Examples + /// + /// ``` + /// #![feature(unique_rc_arc)] + /// use std::sync::UniqueArc; + /// + /// let five = UniqueArc::new(5); + /// + /// assert!(five == UniqueArc::new(5)); + /// ``` + #[inline] + fn eq(&self, other: &Self) -> bool { + PartialEq::eq(&**self, &**other) + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl PartialOrd for UniqueArc { + /// Partial comparison for two `UniqueArc`s. + /// + /// The two are compared by calling `partial_cmp()` on their inner values. + /// + /// # Examples + /// + /// ``` + /// #![feature(unique_rc_arc)] + /// use std::sync::UniqueArc; + /// use std::cmp::Ordering; + /// + /// let five = UniqueArc::new(5); + /// + /// assert_eq!(Some(Ordering::Less), five.partial_cmp(&UniqueArc::new(6))); + /// ``` + #[inline(always)] + fn partial_cmp(&self, other: &UniqueArc) -> Option { + (**self).partial_cmp(&**other) + } + + /// Less-than comparison for two `UniqueArc`s. + /// + /// The two are compared by calling `<` on their inner values. + /// + /// # Examples + /// + /// ``` + /// #![feature(unique_rc_arc)] + /// use std::sync::UniqueArc; + /// + /// let five = UniqueArc::new(5); + /// + /// assert!(five < UniqueArc::new(6)); + /// ``` + #[inline(always)] + fn lt(&self, other: &UniqueArc) -> bool { + **self < **other + } + + /// 'Less than or equal to' comparison for two `UniqueArc`s. + /// + /// The two are compared by calling `<=` on their inner values. + /// + /// # Examples + /// + /// ``` + /// #![feature(unique_rc_arc)] + /// use std::sync::UniqueArc; + /// + /// let five = UniqueArc::new(5); + /// + /// assert!(five <= UniqueArc::new(5)); + /// ``` + #[inline(always)] + fn le(&self, other: &UniqueArc) -> bool { + **self <= **other + } + + /// Greater-than comparison for two `UniqueArc`s. + /// + /// The two are compared by calling `>` on their inner values. + /// + /// # Examples + /// + /// ``` + /// #![feature(unique_rc_arc)] + /// use std::sync::UniqueArc; + /// + /// let five = UniqueArc::new(5); + /// + /// assert!(five > UniqueArc::new(4)); + /// ``` + #[inline(always)] + fn gt(&self, other: &UniqueArc) -> bool { + **self > **other + } + + /// 'Greater than or equal to' comparison for two `UniqueArc`s. + /// + /// The two are compared by calling `>=` on their inner values. + /// + /// # Examples + /// + /// ``` + /// #![feature(unique_rc_arc)] + /// use std::sync::UniqueArc; + /// + /// let five = UniqueArc::new(5); + /// + /// assert!(five >= UniqueArc::new(5)); + /// ``` + #[inline(always)] + fn ge(&self, other: &UniqueArc) -> bool { + **self >= **other + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl Ord for UniqueArc { + /// Comparison for two `UniqueArc`s. + /// + /// The two are compared by calling `cmp()` on their inner values. + /// + /// # Examples + /// + /// ``` + /// #![feature(unique_rc_arc)] + /// use std::sync::UniqueArc; + /// use std::cmp::Ordering; + /// + /// let five = UniqueArc::new(5); + /// + /// assert_eq!(Ordering::Less, five.cmp(&UniqueArc::new(6))); + /// ``` + #[inline] + fn cmp(&self, other: &UniqueArc) -> Ordering { + (**self).cmp(&**other) + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl Eq for UniqueArc {} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl Hash for UniqueArc { + fn hash(&self, state: &mut H) { + (**self).hash(state); + } +} + +impl UniqueArc { + /// Creates a new `UniqueArc`. + /// + /// Weak references to this `UniqueArc` can be created with [`UniqueArc::downgrade`]. Upgrading + /// these weak references will fail before the `UniqueArc` has been converted into an [`Arc`]. + /// After converting the `UniqueArc` into an [`Arc`], any weak references created beforehand will + /// point to the new [`Arc`]. + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "unique_rc_arc", issue = "112566")] + #[must_use] + pub fn new(value: T) -> Self { + Self::new_in(value, Global) + } + + /// Like [`new`](Self::new), but returns an error if the allocation + /// fails, instead of calling [`handle_alloc_error`]. + #[unstable(feature = "unique_rc_arc", issue = "112566")] + pub fn try_new(value: T) -> Result { + Self::try_new_in(value, Global) + } +} + +impl UniqueArc { + /// Creates a new `UniqueArc` in the provided allocator. + /// + /// Weak references to this `UniqueArc` can be created with [`UniqueArc::downgrade`]. Upgrading + /// these weak references will fail before the `UniqueArc` has been converted into an [`Arc`]. + /// After converting the `UniqueArc` into an [`Arc`], any weak references created beforehand will + /// point to the new [`Arc`]. + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "unique_rc_arc", issue = "112566")] + // #[unstable(feature = "allocator_api", issue = "163177")] + #[must_use] + pub fn new_in(data: T, alloc: A) -> Self { + let (ptr, alloc) = Box::into_non_null_with_allocator(Box::new_in( + ArcInner { + strong: atomic::AtomicUsize::new(0), + // keep one weak reference so if all the weak pointers that are created are dropped + // the UniqueArc still stays valid. + weak: atomic::AtomicUsize::new(1), + data, + }, + alloc, + )); + Self { ptr, _marker: PhantomData, _marker2: PhantomData, alloc } + } + + /// Like [`new_in`](Self::new_in), but returns an error if the allocation + /// fails, instead of calling [`handle_alloc_error`]. + #[unstable(feature = "unique_rc_arc", issue = "112566")] + // #[unstable(feature = "allocator_api", issue = "163177")] + pub fn try_new_in(data: T, alloc: A) -> Result { + let (ptr, alloc) = Box::into_non_null_with_allocator(Box::try_new_in( + ArcInner { + strong: atomic::AtomicUsize::new(0), + // keep one weak reference so if all the weak pointers that are created are dropped + // the UniqueArc still stays valid. + weak: atomic::AtomicUsize::new(1), + data, + }, + alloc, + )?); + Ok(Self { ptr, _marker: PhantomData, _marker2: PhantomData, alloc }) + } + + /// Consumes the `UniqueArc`, returning its wrapped value and allocator. + #[unstable(feature = "unique_rc_arc", issue = "112566")] + // #[unstable(feature = "allocator_api", issue = "163177")] + #[must_use] + pub fn unwrap_with_allocator(this: Self) -> (T, A) { + let inner_ptr = this.ptr; + let (data_ptr, alloc) = Self::into_raw_with_allocator(this); + + // SAFETY: Conceptually moves out of the `UniqueRc`. + // We do not use the data inside ever again. + let val = unsafe { data_ptr.read() }; + + // Drop the strong-weak ref + drop(Weak { ptr: inner_ptr, alloc: &alloc }); + + (val, alloc) + } + + /// Consumes the `UniqueArc`, returning its wrapped value. + #[unstable(feature = "unique_rc_arc", issue = "112566")] + #[must_use] + pub fn unwrap(this: Self) -> T { + Self::unwrap_with_allocator(this).0 + } + + /// Maps the value in a `UniqueArc`, reusing the allocation if possible. + /// + /// `f` is called on a reference to the value in the `UniqueArc`, and the result is returned, + /// also in a `UniqueArc`. + /// + /// Note: this is an associated function, which means that you have + /// to call it as `UniqueArc::map(u, f)` instead of `u.map(f)`. This + /// is so that there is no conflict with a method on the inner type. + /// + /// # Examples + /// + /// ``` + /// #![feature(unique_rc_arc)] + /// + /// use std::sync::UniqueArc; + /// + /// let r = UniqueArc::new(7); + /// let new = UniqueArc::map(r, |i| i + 7); + /// assert_eq!(*new, 14); + /// ``` + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "unique_rc_arc", issue = "112566")] + pub fn map(this: Self, f: impl FnOnce(T) -> U) -> UniqueArc { + if size_of::() == size_of::() + && align_of::() == align_of::() + && UniqueArc::weak_count(&this) == 0 + { + // ignore-tidy-undocumented-unsafe + unsafe { + let (ptr, alloc) = UniqueArc::into_raw_with_allocator(this); + let value = ptr.read(); + let allocation = + UniqueArc::from_raw_with_allocator(ptr.cast::>(), alloc); + + UniqueArc::write(allocation, f(value)) + } + } else { + let (val, alloc) = UniqueArc::unwrap_with_allocator(this); + UniqueArc::new_in(f(val), alloc) + } + } + + /// Attempts to map the value in a `UniqueArc`, reusing the allocation if possible. + /// + /// `f` is called on a reference to the value in the `UniqueArc`, and if the operation succeeds, + /// the result is returned, also in a `UniqueArc`. + /// + /// Note: this is an associated function, which means that you have + /// to call it as `UniqueArc::try_map(u, f)` instead of `u.try_map(f)`. This + /// is so that there is no conflict with a method on the inner type. + /// + /// # Examples + /// + /// ``` + /// #![feature(smart_pointer_try_map)] + /// #![feature(unique_rc_arc)] + /// + /// use std::sync::UniqueArc; + /// + /// let b = UniqueArc::new(7); + /// let new = UniqueArc::try_map(b, u32::try_from).unwrap(); + /// assert_eq!(*new, 7); + /// ``` + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "smart_pointer_try_map", issue = "144419")] + pub fn try_map( + this: Self, + f: impl FnOnce(T) -> R, + ) -> >>::TryType + where + R: Try, + R::Residual: Residual>, + { + if size_of::() == size_of::() + && align_of::() == align_of::() + && UniqueArc::weak_count(&this) == 0 + { + // ignore-tidy-undocumented-unsafe + unsafe { + let (ptr, alloc) = UniqueArc::into_raw_with_allocator(this); + let value = ptr.read(); + let allocation = UniqueArc::from_raw_with_allocator( + ptr.cast::>(), + alloc, + ); + + try { UniqueArc::write(allocation, f(value)?) } + } + } else { + let (val, alloc) = UniqueArc::unwrap_with_allocator(this); + try { UniqueArc::new_in(f(val)?, alloc) } + } + } +} + +impl UniqueArc { + #[cfg(not(no_global_oom_handling))] + unsafe fn from_raw_with_allocator(ptr: *const T, alloc: A) -> Self { + // SAFETY: Upheld by caller. + let offset = unsafe { data_offset(ptr) }; + + // Reverse the offset to find the original ArcInner. + // SAFETY: Upheld by caller. + let rc_ptr = unsafe { ptr.byte_sub(offset) as *mut ArcInner }; + + Self { + // SAFETY: Upheld by caller. + ptr: unsafe { NonNull::new_unchecked(rc_ptr) }, + _marker: PhantomData, + _marker2: PhantomData, + alloc, + } + } + + fn into_raw_with_allocator(this: Self) -> (*const T, A) { + let this = ManuallyDrop::new(this); + // SAFETY: The copy of the allocator stored in `this` is forgotten + (Self::as_ptr(&*this), unsafe { ptr::read(&this.alloc) }) + } + + /// Converts the `UniqueArc` into a regular [`Arc`]. + /// + /// This consumes the `UniqueArc` and returns a regular [`Arc`] that contains the `value` that + /// is passed to `into_arc`. + /// + /// Any weak references created before this method is called can now be upgraded to strong + /// references. + #[unstable(feature = "unique_rc_arc", issue = "112566")] + #[must_use] + pub fn into_arc(this: Self) -> Arc { + let this = ManuallyDrop::new(this); + + // Move the allocator out. + // SAFETY: `this.alloc` will not be accessed again, nor dropped because it is in + // a `ManuallyDrop`. + let alloc: A = unsafe { ptr::read(&this.alloc) }; + + // SAFETY: This pointer was allocated at creation time so we know it is valid. + unsafe { + // Convert our weak reference into a strong reference + (*this.ptr.as_ptr()).strong.store(1, Release); + Arc::from_inner_in(this.ptr, alloc) + } + } + + #[cfg(not(no_global_oom_handling))] + fn weak_count(this: &Self) -> usize { + this.inner().weak.load(Acquire) - 1 + } + + #[cfg(not(no_global_oom_handling))] + fn inner(&self) -> &ArcInner { + // SAFETY: while this UniqueArc is alive we're guaranteed that the inner pointer is valid. + unsafe { self.ptr.as_ref() } + } + + fn as_ptr(this: &Self) -> *const T { + let ptr: *mut ArcInner = NonNull::as_ptr(this.ptr); + + // SAFETY: This cannot go through Deref::deref or UniqueArc::inner because + // this is required to retain raw/mut provenance such that e.g. `get_mut` can + // write through the pointer after the Rc is recovered through `from_raw`. + unsafe { &raw mut (*ptr).data } + } + + #[inline] + fn into_inner_with_allocator(this: Self) -> (NonNull>, A) { + let this = mem::ManuallyDrop::new(this); + // SAFETY: Pointer is valid for reads and only read once. + (this.ptr, unsafe { ptr::read(&this.alloc) }) + } + + #[inline] + unsafe fn from_inner_in(ptr: NonNull>, alloc: A) -> Self { + Self { ptr, _marker: PhantomData, _marker2: PhantomData, alloc } + } +} + +impl UniqueArc { + /// Creates a new weak reference to the `UniqueArc`. + /// + /// Attempting to upgrade this weak reference will fail before the `UniqueArc` has been converted + /// to a [`Arc`] using [`UniqueArc::into_arc`]. + #[unstable(feature = "unique_rc_arc", issue = "112566")] + #[must_use] + pub fn downgrade(this: &Self) -> Weak { + // Using a relaxed ordering is alright here, as knowledge of the + // original reference prevents other threads from erroneously deleting + // the object or converting the object to a normal `Arc`. + // + // Note that we don't need to test if the weak counter is locked because there + // are no such operations like `Arc::get_mut` or `Arc::make_mut` that will lock + // the weak counter. + // + // SAFETY: This pointer was allocated at creation time so we know it is valid. + let old_size = unsafe { (*this.ptr.as_ptr()).weak.fetch_add(1, Relaxed) }; + + // See comments in Arc::clone() for why we do this (for mem::forget). + if old_size > MAX_REFCOUNT { + intrinsics::abort_immediate(); + } + + Weak { ptr: this.ptr, alloc: this.alloc.clone() } + } +} + +impl UniqueArc, A> { + /// Writes the value and converts to `UniqueArc`. + /// + /// This method converts similarly to [`assume_init`](Self::assume_init) but + /// writes `value` into it before conversion, thus guaranteeing safety. + #[unstable(feature = "unique_rc_arc", issue = "112566")] + #[must_use] + pub fn write(mut this: Self, value: T) -> UniqueArc { + // SAFETY: Writing initialises the wrapped value. + unsafe { + this.write(value); + this.assume_init() + } + } + + /// Converts to `UniqueArc`. + /// + /// # Safety + /// + /// As with [`MaybeUninit::assume_init`], + /// it is up to the caller to guarantee that the value + /// really is in an initialized state. + /// Calling this when the content is not yet fully initialized + /// causes immediate undefined behavior. + /// + /// [`MaybeUninit::assume_init`]: mem::MaybeUninit::assume_init + #[unstable(feature = "unique_rc_arc", issue = "112566")] + #[must_use] + pub unsafe fn assume_init(self) -> UniqueArc { + let (ptr, alloc) = UniqueArc::into_inner_with_allocator(self); + // SAFETY: Upheld by caller. + unsafe { UniqueArc::from_inner_in(ptr.cast(), alloc) } + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl Deref for UniqueArc { + type Target = T; + + fn deref(&self) -> &T { + // SAFETY: This pointer was allocated at creation time so we know it is valid. + unsafe { &self.ptr.as_ref().data } + } +} + +// #[unstable(feature = "unique_rc_arc", issue = "112566")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] +unsafe impl PinSafePointer for UniqueArc {} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl DerefMut for UniqueArc { + fn deref_mut(&mut self) -> &mut T { + // SAFETY: This pointer was allocated at creation time so we know it is valid. We know we + // have unique ownership and therefore it's safe to make a mutable reference because + // `UniqueArc` owns the only strong reference to itself. + // We also need to be careful to only create a mutable reference to the `data` field, + // as a mutable reference to the entire `ArcInner` would assert uniqueness over the + // ref count fields too, invalidating any attempt by `Weak`s to access the ref count. + unsafe { &mut (*self.ptr.as_ptr()).data } + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +// #[unstable(feature = "deref_pure_trait", issue = "87121")] +unsafe impl DerefPure for UniqueArc {} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for UniqueArc { + fn drop(&mut self) { + // See `Arc::drop_slow` which drops an `Arc` with a strong count of 0. + // SAFETY: This pointer was allocated at creation time so we know it is valid. + let _weak = Weak { ptr: self.ptr, alloc: &self.alloc }; + + // ignore-tidy-undocumented-unsafe + unsafe { ptr::drop_in_place(&mut (*self.ptr.as_ptr()).data) }; + } +} + +#[stable(feature = "allocator_api", since = "1.100.0")] +unsafe impl Allocator for Arc { + #[inline] + fn allocate(&self, layout: Layout) -> Result, AllocError> { + (**self).allocate(layout) + } + + #[inline] + fn allocate_zeroed(&self, layout: Layout) -> Result, AllocError> { + (**self).allocate_zeroed(layout) + } + + #[inline] + unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { + // SAFETY: the safety contract must be upheld by the caller + unsafe { (**self).deallocate(ptr, layout) } + } + + #[inline] + unsafe fn grow( + &self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + ) -> Result, AllocError> { + // SAFETY: the safety contract must be upheld by the caller + unsafe { (**self).grow(ptr, old_layout, new_layout) } + } + + #[inline] + unsafe fn grow_zeroed( + &self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + ) -> Result, AllocError> { + // SAFETY: the safety contract must be upheld by the caller + unsafe { (**self).grow_zeroed(ptr, old_layout, new_layout) } + } + + #[inline] + unsafe fn shrink( + &self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + ) -> Result, AllocError> { + // SAFETY: the safety contract must be upheld by the caller + unsafe { (**self).shrink(ptr, old_layout, new_layout) } + } +} +#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] +#[unstable_feature_bound(allocator_ext)] +impl AllocatorNightly for Arc {} + +#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] +unsafe impl AllocatorClone for Arc {} diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rcs/rc.rs similarity index 94% rename from library/alloc/src/rc.rs rename to library/alloc/src/rcs/rc.rs index 7e793aea71e3c..696bd733b992d 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rcs/rc.rs @@ -248,7 +248,6 @@ use core::clone::TrivialClone; use core::clone::{CloneToUninit, Share, UseCloned}; use core::cmp::Ordering; use core::hash::{Hash, Hasher}; -use core::intrinsics::abort; #[cfg(not(no_global_oom_handling))] use core::iter; use core::marker::{PhantomData, Unsize}; @@ -264,11 +263,13 @@ use core::pin::PinSafePointer; use core::ptr::{self, NonNull, drop_in_place}; #[cfg(not(no_global_oom_handling))] use core::slice::from_raw_parts_mut; -use core::{borrow, fmt, hint}; +use core::{borrow, fmt, hint, intrinsics}; -use crate::alloc::{AllocError, Allocator, AllocatorClone, Global, Layout}; #[cfg(not(no_global_oom_handling))] -use crate::alloc::{AllocatorNightly, handle_alloc_error}; +use crate::alloc::handle_alloc_error; +use crate::alloc::{ + AllocError, Allocator, AllocatorClone, AllocatorNightly, Global, Layout, StaticAllocator, +}; use crate::borrow::{Cow, ToOwned}; use crate::boxed::Box; #[cfg(not(no_global_oom_handling))] @@ -323,7 +324,10 @@ fn rc_inner_layout_for_value_layout(layout: Layout) -> Layout { pub struct Rc< T: ?Sized, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, + // FIXME: When stabilizing this parameter, the `AllocatorNightly` + // bound on the `From` impl *must* be weaked to `Allocator`, + // as `Box` is fundamental! + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] A: Allocator = Global, > { ptr: NonNull>, phantom: PhantomData>, @@ -566,13 +570,13 @@ impl Rc { /// # Examples /// /// ``` - /// #![feature(allocator_api)] + /// #![feature(allocator_ext)] /// use std::rc::Rc; /// /// let five = Rc::try_new(5); /// # Ok::<(), std::alloc::AllocError>(()) /// ``` - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] pub fn try_new(value: T) -> Result, AllocError> { // SAFETY: There is an implicit weak pointer owned by all the strong // pointers, which ensures that the weak destructor never frees @@ -592,7 +596,7 @@ impl Rc { /// # Examples /// /// ``` - /// #![feature(allocator_api)] + /// #![feature(allocator_ext)] /// /// use std::rc::Rc; /// @@ -606,7 +610,7 @@ impl Rc { /// assert_eq!(*five, 5); /// # Ok::<(), std::alloc::AllocError>(()) /// ``` - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] pub fn try_new_uninit() -> Result>, AllocError> { // ignore-tidy-undocumented-unsafe unsafe { @@ -627,7 +631,7 @@ impl Rc { /// # Examples /// /// ``` - /// #![feature(allocator_api)] + /// #![feature(allocator_ext)] /// /// use std::rc::Rc; /// @@ -639,7 +643,7 @@ impl Rc { /// ``` /// /// [zeroed]: mem::MaybeUninit::zeroed - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] pub fn try_new_zeroed() -> Result>, AllocError> { // ignore-tidy-undocumented-unsafe unsafe { @@ -667,7 +671,7 @@ impl Rc { /// # Examples /// /// ``` - /// #![feature(allocator_api)] + /// #![feature(allocator_ext)] /// /// use std::rc::Rc; /// use std::alloc::System; @@ -675,7 +679,7 @@ impl Rc { /// let five = Rc::new_in(5, System); /// ``` #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] #[inline] pub fn new_in(value: T, alloc: A) -> Rc { // NOTE: Prefer match over unwrap_or_else since closure sometimes not inlineable. @@ -692,7 +696,7 @@ impl Rc { /// /// ``` /// #![feature(get_mut_unchecked)] - /// #![feature(allocator_api)] + /// #![feature(allocator_ext)] /// /// use std::rc::Rc; /// use std::alloc::System; @@ -709,7 +713,7 @@ impl Rc { /// assert_eq!(*five, 5) /// ``` #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] #[inline] pub fn new_uninit_in(alloc: A) -> Rc, A> { // ignore-tidy-undocumented-unsafe @@ -734,7 +738,7 @@ impl Rc { /// # Examples /// /// ``` - /// #![feature(allocator_api)] + /// #![feature(allocator_ext)] /// /// use std::rc::Rc; /// use std::alloc::System; @@ -747,7 +751,7 @@ impl Rc { /// /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] #[inline] pub fn new_zeroed_in(alloc: A) -> Rc, A> { // ignore-tidy-undocumented-unsafe @@ -793,7 +797,7 @@ impl Rc { /// [`new_cyclic`]: Rc::new_cyclic /// [`upgrade`]: Weak::upgrade #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] pub fn new_cyclic_in(data_fn: F, alloc: A) -> Rc where F: FnOnce(&Weak) -> T, @@ -845,14 +849,14 @@ impl Rc { /// # Examples /// /// ``` - /// #![feature(allocator_api)] + /// #![feature(allocator_ext)] /// use std::rc::Rc; /// use std::alloc::System; /// /// let five = Rc::try_new_in(5, System); /// # Ok::<(), std::alloc::AllocError>(()) /// ``` - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] #[inline] pub fn try_new_in(value: T, alloc: A) -> Result { // There is an implicit weak pointer owned by all the strong @@ -873,7 +877,7 @@ impl Rc { /// # Examples /// /// ``` - /// #![feature(allocator_api)] + /// #![feature(allocator_ext)] /// #![feature(get_mut_unchecked)] /// /// use std::rc::Rc; @@ -891,7 +895,7 @@ impl Rc { /// assert_eq!(*five, 5); /// # Ok::<(), std::alloc::AllocError>(()) /// ``` - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] #[inline] pub fn try_new_uninit_in(alloc: A) -> Result, A>, AllocError> { // ignore-tidy-undocumented-unsafe @@ -917,7 +921,7 @@ impl Rc { /// # Examples /// /// ``` - /// #![feature(allocator_api)] + /// #![feature(allocator_ext)] /// /// use std::rc::Rc; /// use std::alloc::System; @@ -930,7 +934,7 @@ impl Rc { /// ``` /// /// [zeroed]: mem::MaybeUninit::zeroed - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] #[inline] pub fn try_new_zeroed_in(alloc: A) -> Result, A>, AllocError> { // ignore-tidy-undocumented-unsafe @@ -949,11 +953,11 @@ impl Rc { /// Constructs a new `Pin>` in the provided allocator. If `T` does not implement `Unpin`, then /// `value` will be pinned in memory and unable to be moved. #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] #[inline] pub fn pin_in(value: T, alloc: A) -> Pin where - A: 'static, + A: StaticAllocator, { // SAFETY: We own and create the pinned pointer. unsafe { Pin::new_unchecked(Rc::new_in(value, alloc)) } @@ -1056,7 +1060,7 @@ impl Rc { /// assert_eq!(*new, 14); /// ``` #[cfg(not(no_global_oom_handling))] - #[stable(feature = "smart_pointer_map", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "smart_pointer_map", since = "1.100.0")] pub fn map(this: Self, f: impl FnOnce(&T) -> U) -> Rc { if size_of::() == size_of::() && align_of::() == align_of::() @@ -1204,7 +1208,7 @@ impl Rc<[T], A> { /// /// ``` /// #![feature(get_mut_unchecked)] - /// #![feature(allocator_api)] + /// #![feature(allocator_ext)] /// /// use std::rc::Rc; /// use std::alloc::System; @@ -1223,7 +1227,7 @@ impl Rc<[T], A> { /// assert_eq!(*values, [1, 2, 3]) /// ``` #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] #[inline] pub fn new_uninit_slice_in(len: usize, alloc: A) -> Rc<[mem::MaybeUninit], A> { // ignore-tidy-undocumented-unsafe @@ -1239,7 +1243,7 @@ impl Rc<[T], A> { /// # Examples /// /// ``` - /// #![feature(allocator_api)] + /// #![feature(allocator_ext)] /// /// use std::rc::Rc; /// use std::alloc::System; @@ -1252,7 +1256,7 @@ impl Rc<[T], A> { /// /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] #[inline] pub fn new_zeroed_slice_in(len: usize, alloc: A) -> Rc<[mem::MaybeUninit], A> { // ignore-tidy-undocumented-unsafe @@ -1361,14 +1365,13 @@ impl Rc { /// /// ``` /// #![feature(clone_from_ref)] - /// #![feature(allocator_api)] /// use std::rc::Rc; /// /// let hello: Rc = Rc::try_clone_from_ref("hello")?; /// # Ok::<(), std::alloc::AllocError>(()) /// ``` #[unstable(feature = "clone_from_ref", issue = "149075")] - //#[unstable(feature = "allocator_api", issue = "32838")] + //#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] pub fn try_clone_from_ref(value: &T) -> Result, AllocError> { Rc::try_clone_from_ref_in(value, Global) } @@ -1381,7 +1384,7 @@ impl Rc { /// /// ``` /// #![feature(clone_from_ref)] - /// #![feature(allocator_api)] + /// #![feature(allocator_ext)] /// use std::rc::Rc; /// use std::alloc::System; /// @@ -1389,7 +1392,7 @@ impl Rc { /// ``` #[cfg(not(no_global_oom_handling))] #[unstable(feature = "clone_from_ref", issue = "149075")] - //#[unstable(feature = "allocator_api", issue = "32838")] + //#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] pub fn clone_from_ref_in(value: &T, alloc: A) -> Rc { // `in_progress` drops the allocation if we panic before finishing initializing it. let mut in_progress: UniqueRcUninit = UniqueRcUninit::new(value, alloc); @@ -1410,7 +1413,7 @@ impl Rc { /// /// ``` /// #![feature(clone_from_ref)] - /// #![feature(allocator_api)] + /// #![feature(allocator_ext)] /// use std::rc::Rc; /// use std::alloc::System; /// @@ -1418,7 +1421,7 @@ impl Rc { /// # Ok::<(), std::alloc::AllocError>(()) /// ``` #[unstable(feature = "clone_from_ref", issue = "149075")] - //#[unstable(feature = "allocator_api", issue = "32838")] + //#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] pub fn try_clone_from_ref_in(value: &T, alloc: A) -> Result, AllocError> { // `in_progress` drops the allocation if we panic before finishing initializing it. let mut in_progress: UniqueRcUninit = UniqueRcUninit::try_new(value, alloc)?; @@ -1676,6 +1679,7 @@ impl Rc { /// } /// ``` #[inline] + #[must_use] #[unstable(feature = "arc_raw_get_strong", issue = "157021")] pub unsafe fn strong_count_from_raw(ptr: *const T) -> usize { // SAFETY: Upheld by caller. @@ -1696,7 +1700,7 @@ impl Rc { /// to call it as `Rc::allocator(&r)` instead of `r.allocator()`. This /// is so that there is no conflict with a method on the inner type. #[inline] - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] pub fn allocator(this: &Self) -> &A { &this.alloc } @@ -1709,7 +1713,7 @@ impl Rc { /// # Examples /// /// ``` - /// #![feature(allocator_api)] + /// #![feature(allocator_ext)] /// use std::rc::Rc; /// use std::alloc::System; /// @@ -1720,7 +1724,7 @@ impl Rc { /// assert_eq!(&*x, "hello"); /// ``` #[must_use = "losing the pointer will leak memory"] - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] pub fn into_raw_with_allocator(this: Self) -> (*const T, A) { let this = mem::ManuallyDrop::new(this); let ptr = Self::as_ptr(&this); @@ -1791,7 +1795,7 @@ impl Rc { /// # Examples /// /// ``` - /// #![feature(allocator_api)] + /// #![feature(allocator_ext)] /// /// use std::rc::Rc; /// use std::alloc::System; @@ -1813,7 +1817,7 @@ impl Rc { /// Convert a slice back into its original array: /// /// ``` - /// #![feature(allocator_api)] + /// #![feature(allocator_ext)] /// /// use std::rc::Rc; /// use std::alloc::System; @@ -1826,7 +1830,7 @@ impl Rc { /// assert_eq!(&*x, &[1, 2, 3]); /// } /// ``` - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] pub unsafe fn from_raw_in(ptr: *const T, alloc: A) -> Self { // ignore-tidy-undocumented-unsafe let offset = unsafe { data_offset(ptr) }; @@ -1876,6 +1880,7 @@ impl Rc { /// assert_eq!(1, Rc::weak_count(&five)); /// ``` #[inline] + #[must_use] #[stable(feature = "rc_counts", since = "1.15.0")] pub fn weak_count(this: &Self) -> usize { this.inner().weak() - 1 @@ -1894,6 +1899,7 @@ impl Rc { /// assert_eq!(2, Rc::strong_count(&five)); /// ``` #[inline] + #[must_use] #[stable(feature = "rc_counts", since = "1.15.0")] pub fn strong_count(this: &Self) -> usize { this.inner().strong() @@ -1915,7 +1921,7 @@ impl Rc { /// # Examples /// /// ``` - /// #![feature(allocator_api)] + /// #![feature(allocator_ext)] /// /// use std::rc::Rc; /// use std::alloc::System; @@ -1933,7 +1939,7 @@ impl Rc { /// } /// ``` #[inline] - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] pub unsafe fn increment_strong_count_in(ptr: *const T, alloc: A) where A: AllocatorClone, @@ -1962,7 +1968,7 @@ impl Rc { /// # Examples /// /// ``` - /// #![feature(allocator_api)] + /// #![feature(allocator_ext)] /// /// use std::rc::Rc; /// use std::alloc::System; @@ -1980,16 +1986,61 @@ impl Rc { /// } /// ``` #[inline] - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] pub unsafe fn decrement_strong_count_in(ptr: *const T, alloc: A) { // SAFETY: Upheld by caller. unsafe { drop(Rc::from_raw_in(ptr, alloc)) }; } - /// Returns `true` if there are no other `Rc` or [`Weak`] pointers to - /// this allocation. + /// Determine whether this is the unique reference to the underlying data. + /// + /// Returns `true` if there are no other `Rc` or [`Weak`] pointers to the same allocation; + /// returns `false` otherwise. + /// + /// If this function returns `true`, it is safe to call [`get_mut_unchecked`] + /// immediately afterward. + /// + /// # Examples + /// + /// ``` + /// #![feature(arc_is_unique)] + /// + /// use std::rc::Rc; + /// + /// let x = Rc::new(3); + /// assert!(Rc::is_unique(&x)); + /// + /// let y = Rc::clone(&x); + /// assert!(!Rc::is_unique(&x)); + /// drop(y); + /// + /// // Weak references also count, because they could be upgraded at any time. + /// let z = Rc::downgrade(&x); + /// assert!(!Rc::is_unique(&x)); + /// ``` + /// + /// # Pointer invalidation + /// + /// This function will always return the same value as `Rc::get_mut(rc).is_some()`. However, + /// unlike that operation it does not produce any mutable references to the underlying data, + /// meaning no pointers to the data inside the `Rc` are invalidated by the call. Thus, the + /// following code is valid, even though it would be UB if it used `Rc::get_mut`: + /// + /// ``` + /// #![feature(arc_is_unique)] + /// + /// use std::rc::Rc; + /// + /// let rc = Rc::new(5); + /// let pointer: *const i32 = &*rc; + /// assert!(Rc::is_unique(&rc)); + /// assert_eq!(unsafe { *pointer }, 5); + /// ``` + /// + /// [`get_mut_unchecked`]: Self::get_mut_unchecked #[inline] - fn is_unique(this: &Self) -> bool { + #[unstable(feature = "arc_is_unique", issue = "138938")] + pub fn is_unique(this: &Self) -> bool { Rc::weak_count(this) == 0 && Rc::strong_count(this) == 1 } @@ -2550,11 +2601,11 @@ impl Deref for Rc { // accordingly. Since this type is not fundamental, downstream crates cannot // provide malicious implementations of any of the traits relevant for `Pin`. #[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] -unsafe impl PinSafePointer for Rc {} +unsafe impl PinSafePointer for Rc {} //#[unstable(feature = "unique_rc_arc", issue = "112566")] #[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] -unsafe impl PinSafePointer for UniqueRc {} +unsafe impl PinSafePointer for UniqueRc {} #[unstable(feature = "deref_pure_trait", issue = "87121")] unsafe impl DerefPure for Rc {} @@ -3304,7 +3355,7 @@ impl> ToRcSlice for I { #[rustc_diagnostic_item = "RcWeak"] pub struct Weak< T: ?Sized, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] A: Allocator = Global, > { // This is a `NonNull` to allow optimizing the size of this type in enums, // but it is not necessarily a valid pointer. @@ -3369,7 +3420,7 @@ impl Weak { /// assert!(empty.upgrade().is_none()); /// ``` #[inline] - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] pub fn new_in(alloc: A) -> Weak { Weak { ptr: NonNull::without_provenance(NonZeroUsize::MAX), alloc } } @@ -3473,7 +3524,7 @@ impl Weak { impl Weak { /// Returns a reference to the underlying allocator. #[inline] - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] pub fn allocator(&self) -> &A { &self.alloc } @@ -3532,7 +3583,7 @@ impl Weak { /// # Examples /// /// ``` - /// #![feature(allocator_api)] + /// #![feature(allocator_ext)] /// use std::rc::{Rc, Weak}; /// use std::alloc::System; /// @@ -3551,7 +3602,7 @@ impl Weak { /// [`as_ptr`]: Weak::as_ptr #[must_use = "losing the pointer will leak memory"] #[inline] - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] pub fn into_raw_with_allocator(self) -> (*const T, A) { let this = mem::ManuallyDrop::new(self); let result = this.as_ptr(); @@ -3603,7 +3654,7 @@ impl Weak { /// [`upgrade`]: Weak::upgrade /// [`new`]: Weak::new #[inline] - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] pub unsafe fn from_raw_in(ptr: *const T, alloc: A) -> Self { // See Weak::as_ptr for context on how the input pointer is derived. @@ -3893,8 +3944,8 @@ trait RcInnerPtr { // We want to abort on overflow instead of dropping the value. // Checking for overflow after the store instead of before // allows for slightly better code generation. - if core::intrinsics::unlikely(strong == 0) { - abort(); + if intrinsics::unlikely(strong == 0) { + intrinsics::abort_immediate(); } } @@ -3926,8 +3977,8 @@ trait RcInnerPtr { // We want to abort on overflow instead of dropping the value. // Checking for overflow after the store instead of before // allows for slightly better code generation. - if core::intrinsics::unlikely(weak == 0) { - abort(); + if intrinsics::unlikely(weak == 0) { + intrinsics::abort_immediate(); } } @@ -4039,7 +4090,7 @@ fn data_offset_alignment(alignment: Alignment) -> usize { #[unstable(feature = "unique_rc_arc", issue = "112566")] pub struct UniqueRc< T: ?Sized, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] A: Allocator = Global, > { ptr: NonNull>, // Define the ownership of `RcInner` for drop-check @@ -4319,6 +4370,13 @@ impl UniqueRc { pub fn new(value: T) -> Self { Self::new_in(value, Global) } + + /// Like [`new`](Self::new), but returns an error if the allocation + /// fails, instead of calling [`handle_alloc_error`]. + #[unstable(feature = "unique_rc_arc", issue = "112566")] + pub fn try_new(value: T) -> Result { + Self::try_new_in(value, Global) + } } impl UniqueRc { @@ -4330,8 +4388,8 @@ impl UniqueRc { /// point to the new [`Rc`]. #[cfg(not(no_global_oom_handling))] #[unstable(feature = "unique_rc_arc", issue = "112566")] + // #[unstable(feature = "allocator_api", issue = "163177")] #[must_use] - // #[unstable(feature = "allocator_api", issue = "32838")] pub fn new_in(value: T, alloc: A) -> Self { let (ptr, alloc) = Box::into_non_null_with_allocator(Box::new_in( RcInner { @@ -4346,8 +4404,29 @@ impl UniqueRc { Self { ptr, _marker: PhantomData, _marker2: PhantomData, alloc } } - #[cfg(not(no_global_oom_handling))] - fn unwrap_with_allocator(this: Self) -> (T, A) { + /// Like [`new_in`](Self::new_in), but returns an error if the allocation + /// fails, instead of calling [`handle_alloc_error`]. + #[unstable(feature = "unique_rc_arc", issue = "112566")] + // #[unstable(feature = "allocator_api", issue = "163177")] + pub fn try_new_in(value: T, alloc: A) -> Result { + let (ptr, alloc) = Box::into_non_null_with_allocator(Box::try_new_in( + RcInner { + strong: Cell::new(0), + // keep one weak reference so if all the weak pointers that are created are dropped + // the UniqueRc still stays valid. + weak: Cell::new(1), + value, + }, + alloc, + )?); + Ok(Self { ptr, _marker: PhantomData, _marker2: PhantomData, alloc }) + } + + /// Consumes the `UniqueRc`, returning its wrapped value and allocator. + #[unstable(feature = "unique_rc_arc", issue = "112566")] + // #[unstable(feature = "allocator_api", issue = "163177")] + #[must_use] + pub fn unwrap_with_allocator(this: Self) -> (T, A) { let inner_ptr = this.ptr; let (data_ptr, alloc) = Self::into_raw_with_allocator(this); @@ -4361,6 +4440,13 @@ impl UniqueRc { (val, alloc) } + /// Consumes the `UniqueRc`, returning its wrapped value. + #[unstable(feature = "unique_rc_arc", issue = "112566")] + #[must_use] + pub fn unwrap(this: Self) -> T { + Self::unwrap_with_allocator(this).0 + } + /// Maps the value in a `UniqueRc`, reusing the allocation if possible. /// /// `f` is called on a reference to the value in the `UniqueRc`, and the result is returned, @@ -4392,11 +4478,10 @@ impl UniqueRc { unsafe { let (ptr, alloc) = UniqueRc::into_raw_with_allocator(this); let value = ptr.read(); - let mut allocation = + let allocation = UniqueRc::from_raw_with_allocator(ptr.cast::>(), alloc); - allocation.write(f(value)); - allocation.assume_init() + UniqueRc::write(allocation, f(value)) } } else { let (val, alloc) = UniqueRc::unwrap_with_allocator(this); @@ -4443,13 +4528,12 @@ impl UniqueRc { unsafe { let (ptr, alloc) = UniqueRc::into_raw_with_allocator(this); let value = ptr.read(); - let mut allocation = UniqueRc::from_raw_with_allocator( + let allocation = UniqueRc::from_raw_with_allocator( ptr.cast::>(), alloc, ); - allocation.write(f(value)?); - try { allocation.assume_init() } + try { UniqueRc::write(allocation, f(value)?) } } } else { let (val, alloc) = UniqueRc::unwrap_with_allocator(this); @@ -4477,7 +4561,6 @@ impl UniqueRc { } } - #[cfg(not(no_global_oom_handling))] fn into_raw_with_allocator(this: Self) -> (*const T, A) { let this = ManuallyDrop::new(this); // SAFETY: The copy of the allocator stored in `this` is forgotten @@ -4519,7 +4602,6 @@ impl UniqueRc { unsafe { self.ptr.as_ref() } } - #[cfg(not(no_global_oom_handling))] fn as_ptr(this: &Self) -> *const T { let ptr: *mut RcInner = NonNull::as_ptr(this.ptr); @@ -4530,7 +4612,6 @@ impl UniqueRc { } #[inline] - #[cfg(not(no_global_oom_handling))] fn into_inner_with_allocator(this: Self) -> (NonNull>, A) { let this = mem::ManuallyDrop::new(this); // SAFETY: Pointer is valid for reads. @@ -4538,7 +4619,6 @@ impl UniqueRc { } #[inline] - #[cfg(not(no_global_oom_handling))] unsafe fn from_inner_in(ptr: NonNull>, alloc: A) -> Self { Self { ptr, _marker: PhantomData, _marker2: PhantomData, alloc } } @@ -4560,9 +4640,35 @@ impl UniqueRc { } } -#[cfg(not(no_global_oom_handling))] impl UniqueRc, A> { - unsafe fn assume_init(self) -> UniqueRc { + /// Writes the value and converts to `UniqueRc`. + /// + /// This method converts similarly to [`assume_init`](Self::assume_init) but + /// writes `value` into it before conversion, thus guaranteeing safety. + #[unstable(feature = "unique_rc_arc", issue = "112566")] + #[must_use] + pub fn write(mut this: Self, value: T) -> UniqueRc { + // SAFETY: Writing initialises the wrapped value. + unsafe { + this.write(value); + this.assume_init() + } + } + + /// Converts to `UniqueRc`. + /// + /// # Safety + /// + /// As with [`MaybeUninit::assume_init`], + /// it is up to the caller to guarantee that the value + /// really is in an initialized state. + /// Calling this when the content is not yet fully initialized + /// causes immediate undefined behavior. + /// + /// [`MaybeUninit::assume_init`]: mem::MaybeUninit::assume_init + #[unstable(feature = "unique_rc_arc", issue = "112566")] + #[must_use] + pub unsafe fn assume_init(self) -> UniqueRc { let (ptr, alloc) = UniqueRc::into_inner_with_allocator(self); // SAFETY: Upheld by caller. unsafe { UniqueRc::from_inner_in(ptr.cast(), alloc) } @@ -4687,7 +4793,7 @@ impl Drop for UniqueRcUninit { } } -#[unstable(feature = "allocator_api", issue = "32838")] +#[stable(feature = "allocator_api", since = "1.100.0")] unsafe impl Allocator for Rc { #[inline] fn allocate(&self, layout: Layout) -> Result, AllocError> { @@ -4738,6 +4844,9 @@ unsafe impl Allocator for Rc { unsafe { (**self).shrink(ptr, old_layout, new_layout) } } } +#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] +#[unstable_feature_bound(allocator_ext)] +impl AllocatorNightly for Rc {} -#[unstable(feature = "allocator_api", issue = "32838")] +#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] unsafe impl AllocatorClone for Rc {} diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index 4741fe12ae89c..438549160a1c6 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -381,7 +381,7 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(allocator_api)] + /// #![feature(allocator_ext)] /// /// use std::alloc::System; /// @@ -392,7 +392,7 @@ impl [T] { #[cfg(not(no_global_oom_handling))] #[rustc_allow_incoherent_impl] #[inline] - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] pub fn to_vec_in(&self, alloc: A) -> Vec where T: Clone, diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index cd1c149c3c4fc..a736a25ccdeb8 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -73,7 +73,8 @@ impl> Join<&str> for [S] { type Output = String; fn join(slice: &Self, sep: &str) -> String { - // ignore-tidy-undocumented-unsafe + // SAFETY: slice can be borrowed as `[&str]` and sep is `&str`, both are valid UTF-8, + // so the returned `Vec` of `join_generic_copy` is also valid UTF-8. unsafe { String::from_utf8_unchecked(join_generic_copy(slice, sep.as_bytes())) } } } @@ -183,30 +184,37 @@ where let pos = result.len(); debug_assert!(reserved_len >= pos); - // ignore-tidy-undocumented-unsafe - unsafe { - let target = result.spare_capacity_mut().get_unchecked_mut(..reserved_len - pos); - // Convert the separator and slices to slices of MaybeUninit - // to simplify implementation in specialize_for_lengths. - let sep_uninit = core::slice::from_raw_parts(sep.as_ptr().cast(), sep.len()); - let iter_uninit = iter.map(|it| { - let it = it.borrow().as_ref(); - core::slice::from_raw_parts(it.as_ptr().cast(), it.len()) - }); + // SAFETY: The size of remaining spare is at least `reserved_len - pos`. + let target = unsafe { result.spare_capacity_mut().get_unchecked_mut(..reserved_len - pos) }; + + // Convert the separator and slices to slices of MaybeUninit + // to simplify implementation in specialize_for_lengths. + // SAFETY: `sep` is a slice of `T`, so `sep.as_ptr().cast()` is a valid pointer, + // and `sep.len()` is the number of elements in the slice, so `from_raw_parts` is safe. + let sep_uninit = unsafe { core::slice::from_raw_parts(sep.as_ptr().cast(), sep.len()) }; + let iter_uninit = iter.map(|it| { + let it = it.borrow().as_ref(); + // SAFETY: `it` is a slice of `T`, so `it.as_ptr().cast()` is a valid pointer, + // and `it.len()` is the number of elements in the slice, so `from_raw_parts` is safe. + unsafe { core::slice::from_raw_parts(it.as_ptr().cast(), it.len()) } + }); - // copy separator and slices over without bounds checks. - // `specialize_for_lengths!` internally calls `s.borrow()`, but because it uses - // the bounds-checked `split_at_mut` any misbehaving implementation - // will not write out of bounds. - let remain = specialize_for_lengths!(sep_uninit, target, iter_uninit; 0, 1, 2, 3, 4); + // copy separator and slices over without bounds checks. + // `specialize_for_lengths!` internally calls `s.borrow()`, but because it uses + // the bounds-checked `split_at_mut` any misbehaving implementation + // will not write out of bounds. + let remain = specialize_for_lengths!(sep_uninit, target, iter_uninit; 0, 1, 2, 3, 4); - // A weird borrow implementation may return different - // slices for the length calculation and the actual copy. - // Make sure we don't expose uninitialized bytes to the caller. - let result_len = reserved_len - remain.len(); + // A weird borrow implementation may return different + // slices for the length calculation and the actual copy. + // Make sure we don't expose uninitialized bytes to the caller. + let result_len = reserved_len - remain.len(); + // SAFETY: `result_len` is less than `reserved_len`, and all elements in `0..result_len` are initialized. + unsafe { result.set_len(result_len); } + result } @@ -250,7 +258,7 @@ impl ToOwned for str { #[inline] fn to_owned(&self) -> String { - // ignore-tidy-undocumented-unsafe + // SAFETY: `self` is a valid UTF-8 str. unsafe { String::from_utf8_unchecked(self.as_bytes().to_owned()) } } @@ -319,7 +327,7 @@ impl str { _ => None, } { if let [to_byte] = to.as_bytes() { - // ignore-tidy-undocumented-unsafe + // SAFETY: `self` is a valid UTF-8 str, `from_byte` and `to_byte` are ASCII bytes. return unsafe { replace_ascii(self.as_bytes(), from_byte, *to_byte) }; } } @@ -332,12 +340,16 @@ impl str { let mut result = String::with_capacity(default_capacity); let mut last_end = 0; for (start, part) in self.match_indices(from) { - // ignore-tidy-undocumented-unsafe + // SAFETY: `last_end` does not exceed `start`, and `start` does not exceed `self.len()`. + // Therefore, `last_end..start` is within bounds of `self`. + // For each iteration, `last_end` and `start` lie on UTF-8 sequence boundaries. result.push_str(unsafe { self.get_unchecked(last_end..start) }); result.push_str(to); last_end = start + part.len(); } - // ignore-tidy-undocumented-unsafe + // SAFETY: `last_end` is the start of the remaining unmatched suffix of `self`. + // It is 0 or the end of a match returned by `match_indices`, so it is + // a UTF-8 boundary within `self`. `self.len()` is also a UTF-8 boundary. result.push_str(unsafe { self.get_unchecked(last_end..self.len()) }); result } @@ -374,12 +386,16 @@ impl str { let mut result = String::with_capacity(32); let mut last_end = 0; for (start, part) in self.match_indices(pat).take(count) { - // ignore-tidy-undocumented-unsafe + // SAFETY: `last_end` does not exceed `start`, and `start` does not exceed `self.len()`. + // Therefore, `last_end..start` is within bounds of `self`. + // For each iteration, `last_end` and `start` lie on UTF-8 sequence boundaries. result.push_str(unsafe { self.get_unchecked(last_end..start) }); result.push_str(to); last_end = start + part.len(); } - // ignore-tidy-undocumented-unsafe + // SAFETY: `last_end` is the start of the remaining suffix of `self`. + // It is 0 or the end of a match returned by `match_indices`, so it is + // a UTF-8 boundary within `self`. `self.len()` is also a UTF-8 boundary. result.push_str(unsafe { self.get_unchecked(last_end..self.len()) }); result } @@ -793,7 +809,7 @@ impl str { #[inline] pub fn into_string(self: Box) -> String { let slice = Box::<[u8]>::from(self); - // ignore-tidy-undocumented-unsafe + // SAFETY: `slice` is a valid UTF-8 sequence because it is created from a `Box`. unsafe { String::from_utf8_unchecked(slice.into_vec()) } } @@ -823,7 +839,7 @@ impl str { #[stable(feature = "repeat_str", since = "1.16.0")] #[inline] pub fn repeat(&self, n: usize) -> String { - // ignore-tidy-undocumented-unsafe + // SAFETY: The created Vec is valid UTF-8 because `self` is str. unsafe { String::from_utf8_unchecked(self.as_bytes().repeat(n)) } } @@ -984,9 +1000,9 @@ pub unsafe fn convert_while_ascii(s: &str, convert: fn(&u8) -> u8) -> (String, & } ascii_prefix_len += N; - // ignore-tidy-undocumented-unsafe + // SAFETY: checked in loop condition. slice = unsafe { slice.get_unchecked(N..) }; - // ignore-tidy-undocumented-unsafe + // SAFETY: out_slice has at least same length as input slice. out_slice = unsafe { out_slice.get_unchecked_mut(N..) }; } @@ -1001,9 +1017,9 @@ pub unsafe fn convert_while_ascii(s: &str, convert: fn(&u8) -> u8) -> (String, & *out_slice.get_unchecked_mut(0) = MaybeUninit::new(convert(&byte)); } ascii_prefix_len += 1; - // ignore-tidy-undocumented-unsafe + // SAFETY: slice has at least one byte, so slicing from 1.. is safe slice = unsafe { slice.get_unchecked(1..) }; - // ignore-tidy-undocumented-unsafe + // SAFETY: out_slice has at least same length as input slice out_slice = unsafe { out_slice.get_unchecked_mut(1..) }; } @@ -1024,6 +1040,11 @@ pub unsafe fn convert_while_ascii(s: &str, convert: fn(&u8) -> u8) -> (String, & #[allow(dead_code)] /// Faster implementation of string replacement for ASCII to ASCII cases. /// Should produce fast vectorized code. +/// +/// # Safety +/// +/// * `utf8_bytes` must contain valid UTF-8. +/// * Both `from` and `to` must be ASCII bytes (at most `0x7F`). unsafe fn replace_ascii(utf8_bytes: &[u8], from: u8, to: u8) -> String { let result: Vec = utf8_bytes.iter().map(|b| if *b == from { to } else { *b }).collect(); // SAFETY: We replaced ascii with ascii on valid utf8 strings. diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index b363b6862f7e5..03c07b15712bb 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -802,7 +802,7 @@ impl String { let (chunks, []) = v.as_chunks::<2>() else { return Err(FromUtf16Error { kind: FromUtf16ErrorKind::OddBytes }); }; - // ignore-tidy-undocumented-unsafe + // SAFETY: The aligned part of `v` can be transmuted into a valid u16 slice. match (cfg!(target_endian = "little"), unsafe { v.align_to::() }) { (true, ([], v, [])) => Self::from_utf16(v), _ => { @@ -838,7 +838,7 @@ impl String { #[cfg(not(no_global_oom_handling))] #[stable(feature = "str_from_utf16_endian", since = "1.98.0")] pub fn from_utf16le_lossy(v: &[u8]) -> String { - // ignore-tidy-undocumented-unsafe + // SAFETY: The aligned part of `v` can be transmuted into a valid u16 slice. match (cfg!(target_endian = "little"), unsafe { v.align_to::() }) { (true, ([], v, [])) => Self::from_utf16_lossy(v), (true, ([], v, [_remainder])) => Self::from_utf16_lossy(v) + "\u{FFFD}", @@ -877,7 +877,7 @@ impl String { let (chunks, []) = v.as_chunks::<2>() else { return Err(FromUtf16Error { kind: FromUtf16ErrorKind::OddBytes }); }; - // ignore-tidy-undocumented-unsafe + // SAFETY: The aligned part of `v` can be transmuted into a valid u16 slice. match (cfg!(target_endian = "big"), unsafe { v.align_to::() }) { (true, ([], v, [])) => Self::from_utf16(v), _ => { @@ -913,7 +913,7 @@ impl String { #[cfg(not(no_global_oom_handling))] #[stable(feature = "str_from_utf16_endian", since = "1.98.0")] pub fn from_utf16be_lossy(v: &[u8]) -> String { - // ignore-tidy-undocumented-unsafe + // SAFETY: The aligned part of `v` can be transmuted into a valid u16 slice. match (cfg!(target_endian = "big"), unsafe { v.align_to::() }) { (true, ([], v, [])) => Self::from_utf16_lossy(v), (true, ([], v, [_remainder])) => Self::from_utf16_lossy(v) + "\u{FFFD}", @@ -1143,7 +1143,8 @@ impl String { let additional: Saturating = slice.iter().map(|x| Saturating(x.len())).sum(); self.reserve(additional.0); let (ptr, len, cap) = core::mem::take(self).into_raw_parts(); - // ignore-tidy-undocumented-unsafe + // SAFETY: `self` have reserved enough space for strs in `slice`, and we are copying from valid UTF-8 slices. + // Therefore all unsafe operations are safe. unsafe { let mut dst = ptr.add(len); for new in slice { @@ -1532,7 +1533,7 @@ impl String { pub fn pop(&mut self) -> Option { let ch = self.chars().rev().next()?; let newlen = self.len() - ch.len_utf8(); - // ignore-tidy-undocumented-unsafe + // SAFETY: `newlen` is less than old length. unsafe { self.vec.set_len(newlen); } @@ -1571,7 +1572,11 @@ impl String { let next = idx + ch.len_utf8(); let len = self.len(); - // ignore-tidy-undocumented-unsafe + // SAFETY: + // * Since `next` is not bigger than `len`, `self.vec.as_ptr().add(next)` + // is valid for reads and `self.vec.as_mut_ptr().add(idx)` is valid for writes. + // Both pointers are valid for `len - next` bytes. + // * new length is less than old length. unsafe { ptr::copy(self.vec.as_ptr().add(next), self.vec.as_mut_ptr().add(idx), len - next); self.vec.set_len(len - (next - idx)); @@ -1645,7 +1650,7 @@ impl String { len += count; } - // ignore-tidy-undocumented-unsafe + // SAFETY: `len` is less than or equal to the original length of the string. unsafe { self.vec.set_len(len); } @@ -1954,7 +1959,7 @@ impl String { pub fn split_off(&mut self, at: usize) -> String { assert!(self.is_char_boundary(at)); let other = self.vec.split_off(at); - // ignore-tidy-undocumented-unsafe + // SAFETY: `other` contains only valid UTF-8 because `at` is on a `UTF-8` code point boundary. unsafe { String::from_utf8_unchecked(other) } } @@ -2224,7 +2229,7 @@ impl String { #[inline] pub fn into_boxed_str(self) -> Box { let slice = self.vec.into_boxed_slice(); - // ignore-tidy-undocumented-unsafe + // SAFETY: `slice` contains only valid UTF-8 because `String` is guaranteed to be valid UTF-8. unsafe { from_boxed_utf8_unchecked(slice) } } @@ -2256,7 +2261,7 @@ impl String { #[inline] pub fn leak<'a>(self) -> &'a mut str { let slice = self.vec.leak(); - // ignore-tidy-undocumented-unsafe + // SAFETY: `slice` contains only valid UTF-8 because `String` is guaranteed to be valid UTF-8. unsafe { from_utf8_unchecked_mut(slice) } } } @@ -3586,14 +3591,12 @@ unsafe impl Send for Drain<'_> {} #[stable(feature = "drain", since = "1.6.0")] impl Drop for Drain<'_> { fn drop(&mut self) { - // ignore-tidy-undocumented-unsafe - unsafe { - // Use Vec::drain. "Reaffirm" the bounds checks to avoid - // panic code being inserted again. - let self_vec = (*self.string).as_mut_vec(); - if self.start <= self.end && self.end <= self_vec.len() { - self_vec.drain(self.start..self.end); - } + // Use Vec::drain. "Reaffirm" the bounds checks to avoid + // panic code being inserted again. + // SAFETY: We only use the returned Vec to call drain. + let self_vec = unsafe { &mut *(*self.string).as_mut_vec() }; + if self.start <= self.end && self.end <= self_vec.len() { + self_vec.drain(self.start..self.end); } } } diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 52b3b13fa5a0e..afb1848d21a24 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -8,5152 +8,7 @@ //! loads and stores of pointers. This may be detected at compile time using //! `#[cfg(target_has_atomic = "ptr")]`. -use core::any::Any; -use core::cell::CloneFromCell; -#[cfg(not(no_global_oom_handling))] -use core::clone::TrivialClone; -use core::clone::{CloneToUninit, Share, UseCloned}; -use core::cmp::Ordering; -use core::hash::{Hash, Hasher}; -use core::intrinsics::abort; -#[cfg(not(no_global_oom_handling))] -use core::iter; -use core::marker::{PhantomData, Unsize}; -use core::mem::{self, Alignment, ManuallyDrop}; -use core::num::NonZeroUsize; -use core::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn, LegacyReceiver}; -#[cfg(not(no_global_oom_handling))] -use core::ops::{Residual, Try}; -use core::panic::{RefUnwindSafe, UnwindSafe}; -use core::pin::{Pin, PinSafePointer}; -use core::ptr::{self, NonNull}; -#[cfg(not(no_global_oom_handling))] -use core::slice::from_raw_parts_mut; -use core::sync::atomic::Ordering::{Acquire, Relaxed, Release}; -use core::sync::atomic::{self, Atomic}; -use core::{borrow, fmt, hint}; - -use crate::alloc::{AllocError, Allocator, AllocatorClone, Global, Layout}; -#[cfg(not(no_global_oom_handling))] -use crate::alloc::{AllocatorNightly, handle_alloc_error}; -use crate::borrow::{Cow, ToOwned}; -use crate::boxed::Box; -use crate::rc::is_dangling; -#[cfg(not(no_global_oom_handling))] -use crate::string::String; -#[cfg(not(no_global_oom_handling))] -use crate::vec::Vec; - -/// A soft limit on the amount of references that may be made to an `Arc`. -/// -/// Going above this limit will abort your program (although not -/// necessarily) at _exactly_ `MAX_REFCOUNT + 1` references. -/// Trying to go above it might call a `panic` (if not actually going above it). -/// -/// This is a global invariant, and also applies when using a compare-exchange loop. -/// -/// See comment in `Arc::clone`. -const MAX_REFCOUNT: usize = (isize::MAX) as usize; - -#[cold] -#[cfg_attr(not(panic = "immediate-abort"), inline(never))] -#[cfg_attr(panic = "immediate-abort", inline)] -#[track_caller] -fn panic_arc_overflow() -> ! { - panic!("Arc counter overflow"); -} - -#[cfg(not(sanitize = "thread"))] -macro_rules! acquire { - ($x:expr) => { - atomic::fence(Acquire) - }; -} - -// ThreadSanitizer does not support memory fences. To avoid false positive -// reports in Arc / Weak implementation use atomic loads for synchronization -// instead. -#[cfg(sanitize = "thread")] -macro_rules! acquire { - ($x:expr) => { - $x.load(Acquire) - }; -} - -/// A thread-safe reference-counting pointer. 'Arc' stands for 'Atomically -/// Reference Counted'. -/// -/// The type `Arc` provides shared ownership of a value of type `T`, -/// allocated in the heap. Invoking [`clone`][clone] on `Arc` produces -/// a new `Arc` instance, which points to the same allocation on the heap as the -/// source `Arc`, while increasing a reference count. When the last `Arc` -/// pointer to a given allocation is destroyed, the value stored in that allocation (often -/// referred to as "inner value") is also dropped. -/// -/// Shared references in Rust disallow mutation by default, and `Arc` is no -/// exception: you cannot generally obtain a mutable reference to something -/// inside an `Arc`. If you do need to mutate through an `Arc`, you have several options: -/// -/// 1. Use interior mutability with synchronization primitives like [`Mutex`][mutex], -/// [`RwLock`][rwlock], or one of the [`Atomic`][atomic] types. -/// -/// 2. Use clone-on-write semantics with [`Arc::make_mut`] which provides efficient mutation -/// without requiring interior mutability. This approach clones the data only when -/// needed (when there are multiple references) and can be more efficient when mutations -/// are infrequent. -/// -/// 3. Use [`Arc::get_mut`] when you know your `Arc` is not shared (has a reference count of 1), -/// which provides direct mutable access to the inner value without any cloning. -/// -/// ``` -/// use std::sync::Arc; -/// -/// let mut data = Arc::new(vec![1, 2, 3]); -/// -/// // This will clone the vector only if there are other references to it -/// Arc::make_mut(&mut data).push(4); -/// -/// assert_eq!(*data, vec![1, 2, 3, 4]); -/// ``` -/// -/// **Note**: This type is only available on platforms that support atomic -/// loads and stores of pointers, which includes all platforms that support -/// the `std` crate but not all those which only support [`alloc`](crate). -/// This may be detected at compile time using `#[cfg(target_has_atomic = "ptr")]`. -/// -/// ## Thread Safety -/// -/// Unlike [`Rc`], `Arc` uses atomic operations for its reference -/// counting. This means that it is thread-safe. The disadvantage is that -/// atomic operations are more expensive than ordinary memory accesses. If you -/// are not sharing reference-counted allocations between threads, consider using -/// [`Rc`] for lower overhead. [`Rc`] is a safe default, because the -/// compiler will catch any attempt to send an [`Rc`] between threads. -/// However, a library might choose `Arc` in order to give library consumers -/// more flexibility. -/// -/// `Arc` will implement [`Send`] and [`Sync`] as long as the `T` implements -/// [`Send`] and [`Sync`]. Why can't you put a non-thread-safe type `T` in an -/// `Arc` to make it thread-safe? This may be a bit counter-intuitive at -/// first: after all, isn't the point of `Arc` thread safety? The key is -/// this: `Arc` makes it thread safe to have multiple ownership of the same -/// data, but it doesn't add thread safety to its data. Consider -/// Arc<[RefCell\]>. [`RefCell`] isn't [`Sync`], and if `Arc` was always -/// [`Send`], Arc<[RefCell\]> would be as well. But then we'd have a problem: -/// [`RefCell`] is not thread safe; it keeps track of the borrowing count using -/// non-atomic operations. -/// -/// In the end, this means that you may need to pair `Arc` with some sort of -/// [`std::sync`] type, usually [`Mutex`][mutex]. -/// -/// ## Breaking cycles with `Weak` -/// -/// The [`downgrade`][downgrade] method can be used to create a non-owning -/// [`Weak`] pointer. A [`Weak`] pointer can be [`upgrade`][upgrade]d -/// to an `Arc`, but this will return [`None`] if the value stored in the allocation has -/// already been dropped. In other words, `Weak` pointers do not keep the value -/// inside the allocation alive; however, they *do* keep the allocation -/// (the backing store for the value) alive. -/// -/// A cycle between `Arc` pointers will never be deallocated. For this reason, -/// [`Weak`] is used to break cycles. For example, a tree could have -/// strong `Arc` pointers from parent nodes to children, and [`Weak`] -/// pointers from children back to their parents. -/// -/// # Cloning references -/// -/// Creating a new reference from an existing reference-counted pointer is done using the -/// `Clone` trait implemented for [`Arc`][Arc] and [`Weak`][Weak]. -/// -/// ``` -/// use std::sync::Arc; -/// let foo = Arc::new(vec![1.0, 2.0, 3.0]); -/// // The two syntaxes below are equivalent. -/// let a = foo.clone(); -/// let b = Arc::clone(&foo); -/// // a, b, and foo are all Arcs that point to the same memory location -/// ``` -/// -/// ## `Deref` behavior -/// -/// `Arc` automatically dereferences to `T` (via the [`Deref`] trait), -/// so you can call `T`'s methods on a value of type `Arc`. To avoid name -/// clashes with `T`'s methods, the methods of `Arc` itself are associated -/// functions, called using [fully qualified syntax]: -/// -/// ``` -/// use std::sync::Arc; -/// -/// let my_arc = Arc::new(()); -/// let my_weak = Arc::downgrade(&my_arc); -/// ``` -/// -/// `Arc`'s implementations of traits like `Clone` may also be called using -/// fully qualified syntax. Some people prefer to use fully qualified syntax, -/// while others prefer using method-call syntax. -/// -/// ``` -/// use std::sync::Arc; -/// -/// let arc = Arc::new(()); -/// // Method-call syntax -/// let arc2 = arc.clone(); -/// // Fully qualified syntax -/// let arc3 = Arc::clone(&arc); -/// ``` -/// -/// [`Weak`][Weak] does not auto-dereference to `T`, because the inner value may have -/// already been dropped. -/// -/// [`Rc`]: crate::rc::Rc -/// [clone]: Clone::clone -/// [mutex]: ../../std/sync/struct.Mutex.html -/// [rwlock]: ../../std/sync/struct.RwLock.html -/// [atomic]: core::sync::atomic -/// [downgrade]: Arc::downgrade -/// [upgrade]: Weak::upgrade -/// [RefCell\]: core::cell::RefCell -/// [`RefCell`]: core::cell::RefCell -/// [`std::sync`]: ../../std/sync/index.html -/// [`Arc::clone(&from)`]: Arc::clone -/// [fully qualified syntax]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#fully-qualified-syntax-for-disambiguation-calling-methods-with-the-same-name -/// -/// # Examples -/// -/// Sharing some immutable data between threads: -/// -/// ``` -/// use std::sync::Arc; -/// use std::thread; -/// -/// let five = Arc::new(5); -/// -/// for _ in 0..10 { -/// let five = Arc::clone(&five); -/// -/// thread::spawn(move || { -/// println!("{five:?}"); -/// }); -/// } -/// ``` -/// -/// Sharing a mutable [`AtomicUsize`]: -/// -/// [`AtomicUsize`]: core::sync::atomic::AtomicUsize "sync::atomic::AtomicUsize" -/// -/// ``` -/// use std::sync::Arc; -/// use std::sync::atomic::{AtomicUsize, Ordering}; -/// use std::thread; -/// -/// let val = Arc::new(AtomicUsize::new(5)); -/// -/// for _ in 0..10 { -/// let val = Arc::clone(&val); -/// -/// thread::spawn(move || { -/// let v = val.fetch_add(1, Ordering::Relaxed); -/// println!("{v:?}"); -/// }); -/// } -/// ``` -/// -/// See the [`rc` documentation][rc_examples] for more examples of reference -/// counting in general. -/// -/// [rc_examples]: crate::rc#examples -#[doc(search_unbox)] -#[rustc_diagnostic_item = "Arc"] -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_insignificant_dtor] -#[diagnostic::on_move( - message = "the type `{Self}` does not implement `Copy`", - label = "this move could be avoided by cloning the original `{Self}`, which is inexpensive", - note = "consider using `Arc::clone`" -)] -pub struct Arc< - T: ?Sized, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> { - ptr: NonNull>, - phantom: PhantomData>, - alloc: A, -} - -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Send for Arc {} -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Sync for Arc {} - -#[stable(feature = "catch_unwind", since = "1.9.0")] -impl UnwindSafe - for Arc -{ -} - -#[unstable(feature = "coerce_unsized", issue = "18598")] -impl, U: ?Sized, A: Allocator> CoerceUnsized> for Arc {} - -#[unstable(feature = "dispatch_from_dyn", issue = "none")] -impl, U: ?Sized> DispatchFromDyn> for Arc {} - -// SAFETY: `Arc::clone` doesn't access any `Cell`s which could contain the `Arc` being cloned. -#[unstable(feature = "cell_get_cloned", issue = "145329")] -unsafe impl CloneFromCell for Arc {} - -impl Arc { - unsafe fn from_inner(ptr: NonNull>) -> Self { - // SAFETY: Upheld by caller. - unsafe { Self::from_inner_in(ptr, Global) } - } - - unsafe fn from_ptr(ptr: *mut ArcInner) -> Self { - // SAFETY: Upheld by caller. - unsafe { Self::from_ptr_in(ptr, Global) } - } -} - -impl Arc { - #[inline] - fn into_inner_with_allocator(this: Self) -> (NonNull>, A) { - let this = mem::ManuallyDrop::new(this); - // SAFETY: Pointer is valid for reads. - (this.ptr, unsafe { ptr::read(&this.alloc) }) - } - - #[inline] - unsafe fn from_inner_in(ptr: NonNull>, alloc: A) -> Self { - Self { ptr, phantom: PhantomData, alloc } - } - - #[inline] - unsafe fn from_ptr_in(ptr: *mut ArcInner, alloc: A) -> Self { - // SAFETY: Upheld by caller. - unsafe { Self::from_inner_in(NonNull::new_unchecked(ptr), alloc) } - } -} - -/// `Weak` is a version of [`Arc`] that holds a non-owning reference to the -/// managed allocation. -/// -/// The allocation is accessed by calling [`upgrade`] on the `Weak` -/// pointer, which returns an [Option]<[Arc]\>. -/// -/// Since a `Weak` reference does not count towards ownership, it will not -/// prevent the value stored in the allocation from being dropped, and `Weak` itself makes no -/// guarantees about the value still being present. Thus it may return [`None`] -/// when [`upgrade`]d. Note however that a `Weak` reference *does* prevent the allocation -/// itself (the backing store) from being deallocated. -/// -/// A `Weak` pointer is useful for keeping a temporary reference to the allocation -/// managed by [`Arc`] without preventing its inner value from being dropped. It is also used to -/// prevent circular references between [`Arc`] pointers, since mutual owning references -/// would never allow either [`Arc`] to be dropped. For example, a tree could -/// have strong [`Arc`] pointers from parent nodes to children, and `Weak` -/// pointers from children back to their parents. -/// -/// The typical way to obtain a `Weak` pointer is to call [`Arc::downgrade`]. -/// -/// [`upgrade`]: Weak::upgrade -#[stable(feature = "arc_weak", since = "1.4.0")] -#[rustc_diagnostic_item = "ArcWeak"] -pub struct Weak< - T: ?Sized, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> { - // This is a `NonNull` to allow optimizing the size of this type in enums, - // but it is not necessarily a valid pointer. - // `Weak::new` sets this to `usize::MAX` so that it doesn’t need - // to allocate space on the heap. That's not a value a real pointer - // will ever have because ArcInner has alignment at least 2. - ptr: NonNull>, - alloc: A, -} - -#[stable(feature = "arc_weak", since = "1.4.0")] -unsafe impl Send for Weak {} -#[stable(feature = "arc_weak", since = "1.4.0")] -unsafe impl Sync for Weak {} - -#[unstable(feature = "coerce_unsized", issue = "18598")] -impl, U: ?Sized, A: Allocator> CoerceUnsized> for Weak {} -#[unstable(feature = "dispatch_from_dyn", issue = "none")] -impl, U: ?Sized> DispatchFromDyn> for Weak {} - -// SAFETY: `Weak::clone` doesn't access any `Cell`s which could contain the `Weak` being cloned. -#[unstable(feature = "cell_get_cloned", issue = "145329")] -unsafe impl CloneFromCell for Weak {} - -#[stable(feature = "arc_weak", since = "1.4.0")] -impl fmt::Debug for Weak { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "(Weak)") - } -} - -// This is repr(C) to future-proof against possible field-reordering, which -// would interfere with otherwise safe [into|from]_raw() of transmutable -// inner types. -// Unlike RcInner, repr(align(2)) is not strictly required because atomic types -// have the alignment same as its size, but we use it for consistency and clarity. -#[repr(C, align(2))] -struct ArcInner { - strong: Atomic, - - // the value usize::MAX acts as a sentinel for temporarily "locking" the - // weak count, preventing `Arc::downgrade` from racing to create new - // `Weak` references. `Arc::is_unique` (which backs `Arc::get_mut`) - // needs to observe both the strong and weak counts as indicating - // uniqueness in one logical atomic step; since they live in separate - // atomic words, it locks the weak count while reading the strong - // count to keep the two reads consistent. - weak: Atomic, - - data: T, -} - -/// Calculate layout for `ArcInner` using the inner value's layout -fn arcinner_layout_for_value_layout(layout: Layout) -> Layout { - // Calculate layout using the given value layout. - // Previously, layout was calculated on the expression - // `&*(ptr as *const ArcInner)`, but this created a misaligned - // reference (see #54908). - Layout::new::>() - .extend(layout) - .unwrap_or_else(|_| panic!("capacity overflow")) - .0 - .pad_to_align() -} - -unsafe impl Send for ArcInner {} -unsafe impl Sync for ArcInner {} - -impl Arc { - /// Constructs a new `Arc`. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// ``` - #[cfg(not(no_global_oom_handling))] - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new(data: T) -> Arc { - // Start the weak pointer count as 1 which is the weak pointer that's - // held by all the strong pointers (kinda), see std/rc.rs for more info - let x: Box<_> = Box::new(ArcInner { - strong: atomic::AtomicUsize::new(1), - weak: atomic::AtomicUsize::new(1), - data, - }); - // SAFETY: Pointer is valid. - unsafe { Self::from_inner(Box::into_non_null(x)) } - } - - /// Constructs a new `Arc` while giving you a `Weak` to the allocation, - /// to allow you to construct a `T` which holds a weak pointer to itself. - /// - /// Generally, a structure circularly referencing itself, either directly or - /// indirectly, should not hold a strong reference to itself to prevent a memory leak. - /// Using this function, you get access to the weak pointer during the - /// initialization of `T`, before the `Arc` is created, such that you can - /// clone and store it inside the `T`. - /// - /// `new_cyclic` first allocates the managed allocation for the `Arc`, - /// then calls your closure, giving it a `Weak` to this allocation, - /// and only afterwards completes the construction of the `Arc` by placing - /// the `T` returned from your closure into the allocation. - /// - /// Since the new `Arc` is not fully-constructed until `Arc::new_cyclic` - /// returns, calling [`upgrade`] on the weak reference inside your closure will - /// fail and result in a `None` value. - /// - /// # Panics - /// - /// If `data_fn` panics, the panic is propagated to the caller, and the - /// temporary [`Weak`] is dropped normally. - /// - /// # Example - /// - /// ``` - /// # #![allow(dead_code)] - /// use std::sync::{Arc, Weak}; - /// - /// struct Gadget { - /// me: Weak, - /// } - /// - /// impl Gadget { - /// /// Constructs a reference counted Gadget. - /// fn new() -> Arc { - /// // `me` is a `Weak` pointing at the new allocation of the - /// // `Arc` we're constructing. - /// Arc::new_cyclic(|me| { - /// // Create the actual struct here. - /// Gadget { me: me.clone() } - /// }) - /// } - /// - /// /// Returns a reference counted pointer to Self. - /// fn me(&self) -> Arc { - /// self.me.upgrade().unwrap() - /// } - /// } - /// ``` - /// [`upgrade`]: Weak::upgrade - #[cfg(not(no_global_oom_handling))] - #[inline] - #[stable(feature = "arc_new_cyclic", since = "1.60.0")] - pub fn new_cyclic(data_fn: F) -> Arc - where - F: FnOnce(&Weak) -> T, - { - Self::new_cyclic_in(data_fn, Global) - } - - /// Constructs a new `Arc` with uninitialized contents. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let mut five = Arc::::new_uninit(); - /// - /// // Deferred initialization: - /// Arc::get_mut(&mut five).unwrap().write(5); - /// - /// let five = unsafe { five.assume_init() }; - /// - /// assert_eq!(*five, 5) - /// ``` - #[cfg(not(no_global_oom_handling))] - #[inline] - #[stable(feature = "new_uninit", since = "1.82.0")] - #[must_use] - pub fn new_uninit() -> Arc> { - // ignore-tidy-undocumented-unsafe - unsafe { - Arc::from_ptr(Arc::allocate_for_layout( - Layout::new::(), - |layout| Global.allocate(layout), - <*mut u8>::cast, - )) - } - } - - /// Constructs a new `Arc` with uninitialized contents, with the memory - /// being filled with `0` bytes. - /// - /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and incorrect usage - /// of this method. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let zero = Arc::::new_zeroed(); - /// let zero = unsafe { zero.assume_init() }; - /// - /// assert_eq!(*zero, 0) - /// ``` - /// - /// [zeroed]: mem::MaybeUninit::zeroed - #[cfg(not(no_global_oom_handling))] - #[inline] - #[stable(feature = "new_zeroed_alloc", since = "1.92.0")] - #[must_use] - pub fn new_zeroed() -> Arc> { - // ignore-tidy-undocumented-unsafe - unsafe { - Arc::from_ptr(Arc::allocate_for_layout( - Layout::new::(), - |layout| Global.allocate_zeroed(layout), - <*mut u8>::cast, - )) - } - } - - /// Constructs a new `Pin>`. If `T` does not implement `Unpin`, then - /// `data` will be pinned in memory and unable to be moved. - #[cfg(not(no_global_oom_handling))] - #[stable(feature = "pin", since = "1.33.0")] - #[must_use] - pub fn pin(data: T) -> Pin> { - // SAFETY: We own and create the pinned pointer. - unsafe { Pin::new_unchecked(Arc::new(data)) } - } - - /// Constructs a new `Pin>`, return an error if allocation fails. - #[unstable(feature = "allocator_api", issue = "32838")] - #[inline] - pub fn try_pin(data: T) -> Result>, AllocError> { - // SAFETY: We own and create the pinned pointer. - unsafe { Ok(Pin::new_unchecked(Arc::try_new(data)?)) } - } - - /// Constructs a new `Arc`, returning an error if allocation fails. - /// - /// # Examples - /// - /// ``` - /// #![feature(allocator_api)] - /// use std::sync::Arc; - /// - /// let five = Arc::try_new(5)?; - /// # Ok::<(), std::alloc::AllocError>(()) - /// ``` - #[unstable(feature = "allocator_api", issue = "32838")] - #[inline] - pub fn try_new(data: T) -> Result, AllocError> { - // Start the weak pointer count as 1 which is the weak pointer that's - // held by all the strong pointers (kinda), see std/rc.rs for more info - let x: Box<_> = Box::try_new(ArcInner { - strong: atomic::AtomicUsize::new(1), - weak: atomic::AtomicUsize::new(1), - data, - })?; - // SAFETY: Pointer is valid. - unsafe { Ok(Self::from_inner(Box::into_non_null(x))) } - } - - /// Constructs a new `Arc` with uninitialized contents, returning an error - /// if allocation fails. - /// - /// # Examples - /// - /// ``` - /// #![feature(allocator_api)] - /// - /// use std::sync::Arc; - /// - /// let mut five = Arc::::try_new_uninit()?; - /// - /// // Deferred initialization: - /// Arc::get_mut(&mut five).unwrap().write(5); - /// - /// let five = unsafe { five.assume_init() }; - /// - /// assert_eq!(*five, 5); - /// # Ok::<(), std::alloc::AllocError>(()) - /// ``` - #[unstable(feature = "allocator_api", issue = "32838")] - pub fn try_new_uninit() -> Result>, AllocError> { - // ignore-tidy-undocumented-unsafe - unsafe { - Ok(Arc::from_ptr(Arc::try_allocate_for_layout( - Layout::new::(), - |layout| Global.allocate(layout), - <*mut u8>::cast, - )?)) - } - } - - /// Constructs a new `Arc` with uninitialized contents, with the memory - /// being filled with `0` bytes, returning an error if allocation fails. - /// - /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and incorrect usage - /// of this method. - /// - /// # Examples - /// - /// ``` - /// #![feature( allocator_api)] - /// - /// use std::sync::Arc; - /// - /// let zero = Arc::::try_new_zeroed()?; - /// let zero = unsafe { zero.assume_init() }; - /// - /// assert_eq!(*zero, 0); - /// # Ok::<(), std::alloc::AllocError>(()) - /// ``` - /// - /// [zeroed]: mem::MaybeUninit::zeroed - #[unstable(feature = "allocator_api", issue = "32838")] - pub fn try_new_zeroed() -> Result>, AllocError> { - // ignore-tidy-undocumented-unsafe - unsafe { - Ok(Arc::from_ptr(Arc::try_allocate_for_layout( - Layout::new::(), - |layout| Global.allocate_zeroed(layout), - <*mut u8>::cast, - )?)) - } - } -} - -impl Arc { - /// Constructs a new `Arc` in the provided allocator. - /// - /// # Examples - /// - /// ``` - /// #![feature(allocator_api)] - /// - /// use std::sync::Arc; - /// use std::alloc::System; - /// - /// let five = Arc::new_in(5, System); - /// ``` - #[inline] - #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "allocator_api", issue = "32838")] - pub fn new_in(data: T, alloc: A) -> Arc { - // Start the weak pointer count as 1 which is the weak pointer that's - // held by all the strong pointers (kinda), see std/rc.rs for more info - let x = Box::new_in( - ArcInner { - strong: atomic::AtomicUsize::new(1), - weak: atomic::AtomicUsize::new(1), - data, - }, - alloc, - ); - let (ptr, alloc) = Box::into_non_null_with_allocator(x); - // SAFETY: Pointer is valid. - unsafe { Self::from_inner_in(ptr, alloc) } - } - - /// Constructs a new `Arc` with uninitialized contents in the provided allocator. - /// - /// # Examples - /// - /// ``` - /// #![feature(get_mut_unchecked)] - /// #![feature(allocator_api)] - /// - /// use std::sync::Arc; - /// use std::alloc::System; - /// - /// let mut five = Arc::::new_uninit_in(System); - /// - /// let five = unsafe { - /// // Deferred initialization: - /// Arc::get_mut_unchecked(&mut five).as_mut_ptr().write(5); - /// - /// five.assume_init() - /// }; - /// - /// assert_eq!(*five, 5) - /// ``` - #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "allocator_api", issue = "32838")] - #[inline] - pub fn new_uninit_in(alloc: A) -> Arc, A> { - // ignore-tidy-undocumented-unsafe - unsafe { - Arc::from_ptr_in( - Arc::allocate_for_layout( - Layout::new::(), - |layout| alloc.allocate(layout), - <*mut u8>::cast, - ), - alloc, - ) - } - } - - /// Constructs a new `Arc` with uninitialized contents, with the memory - /// being filled with `0` bytes, in the provided allocator. - /// - /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and incorrect usage - /// of this method. - /// - /// # Examples - /// - /// ``` - /// #![feature(allocator_api)] - /// - /// use std::sync::Arc; - /// use std::alloc::System; - /// - /// let zero = Arc::::new_zeroed_in(System); - /// let zero = unsafe { zero.assume_init() }; - /// - /// assert_eq!(*zero, 0) - /// ``` - /// - /// [zeroed]: mem::MaybeUninit::zeroed - #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "allocator_api", issue = "32838")] - #[inline] - pub fn new_zeroed_in(alloc: A) -> Arc, A> { - // ignore-tidy-undocumented-unsafe - unsafe { - Arc::from_ptr_in( - Arc::allocate_for_layout( - Layout::new::(), - |layout| alloc.allocate_zeroed(layout), - <*mut u8>::cast, - ), - alloc, - ) - } - } - - /// Constructs a new `Arc` in the given allocator while giving you a `Weak` to the allocation, - /// to allow you to construct a `T` which holds a weak pointer to itself. - /// - /// Generally, a structure circularly referencing itself, either directly or - /// indirectly, should not hold a strong reference to itself to prevent a memory leak. - /// Using this function, you get access to the weak pointer during the - /// initialization of `T`, before the `Arc` is created, such that you can - /// clone and store it inside the `T`. - /// - /// `new_cyclic_in` first allocates the managed allocation for the `Arc`, - /// then calls your closure, giving it a `Weak` to this allocation, - /// and only afterwards completes the construction of the `Arc` by placing - /// the `T` returned from your closure into the allocation. - /// - /// Since the new `Arc` is not fully-constructed until `Arc::new_cyclic_in` - /// returns, calling [`upgrade`] on the weak reference inside your closure will - /// fail and result in a `None` value. - /// - /// # Panics - /// - /// If `data_fn` panics, the panic is propagated to the caller, and the - /// temporary [`Weak`] is dropped normally. - /// - /// # Example - /// - /// See [`new_cyclic`] - /// - /// [`new_cyclic`]: Arc::new_cyclic - /// [`upgrade`]: Weak::upgrade - #[cfg(not(no_global_oom_handling))] - #[inline] - #[unstable(feature = "allocator_api", issue = "32838")] - pub fn new_cyclic_in(data_fn: F, alloc: A) -> Arc - where - F: FnOnce(&Weak) -> T, - { - // Construct the inner in the "uninitialized" state with a single - // weak reference. - let (uninit_ptr, alloc) = Box::into_non_null_with_allocator(Box::new_in( - ArcInner { - strong: atomic::AtomicUsize::new(0), - weak: atomic::AtomicUsize::new(1), - data: mem::MaybeUninit::::uninit(), - }, - alloc, - )); - let init_ptr: NonNull> = uninit_ptr.cast(); - - let weak = Weak { ptr: init_ptr, alloc }; - - // It's important we don't give up ownership of the weak pointer, or - // else the memory might be freed by the time `data_fn` returns. If - // we really wanted to pass ownership, we could create an additional - // weak pointer for ourselves, but this would result in additional - // updates to the weak reference count which might not be necessary - // otherwise. - let data = data_fn(&weak); - - // Now we can properly initialize the inner value and turn our weak - // reference into a strong reference. - let inner = init_ptr.as_ptr(); - // ignore-tidy-undocumented-unsafe - unsafe { - ptr::write(&raw mut (*inner).data, data); - - // The above write to the data field must be visible to any threads which - // observe a non-zero strong count. Therefore we need at least "Release" ordering - // in order to synchronize with the `compare_exchange_weak` in `Weak::upgrade`. - // - // "Acquire" ordering is not required. When considering the possible behaviors - // of `data_fn` we only need to look at what it could do with a reference to a - // non-upgradeable `Weak`: - // - It can *clone* the `Weak`, increasing the weak reference count. - // - It can drop those clones, decreasing the weak reference count (but never to zero). - // - // These side effects do not impact us in any way, and no other side effects are - // possible with safe code alone. - let prev_value = (*inner).strong.fetch_add(1, Release); - debug_assert_eq!(prev_value, 0, "No prior strong references should exist"); - - // Strong references should collectively own a shared weak reference, - // so don't run the destructor for our old weak reference. - // Calling into_raw_with_allocator has the double effect of giving us back the allocator, - // and forgetting the weak reference. - let alloc = weak.into_raw_with_allocator().1; - - Arc::from_inner_in(init_ptr, alloc) - } - } - - /// Constructs a new `Pin>` in the provided allocator. If `T` does not implement `Unpin`, - /// then `data` will be pinned in memory and unable to be moved. - #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "allocator_api", issue = "32838")] - #[inline] - pub fn pin_in(data: T, alloc: A) -> Pin> - where - A: 'static, - { - // SAFETY: We own and create the pinned pointer. - unsafe { Pin::new_unchecked(Arc::new_in(data, alloc)) } - } - - /// Constructs a new `Pin>` in the provided allocator, return an error if allocation - /// fails. - #[inline] - #[unstable(feature = "allocator_api", issue = "32838")] - pub fn try_pin_in(data: T, alloc: A) -> Result>, AllocError> - where - A: 'static, - { - // SAFETY: We own and create the pinned pointer. - unsafe { Ok(Pin::new_unchecked(Arc::try_new_in(data, alloc)?)) } - } - - /// Constructs a new `Arc` in the provided allocator, returning an error if allocation fails. - /// - /// # Examples - /// - /// ``` - /// #![feature(allocator_api)] - /// - /// use std::sync::Arc; - /// use std::alloc::System; - /// - /// let five = Arc::try_new_in(5, System)?; - /// # Ok::<(), std::alloc::AllocError>(()) - /// ``` - #[unstable(feature = "allocator_api", issue = "32838")] - #[inline] - pub fn try_new_in(data: T, alloc: A) -> Result, AllocError> { - // Start the weak pointer count as 1 which is the weak pointer that's - // held by all the strong pointers (kinda), see std/rc.rs for more info - let x = Box::try_new_in( - ArcInner { - strong: atomic::AtomicUsize::new(1), - weak: atomic::AtomicUsize::new(1), - data, - }, - alloc, - )?; - let (ptr, alloc) = Box::into_non_null_with_allocator(x); - // SAFETY: Pointer is valid since we created it. - Ok(unsafe { Self::from_inner_in(ptr, alloc) }) - } - - /// Constructs a new `Arc` with uninitialized contents, in the provided allocator, returning an - /// error if allocation fails. - /// - /// # Examples - /// - /// ``` - /// #![feature(allocator_api)] - /// #![feature(get_mut_unchecked)] - /// - /// use std::sync::Arc; - /// use std::alloc::System; - /// - /// let mut five = Arc::::try_new_uninit_in(System)?; - /// - /// let five = unsafe { - /// // Deferred initialization: - /// Arc::get_mut_unchecked(&mut five).as_mut_ptr().write(5); - /// - /// five.assume_init() - /// }; - /// - /// assert_eq!(*five, 5); - /// # Ok::<(), std::alloc::AllocError>(()) - /// ``` - #[unstable(feature = "allocator_api", issue = "32838")] - #[inline] - pub fn try_new_uninit_in(alloc: A) -> Result, A>, AllocError> { - // ignore-tidy-undocumented-unsafe - unsafe { - Ok(Arc::from_ptr_in( - Arc::try_allocate_for_layout( - Layout::new::(), - |layout| alloc.allocate(layout), - <*mut u8>::cast, - )?, - alloc, - )) - } - } - - /// Constructs a new `Arc` with uninitialized contents, with the memory - /// being filled with `0` bytes, in the provided allocator, returning an error if allocation - /// fails. - /// - /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and incorrect usage - /// of this method. - /// - /// # Examples - /// - /// ``` - /// #![feature(allocator_api)] - /// - /// use std::sync::Arc; - /// use std::alloc::System; - /// - /// let zero = Arc::::try_new_zeroed_in(System)?; - /// let zero = unsafe { zero.assume_init() }; - /// - /// assert_eq!(*zero, 0); - /// # Ok::<(), std::alloc::AllocError>(()) - /// ``` - /// - /// [zeroed]: mem::MaybeUninit::zeroed - #[unstable(feature = "allocator_api", issue = "32838")] - #[inline] - pub fn try_new_zeroed_in(alloc: A) -> Result, A>, AllocError> { - // ignore-tidy-undocumented-unsafe - unsafe { - Ok(Arc::from_ptr_in( - Arc::try_allocate_for_layout( - Layout::new::(), - |layout| alloc.allocate_zeroed(layout), - <*mut u8>::cast, - )?, - alloc, - )) - } - } - /// Returns the inner value, if the `Arc` has exactly one strong reference. - /// - /// Otherwise, an [`Err`] is returned with the same `Arc` that was - /// passed in. - /// - /// This will succeed even if there are outstanding weak references. - /// - /// It is strongly recommended to use [`Arc::into_inner`] instead if you don't - /// keep the `Arc` in the [`Err`] case. - /// Immediately dropping the [`Err`]-value, as the expression - /// `Arc::try_unwrap(this).ok()` does, can cause the strong count to - /// drop to zero and the inner value of the `Arc` to be dropped. - /// For instance, if two threads execute such an expression in parallel, - /// there is a race condition without the possibility of unsafety: - /// The threads could first both check whether they own the last instance - /// in `Arc::try_unwrap`, determine that they both do not, and then both - /// discard and drop their instance in the call to [`ok`][`Result::ok`]. - /// In this scenario, the value inside the `Arc` is safely destroyed - /// by exactly one of the threads, but neither thread will ever be able - /// to use the value. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let x = Arc::new(3); - /// assert_eq!(Arc::try_unwrap(x), Ok(3)); - /// - /// let x = Arc::new(4); - /// let _y = Arc::clone(&x); - /// assert_eq!(*Arc::try_unwrap(x).unwrap_err(), 4); - /// ``` - #[inline] - #[stable(feature = "arc_unique", since = "1.4.0")] - pub fn try_unwrap(this: Self) -> Result { - if this.inner().strong.compare_exchange(1, 0, Relaxed, Relaxed).is_err() { - return Err(this); - } - - acquire!(this.inner().strong); - - let this = ManuallyDrop::new(this); - // SAFETY: Pointer is valid for reads, contains initialised memory, - // and not dropped multiple times (we return it). - let elem: T = unsafe { ptr::read(&this.ptr.as_ref().data) }; - // SAFETY: As above, but we explicitly drop the allocator only once - // upon creating and dropping a weak pointer. - let alloc: A = unsafe { ptr::read(&this.alloc) }; // copy the allocator - - // Make a weak pointer to clean up the implicit strong-weak reference - let _weak = Weak { ptr: this.ptr, alloc }; - - Ok(elem) - } - - /// Returns the inner value, if the `Arc` has exactly one strong reference. - /// - /// Otherwise, [`None`] is returned and the `Arc` is dropped. - /// - /// This will succeed even if there are outstanding weak references. - /// - /// If `Arc::into_inner` is called on every clone of this `Arc`, - /// it is guaranteed that exactly one of the calls returns the inner value. - /// This means in particular that the inner value is not dropped. - /// - /// [`Arc::try_unwrap`] is conceptually similar to `Arc::into_inner`, but it - /// is meant for different use-cases. If used as a direct replacement - /// for `Arc::into_inner` anyway, such as with the expression - /// [Arc::try_unwrap]\(this).[ok][Result::ok](), then it does - /// **not** give the same guarantee as described in the previous paragraph. - /// For more information, see the examples below and read the documentation - /// of [`Arc::try_unwrap`]. - /// - /// # Examples - /// - /// Minimal example demonstrating the guarantee that `Arc::into_inner` gives. - /// ``` - /// use std::sync::Arc; - /// - /// let x = Arc::new(3); - /// let y = Arc::clone(&x); - /// - /// // Two threads calling `Arc::into_inner` on both clones of an `Arc`: - /// let x_thread = std::thread::spawn(|| Arc::into_inner(x)); - /// let y_thread = std::thread::spawn(|| Arc::into_inner(y)); - /// - /// let x_inner_value = x_thread.join().unwrap(); - /// let y_inner_value = y_thread.join().unwrap(); - /// - /// // One of the threads is guaranteed to receive the inner value: - /// assert!(matches!( - /// (x_inner_value, y_inner_value), - /// (None, Some(3)) | (Some(3), None) - /// )); - /// // The result could also be `(None, None)` if the threads called - /// // `Arc::try_unwrap(x).ok()` and `Arc::try_unwrap(y).ok()` instead. - /// ``` - /// - /// A more practical example demonstrating the need for `Arc::into_inner`: - /// ``` - /// use std::sync::Arc; - /// - /// // Definition of a simple singly linked list using `Arc`: - /// #[derive(Clone)] - /// struct LinkedList(Option>>); - /// struct Node(T, Option>>); - /// - /// // Dropping a long `LinkedList` relying on the destructor of `Arc` - /// // can cause a stack overflow. To prevent this, we can provide a - /// // manual `Drop` implementation that does the destruction in a loop: - /// impl Drop for LinkedList { - /// fn drop(&mut self) { - /// let mut link = self.0.take(); - /// while let Some(arc_node) = link.take() { - /// if let Some(Node(_value, next)) = Arc::into_inner(arc_node) { - /// link = next; - /// } - /// } - /// } - /// } - /// - /// // Implementation of `new` and `push` omitted - /// impl LinkedList { - /// /* ... */ - /// # fn new() -> Self { - /// # LinkedList(None) - /// # } - /// # fn push(&mut self, x: T) { - /// # self.0 = Some(Arc::new(Node(x, self.0.take()))); - /// # } - /// } - /// - /// // The following code could have still caused a stack overflow - /// // despite the manual `Drop` impl if that `Drop` impl had used - /// // `Arc::try_unwrap(arc).ok()` instead of `Arc::into_inner(arc)`. - /// - /// // Create a long list and clone it - /// let mut x = LinkedList::new(); - /// let size = 100000; - /// # let size = if cfg!(miri) { 100 } else { size }; - /// for i in 0..size { - /// x.push(i); // Adds i to the front of x - /// } - /// let y = x.clone(); - /// - /// // Drop the clones in parallel - /// let x_thread = std::thread::spawn(|| drop(x)); - /// let y_thread = std::thread::spawn(|| drop(y)); - /// x_thread.join().unwrap(); - /// y_thread.join().unwrap(); - /// ``` - #[inline] - #[stable(feature = "arc_into_inner", since = "1.70.0")] - pub fn into_inner(this: Self) -> Option { - // Make sure that the ordinary `Drop` implementation isn’t called as well - let mut this = mem::ManuallyDrop::new(this); - - // Following the implementation of `drop` and `drop_slow` - if this.inner().strong.fetch_sub(1, Release) != 1 { - return None; - } - - acquire!(this.inner().strong); - - // SAFETY: This mirrors the line - // - // unsafe { ptr::drop_in_place(Self::get_mut_unchecked(self)) }; - // - // in `drop_slow`. Instead of dropping the value behind the pointer, - // it is read and eventually returned; `ptr::read` has the same - // safety conditions as `ptr::drop_in_place`. - let inner = unsafe { ptr::read(Self::get_mut_unchecked(&mut this)) }; - // SAFETY: Pointer is valid for reads. - let alloc = unsafe { ptr::read(&this.alloc) }; - - drop(Weak { ptr: this.ptr, alloc }); - - Some(inner) - } - - /// Maps the value in an `Arc`, reusing the allocation if possible. - /// - /// `f` is called on a reference to the value in the `Arc`, and the result is returned, also in - /// an `Arc`. - /// - /// Note: this is an associated function, which means that you have - /// to call it as `Arc::map(a, f)` instead of `r.map(a)`. This - /// is so that there is no conflict with a method on the inner type. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let r = Arc::new(7); - /// let new = Arc::map(r, |i| i + 7); - /// assert_eq!(*new, 14); - /// ``` - #[cfg(not(no_global_oom_handling))] - #[stable(feature = "smart_pointer_map", since = "CURRENT_RUSTC_VERSION")] - pub fn map(this: Self, f: impl FnOnce(&T) -> U) -> Arc { - if size_of::() == size_of::() - && align_of::() == align_of::() - && Arc::is_unique(&this) - { - // ignore-tidy-undocumented-unsafe - unsafe { - let (ptr, alloc) = Arc::into_raw_with_allocator(this); - let value = ptr.read(); - let mut allocation = Arc::from_raw_in(ptr.cast::>(), alloc); - - Arc::get_mut_unchecked(&mut allocation).write(f(&value)); - allocation.assume_init() - } - } else { - let output = f(&*this); - let (ptr, alloc) = Arc::into_raw_with_allocator(this); - // ignore-tidy-undocumented-unsafe - unsafe { Arc::decrement_strong_count_in(ptr, &alloc) } - - Arc::new_in(output, alloc) - } - } - - /// Attempts to map the value in an `Arc`, reusing the allocation if possible. - /// - /// `f` is called on a reference to the value in the `Arc`, and if the operation succeeds, the - /// result is returned, also in an `Arc`. - /// - /// Note: this is an associated function, which means that you have - /// to call it as `Arc::try_map(a, f)` instead of `a.try_map(f)`. This - /// is so that there is no conflict with a method on the inner type. - /// - /// # Examples - /// - /// ``` - /// #![feature(smart_pointer_try_map)] - /// - /// use std::sync::Arc; - /// - /// let b = Arc::new(7); - /// let new = Arc::try_map(b, |&i| u32::try_from(i)).unwrap(); - /// assert_eq!(*new, 7); - /// ``` - #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "smart_pointer_try_map", issue = "144419")] - pub fn try_map( - this: Self, - f: impl FnOnce(&T) -> R, - ) -> >>::TryType - where - R: Try, - R::Residual: Residual>, - { - if size_of::() == size_of::() - && align_of::() == align_of::() - && Arc::is_unique(&this) - { - // ignore-tidy-undocumented-unsafe - unsafe { - let (ptr, alloc) = Arc::into_raw_with_allocator(this); - let value = ptr.read(); - let mut allocation = - Arc::from_raw_in(ptr.cast::>(), alloc); - - Arc::get_mut_unchecked(&mut allocation).write(f(&value)?); - try { allocation.assume_init() } - } - } else { - let output = f(&*this)?; - let (ptr, alloc) = Arc::into_raw_with_allocator(this); - // ignore-tidy-undocumented-unsafe - unsafe { Arc::decrement_strong_count_in(ptr, &alloc) } - - try { Arc::new_in(output, alloc) } - } - } -} - -impl Arc<[T]> { - /// Constructs a new atomically reference-counted slice with uninitialized contents. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let mut values = Arc::<[u32]>::new_uninit_slice(3); - /// - /// // Deferred initialization: - /// let data = Arc::get_mut(&mut values).unwrap(); - /// data[0].write(1); - /// data[1].write(2); - /// data[2].write(3); - /// - /// let values = unsafe { values.assume_init() }; - /// - /// assert_eq!(*values, [1, 2, 3]) - /// ``` - #[cfg(not(no_global_oom_handling))] - #[inline] - #[stable(feature = "new_uninit", since = "1.82.0")] - #[must_use] - pub fn new_uninit_slice(len: usize) -> Arc<[mem::MaybeUninit]> { - // ignore-tidy-undocumented-unsafe - unsafe { Arc::from_ptr(Arc::allocate_for_slice(len)) } - } - - /// Constructs a new atomically reference-counted slice with uninitialized contents, with the memory being - /// filled with `0` bytes. - /// - /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and - /// incorrect usage of this method. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let values = Arc::<[u32]>::new_zeroed_slice(3); - /// let values = unsafe { values.assume_init() }; - /// - /// assert_eq!(*values, [0, 0, 0]) - /// ``` - /// - /// [zeroed]: mem::MaybeUninit::zeroed - #[cfg(not(no_global_oom_handling))] - #[inline] - #[stable(feature = "new_zeroed_alloc", since = "1.92.0")] - #[must_use] - pub fn new_zeroed_slice(len: usize) -> Arc<[mem::MaybeUninit]> { - // ignore-tidy-undocumented-unsafe - unsafe { - Arc::from_ptr(Arc::allocate_for_layout( - Layout::array::(len).unwrap(), - |layout| Global.allocate_zeroed(layout), - |mem| mem.cast::().cast_slice(len) as *mut ArcInner<[mem::MaybeUninit]>, - )) - } - } -} - -impl Arc<[T], A> { - /// Constructs a new atomically reference-counted slice with uninitialized contents in the - /// provided allocator. - /// - /// # Examples - /// - /// ``` - /// #![feature(get_mut_unchecked)] - /// #![feature(allocator_api)] - /// - /// use std::sync::Arc; - /// use std::alloc::System; - /// - /// let mut values = Arc::<[u32], _>::new_uninit_slice_in(3, System); - /// - /// let values = unsafe { - /// // Deferred initialization: - /// Arc::get_mut_unchecked(&mut values)[0].as_mut_ptr().write(1); - /// Arc::get_mut_unchecked(&mut values)[1].as_mut_ptr().write(2); - /// Arc::get_mut_unchecked(&mut values)[2].as_mut_ptr().write(3); - /// - /// values.assume_init() - /// }; - /// - /// assert_eq!(*values, [1, 2, 3]) - /// ``` - #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "allocator_api", issue = "32838")] - #[inline] - pub fn new_uninit_slice_in(len: usize, alloc: A) -> Arc<[mem::MaybeUninit], A> { - // ignore-tidy-undocumented-unsafe - unsafe { Arc::from_ptr_in(Arc::allocate_for_slice_in(len, &alloc), alloc) } - } - - /// Constructs a new atomically reference-counted slice with uninitialized contents, with the memory being - /// filled with `0` bytes, in the provided allocator. - /// - /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and - /// incorrect usage of this method. - /// - /// # Examples - /// - /// ``` - /// #![feature(allocator_api)] - /// - /// use std::sync::Arc; - /// use std::alloc::System; - /// - /// let values = Arc::<[u32], _>::new_zeroed_slice_in(3, System); - /// let values = unsafe { values.assume_init() }; - /// - /// assert_eq!(*values, [0, 0, 0]) - /// ``` - /// - /// [zeroed]: mem::MaybeUninit::zeroed - #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "allocator_api", issue = "32838")] - #[inline] - pub fn new_zeroed_slice_in(len: usize, alloc: A) -> Arc<[mem::MaybeUninit], A> { - // ignore-tidy-undocumented-unsafe - unsafe { - Arc::from_ptr_in( - Arc::allocate_for_layout( - Layout::array::(len).unwrap(), - |layout| alloc.allocate_zeroed(layout), - |mem| mem.cast::().cast_slice(len) as *mut ArcInner<[mem::MaybeUninit]>, - ), - alloc, - ) - } - } - - /// Converts the reference-counted slice into a reference-counted array. - /// - /// This operation does not reallocate; the underlying array of the slice is simply reinterpreted as an array type. - /// - /// # Errors - /// - /// Returns the original `Arc<[T]>` in the `Err` variant if `self.len()` does not equal `N`. - /// - /// # Examples - /// - /// ``` - /// #![feature(alloc_slice_into_array)] - /// use std::sync::Arc; - /// - /// let arc_slice: Arc<[i32]> = Arc::new([1, 2, 3]); - /// - /// let arc_array: Arc<[i32; 3]> = arc_slice.into_array().unwrap(); - /// ``` - #[unstable(feature = "alloc_slice_into_array", issue = "148082")] - #[inline] - pub fn into_array(self) -> Result, Self> { - if self.len() == N { - let (ptr, alloc) = Self::into_raw_with_allocator(self); - let ptr = ptr as *const [T; N]; - - // SAFETY: The underlying array of a slice has the exact same layout as an actual array `[T; N]` if `N` is equal to the slice's length. - let me = unsafe { Arc::from_raw_in(ptr, alloc) }; - Ok(me) - } else { - Err(self) - } - } -} - -impl Arc, A> { - /// Converts to `Arc`. - /// - /// # Safety - /// - /// As with [`MaybeUninit::assume_init`], - /// it is up to the caller to guarantee that the inner value - /// really is in an initialized state. - /// Calling this when the content is not yet fully initialized - /// causes immediate undefined behavior. - /// - /// [`MaybeUninit::assume_init`]: mem::MaybeUninit::assume_init - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let mut five = Arc::::new_uninit(); - /// - /// // Deferred initialization: - /// Arc::get_mut(&mut five).unwrap().write(5); - /// - /// let five = unsafe { five.assume_init() }; - /// - /// assert_eq!(*five, 5) - /// ``` - #[stable(feature = "new_uninit", since = "1.82.0")] - #[must_use = "`self` will be dropped if the result is not used"] - #[inline] - pub unsafe fn assume_init(self) -> Arc { - let (ptr, alloc) = Arc::into_inner_with_allocator(self); - // ignore-tidy-undocumented-unsafe - unsafe { Arc::from_inner_in(ptr.cast(), alloc) } - } -} - -impl Arc { - /// Constructs a new `Arc` with a clone of `value`. - /// - /// # Examples - /// - /// ``` - /// #![feature(clone_from_ref)] - /// use std::sync::Arc; - /// - /// let hello: Arc = Arc::clone_from_ref("hello"); - /// ``` - #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "clone_from_ref", issue = "149075")] - pub fn clone_from_ref(value: &T) -> Arc { - Arc::clone_from_ref_in(value, Global) - } - - /// Constructs a new `Arc` with a clone of `value`, returning an error if allocation fails - /// - /// # Examples - /// - /// ``` - /// #![feature(clone_from_ref)] - /// #![feature(allocator_api)] - /// use std::sync::Arc; - /// - /// let hello: Arc = Arc::try_clone_from_ref("hello")?; - /// # Ok::<(), std::alloc::AllocError>(()) - /// ``` - #[unstable(feature = "clone_from_ref", issue = "149075")] - //#[unstable(feature = "allocator_api", issue = "32838")] - pub fn try_clone_from_ref(value: &T) -> Result, AllocError> { - Arc::try_clone_from_ref_in(value, Global) - } -} - -impl Arc { - /// Constructs a new `Arc` with a clone of `value` in the provided allocator. - /// - /// # Examples - /// - /// ``` - /// #![feature(clone_from_ref)] - /// #![feature(allocator_api)] - /// use std::sync::Arc; - /// use std::alloc::System; - /// - /// let hello: Arc = Arc::clone_from_ref_in("hello", System); - /// ``` - #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "clone_from_ref", issue = "149075")] - //#[unstable(feature = "allocator_api", issue = "32838")] - pub fn clone_from_ref_in(value: &T, alloc: A) -> Arc { - // `in_progress` drops the allocation if we panic before finishing initializing it. - let mut in_progress: UniqueArcUninit = UniqueArcUninit::new(value, alloc); - - // Initialize with clone of value. - // ignore-tidy-undocumented-unsafe - unsafe { - // Clone. If the clone panics, `in_progress` will be dropped and clean up. - value.clone_to_uninit(in_progress.data_ptr().cast()); - // Cast type of pointer, now that it is initialized. - in_progress.into_arc() - } - } - - /// Constructs a new `Arc` with a clone of `value` in the provided allocator, returning an error if allocation fails - /// - /// # Examples - /// - /// ``` - /// #![feature(clone_from_ref)] - /// #![feature(allocator_api)] - /// use std::sync::Arc; - /// use std::alloc::System; - /// - /// let hello: Arc = Arc::try_clone_from_ref_in("hello", System)?; - /// # Ok::<(), std::alloc::AllocError>(()) - /// ``` - #[unstable(feature = "clone_from_ref", issue = "149075")] - //#[unstable(feature = "allocator_api", issue = "32838")] - pub fn try_clone_from_ref_in(value: &T, alloc: A) -> Result, AllocError> { - // `in_progress` drops the allocation if we panic before finishing initializing it. - let mut in_progress: UniqueArcUninit = UniqueArcUninit::try_new(value, alloc)?; - - // Initialize with clone of value. - // ignore-tidy-undocumented-unsafe - let initialized_clone = unsafe { - // Clone. If the clone panics, `in_progress` will be dropped and clean up. - value.clone_to_uninit(in_progress.data_ptr().cast()); - // Cast type of pointer, now that it is initialized. - in_progress.into_arc() - }; - - Ok(initialized_clone) - } -} - -impl Arc<[mem::MaybeUninit], A> { - /// Converts to `Arc<[T]>`. - /// - /// # Safety - /// - /// As with [`MaybeUninit::assume_init`], - /// it is up to the caller to guarantee that the inner value - /// really is in an initialized state. - /// Calling this when the content is not yet fully initialized - /// causes immediate undefined behavior. - /// - /// [`MaybeUninit::assume_init`]: mem::MaybeUninit::assume_init - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let mut values = Arc::<[u32]>::new_uninit_slice(3); - /// - /// // Deferred initialization: - /// let data = Arc::get_mut(&mut values).unwrap(); - /// data[0].write(1); - /// data[1].write(2); - /// data[2].write(3); - /// - /// let values = unsafe { values.assume_init() }; - /// - /// assert_eq!(*values, [1, 2, 3]) - /// ``` - #[stable(feature = "new_uninit", since = "1.82.0")] - #[must_use = "`self` will be dropped if the result is not used"] - #[inline] - pub unsafe fn assume_init(self) -> Arc<[T], A> { - let (ptr, alloc) = Arc::into_inner_with_allocator(self); - // SAFETY: Upheld by caller. - unsafe { Arc::from_ptr_in(ptr.as_ptr() as _, alloc) } - } -} - -impl Arc { - /// Constructs an `Arc` from a raw pointer. - /// - /// The raw pointer must have been previously returned by a call to - /// [`Arc::into_raw`][into_raw] or [`Arc::into_raw_with_allocator`][into_raw_with_allocator]. - /// - /// # Safety - /// - /// * Creating a `Arc` from a pointer other than one returned from - /// [`Arc::into_raw`][into_raw] or [`Arc::into_raw_with_allocator`][into_raw_with_allocator] - /// is undefined behavior. - /// * If `U` is sized, it must have the same size and alignment as `T`. This - /// is trivially true if `U` is `T`. - /// * If `U` is unsized, its data pointer must have the same size and - /// alignment as `T`. This is trivially true if `Arc` was constructed - /// through `Arc` and then converted to `Arc` through an [unsized - /// coercion]. - /// * Note that if `U` or `U`'s data pointer is not `T` but has the same size - /// and alignment, this is basically like transmuting references of - /// different types. See [`mem::transmute`][transmute] for more information - /// on what restrictions apply in this case. - /// * The raw pointer must point to a block of memory allocated by the global allocator. - /// * The user of `from_raw` has to make sure a specific value of `T` is only - /// dropped once. - /// - /// This function is unsafe because improper use may lead to memory unsafety, - /// even if the returned `Arc` is never accessed. - /// - /// [into_raw]: Arc::into_raw - /// [into_raw_with_allocator]: Arc::into_raw_with_allocator - /// [transmute]: core::mem::transmute - /// [unsized coercion]: https://doc.rust-lang.org/reference/type-coercions.html#unsized-coercions - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let x = Arc::new("hello".to_owned()); - /// let x_ptr = Arc::into_raw(x); - /// - /// unsafe { - /// // Convert back to an `Arc` to prevent leak. - /// let x = Arc::from_raw(x_ptr); - /// assert_eq!(&*x, "hello"); - /// - /// // Further calls to `Arc::from_raw(x_ptr)` would be memory-unsafe. - /// } - /// - /// // The memory was freed when `x` went out of scope above, so `x_ptr` is now dangling! - /// ``` - /// - /// Convert a slice back into its original array: - /// - /// ``` - /// use std::sync::Arc; - /// - /// let x: Arc<[u32]> = Arc::new([1, 2, 3]); - /// let x_ptr: *const [u32] = Arc::into_raw(x); - /// - /// unsafe { - /// let x: Arc<[u32; 3]> = Arc::from_raw(x_ptr.cast::<[u32; 3]>()); - /// assert_eq!(&*x, &[1, 2, 3]); - /// } - /// ``` - #[inline] - #[stable(feature = "rc_raw", since = "1.17.0")] - pub unsafe fn from_raw(ptr: *const T) -> Self { - // SAFETY: Upheld by caller. - unsafe { Arc::from_raw_in(ptr, Global) } - } - - /// Consumes the `Arc`, returning the wrapped pointer. - /// - /// To avoid a memory leak the pointer must be converted back to an `Arc` using - /// [`Arc::from_raw`]. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let x = Arc::new("hello".to_owned()); - /// let x_ptr = Arc::into_raw(x); - /// assert_eq!(unsafe { &*x_ptr }, "hello"); - /// # // Prevent leaks for Miri. - /// # drop(unsafe { Arc::from_raw(x_ptr) }); - /// ``` - #[must_use = "losing the pointer will leak memory"] - #[stable(feature = "rc_raw", since = "1.17.0")] - #[rustc_never_returns_null_ptr] - pub fn into_raw(this: Self) -> *const T { - let this = ManuallyDrop::new(this); - Self::as_ptr(&*this) - } - - /// Increments the strong reference count on the `Arc` associated with the - /// provided pointer by one. - /// - /// # Safety - /// - /// The pointer must have been obtained through `Arc::into_raw` and must satisfy the - /// same layout requirements specified in [`Arc::from_raw_in`][from_raw_in]. - /// The associated `Arc` instance must be valid (i.e. the strong count must be at - /// least 1) for the duration of this method, and `ptr` must point to a block of memory - /// allocated by the global allocator. - /// - /// [from_raw_in]: Arc::from_raw_in - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// - /// unsafe { - /// let ptr = Arc::into_raw(five); - /// Arc::increment_strong_count(ptr); - /// - /// // This assertion is deterministic because we haven't shared - /// // the `Arc` between threads. - /// let five = Arc::from_raw(ptr); - /// assert_eq!(2, Arc::strong_count(&five)); - /// # // Prevent leaks for Miri. - /// # Arc::decrement_strong_count(ptr); - /// } - /// ``` - #[inline] - #[stable(feature = "arc_mutate_strong_count", since = "1.51.0")] - pub unsafe fn increment_strong_count(ptr: *const T) { - // SAFETY: Upheld by caller. - unsafe { Arc::increment_strong_count_in(ptr, Global) } - } - - /// Decrements the strong reference count on the `Arc` associated with the - /// provided pointer by one. - /// - /// # Safety - /// - /// The pointer must have been obtained through `Arc::into_raw` and must satisfy the - /// same layout requirements specified in [`Arc::from_raw_in`][from_raw_in]. - /// The associated `Arc` instance must be valid (i.e. the strong count must be at - /// least 1) when invoking this method, and `ptr` must point to a block of memory - /// allocated by the global allocator. This method can be used to release the final - /// `Arc` and backing storage, but **should not** be called after the final `Arc` has been - /// released. - /// - /// [from_raw_in]: Arc::from_raw_in - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// - /// unsafe { - /// let ptr = Arc::into_raw(five); - /// Arc::increment_strong_count(ptr); - /// - /// // Those assertions are deterministic because we haven't shared - /// // the `Arc` between threads. - /// let five = Arc::from_raw(ptr); - /// assert_eq!(2, Arc::strong_count(&five)); - /// Arc::decrement_strong_count(ptr); - /// assert_eq!(1, Arc::strong_count(&five)); - /// } - /// ``` - #[inline] - #[stable(feature = "arc_mutate_strong_count", since = "1.51.0")] - pub unsafe fn decrement_strong_count(ptr: *const T) { - // SAFETY: Upheld by caller. - unsafe { Arc::decrement_strong_count_in(ptr, Global) } - } - - /// Gets the number of strong (`Arc`) pointers to the allocation behind the given raw - /// pointer. - /// - /// This method does not consume or drop the `Arc` behind this pointer. - /// - /// # Safety - /// - /// The pointer must point to (and have valid metadata for) the value inside a live `Arc` - /// allocation, such as a pointer returned by [`Arc::into_raw`], - /// [`Arc::into_raw_with_allocator`], or [`Arc::as_ptr`]. - /// `T` must have the same alignment as that value. - /// The associated `Arc` instance must be valid (i.e. the strong count must be at - /// least 1) for the duration of this method. - /// - /// Using this method correctly also requires extra care: another thread can change the - /// strong count at any time, including between calling this method and acting on the - /// result. - /// - /// # Examples - /// - /// ``` - /// #![feature(arc_raw_get_strong)] - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// let _also_five = Arc::clone(&five); - /// let ptr = Arc::into_raw(five); - /// - /// unsafe { - /// // This assertion is deterministic because we haven't shared - /// // the `Arc` between threads. - /// assert_eq!(2, Arc::strong_count_from_raw(ptr)); - /// - /// // Convert back to an `Arc` to avoid leaking memory. - /// let five = Arc::from_raw(ptr); - /// assert_eq!(2, Arc::strong_count(&five)); - /// } - /// ``` - #[inline] - #[must_use] - #[unstable(feature = "arc_raw_get_strong", issue = "157021")] - pub unsafe fn strong_count_from_raw(ptr: *const T) -> usize { - // SAFETY: Upheld by caller. - let offset = unsafe { data_offset(ptr) }; - // Reverse the offset to find the original ArcInner. - // SAFETY: Caller ensures this pointer was to an `Arc` allocation, - // so offsetting must be inbounds. - let arc_ptr = unsafe { ptr.byte_sub(offset) as *mut ArcInner }; - // SAFETY: Per the above, an `ArcInner` is stored here. - unsafe { (*arc_ptr).strong.load(Relaxed) } - } -} - -impl Arc { - /// Returns a reference to the underlying allocator. - /// - /// Note: this is an associated function, which means that you have - /// to call it as `Arc::allocator(&a)` instead of `a.allocator()`. This - /// is so that there is no conflict with a method on the inner type. - #[inline] - #[unstable(feature = "allocator_api", issue = "32838")] - pub fn allocator(this: &Self) -> &A { - &this.alloc - } - - /// Consumes the `Arc`, returning the wrapped pointer and allocator. - /// - /// To avoid a memory leak the pointer must be converted back to an `Arc` using - /// [`Arc::from_raw_in`]. - /// - /// # Examples - /// - /// ``` - /// #![feature(allocator_api)] - /// use std::sync::Arc; - /// use std::alloc::System; - /// - /// let x = Arc::new_in("hello".to_owned(), System); - /// let (ptr, alloc) = Arc::into_raw_with_allocator(x); - /// assert_eq!(unsafe { &*ptr }, "hello"); - /// let x = unsafe { Arc::from_raw_in(ptr, alloc) }; - /// assert_eq!(&*x, "hello"); - /// ``` - #[must_use = "losing the pointer will leak memory"] - #[unstable(feature = "allocator_api", issue = "32838")] - pub fn into_raw_with_allocator(this: Self) -> (*const T, A) { - let this = mem::ManuallyDrop::new(this); - let ptr = Self::as_ptr(&this); - // SAFETY: `this` is ManuallyDrop so the allocator will not be double-dropped - let alloc = unsafe { ptr::read(&this.alloc) }; - (ptr, alloc) - } - - /// Provides a raw pointer to the data. - /// - /// The counts are not affected in any way and the `Arc` is not consumed. The pointer is valid for - /// as long as there are strong counts in the `Arc`. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let x = Arc::new("hello".to_owned()); - /// let y = Arc::clone(&x); - /// let x_ptr = Arc::as_ptr(&x); - /// assert_eq!(x_ptr, Arc::as_ptr(&y)); - /// assert_eq!(unsafe { &*x_ptr }, "hello"); - /// ``` - #[must_use] - #[stable(feature = "rc_as_ptr", since = "1.45.0")] - #[rustc_never_returns_null_ptr] - pub fn as_ptr(this: &Self) -> *const T { - let ptr: *mut ArcInner = NonNull::as_ptr(this.ptr); - - // SAFETY: This cannot go through Deref::deref or ArcInnerPtr::inner because - // this is required to retain raw/mut provenance such that e.g. `get_mut` can - // write through the pointer after the Arc is recovered through `from_raw`. - unsafe { &raw mut (*ptr).data } - } - - /// Constructs an `Arc` from a raw pointer. - /// - /// The raw pointer must have been previously returned by a call to [`Arc::into_raw`][into_raw] or [`Arc::into_raw_with_allocator`][into_raw_with_allocator]. - /// - /// # Safety - /// - /// * Creating a `Arc` from a pointer other than one returned from - /// [`Arc::into_raw`][into_raw] or [`Arc::into_raw_with_allocator`][into_raw_with_allocator] - /// is undefined behavior. - /// * If `U` is sized, it must have the same size and alignment as `T`. This - /// is trivially true if `U` is `T`. - /// * If `U` is unsized, its data pointer must have the same size and - /// alignment as `T`. This is trivially true if `Arc` was constructed - /// through `Arc` and then converted to `Arc` through an [unsized - /// coercion]. - /// * Note that if `U` or `U`'s data pointer is not `T` but has the same size - /// and alignment, this is basically like transmuting references of - /// different types. See [`mem::transmute`][transmute] for more information - /// on what restrictions apply in this case. - /// * The raw pointer must point to a block of memory allocated by `alloc` - /// * The user of `from_raw` has to make sure a specific value of `T` is only - /// dropped once. - /// - /// This function is unsafe because improper use may lead to memory unsafety, - /// even if the returned `Arc` is never accessed. - /// - /// [into_raw]: Arc::into_raw - /// [into_raw_with_allocator]: Arc::into_raw_with_allocator - /// [transmute]: core::mem::transmute - /// [unsized coercion]: https://doc.rust-lang.org/reference/type-coercions.html#unsized-coercions - /// - /// # Examples - /// - /// ``` - /// #![feature(allocator_api)] - /// - /// use std::sync::Arc; - /// use std::alloc::System; - /// - /// let x = Arc::new_in("hello".to_owned(), System); - /// let (x_ptr, alloc) = Arc::into_raw_with_allocator(x); - /// - /// unsafe { - /// // Convert back to an `Arc` to prevent leak. - /// let x = Arc::from_raw_in(x_ptr, System); - /// assert_eq!(&*x, "hello"); - /// - /// // Further calls to `Arc::from_raw(x_ptr)` would be memory-unsafe. - /// } - /// - /// // The memory was freed when `x` went out of scope above, so `x_ptr` is now dangling! - /// ``` - /// - /// Convert a slice back into its original array: - /// - /// ``` - /// #![feature(allocator_api)] - /// - /// use std::sync::Arc; - /// use std::alloc::System; - /// - /// let x: Arc<[u32], _> = Arc::new_in([1, 2, 3], System); - /// let x_ptr: *const [u32] = Arc::into_raw_with_allocator(x).0; - /// - /// unsafe { - /// let x: Arc<[u32; 3], _> = Arc::from_raw_in(x_ptr.cast::<[u32; 3]>(), System); - /// assert_eq!(&*x, &[1, 2, 3]); - /// } - /// ``` - #[inline] - #[unstable(feature = "allocator_api", issue = "32838")] - pub unsafe fn from_raw_in(ptr: *const T, alloc: A) -> Self { - // SAFETY: Upheld by caller. - unsafe { - let offset = data_offset(ptr); - - // Reverse the offset to find the original ArcInner. - let arc_ptr = ptr.byte_sub(offset) as *mut ArcInner; - - Self::from_ptr_in(arc_ptr, alloc) - } - } - - /// Creates a new [`Weak`] pointer to this allocation. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// - /// let weak_five = Arc::downgrade(&five); - /// ``` - #[must_use = "this returns a new `Weak` pointer, \ - without modifying the original `Arc`"] - #[stable(feature = "arc_weak", since = "1.4.0")] - pub fn downgrade(this: &Self) -> Weak - where - A: AllocatorClone, - { - // This Relaxed is OK because we're checking the value in the CAS - // below. - let mut cur = this.inner().weak.load(Relaxed); - - loop { - // check if the weak counter is currently "locked"; if so, spin. - if cur == usize::MAX { - hint::spin_loop(); - cur = this.inner().weak.load(Relaxed); - continue; - } - - // We can't allow the refcount to increase much past `MAX_REFCOUNT`. - if cur > MAX_REFCOUNT { - panic_arc_overflow(); - } - // NOTE: this code currently ignores the possibility of overflow - // into usize::MAX; in general both Rc and Arc need to be adjusted - // to deal with overflow. - - // Unlike with Clone(), we need this to be an Acquire read to - // synchronize with the write coming from `is_unique`, so that the - // events prior to that write happen before this read. - match this.inner().weak.compare_exchange_weak(cur, cur + 1, Acquire, Relaxed) { - Ok(_) => { - // Make sure we do not create a dangling Weak - debug_assert!(!is_dangling(this.ptr.as_ptr())); - return Weak { ptr: this.ptr, alloc: this.alloc.clone() }; - } - Err(old) => cur = old, - } - } - } - - /// Gets the number of [`Weak`] pointers to this allocation. - /// - /// # Safety - /// - /// This method by itself is safe, but using it correctly requires extra care. - /// Another thread can change the weak count at any time, - /// including potentially between calling this method and acting on the result. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// let _weak_five = Arc::downgrade(&five); - /// - /// // This assertion is deterministic because we haven't shared - /// // the `Arc` or `Weak` between threads. - /// assert_eq!(1, Arc::weak_count(&five)); - /// ``` - #[inline] - #[must_use] - #[stable(feature = "arc_counts", since = "1.15.0")] - pub fn weak_count(this: &Self) -> usize { - let cnt = this.inner().weak.load(Relaxed); - // If the weak count is currently locked, the value of the - // count was 0 just before taking the lock. - if cnt == usize::MAX { 0 } else { cnt - 1 } - } - - /// Gets the number of strong (`Arc`) pointers to this allocation. - /// - /// # Safety - /// - /// This method by itself is safe, but using it correctly requires extra care. - /// Another thread can change the strong count at any time, - /// including potentially between calling this method and acting on the result. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// let _also_five = Arc::clone(&five); - /// - /// // This assertion is deterministic because we haven't shared - /// // the `Arc` between threads. - /// assert_eq!(2, Arc::strong_count(&five)); - /// ``` - #[inline] - #[must_use] - #[stable(feature = "arc_counts", since = "1.15.0")] - pub fn strong_count(this: &Self) -> usize { - this.inner().strong.load(Relaxed) - } - - /// Increments the strong reference count on the `Arc` associated with the - /// provided pointer by one. - /// - /// # Safety - /// - /// The pointer must have been obtained through `Arc::into_raw` and must satisfy the - /// same layout requirements specified in [`Arc::from_raw_in`][from_raw_in]. - /// The associated `Arc` instance must be valid (i.e. the strong count must be at - /// least 1) for the duration of this method, and `ptr` must point to a block of memory - /// allocated by `alloc`. - /// - /// [from_raw_in]: Arc::from_raw_in - /// - /// # Examples - /// - /// ``` - /// #![feature(allocator_api)] - /// - /// use std::sync::Arc; - /// use std::alloc::System; - /// - /// let five = Arc::new_in(5, System); - /// - /// unsafe { - /// let (ptr, _alloc) = Arc::into_raw_with_allocator(five); - /// Arc::increment_strong_count_in(ptr, System); - /// - /// // This assertion is deterministic because we haven't shared - /// // the `Arc` between threads. - /// let five = Arc::from_raw_in(ptr, System); - /// assert_eq!(2, Arc::strong_count(&five)); - /// # // Prevent leaks for Miri. - /// # Arc::decrement_strong_count_in(ptr, System); - /// } - /// ``` - #[inline] - #[unstable(feature = "allocator_api", issue = "32838")] - pub unsafe fn increment_strong_count_in(ptr: *const T, alloc: A) - where - A: AllocatorClone, - { - // Retain Arc, but don't touch refcount by wrapping in ManuallyDrop - // SAFETY: Upheld by caller. - let arc = unsafe { mem::ManuallyDrop::new(Arc::from_raw_in(ptr, alloc)) }; - // Now increase refcount, but don't drop new refcount either - let _arc_clone: mem::ManuallyDrop<_> = arc.clone(); - } - - /// Decrements the strong reference count on the `Arc` associated with the - /// provided pointer by one. - /// - /// # Safety - /// - /// The pointer must have been obtained through `Arc::into_raw` and must satisfy the - /// same layout requirements specified in [`Arc::from_raw_in`][from_raw_in]. - /// The associated `Arc` instance must be valid (i.e. the strong count must be at - /// least 1) when invoking this method, and `ptr` must point to a block of memory - /// allocated by `alloc`. This method can be used to release the final - /// `Arc` and backing storage, but **should not** be called after the final `Arc` has been - /// released. - /// - /// [from_raw_in]: Arc::from_raw_in - /// - /// # Examples - /// - /// ``` - /// #![feature(allocator_api)] - /// - /// use std::sync::Arc; - /// use std::alloc::System; - /// - /// let five = Arc::new_in(5, System); - /// - /// unsafe { - /// let (ptr, _alloc) = Arc::into_raw_with_allocator(five); - /// Arc::increment_strong_count_in(ptr, System); - /// - /// // Those assertions are deterministic because we haven't shared - /// // the `Arc` between threads. - /// let five = Arc::from_raw_in(ptr, System); - /// assert_eq!(2, Arc::strong_count(&five)); - /// Arc::decrement_strong_count_in(ptr, System); - /// assert_eq!(1, Arc::strong_count(&five)); - /// } - /// ``` - #[inline] - #[unstable(feature = "allocator_api", issue = "32838")] - pub unsafe fn decrement_strong_count_in(ptr: *const T, alloc: A) { - // SAFETY: Upheld by caller. - unsafe { drop(Arc::from_raw_in(ptr, alloc)) }; - } - - #[inline] - fn inner(&self) -> &ArcInner { - // SAFETY: While this arc is alive we're guaranteed - // that the inner pointer is valid. Furthermore, we know that the - // `ArcInner` structure itself is `Sync` if the inner data is - // `Sync` as well, so we're ok loaning out an immutable pointer to these - // contents. - unsafe { self.ptr.as_ref() } - } - - // Non-inlined part of `drop`. - #[inline(never)] - unsafe fn drop_slow(&mut self) { - // Drop the weak ref collectively held by all strong references when this - // variable goes out of scope. This ensures that the memory is deallocated - // even if the destructor of `T` panics. - // Take a reference to `self.alloc` instead of cloning because 1. it'll last long - // enough, and 2. you should be able to drop `Arc`s with unclonable allocators - let _weak = Weak { ptr: self.ptr, alloc: &self.alloc }; - - // Destroy the data at this time, even though we must not free the box - // allocation itself (there might still be weak pointers lying around). - // We cannot use `get_mut_unchecked` here, because `self.alloc` is borrowed. - // ignore-tidy-undocumented-unsafe - unsafe { ptr::drop_in_place(&mut (*self.ptr.as_ptr()).data) }; - } - - /// Returns `true` if the two `Arc`s point to the same allocation in a vein similar to - /// [`ptr::eq`]. This function ignores the metadata of `dyn Trait` pointers. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// let same_five = Arc::clone(&five); - /// let other_five = Arc::new(5); - /// - /// assert!(Arc::ptr_eq(&five, &same_five)); - /// assert!(!Arc::ptr_eq(&five, &other_five)); - /// ``` - /// - /// [`ptr::eq`]: core::ptr::eq "ptr::eq" - #[inline] - #[must_use] - #[stable(feature = "ptr_eq", since = "1.17.0")] - pub fn ptr_eq(this: &Self, other: &Self) -> bool { - ptr::addr_eq(this.ptr.as_ptr(), other.ptr.as_ptr()) - } -} - -impl Arc { - /// Allocates an `ArcInner` with sufficient space for - /// a possibly-unsized inner value where the value has the layout provided. - /// - /// The function `mem_to_arcinner` is called with the data pointer - /// and must return back a (potentially fat)-pointer for the `ArcInner`. - #[cfg(not(no_global_oom_handling))] - unsafe fn allocate_for_layout( - value_layout: Layout, - allocate: impl FnOnce(Layout) -> Result, AllocError>, - mem_to_arcinner: impl FnOnce(*mut u8) -> *mut ArcInner, - ) -> *mut ArcInner { - let layout = arcinner_layout_for_value_layout(value_layout); - - let ptr = allocate(layout).unwrap_or_else(|_| handle_alloc_error(layout)); - - // ignore-tidy-undocumented-unsafe - unsafe { Self::initialize_arcinner(ptr, layout, mem_to_arcinner) } - } - - /// Allocates an `ArcInner` with sufficient space for - /// a possibly-unsized inner value where the value has the layout provided, - /// returning an error if allocation fails. - /// - /// The function `mem_to_arcinner` is called with the data pointer - /// and must return back a (potentially fat)-pointer for the `ArcInner`. - unsafe fn try_allocate_for_layout( - value_layout: Layout, - allocate: impl FnOnce(Layout) -> Result, AllocError>, - mem_to_arcinner: impl FnOnce(*mut u8) -> *mut ArcInner, - ) -> Result<*mut ArcInner, AllocError> { - let layout = arcinner_layout_for_value_layout(value_layout); - - let ptr = allocate(layout)?; - - // ignore-tidy-undocumented-unsafe - let inner = unsafe { Self::initialize_arcinner(ptr, layout, mem_to_arcinner) }; - - Ok(inner) - } - - unsafe fn initialize_arcinner( - ptr: NonNull<[u8]>, - layout: Layout, - mem_to_arcinner: impl FnOnce(*mut u8) -> *mut ArcInner, - ) -> *mut ArcInner { - let inner = mem_to_arcinner(ptr.as_non_null_ptr().as_ptr()); - // SAFETY: Upheld by caller. - debug_assert_eq!(unsafe { Layout::for_value_raw(inner) }, layout); - - // ignore-tidy-undocumented-unsafe - unsafe { - (&raw mut (*inner).strong).write(atomic::AtomicUsize::new(1)); - (&raw mut (*inner).weak).write(atomic::AtomicUsize::new(1)); - } - - inner - } -} - -impl Arc { - /// Allocates an `ArcInner` with sufficient space for an unsized inner value. - #[inline] - #[cfg(not(no_global_oom_handling))] - unsafe fn allocate_for_ptr_in(ptr: *const T, alloc: &A) -> *mut ArcInner { - // Allocate for the `ArcInner` using the given value. - // ignore-tidy-undocumented-unsafe - unsafe { - Arc::allocate_for_layout( - Layout::for_value_raw(ptr), - |layout| alloc.allocate(layout), - |mem| mem.with_metadata_of(ptr as *const ArcInner), - ) - } - } - - #[cfg(not(no_global_oom_handling))] - fn from_box_in(src: Box) -> Arc { - // ignore-tidy-undocumented-unsafe - unsafe { - let value_size = size_of_val(&*src); - let ptr = Self::allocate_for_ptr_in(&*src, Box::allocator(&src)); - - // Copy value as bytes - ptr::copy_nonoverlapping( - (&raw const *src) as *const u8, - (&raw mut (*ptr).data) as *mut u8, - value_size, - ); - - // Free the allocation without dropping its contents - let (bptr, alloc) = Box::into_raw_with_allocator(src); - let src = Box::from_raw_in(bptr as *mut mem::ManuallyDrop, &alloc); - drop(src); - - Self::from_ptr_in(ptr, alloc) - } - } -} - -impl Arc<[T]> { - /// Allocates an `ArcInner<[T]>` with the given length. - #[cfg(not(no_global_oom_handling))] - unsafe fn allocate_for_slice(len: usize) -> *mut ArcInner<[T]> { - // ignore-tidy-undocumented-unsafe - unsafe { - Self::allocate_for_layout( - Layout::array::(len).unwrap(), - |layout| Global.allocate(layout), - |mem| mem.cast::().cast_slice(len) as *mut ArcInner<[T]>, - ) - } - } - - /// Copy elements from slice into newly allocated `Arc<[T]>` - /// - /// Unsafe because the caller must either take ownership, bind `T: Copy` or - /// bind `T: TrivialClone`. - #[cfg(not(no_global_oom_handling))] - unsafe fn copy_from_slice(v: &[T]) -> Arc<[T]> { - // ignore-tidy-undocumented-unsafe - unsafe { - let ptr = Self::allocate_for_slice(v.len()); - - ptr::copy_nonoverlapping(v.as_ptr(), (&raw mut (*ptr).data) as *mut T, v.len()); - - Self::from_ptr(ptr) - } - } - - /// Constructs an `Arc<[T]>` from an iterator known to be of a certain size. - /// - /// Behavior is undefined should the size be wrong. - #[cfg(not(no_global_oom_handling))] - unsafe fn from_iter_exact(iter: impl Iterator, len: usize) -> Arc<[T]> { - // Panic guard while cloning T elements. - // In the event of a panic, elements that have been written - // into the new ArcInner will be dropped, then the memory freed. - struct Guard { - mem: NonNull, - elems: *mut T, - layout: Layout, - n_elems: usize, - } - - impl Drop for Guard { - fn drop(&mut self) { - // ignore-tidy-undocumented-unsafe - unsafe { - let slice = from_raw_parts_mut(self.elems, self.n_elems); - ptr::drop_in_place(slice); - - Global.deallocate(self.mem, self.layout); - } - } - } - - // ignore-tidy-undocumented-unsafe - unsafe { - let ptr = Self::allocate_for_slice(len); - - let mem = ptr as *mut _ as *mut u8; - let layout = Layout::for_value_raw(ptr); - - // Pointer to first element - let elems = (&raw mut (*ptr).data) as *mut T; - - let mut guard = Guard { mem: NonNull::new_unchecked(mem), elems, layout, n_elems: 0 }; - - for (i, item) in iter.enumerate() { - ptr::write(elems.add(i), item); - guard.n_elems += 1; - } - - // All clear. Forget the guard so it doesn't free the new ArcInner. - mem::forget(guard); - - Self::from_ptr(ptr) - } - } -} - -impl Arc<[T], A> { - /// Allocates an `ArcInner<[T]>` with the given length. - #[inline] - #[cfg(not(no_global_oom_handling))] - unsafe fn allocate_for_slice_in(len: usize, alloc: &A) -> *mut ArcInner<[T]> { - // ignore-tidy-undocumented-unsafe - unsafe { - Arc::allocate_for_layout( - Layout::array::(len).unwrap(), - |layout| alloc.allocate(layout), - |mem| mem.cast::().cast_slice(len) as *mut ArcInner<[T]>, - ) - } - } -} - -/// Specialization trait used for `From<&[T]>`. -#[cfg(not(no_global_oom_handling))] -trait ArcFromSlice { - fn from_slice(slice: &[T]) -> Self; -} - -#[cfg(not(no_global_oom_handling))] -impl ArcFromSlice for Arc<[T]> { - #[inline] - default fn from_slice(v: &[T]) -> Self { - // ignore-tidy-undocumented-unsafe - unsafe { Self::from_iter_exact(v.iter().cloned(), v.len()) } - } -} - -#[cfg(not(no_global_oom_handling))] -impl ArcFromSlice for Arc<[T]> { - #[inline] - fn from_slice(v: &[T]) -> Self { - // SAFETY: `T` implements `TrivialClone`, so this is sound and equivalent - // to the above. - unsafe { Arc::copy_from_slice(v) } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Arc { - /// Makes a clone of the `Arc` pointer. - /// - /// This creates another pointer to the same allocation, increasing the - /// strong reference count. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// - /// let _ = Arc::clone(&five); - /// ``` - #[inline] - fn clone(&self) -> Arc { - // Using a relaxed ordering is alright here, as knowledge of the - // original reference prevents other threads from erroneously deleting - // the object. - // - // As explained in the [Boost documentation][1], Increasing the - // reference counter can always be done with memory_order_relaxed: New - // references to an object can only be formed from an existing - // reference, and passing an existing reference from one thread to - // another must already provide any required synchronization. - // - // [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html) - let old_size = self.inner().strong.fetch_add(1, Relaxed); - - // However we need to guard against massive refcounts in case someone is `mem::forget`ing - // Arcs. If we don't do this the count can overflow and users will use-after free. This - // branch will never be taken in any realistic program. We abort because such a program is - // incredibly degenerate, and we don't care to support it. - // - // This check is not 100% water-proof: we error when the refcount grows beyond `isize::MAX`. - // But we do that check *after* having done the increment, so there is a chance here that - // the worst already happened and we actually do overflow the `usize` counter. However, that - // requires the counter to grow from `isize::MAX` to `usize::MAX` between the increment - // above and the `abort` below, which seems exceedingly unlikely. - // - // This is a global invariant, and also applies when using a compare-exchange loop to increment - // counters in other methods. - // Otherwise, the counter could be brought to an almost-overflow using a compare-exchange loop, - // and then overflow using a few `fetch_add`s. - if old_size > MAX_REFCOUNT { - abort(); - } - - // SAFETY: Pointer is valid & allocator corresponds to the one used to allocate it. - unsafe { Self::from_inner_in(self.ptr, self.alloc.clone()) } - } -} - -#[unstable(feature = "ergonomic_clones", issue = "132290")] -impl UseCloned for Arc {} - -#[unstable(feature = "share_trait", issue = "156756")] -impl Share for Arc {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Deref for Arc { - type Target = T; - - #[inline] - fn deref(&self) -> &T { - &self.inner().data - } -} - -// The API of this pointer type enforces that if the `T` is pinned, then *all* -// clones of this `Arc` are wrapped as `Pin>`. Since an `&Arc` -// could be used to obtain an `Arc` that is not wrapped in `Pin` (and later -// used with `Arc::get_mut`), this means that this type treats `&Arc` as -// evidence that the `T` is not pinned. The implementations of various traits -// are written accordingly. Since this type is not fundamental, downstream -// crates cannot provide malicious implementations of any of the traits relevant -// for `Pin`. -#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] -unsafe impl PinSafePointer for Arc {} - -#[unstable(feature = "deref_pure_trait", issue = "87121")] -unsafe impl DerefPure for Arc {} - -#[unstable(feature = "legacy_receiver_trait", issue = "none")] -impl LegacyReceiver for Arc {} - -#[cfg(not(no_global_oom_handling))] -impl Arc { - /// Makes a mutable reference into the given `Arc`. - /// - /// If there are other `Arc` pointers to the same allocation, then `make_mut` will - /// [`clone`] the inner value to a new allocation to ensure unique ownership. This is also - /// referred to as clone-on-write. - /// - /// However, if there are no other `Arc` pointers to this allocation, but some [`Weak`] - /// pointers, then the [`Weak`] pointers will be dissociated and the inner value will not - /// be cloned. - /// - /// See also [`get_mut`], which will fail rather than cloning the inner value - /// or dissociating [`Weak`] pointers. - /// - /// [`clone`]: Clone::clone - /// [`get_mut`]: Arc::get_mut - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let mut data = Arc::new(5); - /// - /// *Arc::make_mut(&mut data) += 1; // Won't clone anything - /// let mut other_data = Arc::clone(&data); // Won't clone inner data - /// *Arc::make_mut(&mut data) += 1; // Clones inner data - /// *Arc::make_mut(&mut data) += 1; // Won't clone anything - /// *Arc::make_mut(&mut other_data) *= 2; // Won't clone anything - /// - /// // Now `data` and `other_data` point to different allocations. - /// assert_eq!(*data, 8); - /// assert_eq!(*other_data, 12); - /// ``` - /// - /// [`Weak`] pointers will be dissociated: - /// - /// ``` - /// use std::sync::Arc; - /// - /// let mut data = Arc::new(75); - /// let weak = Arc::downgrade(&data); - /// - /// assert!(75 == *data); - /// assert!(75 == *weak.upgrade().unwrap()); - /// - /// *Arc::make_mut(&mut data) += 1; - /// - /// assert!(76 == *data); - /// assert!(weak.upgrade().is_none()); - /// ``` - #[inline] - #[stable(feature = "arc_unique", since = "1.4.0")] - pub fn make_mut(this: &mut Self) -> &mut T { - let size_of_val = size_of_val::(&**this); - - // Note that we hold both a strong reference and a weak reference. - // Thus, releasing our strong reference only will not, by itself, cause - // the memory to be deallocated. - // - // Use Acquire to ensure that we see any writes to `weak` that happen - // before release writes (i.e., decrements) to `strong`. Since we hold a - // weak count, there's no chance the ArcInner itself could be - // deallocated. - if this.inner().strong.compare_exchange(1, 0, Acquire, Relaxed).is_err() { - // Another strong pointer exists, so we must clone. - *this = Arc::clone_from_ref_in(&**this, this.alloc.clone()); - } else if this.inner().weak.load(Relaxed) != 1 { - // Relaxed suffices in the above because this is fundamentally an - // optimization: we are always racing with weak pointers being - // dropped. Worst case, we end up allocated a new Arc unnecessarily. - - // We removed the last strong ref, but there are additional weak - // refs remaining. We'll move the contents to a new Arc, and - // invalidate the other weak refs. - - // Note that it is not possible for the read of `weak` to yield - // usize::MAX (i.e., locked), since the weak count can only be - // locked by a thread with a strong reference. - - // Guard against panics while using the allocator. - // If we unwind before the Arc is overwritten, we expose a strong - // count of 0, resulting in a UAF (#155746, #157203). - // Until the new Arc is written, the old Arc must remain valid - struct Guard<'a, T: ?Sized> { - inner: &'a ArcInner, - } - impl<'a, T: ?Sized> Drop for Guard<'a, T> { - fn drop(&mut self) { - self.inner.strong.store(1, Release); - } - } - let guard = Guard { inner: this.inner() }; - - // Can just steal the data, all that's left is Weaks - // Note that this can panic in two ways: - // - The allocation can fail - // - The allocator clone can fail - let mut in_progress: UniqueArcUninit = - UniqueArcUninit::new(&**this, this.alloc.clone()); - - // ignore-tidy-undocumented-unsafe - unsafe { - // Initialize `in_progress` with move of **this. - // We have to express this in terms of bytes because `T: ?Sized`; there is no - // operation that just copies a value based on its `size_of_val()`. - ptr::copy_nonoverlapping( - ptr::from_ref(&**this).cast::(), - in_progress.data_ptr().cast::(), - size_of_val, - ); - - // We are now safe from panics. - mem::forget(guard); - - // Materialize our own implicit weak pointer, so that it can clean - // up the ArcInner as needed. - // Make sure the allocator is not leaked when the Arc is overwritten. - // Only drop at the end of the scope to avoid panics. - let _weak = Weak { ptr: this.ptr, alloc: ptr::read(&this.alloc) }; - - ptr::write(this, in_progress.into_arc()); - } - } else { - // We were the sole reference of either kind; bump back up the - // strong ref count. - this.inner().strong.store(1, Release); - } - - // SAFETY: As with `get_mut()`, our reference was - // either unique to begin with, or became one upon cloning the contents. - unsafe { Self::get_mut_unchecked(this) } - } -} - -impl Arc { - /// If we have the only reference to `T` then unwrap it. Otherwise, clone `T` and return the - /// clone. - /// - /// Assuming `arc_t` is of type `Arc`, this function is functionally equivalent to - /// `(*arc_t).clone()`, but will avoid cloning the inner value where possible. - /// - /// # Examples - /// - /// ``` - /// # use std::{ptr, sync::Arc}; - /// let inner = String::from("test"); - /// let ptr = inner.as_ptr(); - /// - /// let arc = Arc::new(inner); - /// let inner = Arc::unwrap_or_clone(arc); - /// // The inner value was not cloned - /// assert!(ptr::eq(ptr, inner.as_ptr())); - /// - /// let arc = Arc::new(inner); - /// let arc2 = arc.clone(); - /// let inner = Arc::unwrap_or_clone(arc); - /// // Because there were 2 references, we had to clone the inner value. - /// assert!(!ptr::eq(ptr, inner.as_ptr())); - /// // `arc2` is the last reference, so when we unwrap it we get back - /// // the original `String`. - /// let inner = Arc::unwrap_or_clone(arc2); - /// assert!(ptr::eq(ptr, inner.as_ptr())); - /// ``` - #[inline] - #[stable(feature = "arc_unwrap_or_clone", since = "1.76.0")] - pub fn unwrap_or_clone(this: Self) -> T { - Arc::try_unwrap(this).unwrap_or_else(|arc| (*arc).clone()) - } -} - -impl Arc { - /// Returns a mutable reference into the given `Arc`, if there are - /// no other `Arc` or [`Weak`] pointers to the same allocation. - /// - /// Returns [`None`] otherwise, because it is not safe to - /// mutate a shared value. - /// - /// See also [`make_mut`][make_mut], which will [`clone`][clone] - /// the inner value when there are other `Arc` pointers. - /// - /// [make_mut]: Arc::make_mut - /// [clone]: Clone::clone - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let mut x = Arc::new(3); - /// *Arc::get_mut(&mut x).unwrap() = 4; - /// assert_eq!(*x, 4); - /// - /// let _y = Arc::clone(&x); - /// assert!(Arc::get_mut(&mut x).is_none()); - /// ``` - #[inline] - #[stable(feature = "arc_unique", since = "1.4.0")] - pub fn get_mut(this: &mut Self) -> Option<&mut T> { - if Self::is_unique(this) { - // SAFETY: We're guaranteed that the pointer - // returned is the *only* pointer that will ever be returned to T. Our - // reference count is guaranteed to be 1 at this point, and we required - // the Arc itself to be `mut`, so we're returning the only possible - // reference to the inner data. - unsafe { Some(Arc::get_mut_unchecked(this)) } - } else { - None - } - } - - /// Returns a mutable reference into the given `Arc`, - /// without any check. - /// - /// See also [`get_mut`], which is safe and does appropriate checks. - /// - /// [`get_mut`]: Arc::get_mut - /// - /// # Safety - /// - /// If any other `Arc` or [`Weak`] pointers to the same allocation exist, then - /// they must not be dereferenced or have active borrows for the duration - /// of the returned borrow, and their inner type must be exactly the same as the - /// inner type of this Arc (including lifetimes). This is trivially the case if no - /// such pointers exist, for example immediately after `Arc::new`. - /// - /// # Examples - /// - /// ``` - /// #![feature(get_mut_unchecked)] - /// - /// use std::sync::Arc; - /// - /// let mut x = Arc::new(String::new()); - /// unsafe { - /// Arc::get_mut_unchecked(&mut x).push_str("foo") - /// } - /// assert_eq!(*x, "foo"); - /// ``` - /// Other `Arc` pointers to the same allocation must be to the same type. - /// ```no_run - /// #![feature(get_mut_unchecked)] - /// - /// use std::sync::Arc; - /// - /// let x: Arc = Arc::from("Hello, world!"); - /// let mut y: Arc<[u8]> = x.clone().into(); - /// unsafe { - /// // this is Undefined Behavior, because x's inner type is str, not [u8] - /// Arc::get_mut_unchecked(&mut y).fill(0xff); // 0xff is invalid in UTF-8 - /// } - /// println!("{}", &*x); // Invalid UTF-8 in a str - /// ``` - /// Other `Arc` pointers to the same allocation must be to the exact same type, including lifetimes. - /// ```no_run - /// #![feature(get_mut_unchecked)] - /// - /// use std::sync::Arc; - /// - /// let x: Arc<&str> = Arc::new("Hello, world!"); - /// { - /// let s = String::from("Oh, no!"); - /// let mut y: Arc<&str> = x.clone(); - /// unsafe { - /// // this is Undefined Behavior, because x's inner type - /// // is &'long str, not &'short str - /// *Arc::get_mut_unchecked(&mut y) = &s; - /// } - /// } - /// println!("{}", &*x); // Use-after-free - /// ``` - #[inline] - #[unstable(feature = "get_mut_unchecked", issue = "63292")] - pub unsafe fn get_mut_unchecked(this: &mut Self) -> &mut T { - // We are careful to *not* create a reference covering the "count" fields, as - // this would alias with concurrent access to the reference counts (e.g. by `Weak`). - // ignore-tidy-undocumented-unsafe - unsafe { &mut (*this.ptr.as_ptr()).data } - } - - /// Determine whether this is the unique reference to the underlying data. - /// - /// Returns `true` if there are no other `Arc` or [`Weak`] pointers to the same allocation; - /// returns `false` otherwise. - /// - /// If this function returns `true`, then is guaranteed to be safe to call [`get_mut_unchecked`] - /// on this `Arc`, so long as no clones occur in between. - /// - /// # Examples - /// - /// ``` - /// #![feature(arc_is_unique)] - /// - /// use std::sync::Arc; - /// - /// let x = Arc::new(3); - /// assert!(Arc::is_unique(&x)); - /// - /// let y = Arc::clone(&x); - /// assert!(!Arc::is_unique(&x)); - /// drop(y); - /// - /// // Weak references also count, because they could be upgraded at any time. - /// let z = Arc::downgrade(&x); - /// assert!(!Arc::is_unique(&x)); - /// ``` - /// - /// # Pointer invalidation - /// - /// This function will always return the same value as `Arc::get_mut(arc).is_some()`. However, - /// unlike that operation it does not produce any mutable references to the underlying data, - /// meaning no pointers to the data inside the `Arc` are invalidated by the call. Thus, the - /// following code is valid, even though it would be UB if it used `Arc::get_mut`: - /// - /// ``` - /// #![feature(arc_is_unique)] - /// - /// use std::sync::Arc; - /// - /// let arc = Arc::new(5); - /// let pointer: *const i32 = &*arc; - /// assert!(Arc::is_unique(&arc)); - /// assert_eq!(unsafe { *pointer }, 5); - /// ``` - /// - /// # Atomic orderings - /// - /// Concurrent drops to other `Arc` pointers to the same allocation will synchronize with this - /// call - that is, this call performs an `Acquire` operation on the underlying strong and weak - /// ref counts. This ensures that calling `get_mut_unchecked` is safe. - /// - /// Note that this operation requires locking the weak ref count, so concurrent calls to - /// `downgrade` may spin-loop for a short period of time. - /// - /// [`get_mut_unchecked`]: Self::get_mut_unchecked - #[inline] - #[unstable(feature = "arc_is_unique", issue = "138938")] - pub fn is_unique(this: &Self) -> bool { - // lock the weak pointer count if we appear to be the sole weak pointer - // holder. - // - // The acquire label here ensures a happens-before relationship with any - // writes to `strong` (in particular in `Weak::upgrade`) prior to decrements - // of the `weak` count (via `Weak::drop`, which uses release). If the upgraded - // weak ref was never dropped, the CAS here will fail so we do not care to synchronize. - if this.inner().weak.compare_exchange(1, usize::MAX, Acquire, Relaxed).is_ok() { - // This needs to be an `Acquire` to synchronize with the decrement of the `strong` - // counter in `drop` -- the only access that happens when any but the last reference - // is being dropped. - let unique = this.inner().strong.load(Acquire) == 1; - - // The release write here synchronizes with a read in `downgrade`, - // effectively preventing the above read of `strong` from happening - // after the write. - this.inner().weak.store(1, Release); // release the lock - unique - } else { - false - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Arc { - /// Drops the `Arc`. - /// - /// This will decrement the strong reference count. If the strong reference - /// count reaches zero then the only other references (if any) are - /// [`Weak`], so we `drop` the inner value. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// struct Foo; - /// - /// impl Drop for Foo { - /// fn drop(&mut self) { - /// println!("dropped!"); - /// } - /// } - /// - /// let foo = Arc::new(Foo); - /// let foo2 = Arc::clone(&foo); - /// - /// drop(foo); // Doesn't print anything - /// drop(foo2); // Prints "dropped!" - /// ``` - #[inline] - fn drop(&mut self) { - // Because `fetch_sub` is already atomic, we do not need to synchronize - // with other threads unless we are going to delete the object. This - // same logic applies to the below `fetch_sub` to the `weak` count. - if self.inner().strong.fetch_sub(1, Release) != 1 { - return; - } - - // This fence is needed to prevent reordering of use of the data and - // deletion of the data. Because it is marked `Release`, the decreasing - // of the reference count synchronizes with this `Acquire` fence. This - // means that use of the data happens before decreasing the reference - // count, which happens before this fence, which happens before the - // deletion of the data. - // - // As explained in the [Boost documentation][1], - // - // > It is important to enforce any possible access to the object in one - // > thread (through an existing reference) to *happen before* deleting - // > the object in a different thread. This is achieved by a "release" - // > operation after dropping a reference (any access to the object - // > through this reference must obviously happened before), and an - // > "acquire" operation before deleting the object. - // - // In particular, while the contents of an Arc are usually immutable, it's - // possible to have interior writes to something like a Mutex. Since a - // Mutex is not acquired when it is deleted, we can't rely on its - // synchronization logic to make writes in thread A visible to a destructor - // running in thread B. - // - // Also note that the Acquire fence here could probably be replaced with an - // Acquire load, which could improve performance in highly-contended - // situations. See [2]. - // - // [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html) - // [2]: (https://github.com/rust-lang/rust/pull/41714) - acquire!(self.inner().strong); - - // Make sure we aren't trying to "drop" the shared static for empty slices - // used by Default::default. - debug_assert!( - !ptr::addr_eq(self.ptr.as_ptr(), &STATIC_INNER_SLICE.inner), - "Arcs backed by a static should never reach a strong count of 0. \ - Likely decrement_strong_count or from_raw were called too many times.", - ); - - // ignore-tidy-undocumented-unsafe - unsafe { - self.drop_slow(); - } - } -} - -impl Arc { - /// Attempts to downcast the `Arc` to a concrete type. - /// - /// # Examples - /// - /// ``` - /// use std::any::Any; - /// use std::sync::Arc; - /// - /// fn print_if_string(value: Arc) { - /// if let Ok(string) = value.downcast::() { - /// println!("String ({}): {}", string.len(), string); - /// } - /// } - /// - /// let my_string = "Hello World".to_string(); - /// print_if_string(Arc::new(my_string)); - /// print_if_string(Arc::new(0i8)); - /// ``` - #[inline] - #[stable(feature = "rc_downcast", since = "1.29.0")] - pub fn downcast(self) -> Result, Self> - where - T: Any + Send + Sync, - { - if (*self).is::() { - // SAFETY: Check ensures the typecast is okay. - unsafe { - let (ptr, alloc) = Arc::into_inner_with_allocator(self); - Ok(Arc::from_inner_in(ptr.cast(), alloc)) - } - } else { - Err(self) - } - } - - /// Downcasts the `Arc` to a concrete type. - /// - /// For a safe alternative see [`downcast`]. - /// - /// # Examples - /// - /// ``` - /// #![feature(downcast_unchecked)] - /// - /// use std::any::Any; - /// use std::sync::Arc; - /// - /// let x: Arc = Arc::new(1_usize); - /// - /// unsafe { - /// assert_eq!(*x.downcast_unchecked::(), 1); - /// } - /// ``` - /// - /// # Safety - /// - /// The contained value must be of type `T`. Calling this method - /// with the incorrect type is *undefined behavior*. - /// - /// - /// [`downcast`]: Self::downcast - #[inline] - #[unstable(feature = "downcast_unchecked", issue = "90850")] - pub unsafe fn downcast_unchecked(self) -> Arc - where - T: Any + Send + Sync, - { - // SAFETY: Upheld by caller. - unsafe { - let (ptr, alloc) = Arc::into_inner_with_allocator(self); - Arc::from_inner_in(ptr.cast(), alloc) - } - } -} - -impl Weak { - /// Constructs a new `Weak`, without allocating any memory. - /// Calling [`upgrade`] on the return value always gives [`None`]. - /// - /// [`upgrade`]: Weak::upgrade - /// - /// # Examples - /// - /// ``` - /// use std::sync::Weak; - /// - /// let empty: Weak = Weak::new(); - /// assert!(empty.upgrade().is_none()); - /// ``` - #[inline] - #[stable(feature = "downgraded_weak", since = "1.10.0")] - #[rustc_const_stable(feature = "const_weak_new", since = "1.73.0")] - #[must_use] - pub const fn new() -> Weak { - Weak { ptr: NonNull::without_provenance(NonZeroUsize::MAX), alloc: Global } - } -} - -impl Weak { - /// Constructs a new `Weak`, without allocating any memory, technically in the provided - /// allocator. - /// Calling [`upgrade`] on the return value always gives [`None`]. - /// - /// [`upgrade`]: Weak::upgrade - /// - /// # Examples - /// - /// ``` - /// #![feature(allocator_api)] - /// - /// use std::sync::Weak; - /// use std::alloc::System; - /// - /// let empty: Weak = Weak::new_in(System); - /// assert!(empty.upgrade().is_none()); - /// ``` - #[inline] - #[unstable(feature = "allocator_api", issue = "32838")] - pub fn new_in(alloc: A) -> Weak { - Weak { ptr: NonNull::without_provenance(NonZeroUsize::MAX), alloc } - } -} - -/// Helper type to allow accessing the reference counts without -/// making any assertions about the data field. -struct WeakInner<'a> { - weak: &'a Atomic, - strong: &'a Atomic, -} - -impl Weak { - /// Converts a raw pointer previously created by [`into_raw`] back into `Weak`. - /// - /// This can be used to safely get a strong reference (by calling [`upgrade`] - /// later) or to deallocate the weak count by dropping the `Weak`. - /// - /// It takes ownership of one weak reference (with the exception of pointers created by [`new`], - /// as these don't own anything; the method still works on them). - /// - /// # Safety - /// - /// The pointer must have originated from the [`into_raw`] and must still own its potential - /// weak reference, and must point to a block of memory allocated by global allocator. - /// - /// It is allowed for the strong count to be 0 at the time of calling this. Nevertheless, this - /// takes ownership of one weak reference currently represented as a raw pointer (the weak - /// count is not modified by this operation) and therefore it must be paired with a previous - /// call to [`into_raw`]. - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Weak}; - /// - /// let strong = Arc::new("hello".to_owned()); - /// - /// let raw_1 = Arc::downgrade(&strong).into_raw(); - /// let raw_2 = Arc::downgrade(&strong).into_raw(); - /// - /// assert_eq!(2, Arc::weak_count(&strong)); - /// - /// assert_eq!("hello", &*unsafe { Weak::from_raw(raw_1) }.upgrade().unwrap()); - /// assert_eq!(1, Arc::weak_count(&strong)); - /// - /// drop(strong); - /// - /// // Decrement the last weak count. - /// assert!(unsafe { Weak::from_raw(raw_2) }.upgrade().is_none()); - /// ``` - /// - /// [`new`]: Weak::new - /// [`into_raw`]: Weak::into_raw - /// [`upgrade`]: Weak::upgrade - #[inline] - #[stable(feature = "weak_into_raw", since = "1.45.0")] - pub unsafe fn from_raw(ptr: *const T) -> Self { - // SAFETY: Upheld by caller. - unsafe { Weak::from_raw_in(ptr, Global) } - } - - /// Consumes the `Weak` and turns it into a raw pointer. - /// - /// This converts the weak pointer into a raw pointer, while still preserving the ownership of - /// one weak reference (the weak count is not modified by this operation). It can be turned - /// back into the `Weak` with [`from_raw`]. - /// - /// The same restrictions of accessing the target of the pointer as with - /// [`as_ptr`] apply. - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Weak}; - /// - /// let strong = Arc::new("hello".to_owned()); - /// let weak = Arc::downgrade(&strong); - /// let raw = weak.into_raw(); - /// - /// assert_eq!(1, Arc::weak_count(&strong)); - /// assert_eq!("hello", unsafe { &*raw }); - /// - /// drop(unsafe { Weak::from_raw(raw) }); - /// assert_eq!(0, Arc::weak_count(&strong)); - /// ``` - /// - /// [`from_raw`]: Weak::from_raw - /// [`as_ptr`]: Weak::as_ptr - #[must_use = "losing the pointer will leak memory"] - #[stable(feature = "weak_into_raw", since = "1.45.0")] - pub fn into_raw(self) -> *const T { - ManuallyDrop::new(self).as_ptr() - } -} - -impl Weak { - /// Returns a reference to the underlying allocator. - #[inline] - #[unstable(feature = "allocator_api", issue = "32838")] - pub fn allocator(&self) -> &A { - &self.alloc - } - - /// Returns a raw pointer to the object `T` pointed to by this `Weak`. - /// - /// The pointer is valid only if there are some strong references. The pointer may be dangling, - /// unaligned or even [`null`] otherwise. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// use std::ptr; - /// - /// let strong = Arc::new("hello".to_owned()); - /// let weak = Arc::downgrade(&strong); - /// // Both point to the same object - /// assert!(ptr::eq(&*strong, weak.as_ptr())); - /// // The strong here keeps it alive, so we can still access the object. - /// assert_eq!("hello", unsafe { &*weak.as_ptr() }); - /// - /// drop(strong); - /// // But not any more. We can do weak.as_ptr(), but accessing the pointer would lead to - /// // undefined behavior. - /// // assert_eq!("hello", unsafe { &*weak.as_ptr() }); - /// ``` - /// - /// [`null`]: core::ptr::null "ptr::null" - #[must_use] - #[stable(feature = "weak_into_raw", since = "1.45.0")] - pub fn as_ptr(&self) -> *const T { - let ptr: *mut ArcInner = NonNull::as_ptr(self.ptr); - - if is_dangling(ptr) { - // If the pointer is dangling, we return the sentinel directly. This cannot be - // a valid payload address, as the payload is at least as aligned as ArcInner (usize). - ptr as *const T - } else { - // SAFETY: if is_dangling returns false, then the pointer is dereferenceable. - // The payload may be dropped at this point, and we have to maintain provenance, - // so use raw pointer manipulation. - unsafe { &raw mut (*ptr).data } - } - } - - /// Consumes the `Weak`, returning the wrapped pointer and allocator. - /// - /// This converts the weak pointer into a raw pointer, while still preserving the ownership of - /// one weak reference (the weak count is not modified by this operation). It can be turned - /// back into the `Weak` with [`from_raw_in`]. - /// - /// The same restrictions of accessing the target of the pointer as with - /// [`as_ptr`] apply. - /// - /// # Examples - /// - /// ``` - /// #![feature(allocator_api)] - /// use std::sync::{Arc, Weak}; - /// use std::alloc::System; - /// - /// let strong = Arc::new_in("hello".to_owned(), System); - /// let weak = Arc::downgrade(&strong); - /// let (raw, alloc) = weak.into_raw_with_allocator(); - /// - /// assert_eq!(1, Arc::weak_count(&strong)); - /// assert_eq!("hello", unsafe { &*raw }); - /// - /// drop(unsafe { Weak::from_raw_in(raw, alloc) }); - /// assert_eq!(0, Arc::weak_count(&strong)); - /// ``` - /// - /// [`from_raw_in`]: Weak::from_raw_in - /// [`as_ptr`]: Weak::as_ptr - #[must_use = "losing the pointer will leak memory"] - #[unstable(feature = "allocator_api", issue = "32838")] - pub fn into_raw_with_allocator(self) -> (*const T, A) { - let this = mem::ManuallyDrop::new(self); - let result = this.as_ptr(); - // SAFETY: `this` is ManuallyDrop so the allocator will not be double-dropped - let alloc = unsafe { ptr::read(&this.alloc) }; - (result, alloc) - } - - /// Converts a raw pointer previously created by [`into_raw`] back into `Weak` in the provided - /// allocator. - /// - /// This can be used to safely get a strong reference (by calling [`upgrade`] - /// later) or to deallocate the weak count by dropping the `Weak`. - /// - /// It takes ownership of one weak reference (with the exception of pointers created by [`new`], - /// as these don't own anything; the method still works on them). - /// - /// # Safety - /// - /// The pointer must have originated from the [`into_raw`] and must still own its potential - /// weak reference, and must point to a block of memory allocated by `alloc`. - /// - /// It is allowed for the strong count to be 0 at the time of calling this. Nevertheless, this - /// takes ownership of one weak reference currently represented as a raw pointer (the weak - /// count is not modified by this operation) and therefore it must be paired with a previous - /// call to [`into_raw`]. - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Weak}; - /// - /// let strong = Arc::new("hello".to_owned()); - /// - /// let raw_1 = Arc::downgrade(&strong).into_raw(); - /// let raw_2 = Arc::downgrade(&strong).into_raw(); - /// - /// assert_eq!(2, Arc::weak_count(&strong)); - /// - /// assert_eq!("hello", &*unsafe { Weak::from_raw(raw_1) }.upgrade().unwrap()); - /// assert_eq!(1, Arc::weak_count(&strong)); - /// - /// drop(strong); - /// - /// // Decrement the last weak count. - /// assert!(unsafe { Weak::from_raw(raw_2) }.upgrade().is_none()); - /// ``` - /// - /// [`new`]: Weak::new - /// [`into_raw`]: Weak::into_raw - /// [`upgrade`]: Weak::upgrade - #[inline] - #[unstable(feature = "allocator_api", issue = "32838")] - pub unsafe fn from_raw_in(ptr: *const T, alloc: A) -> Self { - // See Weak::as_ptr for context on how the input pointer is derived. - - let ptr = if is_dangling(ptr) { - // This is a dangling Weak. - ptr as *mut ArcInner - } else { - // Otherwise, we're guaranteed the pointer came from a nondangling Weak. - // SAFETY: data_offset is safe to call, as ptr references a real (potentially dropped) T. - let offset = unsafe { data_offset(ptr) }; - // Thus, we reverse the offset to get the whole ArcInner. - // SAFETY: the pointer originated from a Weak, so this offset is safe. - unsafe { ptr.byte_sub(offset) as *mut ArcInner } - }; - - // SAFETY: we now have recovered the original Weak pointer, so can create the Weak. - Weak { ptr: unsafe { NonNull::new_unchecked(ptr) }, alloc } - } -} - -impl Weak { - /// Attempts to upgrade the `Weak` pointer to an [`Arc`], delaying - /// dropping of the inner value if successful. - /// - /// Returns [`None`] in the following cases: - /// - /// 1. The inner value has since been dropped or moved out. - /// - /// 2. This `Weak` does not point to an allocation. - /// - /// 3. The owning reference this `Weak` is associated with is either not fully-constructed or does not allow an upgrade. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// - /// let weak_five = Arc::downgrade(&five); - /// - /// let strong_five: Option> = weak_five.upgrade(); - /// assert!(strong_five.is_some()); - /// - /// // Destroy all strong pointers. - /// drop(strong_five); - /// drop(five); - /// - /// assert!(weak_five.upgrade().is_none()); - /// ``` - #[must_use = "this returns a new `Arc`, \ - without modifying the original weak pointer"] - #[stable(feature = "arc_weak", since = "1.4.0")] - pub fn upgrade(&self) -> Option> - where - A: AllocatorClone, - { - #[inline] - fn checked_increment(n: usize) -> Option { - // Any write of 0 we can observe leaves the field in permanently zero state. - if n == 0 { - return None; - } - // See comments in `Arc::clone` for why we do this (for `mem::forget`). - if n > MAX_REFCOUNT { - panic_arc_overflow(); - } - Some(n + 1) - } - - // We use a CAS loop to increment the strong count instead of a - // fetch_add as this function should never take the reference count - // from zero to one. - // - // Relaxed is fine for the failure case because we don't have any expectations about the new state. - // Acquire is necessary for the success case to synchronise with `Arc::new_cyclic`, when the inner - // value can be initialized after `Weak` references have already been created. In that case, we - // expect to observe the fully initialized value. - if self.inner()?.strong.try_update(Acquire, Relaxed, checked_increment).is_ok() { - // SAFETY: pointer is not null, verified in checked_increment - unsafe { Some(Arc::from_inner_in(self.ptr, self.alloc.clone())) } - } else { - None - } - } - - /// Gets the number of strong (`Arc`) pointers pointing to this allocation. - /// - /// If `self` was created using [`Weak::new`], this will return 0. - #[must_use] - #[stable(feature = "weak_counts", since = "1.41.0")] - pub fn strong_count(&self) -> usize { - if let Some(inner) = self.inner() { inner.strong.load(Relaxed) } else { 0 } - } - - /// Gets an approximation of the number of `Weak` pointers pointing to this - /// allocation. - /// - /// If `self` was created using [`Weak::new`], or if there are no remaining - /// strong pointers, this will return 0. - /// - /// # Accuracy - /// - /// Due to implementation details, the returned value can be off by 1 in - /// either direction when other threads are manipulating any `Arc`s or - /// `Weak`s pointing to the same allocation. - #[must_use] - #[stable(feature = "weak_counts", since = "1.41.0")] - pub fn weak_count(&self) -> usize { - if let Some(inner) = self.inner() { - let weak = inner.weak.load(Acquire); - let strong = inner.strong.load(Relaxed); - if strong == 0 { - 0 - } else { - // Since we observed that there was at least one strong pointer - // after reading the weak count, we know that the implicit weak - // reference (present whenever any strong references are alive) - // was still around when we observed the weak count, and can - // therefore safely subtract it. - weak - 1 - } - } else { - 0 - } - } - - /// Returns `None` when the pointer is dangling and there is no allocated `ArcInner`, - /// (i.e., when this `Weak` was created by `Weak::new`). - #[inline] - fn inner(&self) -> Option> { - let ptr = self.ptr.as_ptr(); - if is_dangling(ptr) { - None - } else { - // We are careful to *not* create a reference covering the "data" field, as - // the field may be mutated concurrently (for example, if the last `Arc` - // is dropped, the data field will be dropped in-place). - // ignore-tidy-undocumented-unsafe - Some(unsafe { WeakInner { strong: &(*ptr).strong, weak: &(*ptr).weak } }) - } - } - - /// Returns `true` if the two `Weak`s point to the same allocation similar to [`ptr::eq`], or if - /// both don't point to any allocation (because they were created with `Weak::new()`). However, - /// this function ignores the metadata of `dyn Trait` pointers. - /// - /// # Notes - /// - /// Since this compares pointers it means that `Weak::new()` will equal each - /// other, even though they don't point to any allocation. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let first_rc = Arc::new(5); - /// let first = Arc::downgrade(&first_rc); - /// let second = Arc::downgrade(&first_rc); - /// - /// assert!(first.ptr_eq(&second)); - /// - /// let third_rc = Arc::new(5); - /// let third = Arc::downgrade(&third_rc); - /// - /// assert!(!first.ptr_eq(&third)); - /// ``` - /// - /// Comparing `Weak::new`. - /// - /// ``` - /// use std::sync::{Arc, Weak}; - /// - /// let first = Weak::new(); - /// let second = Weak::new(); - /// assert!(first.ptr_eq(&second)); - /// - /// let third_rc = Arc::new(()); - /// let third = Arc::downgrade(&third_rc); - /// assert!(!first.ptr_eq(&third)); - /// ``` - /// - /// [`ptr::eq`]: core::ptr::eq "ptr::eq" - #[inline] - #[must_use] - #[stable(feature = "weak_ptr_eq", since = "1.39.0")] - pub fn ptr_eq(&self, other: &Self) -> bool { - ptr::addr_eq(self.ptr.as_ptr(), other.ptr.as_ptr()) - } -} - -#[stable(feature = "arc_weak", since = "1.4.0")] -impl Clone for Weak { - /// Makes a clone of the `Weak` pointer that points to the same allocation. - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Weak}; - /// - /// let weak_five = Arc::downgrade(&Arc::new(5)); - /// - /// let _ = Weak::clone(&weak_five); - /// ``` - #[inline] - fn clone(&self) -> Weak { - if let Some(inner) = self.inner() { - // See comments in Arc::clone() for why this is relaxed. This can use a - // fetch_add (ignoring the lock) because the weak count is only locked - // where are *no other* weak pointers in existence. (So we can't be - // running this code in that case). - let old_size = inner.weak.fetch_add(1, Relaxed); - - // See comments in Arc::clone() for why we do this (for mem::forget). - if old_size > MAX_REFCOUNT { - abort(); - } - } - - Weak { ptr: self.ptr, alloc: self.alloc.clone() } - } -} - -#[unstable(feature = "ergonomic_clones", issue = "132290")] -impl UseCloned for Weak {} - -#[stable(feature = "downgraded_weak", since = "1.10.0")] -impl Default for Weak { - /// Constructs a new `Weak`, without allocating memory. - /// Calling [`upgrade`] on the return value always - /// gives [`None`]. - /// - /// [`upgrade`]: Weak::upgrade - /// - /// # Examples - /// - /// ``` - /// use std::sync::Weak; - /// - /// let empty: Weak = Default::default(); - /// assert!(empty.upgrade().is_none()); - /// ``` - fn default() -> Weak { - Weak::new() - } -} - -#[stable(feature = "arc_weak", since = "1.4.0")] -unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Weak { - /// Drops the `Weak` pointer. - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Weak}; - /// - /// struct Foo; - /// - /// impl Drop for Foo { - /// fn drop(&mut self) { - /// println!("dropped!"); - /// } - /// } - /// - /// let foo = Arc::new(Foo); - /// let weak_foo = Arc::downgrade(&foo); - /// let other_weak_foo = Weak::clone(&weak_foo); - /// - /// drop(weak_foo); // Doesn't print anything - /// drop(foo); // Prints "dropped!" - /// - /// assert!(other_weak_foo.upgrade().is_none()); - /// ``` - fn drop(&mut self) { - // If we find out that we were the last weak pointer, then its time to - // deallocate the data entirely. See the discussion in Arc::drop() about - // the memory orderings - // - // It's not necessary to check for the locked state here, because the - // weak count can only be locked if there was precisely one weak ref, - // meaning that drop could only subsequently run ON that remaining weak - // ref, which can only happen after the lock is released. - let inner = if let Some(inner) = self.inner() { inner } else { return }; - - if inner.weak.fetch_sub(1, Release) == 1 { - acquire!(inner.weak); - - // Make sure we aren't trying to "deallocate" the shared static for empty slices - // used by Default::default. - debug_assert!( - !ptr::addr_eq(self.ptr.as_ptr(), &STATIC_INNER_SLICE.inner), - "Arc/Weaks backed by a static should never be deallocated. \ - Likely decrement_strong_count or from_raw were called too many times.", - ); - - // ignore-tidy-undocumented-unsafe - unsafe { - self.alloc.deallocate(self.ptr.cast(), Layout::for_value_raw(self.ptr.as_ptr())) - } - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -trait ArcEqIdent { - fn eq(&self, other: &Arc) -> bool; - fn ne(&self, other: &Arc) -> bool; -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ArcEqIdent for Arc { - #[inline] - default fn eq(&self, other: &Arc) -> bool { - **self == **other - } - #[inline] - default fn ne(&self, other: &Arc) -> bool { - **self != **other - } -} - -/// We're doing this specialization here, and not as a more general optimization on `&T`, because it -/// would otherwise add a cost to all equality checks on refs. We assume that `Arc`s are used to -/// store large values, that are slow to clone, but also heavy to check for equality, causing this -/// cost to pay off more easily. It's also more likely to have two `Arc` clones, that point to -/// the same value, than two `&T`s. -/// -/// We can only do this when `T: Eq` as a `PartialEq` might be deliberately irreflexive. -#[stable(feature = "rust1", since = "1.0.0")] -impl ArcEqIdent for Arc { - #[inline] - fn eq(&self, other: &Arc) -> bool { - ptr::eq(self.ptr.as_ptr(), other.ptr.as_ptr()) || **self == **other - } - - #[inline] - fn ne(&self, other: &Arc) -> bool { - !ptr::eq(self.ptr.as_ptr(), other.ptr.as_ptr()) && **self != **other - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for Arc { - /// Equality for two `Arc`s. - /// - /// Two `Arc`s are equal if their inner values are equal, even if they are - /// stored in different allocation. - /// - /// If `T` also implements `Eq` (implying reflexivity of equality), - /// two `Arc`s that point to the same allocation are always equal. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// - /// assert!(five == Arc::new(5)); - /// ``` - #[inline] - fn eq(&self, other: &Arc) -> bool { - ArcEqIdent::eq(self, other) - } - - /// Inequality for two `Arc`s. - /// - /// Two `Arc`s are not equal if their inner values are not equal. - /// - /// If `T` also implements `Eq` (implying reflexivity of equality), - /// two `Arc`s that point to the same value are always equal. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// - /// assert!(five != Arc::new(6)); - /// ``` - #[inline] - fn ne(&self, other: &Arc) -> bool { - ArcEqIdent::ne(self, other) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialOrd for Arc { - /// Partial comparison for two `Arc`s. - /// - /// The two are compared by calling `partial_cmp()` on their inner values. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// use std::cmp::Ordering; - /// - /// let five = Arc::new(5); - /// - /// assert_eq!(Some(Ordering::Less), five.partial_cmp(&Arc::new(6))); - /// ``` - fn partial_cmp(&self, other: &Arc) -> Option { - (**self).partial_cmp(&**other) - } - - /// Less-than comparison for two `Arc`s. - /// - /// The two are compared by calling `<` on their inner values. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// - /// assert!(five < Arc::new(6)); - /// ``` - fn lt(&self, other: &Arc) -> bool { - *(*self) < *(*other) - } - - /// 'Less than or equal to' comparison for two `Arc`s. - /// - /// The two are compared by calling `<=` on their inner values. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// - /// assert!(five <= Arc::new(5)); - /// ``` - fn le(&self, other: &Arc) -> bool { - *(*self) <= *(*other) - } - - /// Greater-than comparison for two `Arc`s. - /// - /// The two are compared by calling `>` on their inner values. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// - /// assert!(five > Arc::new(4)); - /// ``` - fn gt(&self, other: &Arc) -> bool { - *(*self) > *(*other) - } - - /// 'Greater than or equal to' comparison for two `Arc`s. - /// - /// The two are compared by calling `>=` on their inner values. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// - /// assert!(five >= Arc::new(5)); - /// ``` - fn ge(&self, other: &Arc) -> bool { - *(*self) >= *(*other) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl Ord for Arc { - /// Comparison for two `Arc`s. - /// - /// The two are compared by calling `cmp()` on their inner values. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// use std::cmp::Ordering; - /// - /// let five = Arc::new(5); - /// - /// assert_eq!(Ordering::Less, five.cmp(&Arc::new(6))); - /// ``` - fn cmp(&self, other: &Arc) -> Ordering { - (**self).cmp(&**other) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl Eq for Arc {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for Arc { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&**self, f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for Arc { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&**self, f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Pointer for Arc { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Pointer::fmt(&(&raw const **self), f) - } -} - -#[cfg(not(no_global_oom_handling))] -#[stable(feature = "rust1", since = "1.0.0")] -impl Default for Arc { - /// Creates a new `Arc`, with the `Default` value for `T`. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let x: Arc = Default::default(); - /// assert_eq!(*x, 0); - /// ``` - fn default() -> Arc { - // ignore-tidy-undocumented-unsafe - unsafe { - Self::from_inner(Box::into_non_null(Box::write( - Box::new_uninit(), - ArcInner { - strong: atomic::AtomicUsize::new(1), - weak: atomic::AtomicUsize::new(1), - data: T::default(), - }, - ))) - } - } -} - -/// Struct to hold the static `ArcInner` used for empty `Arc` as -/// returned by `Default::default`. -/// -/// Layout notes: -/// * `repr(align(16))` so we can use it for `[T]` with `align_of::() <= 16`. -/// * `repr(C)` so `inner` is at offset 0 (and thus guaranteed to actually be aligned to 16). -/// * `[u8; 1]` (to be initialized with 0) so it can be used for `Arc`. -#[repr(C, align(16))] -struct SliceArcInnerForStatic { - inner: ArcInner<[u8; 1]>, -} -#[cfg(not(no_global_oom_handling))] -const MAX_STATIC_INNER_SLICE_ALIGNMENT: usize = 16; - -static STATIC_INNER_SLICE: SliceArcInnerForStatic = SliceArcInnerForStatic { - inner: ArcInner { - strong: atomic::AtomicUsize::new(1), - weak: atomic::AtomicUsize::new(1), - data: [0], - }, -}; - -#[cfg(not(no_global_oom_handling))] -#[stable(feature = "more_rc_default_impls", since = "1.80.0")] -impl Default for Arc { - /// Creates an empty str inside an Arc - /// - /// This may or may not share an allocation with other Arcs. - #[inline] - fn default() -> Self { - let arc: Arc<[u8]> = Default::default(); - debug_assert!(core::str::from_utf8(&arc).is_ok()); - let (ptr, alloc) = Arc::into_inner_with_allocator(arc); - // ignore-tidy-undocumented-unsafe - unsafe { Arc::from_ptr_in(ptr.as_ptr() as *mut ArcInner, alloc) } - } -} - -#[cfg(not(no_global_oom_handling))] -#[stable(feature = "more_rc_default_impls", since = "1.80.0")] -impl Default for Arc { - /// Creates an empty CStr inside an Arc - /// - /// This may or may not share an allocation with other Arcs. - #[inline] - fn default() -> Self { - use core::ffi::CStr; - let inner: NonNull> = NonNull::from(&STATIC_INNER_SLICE.inner); - let inner: NonNull> = - NonNull::new(inner.as_ptr() as *mut ArcInner).unwrap(); - // `this` semantically is the Arc "owned" by the static, so make sure not to drop it. - let this: mem::ManuallyDrop> = - // ignore-tidy-undocumented-unsafe - unsafe { mem::ManuallyDrop::new(Arc::from_inner(inner)) }; - (*this).clone() - } -} - -#[cfg(not(no_global_oom_handling))] -#[stable(feature = "more_rc_default_impls", since = "1.80.0")] -impl Default for Arc<[T]> { - /// Creates an empty `[T]` inside an Arc - /// - /// This may or may not share an allocation with other Arcs. - #[inline] - fn default() -> Self { - if align_of::() <= MAX_STATIC_INNER_SLICE_ALIGNMENT { - // We take a reference to the whole struct instead of the ArcInner<[u8; 1]> inside it so - // we don't shrink the range of bytes the ptr is allowed to access under Stacked Borrows. - // (Miri complains on 32-bit targets with Arc<[Align16]> otherwise.) - // (Note that NonNull::from(&STATIC_INNER_SLICE.inner) is fine under Tree Borrows.) - let inner: NonNull = NonNull::from(&STATIC_INNER_SLICE); - let inner: NonNull> = inner.cast(); - // `this` semantically is the Arc "owned" by the static, so make sure not to drop it. - let this: mem::ManuallyDrop> = - // ignore-tidy-undocumented-unsafe - unsafe { mem::ManuallyDrop::new(Arc::from_inner(inner)) }; - return (*this).clone(); - } - - // If T's alignment is too large for the static, make a new unique allocation. - let arr: [T; 0] = []; - Arc::from(arr) - } -} - -#[cfg(not(no_global_oom_handling))] -#[stable(feature = "pin_default_impls", since = "1.91.0")] -impl Default for Pin> -where - T: ?Sized, - Arc: Default, -{ - #[inline] - fn default() -> Self { - // SAFETY: We own and create the pinned pointer. - unsafe { Pin::new_unchecked(Arc::::default()) } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Hash for Arc { - fn hash(&self, state: &mut H) { - (**self).hash(state) - } -} - -#[cfg(not(no_global_oom_handling))] -#[stable(feature = "from_for_ptrs", since = "1.6.0")] -impl From for Arc { - /// Converts a `T` into an `Arc` - /// - /// The conversion moves the value into a - /// newly allocated `Arc`. It is equivalent to - /// calling `Arc::new(t)`. - /// - /// # Example - /// ```rust - /// # use std::sync::Arc; - /// let x = 5; - /// let arc = Arc::new(5); - /// - /// assert_eq!(Arc::from(x), arc); - /// ``` - fn from(t: T) -> Self { - Arc::new(t) - } -} - -#[cfg(not(no_global_oom_handling))] -#[stable(feature = "shared_from_array", since = "1.74.0")] -impl From<[T; N]> for Arc<[T]> { - /// Converts a [`[T; N]`](prim@array) into an `Arc<[T]>`. - /// - /// The conversion moves the array into a newly allocated `Arc`. - /// - /// # Example - /// - /// ``` - /// # use std::sync::Arc; - /// let original: [i32; 3] = [1, 2, 3]; - /// let shared: Arc<[i32]> = Arc::from(original); - /// assert_eq!(&[1, 2, 3], &shared[..]); - /// ``` - #[inline] - fn from(v: [T; N]) -> Arc<[T]> { - Arc::<[T; N]>::from(v) - } -} - -#[cfg(not(no_global_oom_handling))] -#[stable(feature = "shared_from_slice", since = "1.21.0")] -impl From<&[T]> for Arc<[T]> { - /// Allocates a reference-counted slice and fills it by cloning `v`'s items. - /// - /// # Example - /// - /// ``` - /// # use std::sync::Arc; - /// let original: &[i32] = &[1, 2, 3]; - /// let shared: Arc<[i32]> = Arc::from(original); - /// assert_eq!(&[1, 2, 3], &shared[..]); - /// ``` - #[inline] - fn from(v: &[T]) -> Arc<[T]> { - >::from_slice(v) - } -} - -#[cfg(not(no_global_oom_handling))] -#[stable(feature = "shared_from_mut_slice", since = "1.84.0")] -impl From<&mut [T]> for Arc<[T]> { - /// Allocates a reference-counted slice and fills it by cloning `v`'s items. - /// - /// # Example - /// - /// ``` - /// # use std::sync::Arc; - /// let mut original = [1, 2, 3]; - /// let original: &mut [i32] = &mut original; - /// let shared: Arc<[i32]> = Arc::from(original); - /// assert_eq!(&[1, 2, 3], &shared[..]); - /// ``` - #[inline] - fn from(v: &mut [T]) -> Arc<[T]> { - Arc::from(&*v) - } -} - -#[cfg(not(no_global_oom_handling))] -#[stable(feature = "shared_from_slice", since = "1.21.0")] -impl From<&str> for Arc { - /// Allocates a reference-counted `str` and copies `v` into it. - /// - /// # Example - /// - /// ``` - /// # use std::sync::Arc; - /// let shared: Arc = Arc::from("eggplant"); - /// assert_eq!("eggplant", &shared[..]); - /// ``` - #[inline] - fn from(v: &str) -> Arc { - let arc = Arc::<[u8]>::from(v.as_bytes()); - // ignore-tidy-undocumented-unsafe - unsafe { Arc::from_raw(Arc::into_raw(arc) as *const str) } - } -} - -#[cfg(not(no_global_oom_handling))] -#[stable(feature = "shared_from_mut_slice", since = "1.84.0")] -impl From<&mut str> for Arc { - /// Allocates a reference-counted `str` and copies `v` into it. - /// - /// # Example - /// - /// ``` - /// # use std::sync::Arc; - /// let mut original = String::from("eggplant"); - /// let original: &mut str = &mut original; - /// let shared: Arc = Arc::from(original); - /// assert_eq!("eggplant", &shared[..]); - /// ``` - #[inline] - fn from(v: &mut str) -> Arc { - Arc::from(&*v) - } -} - -#[cfg(not(no_global_oom_handling))] -#[stable(feature = "shared_from_slice", since = "1.21.0")] -impl From for Arc { - /// Allocates a reference-counted `str` and copies `v` into it. - /// - /// # Example - /// - /// ``` - /// # use std::sync::Arc; - /// let unique: String = "eggplant".to_owned(); - /// let shared: Arc = Arc::from(unique); - /// assert_eq!("eggplant", &shared[..]); - /// ``` - #[inline] - fn from(v: String) -> Arc { - Arc::from(&v[..]) - } -} - -#[cfg(not(no_global_oom_handling))] -#[stable(feature = "shared_from_slice", since = "1.21.0")] -impl From> for Arc { - /// Move a boxed object to a new, reference-counted allocation. - /// - /// # Example - /// - /// ``` - /// # use std::sync::Arc; - /// let unique: Box = Box::from("eggplant"); - /// let shared: Arc = Arc::from(unique); - /// assert_eq!("eggplant", &shared[..]); - /// ``` - #[inline] - fn from(v: Box) -> Arc { - Arc::from_box_in(v) - } -} - -#[cfg(not(no_global_oom_handling))] -#[stable(feature = "shared_from_slice", since = "1.21.0")] -impl From> for Arc<[T], A> { - /// Allocates a reference-counted slice and moves `v`'s items into it. - /// - /// # Example - /// - /// ``` - /// # use std::sync::Arc; - /// let unique: Vec = vec![1, 2, 3]; - /// let shared: Arc<[i32]> = Arc::from(unique); - /// assert_eq!(&[1, 2, 3], &shared[..]); - /// ``` - #[inline] - fn from(v: Vec) -> Arc<[T], A> { - // ignore-tidy-undocumented-unsafe - unsafe { - let (vec_ptr, len, cap, alloc) = v.into_raw_parts_with_allocator(); - - let rc_ptr = Self::allocate_for_slice_in(len, &alloc); - ptr::copy_nonoverlapping(vec_ptr, (&raw mut (*rc_ptr).data) as *mut T, len); - - // Create a `Vec` with length 0, to deallocate the buffer - // without dropping its contents or the allocator - let _ = Vec::from_raw_parts_in(vec_ptr, 0, cap, &alloc); - - Self::from_ptr_in(rc_ptr, alloc) - } - } -} - -#[stable(feature = "shared_from_cow", since = "1.45.0")] -impl<'a, B> From> for Arc -where - B: ToOwned + ?Sized, - Arc: From<&'a B> + From, -{ - /// Creates an atomically reference-counted pointer from a clone-on-write - /// pointer by copying its content. - /// - /// # Example - /// - /// ```rust - /// # use std::sync::Arc; - /// # use std::borrow::Cow; - /// let cow: Cow<'_, str> = Cow::Borrowed("eggplant"); - /// let shared: Arc = Arc::from(cow); - /// assert_eq!("eggplant", &shared[..]); - /// ``` - #[inline] - fn from(cow: Cow<'a, B>) -> Arc { - match cow { - Cow::Borrowed(s) => Arc::from(s), - Cow::Owned(s) => Arc::from(s), - } - } -} - -#[stable(feature = "shared_from_str", since = "1.62.0")] -impl From> for Arc<[u8]> { - /// Converts an atomically reference-counted string slice into a byte slice. - /// - /// # Example - /// - /// ``` - /// # use std::sync::Arc; - /// let string: Arc = Arc::from("eggplant"); - /// let bytes: Arc<[u8]> = Arc::from(string); - /// assert_eq!("eggplant".as_bytes(), bytes.as_ref()); - /// ``` - #[inline] - fn from(rc: Arc) -> Self { - // SAFETY: `str` has the same layout as `[u8]`. - unsafe { Arc::from_raw(Arc::into_raw(rc) as *const [u8]) } - } -} - -#[stable(feature = "boxed_slice_try_from", since = "1.43.0")] -impl TryFrom> for Arc<[T; N], A> { - type Error = Arc<[T], A>; - - fn try_from(boxed_slice: Arc<[T], A>) -> Result { - if boxed_slice.len() == N { - let (ptr, alloc) = Arc::into_inner_with_allocator(boxed_slice); - // ignore-tidy-undocumented-unsafe - Ok(unsafe { Arc::from_inner_in(ptr.cast(), alloc) }) - } else { - Err(boxed_slice) - } - } -} - -#[cfg(not(no_global_oom_handling))] -#[stable(feature = "shared_from_iter", since = "1.37.0")] -impl FromIterator for Arc<[T]> { - /// Takes each element in the `Iterator` and collects it into an `Arc<[T]>`. - /// - /// # Performance characteristics - /// - /// ## The general case - /// - /// In the general case, collecting into `Arc<[T]>` is done by first - /// collecting into a `Vec`. That is, when writing the following: - /// - /// ```rust - /// # use std::sync::Arc; - /// let evens: Arc<[u8]> = (0..10).filter(|&x| x % 2 == 0).collect(); - /// # assert_eq!(&*evens, &[0, 2, 4, 6, 8]); - /// ``` - /// - /// this behaves as if we wrote: - /// - /// ```rust - /// # use std::sync::Arc; - /// let evens: Arc<[u8]> = (0..10).filter(|&x| x % 2 == 0) - /// .collect::>() // The first set of allocations happens here. - /// .into(); // A second allocation for `Arc<[T]>` happens here. - /// # assert_eq!(&*evens, &[0, 2, 4, 6, 8]); - /// ``` - /// - /// This will allocate as many times as needed for constructing the `Vec` - /// and then it will allocate once for turning the `Vec` into the `Arc<[T]>`. - /// - /// ## Iterators of known length - /// - /// When your `Iterator` implements `TrustedLen` and is of an exact size, - /// a single allocation will be made for the `Arc<[T]>`. For example: - /// - /// ```rust - /// # use std::sync::Arc; - /// let evens: Arc<[u8]> = (0..10).collect(); // Just a single allocation happens here. - /// # assert_eq!(&*evens, &*(0..10).collect::>()); - /// ``` - fn from_iter>(iter: I) -> Self { - ToArcSlice::to_arc_slice(iter.into_iter()) - } -} - -#[cfg(not(no_global_oom_handling))] -/// Specialization trait used for collecting into `Arc<[T]>`. -trait ToArcSlice: Iterator + Sized { - fn to_arc_slice(self) -> Arc<[T]>; -} - -#[cfg(not(no_global_oom_handling))] -impl> ToArcSlice for I { - default fn to_arc_slice(self) -> Arc<[T]> { - self.collect::>().into() - } -} - -#[cfg(not(no_global_oom_handling))] -impl> ToArcSlice for I { - fn to_arc_slice(self) -> Arc<[T]> { - // This is the case for a `TrustedLen` iterator. - let (low, high) = self.size_hint(); - if let Some(high) = high { - debug_assert_eq!( - low, - high, - "TrustedLen iterator's size hint is not exact: {:?}", - (low, high) - ); - - // SAFETY: We need to ensure that the iterator has an exact length and we have. - unsafe { Arc::from_iter_exact(self, low) } - } else { - // TrustedLen contract guarantees that `upper_bound == None` implies an iterator - // length exceeding `usize::MAX`. - // The default implementation would collect into a vec which would panic. - // Thus we panic here immediately without invoking `Vec` code. - panic!("capacity overflow"); - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl borrow::Borrow for Arc { - fn borrow(&self) -> &T { - self - } -} - -#[stable(since = "1.5.0", feature = "smart_ptr_as_ref")] -impl AsRef for Arc { - fn as_ref(&self) -> &T { - self - } -} - -#[stable(feature = "pin", since = "1.33.0")] -impl Unpin for Arc {} - -/// Gets the offset within an `ArcInner` for the payload behind a pointer. -/// -/// # Safety -/// -/// The pointer must point to (and have valid metadata for) a previously -/// valid instance of T, but the T is allowed to be dropped. -unsafe fn data_offset(ptr: *const T) -> usize { - // Align the unsized value to the end of the ArcInner. - // Because ArcInner is repr(C), it will always be the last field in memory. - // SAFETY: since the only unsized types possible are slices, trait objects, - // and extern types, the input safety requirement is currently enough to - // satisfy the requirements of Alignment::of_val_raw; this is an implementation - // detail of the language that must not be relied upon outside of std. - unsafe { data_offset_alignment(Alignment::of_val_raw(ptr)) } -} - -#[inline] -fn data_offset_alignment(alignment: Alignment) -> usize { - let layout = Layout::new::>(); - layout.size() + layout.padding_needed_for(alignment) -} - -/// A unique owning pointer to an [`ArcInner`] **that does not imply the contents are initialized,** -/// but will deallocate it (without dropping the value) when dropped. -/// -/// This is a helper for [`Arc::make_mut()`] to ensure correct cleanup on panic. -struct UniqueArcUninit { - ptr: NonNull>, - layout_for_value: Layout, - alloc: Option, -} - -impl UniqueArcUninit { - /// Allocates an ArcInner with layout suitable to contain `for_value` or a clone of it. - #[cfg(not(no_global_oom_handling))] - fn new(for_value: &T, alloc: A) -> UniqueArcUninit { - let layout = Layout::for_value(for_value); - // ignore-tidy-undocumented-unsafe - let ptr = unsafe { - Arc::allocate_for_layout( - layout, - |layout_for_arcinner| alloc.allocate(layout_for_arcinner), - |mem| mem.with_metadata_of(ptr::from_ref(for_value) as *const ArcInner), - ) - }; - Self { ptr: NonNull::new(ptr).unwrap(), layout_for_value: layout, alloc: Some(alloc) } - } - - /// Allocates an ArcInner with layout suitable to contain `for_value` or a clone of it, - /// returning an error if allocation fails. - fn try_new(for_value: &T, alloc: A) -> Result, AllocError> { - let layout = Layout::for_value(for_value); - // ignore-tidy-undocumented-unsafe - let ptr = unsafe { - Arc::try_allocate_for_layout( - layout, - |layout_for_arcinner| alloc.allocate(layout_for_arcinner), - |mem| mem.with_metadata_of(ptr::from_ref(for_value) as *const ArcInner), - )? - }; - Ok(Self { ptr: NonNull::new(ptr).unwrap(), layout_for_value: layout, alloc: Some(alloc) }) - } - - /// Returns the pointer to be written into to initialize the [`Arc`]. - fn data_ptr(&mut self) -> *mut T { - let offset = data_offset_alignment(self.layout_for_value.alignment()); - // ignore-tidy-undocumented-unsafe - unsafe { self.ptr.as_ptr().byte_add(offset) as *mut T } - } - - /// Upgrade this into a normal [`Arc`]. - /// - /// # Safety - /// - /// The data must have been initialized (by writing to [`Self::data_ptr()`]). - unsafe fn into_arc(self) -> Arc { - let mut this = ManuallyDrop::new(self); - let ptr = this.ptr.as_ptr(); - let alloc = this.alloc.take().unwrap(); - - // SAFETY: The pointer is valid as per `UniqueArcUninit::new`, and the caller is responsible - // for having initialized the data. - unsafe { Arc::from_ptr_in(ptr, alloc) } - } -} - -impl Drop for UniqueArcUninit { - fn drop(&mut self) { - // SAFETY: - // * new() produced a pointer safe to deallocate. - // * We own the pointer unless into_arc() was called, which forgets us. - unsafe { - self.alloc.take().unwrap().deallocate( - self.ptr.cast(), - arcinner_layout_for_value_layout(self.layout_for_value), - ); - } - } -} - -#[stable(feature = "arc_error", since = "1.52.0")] -impl core::error::Error for Arc { - #[allow(deprecated)] - fn cause(&self) -> Option<&dyn core::error::Error> { - core::error::Error::cause(&**self) - } - - fn source(&self) -> Option<&(dyn core::error::Error + 'static)> { - core::error::Error::source(&**self) - } - - fn provide<'a>(&'a self, req: &mut core::error::Request<'a>) { - core::error::Error::provide(&**self, req); - } -} - -/// A uniquely owned [`Arc`]. -/// -/// This represents an `Arc` that is known to be uniquely owned -- that is, have exactly one strong -/// reference. Multiple weak pointers can be created, but attempts to upgrade those to strong -/// references will fail unless the `UniqueArc` they point to has been converted into a regular `Arc`. -/// -/// Because it is uniquely owned, the contents of a `UniqueArc` can be freely mutated. A common -/// use case is to have an object be mutable during its initialization phase but then have it become -/// immutable and converted to a normal `Arc`. -/// -/// This can be used as a flexible way to create cyclic data structures, as in the example below. -/// -/// ``` -/// #![feature(unique_rc_arc)] -/// use std::sync::{Arc, Weak, UniqueArc}; -/// -/// struct Gadget { -/// me: Weak, -/// } -/// -/// fn create_gadget() -> Option> { -/// let mut rc = UniqueArc::new(Gadget { -/// me: Weak::new(), -/// }); -/// rc.me = UniqueArc::downgrade(&rc); -/// Some(UniqueArc::into_arc(rc)) -/// } -/// -/// create_gadget().unwrap(); -/// ``` -/// -/// An advantage of using `UniqueArc` over [`Arc::new_cyclic`] to build cyclic data structures is that -/// [`Arc::new_cyclic`]'s `data_fn` parameter cannot be async or return a [`Result`]. As shown in the -/// previous example, `UniqueArc` allows for more flexibility in the construction of cyclic data, -/// including fallible or async constructors. -#[unstable(feature = "unique_rc_arc", issue = "112566")] -pub struct UniqueArc< - T: ?Sized, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> { - ptr: NonNull>, - // Define the ownership of `ArcInner` for drop-check - _marker: PhantomData>, - // Invariance is necessary for soundness: once other `Weak` - // references exist, we already have a form of shared mutability! - _marker2: PhantomData<*mut T>, - alloc: A, -} - -#[unstable(feature = "unique_rc_arc", issue = "112566")] -unsafe impl Send for UniqueArc {} - -#[unstable(feature = "unique_rc_arc", issue = "112566")] -unsafe impl Sync for UniqueArc {} - -#[unstable(feature = "unique_rc_arc", issue = "112566")] -// #[unstable(feature = "coerce_unsized", issue = "18598")] -impl, U: ?Sized, A: Allocator> CoerceUnsized> - for UniqueArc -{ -} - -//#[unstable(feature = "unique_rc_arc", issue = "112566")] -#[unstable(feature = "dispatch_from_dyn", issue = "none")] -impl, U: ?Sized> DispatchFromDyn> for UniqueArc {} - -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl fmt::Display for UniqueArc { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&**self, f) - } -} - -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl fmt::Debug for UniqueArc { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&**self, f) - } -} - -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl fmt::Pointer for UniqueArc { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Pointer::fmt(&(&raw const **self), f) - } -} - -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl borrow::Borrow for UniqueArc { - fn borrow(&self) -> &T { - self - } -} - -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl borrow::BorrowMut for UniqueArc { - fn borrow_mut(&mut self) -> &mut T { - self - } -} - -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl AsRef for UniqueArc { - fn as_ref(&self) -> &T { - self - } -} - -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl AsMut for UniqueArc { - fn as_mut(&mut self) -> &mut T { - self - } -} - -#[cfg(not(no_global_oom_handling))] -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl From for UniqueArc { - #[inline(always)] - fn from(value: T) -> Self { - Self::new(value) - } -} - -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl Unpin for UniqueArc {} - -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl PartialEq for UniqueArc { - /// Equality for two `UniqueArc`s. - /// - /// Two `UniqueArc`s are equal if their inner values are equal. - /// - /// # Examples - /// - /// ``` - /// #![feature(unique_rc_arc)] - /// use std::sync::UniqueArc; - /// - /// let five = UniqueArc::new(5); - /// - /// assert!(five == UniqueArc::new(5)); - /// ``` - #[inline] - fn eq(&self, other: &Self) -> bool { - PartialEq::eq(&**self, &**other) - } -} - -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl PartialOrd for UniqueArc { - /// Partial comparison for two `UniqueArc`s. - /// - /// The two are compared by calling `partial_cmp()` on their inner values. - /// - /// # Examples - /// - /// ``` - /// #![feature(unique_rc_arc)] - /// use std::sync::UniqueArc; - /// use std::cmp::Ordering; - /// - /// let five = UniqueArc::new(5); - /// - /// assert_eq!(Some(Ordering::Less), five.partial_cmp(&UniqueArc::new(6))); - /// ``` - #[inline(always)] - fn partial_cmp(&self, other: &UniqueArc) -> Option { - (**self).partial_cmp(&**other) - } - - /// Less-than comparison for two `UniqueArc`s. - /// - /// The two are compared by calling `<` on their inner values. - /// - /// # Examples - /// - /// ``` - /// #![feature(unique_rc_arc)] - /// use std::sync::UniqueArc; - /// - /// let five = UniqueArc::new(5); - /// - /// assert!(five < UniqueArc::new(6)); - /// ``` - #[inline(always)] - fn lt(&self, other: &UniqueArc) -> bool { - **self < **other - } - - /// 'Less than or equal to' comparison for two `UniqueArc`s. - /// - /// The two are compared by calling `<=` on their inner values. - /// - /// # Examples - /// - /// ``` - /// #![feature(unique_rc_arc)] - /// use std::sync::UniqueArc; - /// - /// let five = UniqueArc::new(5); - /// - /// assert!(five <= UniqueArc::new(5)); - /// ``` - #[inline(always)] - fn le(&self, other: &UniqueArc) -> bool { - **self <= **other - } - - /// Greater-than comparison for two `UniqueArc`s. - /// - /// The two are compared by calling `>` on their inner values. - /// - /// # Examples - /// - /// ``` - /// #![feature(unique_rc_arc)] - /// use std::sync::UniqueArc; - /// - /// let five = UniqueArc::new(5); - /// - /// assert!(five > UniqueArc::new(4)); - /// ``` - #[inline(always)] - fn gt(&self, other: &UniqueArc) -> bool { - **self > **other - } - - /// 'Greater than or equal to' comparison for two `UniqueArc`s. - /// - /// The two are compared by calling `>=` on their inner values. - /// - /// # Examples - /// - /// ``` - /// #![feature(unique_rc_arc)] - /// use std::sync::UniqueArc; - /// - /// let five = UniqueArc::new(5); - /// - /// assert!(five >= UniqueArc::new(5)); - /// ``` - #[inline(always)] - fn ge(&self, other: &UniqueArc) -> bool { - **self >= **other - } -} - -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl Ord for UniqueArc { - /// Comparison for two `UniqueArc`s. - /// - /// The two are compared by calling `cmp()` on their inner values. - /// - /// # Examples - /// - /// ``` - /// #![feature(unique_rc_arc)] - /// use std::sync::UniqueArc; - /// use std::cmp::Ordering; - /// - /// let five = UniqueArc::new(5); - /// - /// assert_eq!(Ordering::Less, five.cmp(&UniqueArc::new(6))); - /// ``` - #[inline] - fn cmp(&self, other: &UniqueArc) -> Ordering { - (**self).cmp(&**other) - } -} - -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl Eq for UniqueArc {} - -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl Hash for UniqueArc { - fn hash(&self, state: &mut H) { - (**self).hash(state); - } -} - -impl UniqueArc { - /// Creates a new `UniqueArc`. - /// - /// Weak references to this `UniqueArc` can be created with [`UniqueArc::downgrade`]. Upgrading - /// these weak references will fail before the `UniqueArc` has been converted into an [`Arc`]. - /// After converting the `UniqueArc` into an [`Arc`], any weak references created beforehand will - /// point to the new [`Arc`]. - #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "unique_rc_arc", issue = "112566")] - #[must_use] - pub fn new(value: T) -> Self { - Self::new_in(value, Global) - } -} - -impl UniqueArc { - /// Creates a new `UniqueArc` in the provided allocator. - /// - /// Weak references to this `UniqueArc` can be created with [`UniqueArc::downgrade`]. Upgrading - /// these weak references will fail before the `UniqueArc` has been converted into an [`Arc`]. - /// After converting the `UniqueArc` into an [`Arc`], any weak references created beforehand will - /// point to the new [`Arc`]. - #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "unique_rc_arc", issue = "112566")] - #[must_use] - // #[unstable(feature = "allocator_api", issue = "32838")] - pub fn new_in(data: T, alloc: A) -> Self { - let (ptr, alloc) = Box::into_non_null_with_allocator(Box::new_in( - ArcInner { - strong: atomic::AtomicUsize::new(0), - // keep one weak reference so if all the weak pointers that are created are dropped - // the UniqueArc still stays valid. - weak: atomic::AtomicUsize::new(1), - data, - }, - alloc, - )); - Self { ptr, _marker: PhantomData, _marker2: PhantomData, alloc } - } - - #[cfg(not(no_global_oom_handling))] - fn unwrap_with_allocator(this: Self) -> (T, A) { - let inner_ptr = this.ptr; - let (data_ptr, alloc) = Self::into_raw_with_allocator(this); - - // SAFETY: Conceptually moves out of the `UniqueRc`. - // We do not use the data inside ever again. - let val = unsafe { data_ptr.read() }; - - drop(Weak { ptr: inner_ptr, alloc: &alloc }); - - (val, alloc) - } - - /// Maps the value in a `UniqueArc`, reusing the allocation if possible. - /// - /// `f` is called on a reference to the value in the `UniqueArc`, and the result is returned, - /// also in a `UniqueArc`. - /// - /// Note: this is an associated function, which means that you have - /// to call it as `UniqueArc::map(u, f)` instead of `u.map(f)`. This - /// is so that there is no conflict with a method on the inner type. - /// - /// # Examples - /// - /// ``` - /// #![feature(unique_rc_arc)] - /// - /// use std::sync::UniqueArc; - /// - /// let r = UniqueArc::new(7); - /// let new = UniqueArc::map(r, |i| i + 7); - /// assert_eq!(*new, 14); - /// ``` - #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "unique_rc_arc", issue = "112566")] - pub fn map(this: Self, f: impl FnOnce(T) -> U) -> UniqueArc { - if size_of::() == size_of::() - && align_of::() == align_of::() - && UniqueArc::weak_count(&this) == 0 - { - // ignore-tidy-undocumented-unsafe - unsafe { - let (ptr, alloc) = UniqueArc::into_raw_with_allocator(this); - let value = ptr.read(); - let mut allocation = - UniqueArc::from_raw_with_allocator(ptr.cast::>(), alloc); - - allocation.write(f(value)); - allocation.assume_init() - } - } else { - let (val, alloc) = UniqueArc::unwrap_with_allocator(this); - UniqueArc::new_in(f(val), alloc) - } - } - - /// Attempts to map the value in a `UniqueArc`, reusing the allocation if possible. - /// - /// `f` is called on a reference to the value in the `UniqueArc`, and if the operation succeeds, - /// the result is returned, also in a `UniqueArc`. - /// - /// Note: this is an associated function, which means that you have - /// to call it as `UniqueArc::try_map(u, f)` instead of `u.try_map(f)`. This - /// is so that there is no conflict with a method on the inner type. - /// - /// # Examples - /// - /// ``` - /// #![feature(smart_pointer_try_map)] - /// #![feature(unique_rc_arc)] - /// - /// use std::sync::UniqueArc; - /// - /// let b = UniqueArc::new(7); - /// let new = UniqueArc::try_map(b, u32::try_from).unwrap(); - /// assert_eq!(*new, 7); - /// ``` - #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "smart_pointer_try_map", issue = "144419")] - pub fn try_map( - this: Self, - f: impl FnOnce(T) -> R, - ) -> >>::TryType - where - R: Try, - R::Residual: Residual>, - { - if size_of::() == size_of::() - && align_of::() == align_of::() - && UniqueArc::weak_count(&this) == 0 - { - // ignore-tidy-undocumented-unsafe - unsafe { - let (ptr, alloc) = UniqueArc::into_raw_with_allocator(this); - let value = ptr.read(); - let mut allocation = UniqueArc::from_raw_with_allocator( - ptr.cast::>(), - alloc, - ); - - allocation.write(f(value)?); - try { allocation.assume_init() } - } - } else { - let (val, alloc) = UniqueArc::unwrap_with_allocator(this); - try { UniqueArc::new_in(f(val)?, alloc) } - } - } -} - -impl UniqueArc { - #[cfg(not(no_global_oom_handling))] - unsafe fn from_raw_with_allocator(ptr: *const T, alloc: A) -> Self { - // SAFETY: Upheld by caller. - let offset = unsafe { data_offset(ptr) }; - - // Reverse the offset to find the original ArcInner. - // SAFETY: Upheld by caller. - let rc_ptr = unsafe { ptr.byte_sub(offset) as *mut ArcInner }; - - Self { - // SAFETY: Upheld by caller. - ptr: unsafe { NonNull::new_unchecked(rc_ptr) }, - _marker: PhantomData, - _marker2: PhantomData, - alloc, - } - } - - #[cfg(not(no_global_oom_handling))] - fn into_raw_with_allocator(this: Self) -> (*const T, A) { - let this = ManuallyDrop::new(this); - // SAFETY: The copy of the allocator stored in `this` is forgotten - (Self::as_ptr(&*this), unsafe { ptr::read(&this.alloc) }) - } - - /// Converts the `UniqueArc` into a regular [`Arc`]. - /// - /// This consumes the `UniqueArc` and returns a regular [`Arc`] that contains the `value` that - /// is passed to `into_arc`. - /// - /// Any weak references created before this method is called can now be upgraded to strong - /// references. - #[unstable(feature = "unique_rc_arc", issue = "112566")] - #[must_use] - pub fn into_arc(this: Self) -> Arc { - let this = ManuallyDrop::new(this); - - // Move the allocator out. - // SAFETY: `this.alloc` will not be accessed again, nor dropped because it is in - // a `ManuallyDrop`. - let alloc: A = unsafe { ptr::read(&this.alloc) }; - - // SAFETY: This pointer was allocated at creation time so we know it is valid. - unsafe { - // Convert our weak reference into a strong reference - (*this.ptr.as_ptr()).strong.store(1, Release); - Arc::from_inner_in(this.ptr, alloc) - } - } - - #[cfg(not(no_global_oom_handling))] - fn weak_count(this: &Self) -> usize { - this.inner().weak.load(Acquire) - 1 - } - - #[cfg(not(no_global_oom_handling))] - fn inner(&self) -> &ArcInner { - // SAFETY: while this UniqueArc is alive we're guaranteed that the inner pointer is valid. - unsafe { self.ptr.as_ref() } - } - - #[cfg(not(no_global_oom_handling))] - fn as_ptr(this: &Self) -> *const T { - let ptr: *mut ArcInner = NonNull::as_ptr(this.ptr); - - // SAFETY: This cannot go through Deref::deref or UniqueArc::inner because - // this is required to retain raw/mut provenance such that e.g. `get_mut` can - // write through the pointer after the Rc is recovered through `from_raw`. - unsafe { &raw mut (*ptr).data } - } - - #[inline] - #[cfg(not(no_global_oom_handling))] - fn into_inner_with_allocator(this: Self) -> (NonNull>, A) { - let this = mem::ManuallyDrop::new(this); - // SAFETY: Pointer is valid for reads and only read once. - (this.ptr, unsafe { ptr::read(&this.alloc) }) - } - - #[inline] - #[cfg(not(no_global_oom_handling))] - unsafe fn from_inner_in(ptr: NonNull>, alloc: A) -> Self { - Self { ptr, _marker: PhantomData, _marker2: PhantomData, alloc } - } -} - -impl UniqueArc { - /// Creates a new weak reference to the `UniqueArc`. - /// - /// Attempting to upgrade this weak reference will fail before the `UniqueArc` has been converted - /// to a [`Arc`] using [`UniqueArc::into_arc`]. - #[unstable(feature = "unique_rc_arc", issue = "112566")] - #[must_use] - pub fn downgrade(this: &Self) -> Weak { - // Using a relaxed ordering is alright here, as knowledge of the - // original reference prevents other threads from erroneously deleting - // the object or converting the object to a normal `Arc`. - // - // Note that we don't need to test if the weak counter is locked because there - // are no such operations like `Arc::get_mut` or `Arc::make_mut` that will lock - // the weak counter. - // - // SAFETY: This pointer was allocated at creation time so we know it is valid. - let old_size = unsafe { (*this.ptr.as_ptr()).weak.fetch_add(1, Relaxed) }; - - // See comments in Arc::clone() for why we do this (for mem::forget). - if old_size > MAX_REFCOUNT { - abort(); - } - - Weak { ptr: this.ptr, alloc: this.alloc.clone() } - } -} - -#[cfg(not(no_global_oom_handling))] -impl UniqueArc, A> { - unsafe fn assume_init(self) -> UniqueArc { - let (ptr, alloc) = UniqueArc::into_inner_with_allocator(self); - // SAFETY: Upheld by caller. - unsafe { UniqueArc::from_inner_in(ptr.cast(), alloc) } - } -} - -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl Deref for UniqueArc { - type Target = T; - - fn deref(&self) -> &T { - // SAFETY: This pointer was allocated at creation time so we know it is valid. - unsafe { &self.ptr.as_ref().data } - } -} - -// #[unstable(feature = "unique_rc_arc", issue = "112566")] -#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] -unsafe impl PinSafePointer for UniqueArc {} - -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl DerefMut for UniqueArc { - fn deref_mut(&mut self) -> &mut T { - // SAFETY: This pointer was allocated at creation time so we know it is valid. We know we - // have unique ownership and therefore it's safe to make a mutable reference because - // `UniqueArc` owns the only strong reference to itself. - // We also need to be careful to only create a mutable reference to the `data` field, - // as a mutable reference to the entire `ArcInner` would assert uniqueness over the - // ref count fields too, invalidating any attempt by `Weak`s to access the ref count. - unsafe { &mut (*self.ptr.as_ptr()).data } - } -} - #[unstable(feature = "unique_rc_arc", issue = "112566")] -// #[unstable(feature = "deref_pure_trait", issue = "87121")] -unsafe impl DerefPure for UniqueArc {} - -#[unstable(feature = "unique_rc_arc", issue = "112566")] -unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for UniqueArc { - fn drop(&mut self) { - // See `Arc::drop_slow` which drops an `Arc` with a strong count of 0. - // SAFETY: This pointer was allocated at creation time so we know it is valid. - let _weak = Weak { ptr: self.ptr, alloc: &self.alloc }; - - // ignore-tidy-undocumented-unsafe - unsafe { ptr::drop_in_place(&mut (*self.ptr.as_ptr()).data) }; - } -} - -#[unstable(feature = "allocator_api", issue = "32838")] -unsafe impl Allocator for Arc { - #[inline] - fn allocate(&self, layout: Layout) -> Result, AllocError> { - (**self).allocate(layout) - } - - #[inline] - fn allocate_zeroed(&self, layout: Layout) -> Result, AllocError> { - (**self).allocate_zeroed(layout) - } - - #[inline] - unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { - // SAFETY: the safety contract must be upheld by the caller - unsafe { (**self).deallocate(ptr, layout) } - } - - #[inline] - unsafe fn grow( - &self, - ptr: NonNull, - old_layout: Layout, - new_layout: Layout, - ) -> Result, AllocError> { - // SAFETY: the safety contract must be upheld by the caller - unsafe { (**self).grow(ptr, old_layout, new_layout) } - } - - #[inline] - unsafe fn grow_zeroed( - &self, - ptr: NonNull, - old_layout: Layout, - new_layout: Layout, - ) -> Result, AllocError> { - // SAFETY: the safety contract must be upheld by the caller - unsafe { (**self).grow_zeroed(ptr, old_layout, new_layout) } - } - - #[inline] - unsafe fn shrink( - &self, - ptr: NonNull, - old_layout: Layout, - new_layout: Layout, - ) -> Result, AllocError> { - // SAFETY: the safety contract must be upheld by the caller - unsafe { (**self).shrink(ptr, old_layout, new_layout) } - } -} - -#[unstable(feature = "allocator_api", issue = "32838")] -unsafe impl AllocatorClone for Arc {} +pub use crate::rcs::arc::UniqueArc; +#[stable(feature = "rust1", since = "1.0.0")] +pub use crate::rcs::arc::{Arc, Weak}; diff --git a/library/alloc/src/vec/drain.rs b/library/alloc/src/vec/drain.rs index df3ff0a9b769f..a4059db012c63 100644 --- a/library/alloc/src/vec/drain.rs +++ b/library/alloc/src/vec/drain.rs @@ -21,7 +21,7 @@ use crate::alloc::{Allocator, Global}; pub struct Drain< 'a, T: 'a, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator + 'a = Global, + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] A: Allocator + 'a = Global, > { /// Index of tail to preserve pub(super) tail_start: usize, @@ -58,7 +58,7 @@ impl<'a, T, A: Allocator> Drain<'a, T, A> { } /// Returns a reference to the underlying allocator. - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] #[must_use] #[inline] pub fn allocator(&self) -> &A { diff --git a/library/alloc/src/vec/extract_if.rs b/library/alloc/src/vec/extract_if.rs index 366ee1c4e0cbf..4396798067942 100644 --- a/library/alloc/src/vec/extract_if.rs +++ b/library/alloc/src/vec/extract_if.rs @@ -22,7 +22,7 @@ pub struct ExtractIf< 'a, T, F, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] A: Allocator = Global, > { vec: &'a mut Vec, /// The index of the item that will be inspected by the next call to `next`. @@ -51,7 +51,7 @@ impl<'a, T, F, A: Allocator> ExtractIf<'a, T, F, A> { } /// Returns a reference to the underlying allocator. - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] #[inline] pub fn allocator(&self) -> &A { self.vec.allocator() diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs index d99d2126810ba..48b1db0d5bf49 100644 --- a/library/alloc/src/vec/into_iter.rs +++ b/library/alloc/src/vec/into_iter.rs @@ -46,7 +46,7 @@ macro non_null { #[rustc_insignificant_dtor] pub struct IntoIter< T, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] A: Allocator = Global, > { pub(super) buf: NonNull, pub(super) phantom: PhantomData, @@ -112,7 +112,7 @@ impl IntoIter { } /// Returns a reference to the underlying allocator. - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] #[inline] pub fn allocator(&self) -> &A { &self.alloc diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 7a9ac8bae32e2..8bcad62226a0b 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -436,7 +436,7 @@ mod sve_retain; #[rustc_insignificant_dtor] #[doc(alias = "list")] #[doc(alias = "vector")] -pub struct Vec { +pub struct Vec { buf: RawVec, len: usize, } @@ -788,7 +788,7 @@ impl Vec { /// ``` #[cfg(not(no_global_oom_handling))] #[inline] - #[stable(feature = "vec_from_fn", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "vec_from_fn", since = "1.100.0")] pub fn from_fn(length: usize, f: F) -> Self where F: FnMut(usize) -> T, @@ -940,8 +940,6 @@ const impl Vec { /// # Examples /// /// ``` - /// #![feature(allocator_api)] - /// /// use std::alloc::System; /// /// let mut vec = Vec::with_capacity_in(10, System); @@ -968,7 +966,7 @@ const impl Vec { /// assert_eq!(vec_units.capacity(), usize::MAX); /// ``` #[inline] - #[unstable(feature = "allocator_api", issue = "32838")] + #[stable(feature = "allocator_api", since = "1.100.0")] pub fn with_capacity_in(capacity: usize, alloc: A) -> Self { Vec { buf: RawVec::with_capacity_in(capacity, alloc), len: 0 } } @@ -1055,14 +1053,13 @@ impl Vec { /// # Examples /// /// ``` - /// #![feature(allocator_api)] - /// /// use std::alloc::System; /// /// let vec: Vec = Vec::new_in(System); /// ``` #[inline] - #[unstable(feature = "allocator_api", issue = "32838")] + #[stable(feature = "allocator_api", since = "1.100.0")] + #[rustc_const_unstable(feature = "allocator_ext", issue = "163177")] pub const fn new_in(alloc: A) -> Self { Vec { buf: RawVec::new_in(alloc), len: 0 } } @@ -1079,7 +1076,7 @@ impl Vec { /// Returns an error if the capacity exceeds `isize::MAX` _bytes_, /// or if the allocator reports allocation failure. #[inline] - #[unstable(feature = "allocator_api", issue = "32838")] + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] // #[unstable(feature = "try_with_capacity", issue = "91913")] pub fn try_with_capacity_in(capacity: usize, alloc: A) -> Result { Ok(Vec { buf: RawVec::try_with_capacity_in(capacity, alloc)?, len: 0 }) @@ -1133,8 +1130,6 @@ impl Vec { /// # Examples /// /// ``` - /// #![feature(allocator_api)] - /// /// use std::alloc::System; /// /// use std::ptr; @@ -1162,8 +1157,6 @@ impl Vec { /// Using memory that was allocated elsewhere: /// /// ```rust - /// #![feature(allocator_api)] - /// /// use std::alloc::{AllocError, Allocator, Global, Layout}; /// /// fn main() { @@ -1185,8 +1178,8 @@ impl Vec { /// } /// ``` #[inline] - #[unstable(feature = "allocator_api", issue = "32838")] - #[rustc_const_unstable(feature = "allocator_api", issue = "32838")] + #[stable(feature = "allocator_api", since = "1.100.0")] + #[rustc_const_unstable(feature = "const_heap", issue = "79597")] pub const unsafe fn from_raw_parts_in( ptr: *mut T, length: usize, @@ -1251,8 +1244,6 @@ impl Vec { /// # Examples /// /// ``` - /// #![feature(allocator_api)] - /// /// use std::alloc::System; /// /// let mut v = Vec::with_capacity_in(3, System); @@ -1278,8 +1269,6 @@ impl Vec { /// Using memory that was allocated elsewhere: /// /// ```rust - /// #![feature(allocator_api)] - /// /// use std::alloc::{AllocError, Allocator, Global, Layout}; /// /// fn main() { @@ -1301,8 +1290,8 @@ impl Vec { /// } /// ``` #[inline] - #[unstable(feature = "allocator_api", issue = "32838")] - #[rustc_const_unstable(feature = "allocator_api", issue = "32838")] + #[stable(feature = "allocator_api", since = "1.100.0")] + #[rustc_const_unstable(feature = "const_heap", issue = "79597")] pub const unsafe fn from_parts_in( ptr: NonNull, length: usize, @@ -1335,8 +1324,6 @@ impl Vec { /// # Examples /// /// ``` - /// #![feature(allocator_api)] - /// /// use std::alloc::System; /// /// let mut v: Vec = Vec::new_in(System); @@ -1356,8 +1343,8 @@ impl Vec { /// assert_eq!(rebuilt, [4294967295, 0, 1]); /// ``` #[must_use = "losing the pointer will leak memory"] - #[unstable(feature = "allocator_api", issue = "32838")] - #[rustc_const_unstable(feature = "allocator_api", issue = "32838")] + #[stable(feature = "allocator_api", since = "1.100.0")] + #[rustc_const_unstable(feature = "const_heap", issue = "79597")] pub const fn into_raw_parts_with_allocator(self) -> (*mut T, usize, usize, A) { let mut me = ManuallyDrop::new(self); let len = me.len(); @@ -1386,8 +1373,6 @@ impl Vec { /// # Examples /// /// ``` - /// #![feature(allocator_api)] - /// /// use std::alloc::System; /// /// let mut v: Vec = Vec::new_in(System); @@ -1407,8 +1392,8 @@ impl Vec { /// assert_eq!(rebuilt, [4294967295, 0, 1]); /// ``` #[must_use = "losing the pointer will leak memory"] - #[unstable(feature = "allocator_api", issue = "32838")] - #[rustc_const_unstable(feature = "allocator_api", issue = "32838")] + #[stable(feature = "allocator_api", since = "1.100.0")] + #[rustc_const_unstable(feature = "const_heap", issue = "79597")] pub const fn into_parts_with_allocator(self) -> (NonNull, usize, usize, A) { let (ptr, len, capacity, alloc) = self.into_raw_parts_with_allocator(); // SAFETY: A `Vec` always has a non-null pointer. @@ -2126,7 +2111,7 @@ impl Vec { } /// Returns a reference to the underlying allocator. - #[unstable(feature = "allocator_api", issue = "32838")] + #[stable(feature = "allocator_api", since = "1.100.0")] #[rustc_const_unstable(feature = "const_heap", issue = "79597")] #[inline] pub const fn allocator(&self) -> &A { @@ -2433,12 +2418,11 @@ impl Vec { /// # Examples /// /// ``` - /// #![feature(vec_try_remove)] /// let mut v = vec![1, 2, 3]; /// assert_eq!(v.try_remove(0), Some(1)); /// assert_eq!(v.try_remove(2), None); /// ``` - #[unstable(feature = "vec_try_remove", issue = "146954")] + #[stable(feature = "vec_try_remove", since = "CURRENT_RUSTC_VERSION")] #[rustc_confusables("delete", "take", "remove")] pub fn try_remove(&mut self, index: usize) -> Option { let len = self.len(); @@ -3822,7 +3806,7 @@ pub fn from_elem(elem: T, n: usize) -> Vec { #[doc(hidden)] #[cfg(not(no_global_oom_handling))] -#[unstable(feature = "allocator_api", issue = "32838")] +#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] pub fn from_elem_in(elem: T, n: usize, alloc: A) -> Vec { ::from_elem(elem, n, alloc) } diff --git a/library/alloc/src/vec/partial_eq.rs b/library/alloc/src/vec/partial_eq.rs index 2d5a839cc8c30..aad307c0a0a10 100644 --- a/library/alloc/src/vec/partial_eq.rs +++ b/library/alloc/src/vec/partial_eq.rs @@ -33,11 +33,11 @@ __impl_slice_eq1! { [] Cow<'_, [T]>, &[U] where T: Clone, #[stable(feature = "ru #[cfg(not(no_global_oom_handling))] __impl_slice_eq1! { [] Cow<'_, [T]>, &mut [U] where T: Clone, #[stable(feature = "rust1", since = "1.0.0")] } #[cfg(not(no_global_oom_handling))] -__impl_slice_eq1! { [A: Allocator] Vec, Cow<'_, [U]> where U: Clone, #[stable(feature = "partialeq_cow_for_vec_and_slice", since = "CURRENT_RUSTC_VERSION")] } +__impl_slice_eq1! { [A: Allocator] Vec, Cow<'_, [U]> where U: Clone, #[stable(feature = "partialeq_cow_for_vec_and_slice", since = "1.100.0")] } #[cfg(not(no_global_oom_handling))] -__impl_slice_eq1! { [] &[T], Cow<'_, [U]> where U: Clone, #[stable(feature = "partialeq_cow_for_vec_and_slice", since = "CURRENT_RUSTC_VERSION")] } +__impl_slice_eq1! { [] &[T], Cow<'_, [U]> where U: Clone, #[stable(feature = "partialeq_cow_for_vec_and_slice", since = "1.100.0")] } #[cfg(not(no_global_oom_handling))] -__impl_slice_eq1! { [] &mut [T], Cow<'_, [U]> where U: Clone, #[stable(feature = "partialeq_cow_for_vec_and_slice", since = "CURRENT_RUSTC_VERSION")] } +__impl_slice_eq1! { [] &mut [T], Cow<'_, [U]> where U: Clone, #[stable(feature = "partialeq_cow_for_vec_and_slice", since = "1.100.0")] } __impl_slice_eq1! { const, [A: Allocator, const N: usize] Vec, [U; N], #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] #[stable(feature = "rust1", since = "1.0.0")] } __impl_slice_eq1! { const, [A: Allocator, const N: usize] Vec, &[U; N], #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] #[stable(feature = "rust1", since = "1.0.0")] } diff --git a/library/alloc/src/vec/peek_mut.rs b/library/alloc/src/vec/peek_mut.rs index 979bcaa1111d5..f84262352289a 100644 --- a/library/alloc/src/vec/peek_mut.rs +++ b/library/alloc/src/vec/peek_mut.rs @@ -15,7 +15,7 @@ use crate::fmt; pub struct PeekMut< 'a, T, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] A: Allocator = Global, > { vec: &'a mut Vec, } diff --git a/library/alloc/src/vec/splice.rs b/library/alloc/src/vec/splice.rs index 534db037c3bdc..103bc5e00e521 100644 --- a/library/alloc/src/vec/splice.rs +++ b/library/alloc/src/vec/splice.rs @@ -20,7 +20,7 @@ use crate::alloc::{Allocator, Global}; pub struct Splice< 'a, I: Iterator + 'a, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator + 'a = Global, + #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] A: Allocator + 'a = Global, > { pub(super) drain: Drain<'a, I::Item, A>, pub(super) replace_with: I, diff --git a/library/alloctests/lib.rs b/library/alloctests/lib.rs index eca8444812521..a5f46099173c0 100644 --- a/library/alloctests/lib.rs +++ b/library/alloctests/lib.rs @@ -1,5 +1,4 @@ #![cfg(test)] -#![allow(unused_attributes)] #![unstable(feature = "alloctests", issue = "none")] #![no_std] // Lints: @@ -14,7 +13,7 @@ // // Library features: // tidy-alphabetical-start -#![feature(allocator_api)] +#![feature(allocator_ext)] #![feature(array_into_iter_constructors)] #![feature(char_internals)] #![feature(const_alloc_error)] @@ -38,7 +37,6 @@ #![feature(maybe_uninit_uninit_array_transpose)] #![feature(ptr_alignment_type)] #![feature(ptr_cast_slice)] -#![feature(ptr_internals)] #![feature(rev_into_inner)] #![feature(sized_type_properties)] #![feature(slice_iter_mut_as_mut_slice)] @@ -52,7 +50,6 @@ #![feature(trusted_random_access)] #![feature(try_reserve_kind)] #![feature(try_trait_v2)] -#![feature(unwrap_infallible)] #![feature(wtf8_internals)] // tidy-alphabetical-end // diff --git a/library/alloctests/tests/c_str_alloc_error.rs b/library/alloctests/tests/c_str_alloc_error.rs index 669a783645baa..1e7d54d75b332 100644 --- a/library/alloctests/tests/c_str_alloc_error.rs +++ b/library/alloctests/tests/c_str_alloc_error.rs @@ -3,7 +3,7 @@ //! rust-lang/rust#70201 gave `::clone_into` a path that moved the //! target `CString`'s buffer out before growing a `Vec`; if that growth's allocation //! failed and unwound, the target was left without its nul terminator. This only -//! reproduces under Miri: in a normal build the `#[global_allocator]` below can't +//! reproduces under Miri: in a normal build, the `#[global_allocator]` below can't //! intercept the reallocation inside `CString::clone_into` (it lives in libstd, which //! library tests link with `-C prefer-dynamic`), so as a regular test it just checks //! the happy path. @@ -46,6 +46,8 @@ unsafe impl GlobalAlloc for OneShotFailingAlloc { } } +// This doesn't actually do anything outside Miri because of `-C prefer-dynamic` which effectively +// hard-codes the default allocator. #[global_allocator] static ALLOC: OneShotFailingAlloc = OneShotFailingAlloc; diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index b2a3e8f8f8e2a..5c8ee92388dd0 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -3,7 +3,7 @@ #![deny(implicit_provenance_casts)] #![deny(unsafe_op_in_unsafe_fn)] #![feature(alloc_io)] -#![feature(allocator_api)] +#![feature(allocator_ext)] #![feature(binary_heap_drain_sorted)] #![feature(binary_heap_into_iter_sorted)] #![feature(binary_heap_pop_if)] @@ -60,7 +60,6 @@ #![feature(unique_rc_arc)] #![feature(vec_deque_retain_range)] #![feature(vec_peek_mut)] -#![feature(vec_try_remove)] #![feature(write_all_vectored)] // tidy-alphabetical-end diff --git a/library/alloctests/tests/vec_deque_alloc_error.rs b/library/alloctests/tests/vec_deque_alloc_error.rs index 21a9118a05bd6..863af31e1cd1e 100644 --- a/library/alloctests/tests/vec_deque_alloc_error.rs +++ b/library/alloctests/tests/vec_deque_alloc_error.rs @@ -1,4 +1,4 @@ -#![feature(alloc_error_hook, allocator_api)] +#![feature(alloc_error_hook, allocator_ext)] use std::alloc::{AllocError, Allocator, Layout, System, set_alloc_error_hook}; use std::collections::VecDeque; diff --git a/library/core/Cargo.toml b/library/core/Cargo.toml index 3f5f9f454a99d..d8f4ec0528dee 100644 --- a/library/core/Cargo.toml +++ b/library/core/Cargo.toml @@ -36,6 +36,7 @@ check-cfg = [ # gate tests. 'cfg(target_has_reliable_f16)', 'cfg(target_has_reliable_f16_math)', + 'cfg(target_has_reliable_f16b)', 'cfg(target_has_reliable_f128)', 'cfg(target_has_reliable_f128_math)', # Prevents use of a static variable for providing platform specific RawOsError diff --git a/library/core/src/alloc/layout.rs b/library/core/src/alloc/layout.rs index edf266174bd4e..0ae297ad294e4 100644 --- a/library/core/src/alloc/layout.rs +++ b/library/core/src/alloc/layout.rs @@ -122,7 +122,7 @@ impl Layout { /// /// # Safety /// - /// This function is unsafe as it does not verify the preconditions from + /// The arguments passed must meet the preconditions from /// [`Layout::from_size_align`]. #[stable(feature = "alloc_layout", since = "1.28.0")] #[rustc_const_stable(feature = "const_alloc_layout_unchecked", since = "1.36.0")] diff --git a/library/core/src/alloc/mod.rs b/library/core/src/alloc/mod.rs index 6069c468f96e4..e30872c09c793 100644 --- a/library/core/src/alloc/mod.rs +++ b/library/core/src/alloc/mod.rs @@ -15,7 +15,7 @@ pub use self::layout::Layout; note = "Name does not follow std convention, use LayoutError", suggestion = "LayoutError" )] -#[allow(deprecated, deprecated_in_future)] +#[allow(deprecated)] pub use self::layout::LayoutErr; #[stable(feature = "alloc_layout_error", since = "1.50.0")] pub use self::layout::LayoutError; @@ -27,19 +27,15 @@ use crate::ptr::{self, NonNull}; /// that may be due to resource exhaustion or to /// something wrong when combining the given input arguments with this /// allocator. -#[unstable(feature = "allocator_api", issue = "32838")] +#[stable(feature = "allocator_api", since = "1.100.0")] #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub struct AllocError; -#[unstable( - feature = "allocator_api", - reason = "the precise API and guarantees it provides may be tweaked.", - issue = "32838" -)] +#[stable(feature = "allocator_api", since = "1.100.0")] impl Error for AllocError {} // (we need this for downstream impl of trait Error) -#[unstable(feature = "allocator_api", issue = "32838")] +#[stable(feature = "allocator_api", since = "1.100.0")] impl fmt::Display for AllocError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("memory allocation failed") @@ -148,7 +144,8 @@ impl fmt::Display for AllocError { /// /// Some of the methods require that a `layout` *fits* a memory block or vice versa. This means /// that the following conditions must hold: -/// * the memory block must be *currently allocated* with alignment of [`layout.align()`], and +/// * the memory block must be *currently allocated* by the allocator, +/// * [`layout.align()`] must be the same as the alignment of the layout used to allocate the block, and /// * [`layout.size()`] must fall in the range `min ..= max`, where: /// - `min` is the size of the layout used to allocate the block, and /// - `max` is the actual size returned from [`allocate`], [`allocate_zeroed`], @@ -196,7 +193,7 @@ impl fmt::Display for AllocError { // and make sure they cannot be triggered before relaxing this: // https://rust.tf/156490 // https://rust.tf/159982 -#[unstable(feature = "allocator_api", issue = "32838")] +#[stable(feature = "allocator_api", since = "1.100.0")] #[rustc_const_unstable(feature = "const_heap", issue = "79597")] pub const unsafe trait Allocator { /// Attempts to allocate a block of memory. @@ -231,6 +228,7 @@ pub const unsafe trait Allocator { /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar. /// /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html + #[stable(feature = "allocator_api", since = "1.100.0")] fn allocate(&self, layout: Layout) -> Result, AllocError>; /// Behaves like `allocate`, but also ensures that the returned memory is zero-initialized. @@ -248,6 +246,7 @@ pub const unsafe trait Allocator { /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar. /// /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html + #[stable(feature = "allocator_api", since = "1.100.0")] fn allocate_zeroed(&self, layout: Layout) -> Result, AllocError> { let ptr = self.allocate(layout)?; // SAFETY: `alloc` returns a valid memory block @@ -271,6 +270,7 @@ pub const unsafe trait Allocator { /// /// [*currently allocated*]: #currently-allocated-memory /// [*fit*]: #memory-fitting + #[stable(feature = "allocator_api", since = "1.100.0")] unsafe fn deallocate(&self, ptr: NonNull, layout: Layout); /// Attempts to extend the memory block. @@ -312,6 +312,7 @@ pub const unsafe trait Allocator { /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar. /// /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html + #[stable(feature = "allocator_api", since = "1.100.0")] unsafe fn grow( &self, ptr: NonNull, @@ -372,6 +373,7 @@ pub const unsafe trait Allocator { /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar. /// /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html + #[stable(feature = "allocator_api", since = "1.100.0")] unsafe fn grow_zeroed( &self, ptr: NonNull, @@ -438,6 +440,7 @@ pub const unsafe trait Allocator { /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar. /// /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html + #[stable(feature = "allocator_api", since = "1.100.0")] unsafe fn shrink( &self, ptr: NonNull, @@ -521,7 +524,7 @@ pub const unsafe trait Allocator { /// [`std::thread::park`]: ../../std/thread/fn.park.html /// [`std::thread::Thread`]: ../../std/thread/struct.Thread.html /// [`unpark`]: ../../std/thread/struct.Thread.html#method.unpark -#[unstable(feature = "allocator_api", issue = "32838")] +#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] #[expect(multiple_supertrait_upcastable)] pub unsafe trait GlobalAllocator: StaticAllocator + Sync + 'static {} @@ -537,7 +540,7 @@ pub unsafe trait GlobalAllocator: StaticAllocator + Sync + 'static {} /// It must also be the case that types which are `AllocatorClone` are either explicitly not /// copyable (such as by containing a `!Copy` field) or that copying them also respects allocator /// equivalence as if it had been a clone. -#[unstable(feature = "allocator_api", issue = "32838")] +#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] pub unsafe trait AllocatorClone: Allocator + Clone {} /// Marks that an allocator and its supertypes will never invalidate currently allocated @@ -568,10 +571,10 @@ pub unsafe trait AllocatorClone: Allocator + Clone {} /// /// [`Pin`]: ../../core/pin/struct.Pin.html /// [unsound]: https://github.com/rust-lang/rust/issues/157089 -#[unstable(feature = "allocator_api", issue = "32838")] +#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] pub unsafe trait StaticAllocator: Allocator {} -#[unstable(feature = "allocator_api", issue = "32838")] +#[stable(feature = "allocator_api", since = "1.100.0")] #[rustc_const_unstable(feature = "const_heap", issue = "79597")] const unsafe impl Allocator for &A where @@ -627,7 +630,7 @@ where } } -#[unstable(feature = "allocator_api", issue = "32838")] +#[stable(feature = "allocator_api", since = "1.100.0")] #[rustc_const_unstable(feature = "const_heap", issue = "79597")] const unsafe impl Allocator for &mut A where @@ -683,13 +686,100 @@ where } } -#[unstable(feature = "allocator_api", issue = "32838")] +#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] +#[rustc_const_unstable(feature = "const_heap", issue = "79597")] +#[unstable_feature_bound(allocator_ext)] +const unsafe impl